Merge 0fdacbd5bf7dcaf2886bfb46ebbd942a094b53ce into 3da85a5078019bcf9f3987f29098706a3fc3d4ae

This commit is contained in:
nico.benaz 2025-06-13 08:59:51 +02:00 committed by GitHub
commit 64db067f35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 1170 additions and 1038 deletions

View File

@ -135,6 +135,10 @@ export const BoundedNumericTextField = ({
<IconButton <IconButton
size="small" size="small"
onClick={() => changeValueWithIncDecButton(1)} onClick={() => changeValueWithIncDecButton(1)}
sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
}}
> >
<AddIcon <AddIcon
sx={{ sx={{
@ -145,6 +149,10 @@ export const BoundedNumericTextField = ({
<IconButton <IconButton
size="small" size="small"
onClick={() => changeValueWithIncDecButton(-1)} onClick={() => changeValueWithIncDecButton(-1)}
sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
}}
> >
<RemoveIcon <RemoveIcon
sx={{ sx={{

View File

@ -0,0 +1,12 @@
import { Slide } from '@mui/material';
import { TransitionProps } from '@mui/material/transitions';
import { forwardRef, ReactElement, Ref } from 'react';
export const TransitionUp = forwardRef(function Transition(
props: TransitionProps & {
children: ReactElement;
},
ref: Ref<unknown>
) {
return <Slide direction="up" ref={ref} {...props} />;
});

View File

@ -108,13 +108,10 @@ export const AppsDevModeNavBar = () => {
<Tabs <Tabs
orientation="vertical" orientation="vertical"
ref={tabsRef} ref={tabsRef}
aria-label={t('core:basic_tabs_example', {
postProcess: 'capitalizeFirstChar',
})}
variant="scrollable" // Make tabs scrollable variant="scrollable" // Make tabs scrollable
scrollButtons={true} scrollButtons={true}
sx={{ sx={{
'& .MuiTabs-indicator': { '&.MuiTabs-indicator': {
backgroundColor: theme.palette.text.primary, backgroundColor: theme.palette.text.primary,
}, },
maxHeight: `275px`, // Ensure the tabs container fits within the available space maxHeight: `275px`, // Ensure the tabs container fits within the available space

View File

@ -190,11 +190,10 @@ export const AppsNavBarDesktop = ({ disableBack }) => {
<Tabs <Tabs
orientation="vertical" orientation="vertical"
ref={tabsRef} ref={tabsRef}
aria-label={t('core:basic_tabs_example')}
variant="scrollable" // Make tabs scrollable variant="scrollable" // Make tabs scrollable
scrollButtons={true} scrollButtons={true}
sx={{ sx={{
'& .MuiTabs-indicator': { '&.MuiTabs-indicator': {
backgroundColor: theme.palette.background.default, backgroundColor: theme.palette.background.default,
}, },
maxHeight: `275px`, // Ensure the tabs container fits within the available space maxHeight: `275px`, // Ensure the tabs container fits within the available space

View File

@ -1,4 +1,5 @@
import React, { import {
SyntheticEvent,
useCallback, useCallback,
useContext, useContext,
useEffect, useEffect,
@ -265,7 +266,7 @@ export const AppsPrivate = ({ myName, myAddress }) => {
} }
}; };
const handleChange = (event: React.SyntheticEvent, newValue: number) => { const handleChange = (event: SyntheticEvent, newValue: number) => {
setValueTabPrivateApp(newValue); setValueTabPrivateApp(newValue);
}; };
@ -353,13 +354,10 @@ export const AppsPrivate = ({ myName, myAddress }) => {
<Tabs <Tabs
value={valueTabPrivateApp} value={valueTabPrivateApp}
onChange={handleChange} onChange={handleChange}
aria-label={t('core:basic_tabs_example', {
postProcess: 'capitalizeFirstChar',
})}
variant={'fullWidth'} variant={'fullWidth'}
scrollButtons="auto" scrollButtons="auto"
sx={{ sx={{
'& .MuiTabs-indicator': { '&.MuiTabs-indicator': {
backgroundColor: theme.palette.background.default, backgroundColor: theme.palette.background.default,
}, },
}} }}

View File

@ -546,7 +546,7 @@ export const ChatDirect = ({
}, []); }, []);
return ( return (
<div <Box
style={{ style={{
background: theme.palette.background.default, background: theme.palette.background.default,
display: 'flex', display: 'flex',
@ -615,7 +615,7 @@ export const ChatDirect = ({
tempChatReferences={tempChatReferences} tempChatReferences={tempChatReferences}
/> />
<div <Box
style={{ style={{
backgroundColor: theme.palette.background.default, backgroundColor: theme.palette.background.default,
bottom: isFocusedParent ? '0px' : 'unset', bottom: isFocusedParent ? '0px' : 'unset',
@ -632,7 +632,7 @@ export const ChatDirect = ({
zIndex: isFocusedParent ? 5 : 'unset', zIndex: isFocusedParent ? 5 : 'unset',
}} }}
> >
<div <Box
style={{ style={{
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@ -720,7 +720,7 @@ export const ChatDirect = ({
</Typography> </Typography>
</Box> </Box>
)} )}
</div> </Box>
<Box <Box
sx={{ sx={{
@ -766,7 +766,7 @@ export const ChatDirect = ({
{` Send`} {` Send`}
</CustomButton> </CustomButton>
</Box> </Box>
</div> </Box>
<LoadingSnackbar <LoadingSnackbar
open={isLoading} open={isLoading}
@ -783,6 +783,6 @@ export const ChatDirect = ({
info={infoSnack} info={infoSnack}
setInfo={setInfoSnack} setInfo={setInfoSnack}
/> />
</div> </Box>
); );
}; };

View File

@ -206,7 +206,7 @@ export const ChatList = ({
width: '100%', width: '100%',
}} }}
> >
<div <Box
style={{ style={{
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@ -215,9 +215,8 @@ export const ChatList = ({
width: '100%', width: '100%',
}} }}
> >
<div <Box
ref={parentRef} ref={parentRef}
className="List"
style={{ style={{
display: 'flex', display: 'flex',
flexGrow: 1, flexGrow: 1,
@ -226,14 +225,14 @@ export const ChatList = ({
position: 'relative', position: 'relative',
}} }}
> >
<div <Box
style={{ sx={{
height: rowVirtualizer.getTotalSize(), height: rowVirtualizer.getTotalSize(),
width: '100%', width: '100%',
}} }}
> >
<div <Box
style={{ sx={{
left: 0, left: 0,
position: 'absolute', position: 'absolute',
top: 0, top: 0,
@ -325,9 +324,9 @@ export const ChatList = ({
// Render fallback if message is null // Render fallback if message is null
if (!message) { if (!message) {
return ( return (
<div <Box
key={virtualRow.index} key={virtualRow.index}
style={{ sx={{
alignItems: 'center', alignItems: 'center',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@ -345,16 +344,16 @@ export const ChatList = ({
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
</Typography> </Typography>
</div> </Box>
); );
} }
return ( return (
<div <Box
data-index={virtualRow.index} //needed for dynamic row height measurement data-index={virtualRow.index} //needed for dynamic row height measurement
ref={rowVirtualizer.measureElement} //measure dynamic row height ref={rowVirtualizer.measureElement} //measure dynamic row height
key={message.signature} key={message.signature}
style={{ sx={{
alignItems: 'center', alignItems: 'center',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@ -395,17 +394,17 @@ export const ChatList = ({
scrollToItem={goToMessage} scrollToItem={goToMessage}
/> />
</ErrorBoundary> </ErrorBoundary>
</div> </Box>
); );
})} })}
</div> </Box>
</div> </Box>
</div> </Box>
{showScrollButton && ( {showScrollButton && (
<Button <Button
onClick={() => scrollToBottom()} onClick={() => scrollToBottom()}
style={{ sx={{
backgroundColor: theme.palette.other.unread, backgroundColor: theme.palette.other.unread,
border: 'none', border: 'none',
borderRadius: '20px', borderRadius: '20px',
@ -428,15 +427,15 @@ export const ChatList = ({
{showScrollDownButton && !showScrollButton && ( {showScrollDownButton && !showScrollButton && (
<Button <Button
onClick={() => scrollToBottom()} onClick={() => scrollToBottom()}
style={{ sx={{
backgroundColor: theme.palette.background.paper, backgroundColor: theme.palette.background.paper,
outline: `1px solid ${theme.palette.primary.light}`,
border: 'none', border: 'none',
borderRadius: '20px', borderRadius: '20px',
bottom: 20, bottom: 20,
color: theme.palette.text.primary, color: theme.palette.text.primary,
cursor: 'pointer', cursor: 'pointer',
fontSize: '16px', fontSize: '16px',
outline: `1px solid ${theme.palette.primary.light}`,
padding: '10px 20px', padding: '10px 20px',
position: 'absolute', position: 'absolute',
right: 20, right: 20,
@ -449,7 +448,7 @@ export const ChatList = ({
})} })}
</Button> </Button>
)} )}
</div> </Box>
{enableMentions && (hasSecretKey || isPrivate === false) && ( {enableMentions && (hasSecretKey || isPrivate === false) && (
<ChatOptions <ChatOptions

View File

@ -786,6 +786,7 @@ const ShowMessage = ({ message, goToMessage, messages }) => {
<Box <Box
sx={{ sx={{
alignItems: 'center', alignItems: 'center',
background: theme.palette.background.surface,
display: 'flex', display: 'flex',
justifyContent: 'space-between', justifyContent: 'space-between',
width: '100%', width: '100%',

View File

@ -213,7 +213,7 @@ export const CreateCommonSecret = ({
return ( return (
<Box <Box
sx={{ sx={{
background: theme.palette.background.default, background: theme.palette.background.paper,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: '25px', gap: '25px',

View File

@ -228,8 +228,8 @@ export const MessageItemComponent = ({
isLast={isLast} isLast={isLast}
onSeen={onSeenFunc} onSeen={onSeenFunc}
> >
<div <Box
style={{ sx={{
backgroundColor: theme.palette.background.paper, backgroundColor: theme.palette.background.paper,
borderRadius: '7px', borderRadius: '7px',
display: 'flex', display: 'flex',
@ -664,7 +664,7 @@ export const MessageItemComponent = ({
</Box> </Box>
</Box> </Box>
</Box> </Box>
</div> </Box>
</MessageWragger> </MessageWragger>
</> </>
); );

View File

@ -185,4 +185,4 @@ export const ContextMenu = ({ children, groupId, getUserSettings }) => {
</CustomStyledMenu> </CustomStyledMenu>
</div> </div>
); );
}; }; // TODO translate

View File

@ -269,11 +269,12 @@ export function ImageViewer({ src = null, alt = '' }) {
<IconButton <IconButton
onClick={handleCloseFullscreen} onClick={handleCloseFullscreen}
sx={{ sx={{
position: 'absolute', bgcolor: theme.palette.background.default,
top: 8,
right: 8,
zIndex: 10,
color: theme.palette.text.primary, color: theme.palette.text.primary,
position: 'absolute',
right: 8,
top: 8,
zIndex: 10,
}} }}
> >
<CloseIcon /> <CloseIcon />

View File

@ -57,27 +57,27 @@ const ControlsContainer = styled(Box)`
`; `;
interface VideoPlayerProps { interface VideoPlayerProps {
src?: string;
poster?: string;
name?: string;
identifier?: string;
service?: string;
autoplay?: boolean; autoplay?: boolean;
from?: string | null;
customStyle?: any; customStyle?: any;
from?: string | null;
identifier?: string;
name?: string;
poster?: string;
service?: string;
src?: string | null;
user?: string; user?: string;
} }
// TODO translate and theme (optional) // TODO translate and theme (optional)
export const VideoPlayer: FC<VideoPlayerProps> = ({ export const VideoPlayer: FC<VideoPlayerProps> = ({
poster,
name,
identifier,
service,
autoplay = true, autoplay = true,
from = null,
customStyle = {}, customStyle = {},
from = null,
identifier,
name,
node, node,
poster,
service,
}) => { }) => {
const keyIdentifier = useMemo(() => { const keyIdentifier = useMemo(() => {
if (name && identifier && service) { if (name && identifier && service) {
@ -549,22 +549,22 @@ export const VideoPlayer: FC<VideoPlayerProps> = ({
)} )}
{((!src && !isLoading) || !startPlay) && ( {((!src && !isLoading) || !startPlay) && (
<Box <Box
position="absolute" alignItems="center"
top={0} bgcolor="rgba(0, 0, 0, 0.6)"
left={0}
right={0}
bottom={0} bottom={0}
display="flex" display="flex"
justifyContent="center" justifyContent="center"
alignItems="center" left={0}
zIndex={500}
bgcolor="rgba(0, 0, 0, 0.6)"
onClick={() => { onClick={() => {
togglePlay(); togglePlay();
}} }}
position="absolute"
right={0}
sx={{ sx={{
cursor: 'pointer', cursor: 'pointer',
}} }}
top={0}
zIndex={500}
> >
<PlayArrow <PlayArrow
sx={{ sx={{
@ -587,7 +587,9 @@ export const VideoPlayer: FC<VideoPlayerProps> = ({
<VideoElement <VideoElement
id={identifier} id={identifier}
ref={videoRef} ref={videoRef}
src={!startPlay ? '' : resourceStatus?.status === 'READY' ? src : ''} src={
!startPlay ? null : resourceStatus?.status === 'READY' ? src : null
}
poster={!startPlay ? poster : ''} poster={!startPlay ? poster : ''}
onTimeUpdate={updateProgress} onTimeUpdate={updateProgress}
autoPlay={autoplay} autoPlay={autoplay}

View File

@ -1,8 +1,5 @@
import { import {
forwardRef,
Fragment, Fragment,
ReactElement,
Ref,
SyntheticEvent, SyntheticEvent,
useContext, useContext,
useEffect, useEffect,
@ -17,8 +14,6 @@ import Typography from '@mui/material/Typography';
import CloseIcon from '@mui/icons-material/Close'; import CloseIcon from '@mui/icons-material/Close';
import ExpandLess from '@mui/icons-material/ExpandLess'; import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore'; import ExpandMore from '@mui/icons-material/ExpandMore';
import Slide from '@mui/material/Slide';
import { TransitionProps } from '@mui/material/transitions';
import { import {
Box, Box,
Collapse, Collapse,
@ -40,7 +35,7 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSetAtom } from 'jotai'; import { useSetAtom } from 'jotai';
import { txListAtom } from '../../atoms/global'; import { txListAtom } from '../../atoms/global';
import { ErrorRounded } from '@mui/icons-material'; import { TransitionUp } from '../../common/Transitions.tsx';
export const Label = styled('label')` export const Label = styled('label')`
display: block; display: block;
@ -50,15 +45,6 @@ export const Label = styled('label')`
margin-bottom: 4px; margin-bottom: 4px;
`; `;
const Transition = forwardRef(function Transition(
props: TransitionProps & {
children: ReactElement;
},
ref: Ref<unknown>
) {
return <Slide direction="up" ref={ref} {...props} />;
});
export const AddGroup = ({ address, open, setOpen }) => { export const AddGroup = ({ address, open, setOpen }) => {
const { show } = useContext(QORTAL_APP_CONTEXT); const { show } = useContext(QORTAL_APP_CONTEXT);
const setTxList = useSetAtom(txListAtom); const setTxList = useSetAtom(txListAtom);
@ -229,13 +215,12 @@ export const AddGroup = ({ address, open, setOpen }) => {
open={open} open={open}
onClose={handleClose} onClose={handleClose}
slots={{ slots={{
transition: Transition, transition: TransitionUp,
}} }}
> >
<AppBar <AppBar
sx={{ sx={{
position: 'relative', position: 'relative',
bgcolor: theme.palette.background.default,
}} }}
> >
<Toolbar> <Toolbar>
@ -252,6 +237,10 @@ export const AddGroup = ({ address, open, setOpen }) => {
color="inherit" color="inherit"
edge="start" edge="start"
onClick={handleClose} onClick={handleClose}
sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
}}
> >
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
@ -274,14 +263,11 @@ export const AddGroup = ({ address, open, setOpen }) => {
<Tabs <Tabs
value={value} value={value}
onChange={handleChange} onChange={handleChange}
aria-label={t('core:basic_tabs_example', {
postProcess: 'capitalizeFirstChar',
})}
variant={'fullWidth'} variant={'fullWidth'}
scrollButtons="auto" scrollButtons="auto"
allowScrollButtonsMobile allowScrollButtonsMobile
sx={{ sx={{
'& .MuiTabs-indicator': { '&.MuiTabs-indicator': {
backgroundColor: theme.palette.background.default, backgroundColor: theme.palette.background.default,
}, },
}} }}
@ -421,10 +407,10 @@ export const AddGroup = ({ address, open, setOpen }) => {
<Box <Box
sx={{ sx={{
display: 'flex',
gap: '15px',
alignItems: 'center', alignItems: 'center',
cursor: 'pointer', cursor: 'pointer',
display: 'flex',
gap: '15px',
}} }}
onClick={() => setOpenAdvance((prev) => !prev)} onClick={() => setOpenAdvance((prev) => !prev)}
> >

View File

@ -406,10 +406,11 @@ export const BlockedUsersModal = () => {
})} })}
onClick={onCancel} onClick={onCancel}
sx={{ sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
position: 'absolute', position: 'absolute',
right: 8, right: 8,
top: 8, top: 8,
color: theme.palette.text.primary,
}} }}
> >
<CloseIcon /> <CloseIcon />

View File

@ -1632,10 +1632,10 @@ export const Group = ({
const renderDirects = () => { const renderDirects = () => {
return ( return (
<div <Box
style={{ style={{
alignItems: 'flex-start', alignItems: 'flex-start',
background: theme.palette.background.default, background: theme.palette.background.surface,
borderRadius: '0px 15px 15px 0px', borderRadius: '0px 15px 15px 0px',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@ -1718,8 +1718,8 @@ export const Group = ({
</ButtonBase> </ButtonBase>
</Box> </Box>
<div <Box
style={{ sx={{
alignItems: 'flex-start', alignItems: 'flex-start',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@ -1763,7 +1763,7 @@ export const Group = ({
sx={{ sx={{
background: background:
direct?.address === selectedDirect?.address && direct?.address === selectedDirect?.address &&
theme.palette.background.default, theme.palette.background.surface,
borderRadius: '2px', borderRadius: '2px',
cursor: 'pointer', cursor: 'pointer',
display: 'flex', display: 'flex',
@ -1782,7 +1782,7 @@ export const Group = ({
<ListItemAvatar> <ListItemAvatar>
<Avatar <Avatar
sx={{ sx={{
background: theme.palette.background.default, background: theme.palette.background.surface,
color: theme.palette.text.primary, color: theme.palette.text.primary,
}} }}
alt={direct?.name || direct?.address} alt={direct?.name || direct?.address}
@ -1803,22 +1803,24 @@ export const Group = ({
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
}) })
} }
primaryTypographyProps={{ slotProps={{
style: { primary: {
color: style: {
direct?.address === selectedDirect?.address && color:
theme.palette.text.primary, direct?.address === selectedDirect?.address &&
textWrap: 'wrap', theme.palette.text.primary,
overflow: 'hidden', textWrap: 'wrap',
fontSize: '16px', overflow: 'hidden',
fontSize: '16px',
},
}, },
}} // Change the color of the primary text secondary: {
secondaryTypographyProps={{ style: {
style: { color:
color: direct?.address === selectedDirect?.address &&
direct?.address === selectedDirect?.address && theme.palette.text.primary,
theme.palette.text.primary, fontSize: '12px',
fontSize: '12px', },
}, },
}} }}
sx={{ sx={{
@ -1827,6 +1829,7 @@ export const Group = ({
fontSize: '16px', fontSize: '16px',
}} }}
/> />
{direct?.sender !== myAddress && {direct?.sender !== myAddress &&
direct?.timestamp && direct?.timestamp &&
((!timestampEnterData[direct?.address] && ((!timestampEnterData[direct?.address] &&
@ -1844,10 +1847,10 @@ export const Group = ({
</ListItem> </ListItem>
</List> </List>
))} ))}
</div> </Box>
<div <Box
style={{ sx={{
display: 'flex', display: 'flex',
width: '100%', width: '100%',
gap: '10px', gap: '10px',
@ -1871,6 +1874,7 @@ export const Group = ({
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
</CustomButton> </CustomButton>
{!isRunningPublicNode && ( {!isRunningPublicNode && (
<CustomButton <CustomButton
onClick={() => { onClick={() => {
@ -1888,8 +1892,8 @@ export const Group = ({
/> />
</CustomButton> </CustomButton>
)} )}
</div> </Box>
</div> </Box>
); );
}; };
@ -1936,7 +1940,7 @@ export const Group = ({
setInfo={setInfoSnack} setInfo={setInfoSnack}
/> />
<div // TODO use Box <Box
style={{ style={{
alignItems: 'flex-start', alignItems: 'flex-start',
display: 'flex', display: 'flex',
@ -2004,7 +2008,7 @@ export const Group = ({
<> <>
<Box <Box
sx={{ sx={{
background: theme.palette.background.default, background: theme.palette.background.surface,
bottom: !(desktopViewMode === 'chat') ? 'unset' : '0px', bottom: !(desktopViewMode === 'chat') ? 'unset' : '0px',
left: !(desktopViewMode === 'chat') ? '-100000px' : '0px', left: !(desktopViewMode === 'chat') ? '-100000px' : '0px',
opacity: !(desktopViewMode === 'chat') ? 0 : 1, opacity: !(desktopViewMode === 'chat') ? 0 : 1,
@ -2458,7 +2462,7 @@ export const Group = ({
}} }}
/> />
<WalletsAppWrapper /> <WalletsAppWrapper />
</div> </Box>
</> </>
); );
}; };

View File

@ -173,6 +173,10 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
aria-label={t('core:comment_other', { aria-label={t('core:comment_other', {
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
}}
> >
<GroupAddIcon <GroupAddIcon
sx={{ sx={{

View File

@ -256,6 +256,10 @@ export const GroupJoinRequests = ({
aria-label={t('core:comment_other', { aria-label={t('core:comment_other', {
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
}}
> >
<GroupAddIcon <GroupAddIcon
sx={{ sx={{

View File

@ -59,8 +59,8 @@ export const GroupList = ({
const [isRunningPublicNode] = useAtom(isRunningPublicNodeAtom); const [isRunningPublicNode] = useAtom(isRunningPublicNodeAtom);
return ( return (
<div <Box
style={{ sx={{
alignItems: 'flex-start', alignItems: 'flex-start',
background: theme.palette.background.surface, background: theme.palette.background.surface,
borderRadius: '0px 15px 15px 0px', borderRadius: '0px 15px 15px 0px',
@ -145,8 +145,8 @@ export const GroupList = ({
</ButtonBase> </ButtonBase>
</Box> </Box>
<div <Box
style={{ sx={{
alignItems: 'flex-start', alignItems: 'flex-start',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@ -176,10 +176,10 @@ export const GroupList = ({
/> />
))} ))}
</List> </List>
</div> </Box>
<div <Box
style={{ sx={{
display: 'flex', display: 'flex',
gap: '10px', gap: '10px',
justifyContent: 'center', justifyContent: 'center',
@ -195,7 +195,7 @@ export const GroupList = ({
> >
<AddCircleOutlineIcon <AddCircleOutlineIcon
sx={{ sx={{
color: theme.palette.text.primary, color: theme.palette.text.secondary,
}} }}
/> />
{t('group:group.group', { postProcess: 'capitalizeFirstChar' })} {t('group:group.group', { postProcess: 'capitalizeFirstChar' })}
@ -213,14 +213,14 @@ export const GroupList = ({
> >
<PersonOffIcon <PersonOffIcon
sx={{ sx={{
color: theme.palette.text.primary, color: theme.palette.text.secondary,
}} }}
/> />
</CustomButton> </CustomButton>
)} )}
</> </>
</div> </Box>
</div> </Box>
); );
}; };
@ -294,20 +294,22 @@ const GroupItem = React.memo(
? 'no messages' ? 'no messages'
: `last message: ${formatEmailDate(group?.timestamp)}` : `last message: ${formatEmailDate(group?.timestamp)}`
} }
primaryTypographyProps={{ slotProps={{
style: { primary: {
color: style: {
group?.groupId === selectedGroup?.groupId && color:
theme.palette.text.primary, group?.groupId === selectedGroup?.groupId &&
fontSize: '16px', theme.palette.text.primary,
fontSize: '16px',
},
}, },
}} secondary: {
secondaryTypographyProps={{ style: {
style: { color:
color: group?.groupId === selectedGroup?.groupId &&
group?.groupId === selectedGroup?.groupId && theme.palette.text.primary,
theme.palette.text.primary, fontSize: '12px',
fontSize: '12px', },
}, },
}} }}
sx={{ sx={{

View File

@ -5,7 +5,7 @@ import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText'; import ListItemText from '@mui/material/ListItemText';
import IconButton from '@mui/material/IconButton'; import IconButton from '@mui/material/IconButton';
import { executeEvent } from '../../utils/events'; import { executeEvent } from '../../utils/events';
import { Box, Typography } from '@mui/material'; import { Box, Typography, useTheme } from '@mui/material';
import { Spacer } from '../../common/Spacer'; import { Spacer } from '../../common/Spacer';
import { CustomLoader } from '../../common/CustomLoader'; import { CustomLoader } from '../../common/CustomLoader';
import VisibilityIcon from '@mui/icons-material/Visibility'; import VisibilityIcon from '@mui/icons-material/Visibility';
@ -14,6 +14,7 @@ import { useTranslation } from 'react-i18next';
export const ListOfThreadPostsWatched = () => { export const ListOfThreadPostsWatched = () => {
const [posts, setPosts] = useState([]); const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const theme = useTheme();
const { t } = useTranslation([ const { t } = useTranslation([
'auth', 'auth',
'core', 'core',
@ -175,6 +176,10 @@ export const ListOfThreadPostsWatched = () => {
aria-label={t('core:comment_other', { aria-label={t('core:comment_other', {
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
}}
> >
<VisibilityIcon <VisibilityIcon
sx={{ sx={{

View File

@ -229,6 +229,10 @@ export const ManageMembers = ({
color="inherit" color="inherit"
edge="start" edge="start"
onClick={handleClose} onClick={handleClose}
sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
}}
> >
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
@ -247,14 +251,11 @@ export const ManageMembers = ({
<Tabs <Tabs
value={value} value={value}
onChange={handleChange} onChange={handleChange}
aria-label={t('core:basic_tabs_example', {
postProcess: 'capitalizeFirstChar',
})}
variant="scrollable" // Make tabs scrollable variant="scrollable" // Make tabs scrollable
scrollButtons="auto" // Show scroll buttons automatically scrollButtons="auto" // Show scroll buttons automatically
allowScrollButtonsMobile // Show scroll buttons on mobile as well allowScrollButtonsMobile // Show scroll buttons on mobile as well
sx={{ sx={{
'& .MuiTabs-indicator': { '&.MuiTabs-indicator': {
backgroundColor: theme.palette.background.default, backgroundColor: theme.palette.background.default,
}, },
maxWidth: '100%', // Ensure the tabs container fits within the available space maxWidth: '100%', // Ensure the tabs container fits within the available space

View File

@ -40,6 +40,7 @@ import { walletVersion } from '../../background/background.ts';
import Base58 from '../../encryption/Base58.ts'; import Base58 from '../../encryption/Base58.ts';
import { QORTAL_APP_CONTEXT } from '../../App'; import { QORTAL_APP_CONTEXT } from '../../App';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { TransitionUp } from '../../common/Transitions.tsx';
const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ const LocalNodeSwitch = styled(Switch)(({ theme }) => ({
padding: 8, padding: 8,
@ -74,15 +75,6 @@ const LocalNodeSwitch = styled(Switch)(({ theme }) => ({
}, },
})); }));
const Transition = forwardRef(function Transition(
props: TransitionProps & {
children: ReactElement;
},
ref: Ref<unknown>
) {
return <Slide direction="up" ref={ref} {...props} />;
});
export const Settings = ({ open, setOpen, rawWallet }) => { export const Settings = ({ open, setOpen, rawWallet }) => {
const [checked, setChecked] = useState(false); const [checked, setChecked] = useState(false);
const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom);
@ -163,7 +155,7 @@ export const Settings = ({ open, setOpen, rawWallet }) => {
open={open} open={open}
onClose={handleClose} onClose={handleClose}
slots={{ slots={{
transition: Transition, transition: TransitionUp,
}} }}
> >
<AppBar sx={{ position: 'relative' }}> <AppBar sx={{ position: 'relative' }}>
@ -181,6 +173,10 @@ export const Settings = ({ open, setOpen, rawWallet }) => {
aria-label={t('core:action.close', { aria-label={t('core:action.close', {
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
}}
> >
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>

View File

@ -30,25 +30,18 @@ const LanguageSelector = () => {
return ( return (
<Box ref={selectorRef}> <Box ref={selectorRef}>
{!showSelect && ( {!showSelect && (
<Tooltip <Button
key={currentLang} onClick={() => setShowSelect(true)}
title={t('core:action.change_language', { style={{
fontSize: '1.3rem',
}}
aria-label={t('core:current_language', {
language: name,
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
> >
<Button {flag}
onClick={() => setShowSelect(true)} </Button>
style={{
fontSize: '1.3rem',
}}
aria-label={t('core:current_language', {
language: name,
postProcess: 'capitalizeFirstChar',
})}
>
{flag}
</Button>
</Tooltip>
)} )}
{showSelect && ( {showSelect && (

View File

@ -4,18 +4,29 @@ import {
Box, Box,
Button, Button,
Card, Card,
Container,
Dialog, Dialog,
DialogActions, DialogActions,
DialogContent, DialogContent,
DialogTitle, DialogTitle,
Divider, Divider,
IconButton, IconButton,
Paper,
Snackbar, Snackbar,
Tab,
Tabs,
Toolbar, Toolbar,
Typography, Typography,
useTheme, useTheme,
} from '@mui/material'; } from '@mui/material';
import { useCallback, useEffect, useMemo, useState } from 'react'; import Grid from '@mui/material/Grid';
import {
SyntheticEvent,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import CloseIcon from '@mui/icons-material/Close'; import CloseIcon from '@mui/icons-material/Close';
import { getBaseApiReact } from '../../App'; import { getBaseApiReact } from '../../App';
import { import {
@ -30,6 +41,7 @@ import { useModal } from '../../hooks/useModal.tsx';
import { useAtom, useSetAtom } from 'jotai'; import { useAtom, useSetAtom } from 'jotai';
import { memberGroupsAtom, txListAtom } from '../../atoms/global'; import { memberGroupsAtom, txListAtom } from '../../atoms/global';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { TransitionUp } from '../../common/Transitions.tsx';
export const Minting = ({ setIsOpenMinting, myAddress, show }) => { export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
const setTxList = useSetAtom(txListAtom); const setTxList = useSetAtom(txListAtom);
@ -37,14 +49,13 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
const [mintingAccounts, setMintingAccounts] = useState([]); const [mintingAccounts, setMintingAccounts] = useState([]);
const [accountInfo, setAccountInfo] = useState(null); const [accountInfo, setAccountInfo] = useState(null);
const [rewardSharePublicKey, setRewardSharePublicKey] = useState('');
const [mintingKey, setMintingKey] = useState(''); const [mintingKey, setMintingKey] = useState('');
const [rewardsharekey, setRewardsharekey] = useState('');
const [rewardShares, setRewardShares] = useState([]); const [rewardShares, setRewardShares] = useState([]);
const [nodeInfos, setNodeInfos] = useState({}); const [nodeInfos, setNodeInfos] = useState({});
const [openSnack, setOpenSnack] = useState(false); const [openSnack, setOpenSnack] = useState(false);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { show: showKey, message } = useModal(); const [adminInfo, setAdminInfo] = useState({});
const [valueMintingTab, setValueMintingTab] = useState(0);
const { isShow: isShowNext, onOk, show: showNext } = useModal(); const { isShow: isShowNext, onOk, show: showNext } = useModal();
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation([ const { t } = useTranslation([
@ -86,9 +97,8 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
const getName = async (address) => { const getName = async (address) => {
try { try {
const response = await fetch( const url = `${getBaseApiReact()}/names/primary/${address}`;
`${getBaseApiReact()}/names/primary/${address}` const response = await fetch(url);
);
const nameData = await response.json(); const nameData = await response.json();
if (nameData?.name) { if (nameData?.name) {
setNames((prev) => { setNames((prev) => {
@ -110,6 +120,13 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
} }
}; };
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
const getAccountInfo = async (address: string, others?: boolean) => { const getAccountInfo = async (address: string, others?: boolean) => {
try { try {
if (!others) { if (!others) {
@ -161,20 +178,6 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
return address; return address;
}; };
const handleAccountInfos = (address, field) => {
if (!address) return undefined;
if (accountInfos[address]) return accountInfos[address]?.[field];
if (accountInfos[address] === null) return undefined;
getAccountInfo(address, true);
return undefined;
};
const calculateBlocksRemainingToLevel1 = (address) => {
if (!address) return undefined;
if (!accountInfos[address]) return undefined;
return 7200 - accountInfos[address]?.blocksMinted || 0;
};
const getNodeInfos = async () => { const getNodeInfos = async () => {
try { try {
const url = `${getBaseApiReact()}/admin/status`; const url = `${getBaseApiReact()}/admin/status`;
@ -206,6 +209,17 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
} }
}, []); }, []);
const getAdminInfo = async () => {
try {
const url = `${getBaseApiReact()}/admin/info`;
const response = await fetch(url);
const data = await response.json();
setAdminInfo(data);
} catch (error) {
console.log(error);
}
};
const addMintingAccount = useCallback(async (val) => { const addMintingAccount = useCallback(async (val) => {
try { try {
setIsLoading(true); setIsLoading(true);
@ -308,6 +322,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
const createRewardShare = useCallback(async (publicKey, recipient) => { const createRewardShare = useCallback(async (publicKey, recipient) => {
const fee = await getFee('REWARD_SHARE'); const fee = await getFee('REWARD_SHARE');
await show({ await show({
message: t('core:message.question.perform_transaction', { message: t('core:message.question.perform_transaction', {
action: 'REWARD_SHARE', action: 'REWARD_SHARE',
@ -315,6 +330,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
}), }),
publishFee: fee.fee + ' QORT', publishFee: fee.fee + ' QORT',
}); });
return await new Promise((res, rej) => { return await new Promise((res, rej) => {
window window
.sendMessage('createRewardShare', { .sendMessage('createRewardShare', {
@ -382,7 +398,6 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
const waitUntilRewardShareIsConfirmed = async (timeoutMs = 600000) => { const waitUntilRewardShareIsConfirmed = async (timeoutMs = 600000) => {
const pollingInterval = 30000; const pollingInterval = 30000;
const startTime = Date.now(); const startTime = Date.now();
const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
while (Date.now() - startTime < timeoutMs) { while (Date.now() - startTime < timeoutMs) {
@ -424,9 +439,11 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
await showNext({ await showNext({
message: '', message: '',
}); });
const privateRewardShare = await getRewardSharePrivateKey( const privateRewardShare = await getRewardSharePrivateKey(
accountInfo?.publicKey accountInfo?.publicKey
); );
setShowWaitDialog(false); setShowWaitDialog(false);
addMintingAccount(privateRewardShare); addMintingAccount(privateRewardShare);
} }
@ -446,62 +463,6 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
} }
}; };
const getPublicKeyFromAddress = async (address) => {
const url = `${getBaseApiReact()}/addresses/publickey/${address}`;
const response = await fetch(url);
const data = await response.text();
return data;
};
const checkIfMinterGroup = async (address) => {
const url = `${getBaseApiReact()}/groups/member/${address}`;
const response = await fetch(url);
const data = await response.json();
return !!data?.find((grp) => grp?.groupId?.toString() === '694');
};
const removeRewardShare = useCallback(async (rewardShare) => {
return await new Promise((res, rej) => {
window
.sendMessage('removeRewardShare', {
rewardShareKeyPairPublicKey: rewardShare.rewardSharePublicKey,
recipient: rewardShare.recipient,
percentageShare: -1,
})
.then((response) => {
if (!response?.error) {
res(response);
setTxList((prev) => [
{
...rewardShare,
...response,
type: 'remove-rewardShare',
label: t('group:message.success.rewardshare_remove', {
postProcess: 'capitalizeFirstChar',
}),
labelDone: t('group:message.success.rewardshare_remove_label', {
postProcess: 'capitalizeFirstChar',
}),
done: false,
},
...prev,
]);
return;
}
rej({ message: response.error });
})
.catch((error) => {
rej({
message:
error.message ||
t('core:message.error.generic', {
postProcess: 'capitalizeFirstChar',
}),
});
});
});
}, []);
useEffect(() => { useEffect(() => {
getNodeInfos(); getNodeInfos();
getMintingAccounts(); getMintingAccounts();
@ -559,20 +520,29 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
return '' + countBlocksString; return '' + countBlocksString;
}; };
const StatCard = ({ label, value }: { label: string; value: string }) => (
<Grid size={{ xs: 12, sm: 6 }}>
<Box textAlign="center">
<Typography variant="subtitle1" fontWeight="bold">
{label}
</Typography>
<Typography>{value}</Typography>
</Box>
</Grid>
);
const handleChange = (event: SyntheticEvent, newValue: number) => {
setValueMintingTab(newValue);
};
return ( return (
<Dialog <Dialog
open={true} open={true}
maxWidth="lg" maxWidth="lg"
fullWidth fullWidth
fullScreen fullScreen
sx={{ slots={{
'& .MuiDialog-paper': { transition: TransitionUp,
height: '100vh',
margin: 0,
maxWidth: '100%',
overflow: 'hidden', // Prevent scrollbars
width: '100%',
},
}} }}
> >
<AppBar sx={{ position: 'relative' }}> <AppBar sx={{ position: 'relative' }}>
@ -590,323 +560,475 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
aria-label={t('core:action.close', { aria-label={t('core:action.close', {
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
}}
> >
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
</Toolbar> </Toolbar>
</AppBar> </AppBar>
<DialogContent <Box
sx={{ sx={{
position: 'relative', bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflowY: 'auto',
}} }}
> >
{isLoading && ( <Box
<Box sx={{ borderBottom: 1, borderColor: theme.palette.text.secondary }}
sx={{
alignItems: 'center',
bottom: 0,
display: 'flex',
justifyContent: 'center',
left: 0,
position: 'absolute',
right: 0,
top: 0,
}}
>
<FidgetSpinner
ariaLabel="fidget-spinner-loading"
height="80"
visible={true}
width="80"
wrapperClass="fidget-spinner-wrapper"
wrapperStyle={{}}
/>
</Box>
)}
<Card
sx={{
backgroundColor: theme.palette.background.default,
padding: '10px',
}}
> >
<Typography> <Tabs
{t('auth:account.account_one', { value={valueMintingTab}
postProcess: 'capitalizeFirstChar', onChange={handleChange}
})} variant={'fullWidth'}
: {handleNames(accountInfo?.address)} scrollButtons="auto"
</Typography> allowScrollButtonsMobile
<Typography>
{t('core:level', {
postProcess: 'capitalizeFirstChar',
})}
: {accountInfo?.level}
</Typography>
<Typography>
{t('group:message.generic.next_level', {
postProcess: 'capitalizeFirstChar',
})}{' '}
{_levelUpBlocks()}
</Typography>
<Typography>
{t('group:message.generic.node_minting', {
postProcess: 'capitalizeFirstChar',
})}{' '}
{nodeInfos?.isMintingPossible?.toString()}
</Typography>
</Card>
<Spacer height="10px" />
{isPartOfMintingGroup && !accountIsMinting && (
<Box
sx={{ sx={{
alignItems: 'center', '&.MuiTabs-indicator': {
display: 'flex', backgroundColor: theme.palette.background.default,
flexDirection: 'column', },
gap: '5px',
width: '100%',
}} }}
> >
<Button <Tab
size="small" label="Minting Details" // TODO translate
onClick={() => {
startMinting();
}}
disabled={mintingAccounts?.length > 1}
sx={{ sx={{
backgroundColor: theme.palette.other.positive, '&.Mui-selected': {
color: 'black', color: theme.palette.text.primary,
fontWeight: 'bold',
opacity: 0.7,
maxWidth: '90%',
width: '200px',
'&:hover': {
backgroundColor: theme.palette.other.positive,
color: 'black',
opacity: 1,
}, },
fontSize: '1rem',
}} }}
variant="contained" {...a11yProps(0)}
> />
{t('core:action.start_minting', { <Tab
postProcess: 'capitalizeFirstChar', label="Minting Actions"
})}
</Button>
{mintingAccounts?.length > 1 && (
<Typography>
{t('group:message.generic.minting_keys_per_node', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
)}
</Box>
)}
<Spacer height="10px" />
{mintingAccounts?.length > 0 && (
<Typography>
{t('group:message.generic.node_minting_account', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
)}
<Card
sx={{
backgroundColor: theme.palette.background.default,
padding: '10px',
}}
>
{accountIsMinting && (
<Box
sx={{ sx={{
display: 'flex', '&.Mui-selected': {
gap: '5px',
flexDirection: 'column',
}}
>
<Typography>
{t('group:message.generic.node_minting_key', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
</Box>
)}
<Spacer height="10px" />
{mintingAccounts?.map((acct) => (
<Box
key={acct?.mintingAccount}
sx={{
display: 'flex',
gap: '10px',
flexDirection: 'column',
}}
>
<Typography>
{t('group:message.generic.minting_account', {
postProcess: 'capitalizeFirstChar',
})}{' '}
{handleNames(acct?.mintingAccount)}
</Typography>
<Button
size="small"
sx={{
backgroundColor: theme.palette.other.danger,
color: theme.palette.text.primary, color: theme.palette.text.primary,
fontWeight: 'bold', },
maxWidth: '90%', fontSize: '1rem',
opacity: 0.7, }}
width: '200px', {...a11yProps(1)}
'&:hover': { />
backgroundColor: theme.palette.other.danger, </Tabs>
color: theme.palette.text.primary, </Box>
opacity: 1,
},
}}
onClick={() => {
removeMintingAccount(acct.publicKey, acct);
}}
variant="contained"
>
{t('group:action.remove_minting_account', {
postProcess: 'capitalizeFirstChar',
})}
</Button>
<Divider /> {valueMintingTab === 0 && (
<>
<Spacer height="10px" /> <DialogContent
</Box>
))}
{mintingAccounts?.length > 1 && (
<Typography>
{t('group:message.generic.minting_keys_per_node_different', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
)}
</Card>
<Spacer height="20px" />
{!isPartOfMintingGroup && (
<Card
sx={{
backgroundColor: theme.palette.background.default,
padding: '10px',
}}
>
<Box
sx={{ sx={{
display: 'flex', position: 'relative',
gap: '5px',
flexDirection: 'column',
width: '100%',
alignItems: 'center',
}} }}
> >
<Typography> <Container maxWidth="md" sx={{ py: 4 }}>
{t('group:message.generic.minter_group', { <Paper elevation={3} sx={{ p: 3, mb: 4, borderRadius: '10px' }}>
postProcess: 'capitalizeFirstChar', <Typography variant="h6" gutterBottom>
})} Blockchain Statistics
</Typography> </Typography>
<Typography> <Grid container spacing={2}>
{t('group:message.generic.mintership_app', { <StatCard
postProcess: 'capitalizeFirstChar', label="Avg. Qortal Blocktime"
})} value="72.84 Seconds"
</Typography> />
<StatCard
label="Avg. Blocks Per Day"
value="1186.16 Blocks"
/>
<StatCard
label="Avg. Created QORT Per Day"
value="3558.48 QORT"
/>
</Grid>
</Paper>
<Spacer height="10px" /> <Paper elevation={3} sx={{ p: 3, mb: 4, borderRadius: '10px' }}>
<Typography variant="h6" gutterBottom>
Minting Account Details
</Typography>
<Button <Grid container spacing={2}>
size="small" <StatCard label="Current Status" value="(Minting)" />
sx={{ <StatCard label="Current Level" value="Level 4" />
backgroundColor: theme.palette.other.positive, <StatCard
color: theme.palette.text.primary, label="Blocks To Next Level"
fontWeight: 'bold', value="139467 Blocks"
opacity: 0.7, />
</Grid>
'&:hover': { <Box mt={2} textAlign="center">
backgroundColor: theme.palette.other.positive, <Typography>
color: 'black', With a 24/7 Minting you will reach level 5 in{' '}
opacity: 1, <strong>117.58 days</strong>!
}, </Typography>
}} </Box>
onClick={() => { </Paper>
executeEvent('addTab', {
data: { service: 'APP', name: 'q-mintership' }, <Paper elevation={3} sx={{ p: 3, borderRadius: '10px' }}>
}); <Typography variant="h6" gutterBottom>
executeEvent('open-apps-mode', {}); Minting Rewards Info
setIsOpenMinting(false); </Typography>
}}
variant="contained" <Grid container spacing={2}>
> <StatCard
{t('group:action.visit_q_mintership', { label="Current Tier"
postProcess: 'capitalizeFirstChar', value="Tier 2 (Level 3 + 4)"
})} />
</Button> <StatCard
</Box> label="Total Minters in The Tier"
</Card> value="77 Minters"
/>
<StatCard label="Tier Share Per Block" value="13%" />
<StatCard
label="Est. Reward Per Block"
value="0.00506494 QORT"
/>
<StatCard
label="Est. Reward Per Day"
value="6.00782338 QORT"
/>
{/* <StatCard label="AdminInfo" value={adminInfo} /> */}
</Grid>
</Paper>
</Container>
</DialogContent>
</>
)} )}
{showWaitDialog && ( {valueMintingTab === 1 && (
<Dialog <>
open={showWaitDialog} <DialogContent
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle
id="alert-dialog-title"
sx={{ sx={{
textAlign: 'center', position: 'relative',
color: theme.palette.text.primary,
fontWeight: 'bold',
opacity: 1, // TODO translate
}} }}
> >
{isShowNext ? 'Confirmed' : 'Please Wait'} {isLoading && (
</DialogTitle> <Box
sx={{
alignItems: 'center',
bottom: 0,
display: 'flex',
justifyContent: 'center',
left: 0,
position: 'absolute',
right: 0,
top: 0,
}}
>
<FidgetSpinner
ariaLabel="fidget-spinner-loading"
height="80"
visible={true}
width="80"
wrapperClass="fidget-spinner-wrapper"
wrapperStyle={{}}
/>
</Box>
)}
<DialogContent> <Card
{!isShowNext && ( sx={{
backgroundColor: theme.palette.background.default,
padding: '10px',
}}
>
<Typography> <Typography>
{t('group:message.success.rewardshare_creation', { {t('auth:account.account_one', {
postProcess: 'capitalizeFirstChar',
})}
: {handleNames(accountInfo?.address)}
</Typography>
<Typography>
{t('core:level', {
postProcess: 'capitalizeFirstChar',
})}
: {accountInfo?.level}
</Typography>
<Typography>
{t('group:message.generic.next_level', {
postProcess: 'capitalizeFirstChar',
})}{' '}
{_levelUpBlocks()}
</Typography>
<Typography>
{t('group:message.generic.node_minting', {
postProcess: 'capitalizeFirstChar',
})}{' '}
{nodeInfos?.isMintingPossible?.toString()}
</Typography>
</Card>
<Spacer height="10px" />
{isPartOfMintingGroup && !accountIsMinting && (
<Box
sx={{
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
gap: '5px',
width: '100%',
}}
>
<Button
size="small"
onClick={() => {
startMinting();
}}
disabled={mintingAccounts?.length > 1}
sx={{
backgroundColor: theme.palette.other.positive,
color: 'black',
fontWeight: 'bold',
opacity: 0.7,
maxWidth: '90%',
width: '200px',
'&:hover': {
backgroundColor: theme.palette.other.positive,
color: 'black',
opacity: 1,
},
}}
variant="contained"
>
{t('core:action.start_minting', {
postProcess: 'capitalizeFirstChar',
})}
</Button>
{mintingAccounts?.length > 1 && (
<Typography>
{t('group:message.generic.minting_keys_per_node', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
)}
</Box>
)}
<Spacer height="10px" />
{mintingAccounts?.length > 0 && (
<Typography>
{t('group:message.generic.node_minting_account', {
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
</Typography> </Typography>
)} )}
{isShowNext && ( <Card
<Typography> sx={{
{t('group:message.success.rewardshare_confirmed', { backgroundColor: theme.palette.background.default,
postProcess: 'capitalizeFirstChar', padding: '10px',
})} }}
</Typography> >
{accountIsMinting && (
<Box
sx={{
display: 'flex',
gap: '5px',
flexDirection: 'column',
}}
>
<Typography>
{t('group:message.generic.node_minting_key', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
</Box>
)}
<Spacer height="10px" />
{mintingAccounts?.map((acct) => (
<Box
key={acct?.mintingAccount}
sx={{
display: 'flex',
gap: '10px',
flexDirection: 'column',
}}
>
<Typography>
{t('group:message.generic.minting_account', {
postProcess: 'capitalizeFirstChar',
})}{' '}
{handleNames(acct?.mintingAccount)}
</Typography>
<Button
size="small"
sx={{
backgroundColor: theme.palette.other.danger,
color: theme.palette.text.primary,
fontWeight: 'bold',
maxWidth: '90%',
opacity: 0.7,
width: '200px',
'&:hover': {
backgroundColor: theme.palette.other.danger,
color: theme.palette.text.primary,
opacity: 1,
},
}}
onClick={() => {
removeMintingAccount(acct.publicKey, acct);
}}
variant="contained"
>
{t('group:action.remove_minting_account', {
postProcess: 'capitalizeFirstChar',
})}
</Button>
<Divider />
<Spacer height="10px" />
</Box>
))}
{mintingAccounts?.length > 1 && (
<Typography>
{t(
'group:message.generic.minting_keys_per_node_different',
{
postProcess: 'capitalizeFirstChar',
}
)}
</Typography>
)}
</Card>
<Spacer height="20px" />
{!isPartOfMintingGroup && (
<Card
sx={{
backgroundColor: theme.palette.background.default,
padding: '10px',
}}
>
<Box
sx={{
display: 'flex',
gap: '5px',
flexDirection: 'column',
width: '100%',
alignItems: 'center',
}}
>
<Typography>
{t('group:message.generic.minter_group', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
<Typography>
{t('group:message.generic.mintership_app', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
<Spacer height="10px" />
<Button
size="small"
sx={{
backgroundColor: theme.palette.other.positive,
color: theme.palette.text.primary,
fontWeight: 'bold',
opacity: 0.7,
'&:hover': {
backgroundColor: theme.palette.other.positive,
color: 'black',
opacity: 1,
},
}}
onClick={() => {
executeEvent('addTab', {
data: { service: 'APP', name: 'q-mintership' },
});
executeEvent('open-apps-mode', {});
setIsOpenMinting(false);
}}
variant="contained"
>
{t('group:action.visit_q_mintership', {
postProcess: 'capitalizeFirstChar',
})}
</Button>
</Box>
</Card>
)}
{showWaitDialog && (
<Dialog
open={showWaitDialog}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle
id="alert-dialog-title"
sx={{
textAlign: 'center',
color: theme.palette.text.primary,
fontWeight: 'bold',
opacity: 1,
}}
>
{isShowNext
? t('core:message.generic.confirmed', {
postProcess: 'capitalizeFirstChar',
})
: t('core:message.generic.wait', {
postProcess: 'capitalizeFirstChar',
})}
</DialogTitle>
<DialogContent>
{!isShowNext && (
<Typography>
{t('group:message.success.rewardshare_creation', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
)}
{isShowNext && (
<Typography>
{t('group:message.success.rewardshare_confirmed', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
)}
</DialogContent>
<DialogActions>
<Button
disabled={!isShowNext}
variant="contained"
onClick={onOk}
autoFocus
>
{t('core:page.next', {
postProcess: 'capitalizeFirstChar',
})}
</Button>
</DialogActions>
</Dialog>
)} )}
</DialogContent> </DialogContent>
</>
<DialogActions>
<Button
disabled={!isShowNext}
variant="contained"
onClick={onOk}
autoFocus
>
{t('core:page.next', { postProcess: 'capitalizeFirstChar' })}
</Button>
</DialogActions>
</Dialog>
)} )}
</DialogContent> </Box>
<Snackbar <Snackbar
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}

View File

@ -515,9 +515,9 @@ export const NotAuthenticated = ({
<Box <Box
sx={{ sx={{
alignItems: 'center',
display: 'flex', display: 'flex',
gap: '10px', gap: '10px',
alignItems: 'center',
}} }}
> >
<HtmlTooltip <HtmlTooltip
@ -550,9 +550,9 @@ export const NotAuthenticated = ({
<Box <Box
sx={{ sx={{
alignItems: 'center',
display: 'flex', display: 'flex',
gap: '10px', gap: '10px',
alignItems: 'center',
}} }}
> >
<HtmlTooltip <HtmlTooltip
@ -628,18 +628,18 @@ export const NotAuthenticated = ({
<Box <Box
sx={{ sx={{
display: 'flex',
gap: '10px',
alignItems: 'center', alignItems: 'center',
borderRadius: '8px',
display: 'flex',
flexDirection: 'column', flexDirection: 'column',
outlineWidth: '0.5px', gap: '10px',
outlineStyle: 'solid', outlineStyle: 'solid',
outlineWidth: '0.5px',
outlineColor: outlineColor:
theme.palette.mode === 'dark' theme.palette.mode === 'dark'
? 'rgba(255, 255, 255, 0.5)' ? 'rgba(255, 255, 255, 0.5)'
: 'rgba(0, 0, 0, 0.3)', : 'rgba(0, 0, 0, 0.3)',
padding: '20px 30px', padding: '20px 30px',
borderRadius: '8px',
}} }}
> >
<> <>
@ -795,11 +795,11 @@ export const NotAuthenticated = ({
<DialogContent> <DialogContent>
<Box <Box
sx={{ sx={{
width: '100% !important',
overflow: 'auto',
height: '60vh',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
height: '60vh',
overflow: 'auto',
width: '100% !important',
}} }}
> >
{mode === 'list' && ( {mode === 'list' && (
@ -894,10 +894,10 @@ export const NotAuthenticated = ({
<Box <Box
sx={{ sx={{
alignItems: 'center',
display: 'flex', display: 'flex',
gap: '10px', gap: '10px',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center',
}} }}
> >
<Button <Button

View File

@ -293,9 +293,9 @@ export const RegisterName = ({
{isNameAvailable === Availability.AVAILABLE && ( {isNameAvailable === Availability.AVAILABLE && (
<Box <Box
sx={{ sx={{
alignItems: 'center',
display: 'flex', display: 'flex',
gap: '5px', gap: '5px',
alignItems: 'center',
}} }}
> >
<CheckIcon <CheckIcon

View File

@ -16,15 +16,21 @@ import { getBaseApiReact } from '../../App';
import { executeEvent } from '../../utils/events'; import { executeEvent } from '../../utils/events';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { memberGroupsAtom, txListAtom } from '../../atoms/global'; import { memberGroupsAtom, txListAtom } from '../../atoms/global';
import { useTranslation } from 'react-i18next';
export const TaskManager = ({ getUserInfo }) => { export const TaskManager = ({ getUserInfo }) => {
const [memberGroups] = useAtom(memberGroupsAtom); const [memberGroups] = useAtom(memberGroupsAtom);
const [txList, setTxList] = useAtom(txListAtom); const [txList, setTxList] = useAtom(txListAtom);
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const intervals = useRef({}); const intervals = useRef({});
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation([
'auth',
'core',
'group',
'question',
'tutorial',
]);
const handleClick = () => { const handleClick = () => {
setOpen((prev) => !prev); setOpen((prev) => !prev);
@ -199,7 +205,11 @@ export const TaskManager = ({ getUserInfo }) => {
)} )}
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Ongoing Transactions" /> <ListItemText
primary={t('core:message.generic.ongoing_transactions', {
postProcess: 'capitalizeFirstChar',
})}
/>
{open ? <ExpandLess /> : <ExpandMore />} {open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton> </ListItemButton>

View File

@ -19,26 +19,15 @@ const ThemeSelector = () => {
return ( return (
<Box ref={selectorRef}> <Box ref={selectorRef}>
<Tooltip <IconButton
title={ onClick={toggleTheme}
themeMode === 'dark' sx={{
? t('core:theme.light_mode', { bgcolor: theme.palette.background.default,
postProcess: 'capitalizeFirstChar', color: theme.palette.text.primary,
}) }}
: t('core:theme.dark_mode', {
postProcess: 'capitalizeFirstChar',
})
}
> >
<IconButton {themeMode === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
onClick={toggleTheme} </IconButton>
sx={{
color: theme.palette.text.secondary,
}}
>
{themeMode === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
</IconButton>
</Tooltip>
</Box> </Box>
); );
}; };

View File

@ -41,15 +41,12 @@ export const Tutorials = () => {
> >
<Tabs <Tabs
sx={{ sx={{
'& .MuiTabs-indicator': { '&.MuiTabs-indicator': {
backgroundColor: theme.palette.background.default, backgroundColor: theme.palette.background.default,
}, },
}} }}
value={multiNumber} value={multiNumber}
onChange={(e, value) => setMultiNumber(value)} onChange={(e, value) => setMultiNumber(value)}
aria-label={t('core:basic_tabs_example', {
postProcess: 'capitalizeFirstChar',
})}
> >
{openTutorialModal?.multi?.map((item, index) => { {openTutorialModal?.multi?.map((item, index) => {
return ( return (
@ -75,10 +72,11 @@ export const Tutorials = () => {
})} })}
onClick={handleClose} onClick={handleClose}
sx={{ sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
position: 'absolute', position: 'absolute',
right: 8, right: 8,
top: 8, top: 8,
color: theme.palette.text.primary,
}} }}
> >
<CloseIcon /> <CloseIcon />
@ -123,10 +121,11 @@ export const Tutorials = () => {
})} })}
onClick={handleClose} onClick={handleClose}
sx={{ sx={{
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
position: 'absolute', position: 'absolute',
right: 8, right: 8,
top: 8, top: 8,
color: theme.palette.text.primary,
}} }}
> >
<CloseIcon /> <CloseIcon />

View File

@ -18,6 +18,8 @@ import {
CircularProgress, CircularProgress,
useTheme, useTheme,
Autocomplete, Autocomplete,
IconButton,
ClickAwayListener,
} from '@mui/material'; } from '@mui/material';
import { import {
getAddressInfo, getAddressInfo,
@ -28,7 +30,7 @@ import { getNameInfo } from '../Group/Group';
import AccountCircleIcon from '@mui/icons-material/AccountCircle'; import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import { Spacer } from '../../common/Spacer'; import { Spacer } from '../../common/Spacer';
import { formatTimestamp } from '../../utils/time'; import { formatTimestamp } from '../../utils/time';
import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen'; import CloseIcon from '@mui/icons-material/Close';
import { import {
executeEvent, executeEvent,
subscribeToEvent, subscribeToEvent,
@ -160,489 +162,481 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
return ( return (
<DrawerUserLookup open={isOpenDrawerLookup} setOpen={setIsOpenDrawerLookup}> <DrawerUserLookup open={isOpenDrawerLookup} setOpen={setIsOpenDrawerLookup}>
<Box <ClickAwayListener onClickAway={onClose}>
sx={{
display: 'flex',
flexDirection: 'column',
height: '100vh',
overflow: 'hidden',
padding: '15px',
}}
>
<Box
sx={{
alignItems: 'center',
display: 'flex',
flexShrink: 0,
gap: '5px',
}}
>
<Autocomplete
value={nameOrAddress}
onChange={(event: any, newValue: string | null) => {
if (!newValue) {
setNameOrAddress('');
return;
}
setNameOrAddress(newValue);
lookupFunc(newValue);
}}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
id="controllable-states-demo"
loading={isLoading}
noOptionsText={t('core:option_no', {
postProcess: 'capitalizeFirstChar',
})}
options={options}
sx={{ width: 300 }}
renderInput={(params) => (
<TextField
autoFocus
autoComplete="off"
{...params}
label={t('auth:address_name', {
postProcess: 'capitalizeFirstChar',
})}
onKeyDown={(e) => {
if (e.key === 'Enter' && nameOrAddress) {
lookupFunc(inputValue);
}
}}
/>
)}
/>
<ButtonBase
sx={{
marginLeft: 'auto',
}}
onClick={() => {
onClose();
}}
>
<CloseFullscreenIcon
sx={{
color: theme.palette.text.primary,
}}
/>
</ButtonBase>
</Box>
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
flexGrow: 1, height: '100vh',
overflow: 'auto', overflow: 'hidden',
padding: '15px',
}} }}
> >
{!isLoadingUser && errorMessage && ( <Box
<Box sx={{
sx={{ alignItems: 'center',
display: 'flex', display: 'flex',
justifyContent: 'center', gap: '5px',
marginTop: '40px', }}
width: '100%', >
<Autocomplete
value={nameOrAddress}
onChange={(event: any, newValue: string | null) => {
if (!newValue) {
setNameOrAddress('');
return;
}
setNameOrAddress(newValue);
lookupFunc(newValue);
}} }}
> inputValue={inputValue}
<Typography>{errorMessage}</Typography> onInputChange={(event, newInputValue) => {
</Box> setInputValue(newInputValue);
)}
{isLoadingUser && (
<Box
sx={{
display: 'flex',
justifyContent: 'center',
marginTop: '40px',
width: '100%',
}} }}
> id="controllable-states-demo"
<CircularProgress loading={isLoading}
sx={{ noOptionsText={t('core:option_no', {
color: theme.palette.text.primary, postProcess: 'capitalizeFirstChar',
}} })}
/> options={options}
</Box> sx={{ flexGrow: 1 }}
)} renderInput={(params) => (
<TextField
autoFocus
autoComplete="off"
{...params}
label={t('auth:address_name', {
postProcess: 'capitalizeFirstChar',
})}
onKeyDown={(e) => {
if (e.key === 'Enter' && nameOrAddress) {
lookupFunc(inputValue);
}
}}
/>
)}
/>
</Box>
{!isLoadingUser && addressInfo && ( <Box
<> sx={{
<Spacer height="30px" /> display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'auto',
}}
>
{!isLoadingUser && errorMessage && (
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
gap: '20px',
justifyContent: 'center', justifyContent: 'center',
marginTop: '40px',
width: '100%', width: '100%',
}} }}
> >
<Card <Typography>{errorMessage}</Typography>
</Box>
)}
{isLoadingUser && (
<Box
sx={{
display: 'flex',
justifyContent: 'center',
marginTop: '40px',
width: '100%',
}}
>
<CircularProgress
sx={{
color: theme.palette.text.primary,
}}
/>
</Box>
)}
{!isLoadingUser && addressInfo && (
<>
<Spacer height="30px" />
<Box
sx={{ sx={{
alignItems: 'center',
background: theme.palette.background.default,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'row',
minHeight: '200px', flexWrap: 'wrap',
minWidth: '320px', gap: '20px',
padding: '15px', justifyContent: 'center',
width: '100%',
}} }}
> >
<Typography <Card
sx={{ sx={{
textAlign: 'center', alignItems: 'center',
background: theme.palette.background.default,
display: 'flex',
flexDirection: 'column',
minHeight: '200px',
minWidth: '320px',
padding: '15px',
}} }}
> >
{addressInfo?.name ?? <Typography
t('auth:message.error.name_not_registered', { sx={{
postProcess: 'capitalizeFirstChar', textAlign: 'center',
})} }}
</Typography> >
{addressInfo?.name ??
t('auth:message.error.name_not_registered', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
<Spacer height="20px" /> <Spacer height="20px" />
<Divider> <Divider>
{addressInfo?.name ? ( {addressInfo?.name ? (
<Avatar <Avatar
sx={{ sx={{
height: '50px', height: '50px',
width: '50px', width: '50px',
'& img': { '& img': {
objectFit: 'fill', objectFit: 'fill',
}, },
}} }}
alt={addressInfo?.name} alt={addressInfo?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${ src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
addressInfo?.name addressInfo?.name
}/qortal_avatar?async=true`} }/qortal_avatar?async=true`}
> >
<AccountCircleIcon
sx={{
fontSize: '50px',
}}
/>
</Avatar>
) : (
<AccountCircleIcon <AccountCircleIcon
sx={{ sx={{
fontSize: '50px', fontSize: '50px',
}} }}
/> />
</Avatar> )}
) : ( </Divider>
<AccountCircleIcon
sx={{
fontSize: '50px',
}}
/>
)}
</Divider>
<Spacer height="20px" /> <Spacer height="20px" />
<Typography <Typography
sx={{ sx={{
textAlign: 'center', textAlign: 'center',
}} }}
> >
{t('core:level', { postProcess: 'capitalizeFirstChar' })}{' '} {t('core:level', { postProcess: 'capitalizeFirstChar' })}{' '}
{addressInfo?.level} {addressInfo?.level}
</Typography> </Typography>
</Card> </Card>
<Card <Card
sx={{
background: theme.palette.background.default,
display: 'flex',
flexDirection: 'column',
gap: '20px',
minHeight: '200px',
minWidth: '320px',
padding: '15px',
}}
>
<Box
sx={{ sx={{
background: theme.palette.background.default,
display: 'flex', display: 'flex',
flexDirection: 'column',
gap: '20px', gap: '20px',
justifyContent: 'space-between', minHeight: '200px',
width: '100%', minWidth: '320px',
padding: '15px',
}} }}
> >
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
flexShrink: 0, gap: '20px',
justifyContent: 'space-between',
width: '100%',
}}
>
<Box
sx={{
display: 'flex',
flexShrink: 0,
}}
>
<Typography>
{t('auth:address', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
</Box>
<Tooltip
title={
<span
style={{
color: theme.palette.text.primary,
fontSize: '14px',
fontWeight: 700,
}}
>
{t('auth:action.copy_address', {
postProcess: 'capitalizeFirstChar',
})}
</span>
}
placement="bottom"
arrow
sx={{ fontSize: '24' }}
slotProps={{
tooltip: {
sx: {
color: theme.palette.text.primary,
backgroundColor: theme.palette.background.default,
},
},
arrow: {
sx: {
color: theme.palette.text.primary,
},
},
}}
>
<ButtonBase
onClick={() => {
navigator.clipboard.writeText(addressInfo?.address);
}}
>
<Typography
sx={{
textAlign: 'end',
}}
>
{addressInfo?.address}
</Typography>
</ButtonBase>
</Tooltip>
</Box>
<Box
sx={{
display: 'flex',
gap: '20px',
justifyContent: 'space-between',
width: '100%',
}} }}
> >
<Typography> <Typography>
{t('auth:address', { {t('core:balance', {
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
</Typography> </Typography>
<Typography>{addressInfo?.balance}</Typography>
</Box> </Box>
<Tooltip <Spacer height="20px" />
title={
<span <Button
style={{ variant="contained"
color: theme.palette.text.primary, onClick={() => {
fontSize: '14px', executeEvent('openPaymentInternal', {
fontWeight: 700, address: addressInfo?.address,
}} name: addressInfo?.name,
> });
{t('auth:action.copy_address', {
postProcess: 'capitalizeFirstChar',
})}
</span>
}
placement="bottom"
arrow
sx={{ fontSize: '24' }}
slotProps={{
tooltip: {
sx: {
color: theme.palette.text.primary,
backgroundColor: theme.palette.background.default,
},
},
arrow: {
sx: {
color: theme.palette.text.primary,
},
},
}} }}
> >
<ButtonBase {t('core:action.send_qort', {
onClick={() => { postProcess: 'capitalizeFirstChar',
navigator.clipboard.writeText(addressInfo?.address); })}
}} </Button>
> </Card>
<Typography </Box>
sx={{ </>
textAlign: 'end', )}
}}
>
{addressInfo?.address}
</Typography>
</ButtonBase>
</Tooltip>
</Box>
<Spacer height="40px" />
{isLoadingPayments && (
<Box
sx={{
display: 'flex',
justifyContent: 'center',
width: '100%',
}}
>
<CircularProgress
sx={{
color: theme.palette.text.primary,
}}
/>
</Box>
)}
{!isLoadingPayments && addressInfo && (
<Card
sx={{
background: theme.palette.background.default,
display: 'flex',
flexDirection: 'column',
overflow: 'auto',
padding: '15px',
}}
>
<Typography>
{t('core:message.generic.most_recent_payment', {
count: 20,
postProcess: 'capitalizeFirstChar',
})}
</Typography>
<Spacer height="20px" />
{!isLoadingPayments && payments?.length === 0 && (
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
gap: '20px', justifyContent: 'center',
justifyContent: 'space-between',
width: '100%', width: '100%',
}} }}
> >
<Typography> <Typography>
{t('core:balance', { {t('core:message.generic.no_payments', {
postProcess: 'capitalizeFirstChar', postProcess: 'capitalizeFirstChar',
})} })}
</Typography> </Typography>
<Typography>{addressInfo?.balance}</Typography>
</Box> </Box>
)}
<Spacer height="20px" /> <Table>
<TableHead>
<Button <TableRow>
variant="contained"
onClick={() => {
executeEvent('openPaymentInternal', {
address: addressInfo?.address,
name: addressInfo?.name,
});
}}
>
{t('core:action.send_qort', {
postProcess: 'capitalizeFirstChar',
})}
</Button>
</Card>
</Box>
</>
)}
<Spacer height="40px" />
{isLoadingPayments && (
<Box
sx={{
display: 'flex',
justifyContent: 'center',
width: '100%',
}}
>
<CircularProgress
sx={{
color: theme.palette.text.primary,
}}
/>
</Box>
)}
{!isLoadingPayments && addressInfo && (
<Card
sx={{
background: theme.palette.background.default,
display: 'flex',
flexDirection: 'column',
overflow: 'auto',
padding: '15px',
}}
>
<Typography>
{t('core:message.generic.most_recent_payment', {
count: 20,
postProcess: 'capitalizeFirstChar',
})}
</Typography>
<Spacer height="20px" />
{!isLoadingPayments && payments?.length === 0 && (
<Box
sx={{
display: 'flex',
justifyContent: 'center',
width: '100%',
}}
>
<Typography>
{t('core:message.generic.no_payments', {
postProcess: 'capitalizeFirstChar',
})}
</Typography>
</Box>
)}
<Table>
<TableHead>
<TableRow>
<TableCell>
{t('core:sender', { postProcess: 'capitalizeFirstChar' })}
</TableCell>
<TableCell>
{t('core:receiver', {
postProcess: 'capitalizeFirstChar',
})}
</TableCell>
<TableCell>
{t('core:amount', { postProcess: 'capitalizeFirstChar' })}
</TableCell>
<TableCell>
{t('core:time.time', {
postProcess: 'capitalizeFirstChar',
})}
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{payments.map((payment, index) => (
<TableRow key={payment?.signature}>
<TableCell> <TableCell>
<Tooltip {t('core:sender', {
title={ postProcess: 'capitalizeFirstChar',
<span })}
style={{
color: theme.palette.text.primary,
fontSize: '14px',
fontWeight: 700,
}}
>
{t('auth:action.copy_address', {
postProcess: 'capitalizeFirstChar',
})}
</span>
}
placement="bottom"
arrow
sx={{ fontSize: '24' }}
slotProps={{
tooltip: {
sx: {
color: theme.palette.text.primary,
backgroundColor:
theme.palette.background.default,
},
},
arrow: {
sx: {
color: theme.palette.text.primary,
},
},
}}
>
<ButtonBase
onClick={() => {
navigator.clipboard.writeText(
payment?.creatorAddress
);
}}
>
{formatAddress(payment?.creatorAddress)}
</ButtonBase>
</Tooltip>
</TableCell> </TableCell>
<TableCell> <TableCell>
<Tooltip {t('core:receiver', {
title={ postProcess: 'capitalizeFirstChar',
<span })}
style={{
color: theme.palette.text.primary,
fontSize: '14px',
fontWeight: 700,
}}
>
{t('auth:action.copy_address', {
postProcess: 'capitalizeFirstChar',
})}
</span>
}
placement="bottom"
arrow
sx={{ fontSize: '24' }}
slotProps={{
tooltip: {
sx: {
color: theme.palette.text.primary,
backgroundColor:
theme.palette.background.default,
},
},
arrow: {
sx: {
color: theme.palette.text.primary,
},
},
}}
>
<ButtonBase
onClick={() => {
navigator.clipboard.writeText(payment?.recipient);
}}
>
{formatAddress(payment?.recipient)}
</ButtonBase>
</Tooltip>
</TableCell> </TableCell>
<TableCell>{payment?.amount}</TableCell>
<TableCell> <TableCell>
{formatTimestamp(payment?.timestamp)} {t('core:amount', {
postProcess: 'capitalizeFirstChar',
})}
</TableCell>
<TableCell>
{t('core:time.time', {
postProcess: 'capitalizeFirstChar',
})}
</TableCell> </TableCell>
</TableRow> </TableRow>
))} </TableHead>
</TableBody>
</Table> <TableBody>
</Card> {payments.map((payment, index) => (
)} <TableRow key={payment?.signature}>
<TableCell>
<Tooltip
title={
<span
style={{
color: theme.palette.text.primary,
fontSize: '14px',
fontWeight: 700,
}}
>
{t('auth:action.copy_address', {
postProcess: 'capitalizeFirstChar',
})}
</span>
}
placement="bottom"
arrow
sx={{ fontSize: '24' }}
slotProps={{
tooltip: {
sx: {
color: theme.palette.text.primary,
backgroundColor:
theme.palette.background.default,
},
},
arrow: {
sx: {
color: theme.palette.text.primary,
},
},
}}
>
<ButtonBase
onClick={() => {
navigator.clipboard.writeText(
payment?.creatorAddress
);
}}
>
{formatAddress(payment?.creatorAddress)}
</ButtonBase>
</Tooltip>
</TableCell>
<TableCell>
<Tooltip
title={
<span
style={{
color: theme.palette.text.primary,
fontSize: '14px',
fontWeight: 700,
}}
>
{t('auth:action.copy_address', {
postProcess: 'capitalizeFirstChar',
})}
</span>
}
placement="bottom"
arrow
sx={{ fontSize: '24' }}
slotProps={{
tooltip: {
sx: {
color: theme.palette.text.primary,
backgroundColor:
theme.palette.background.default,
},
},
arrow: {
sx: {
color: theme.palette.text.primary,
},
},
}}
>
<ButtonBase
onClick={() => {
navigator.clipboard.writeText(
payment?.recipient
);
}}
>
{formatAddress(payment?.recipient)}
</ButtonBase>
</Tooltip>
</TableCell>
<TableCell>{payment?.amount}</TableCell>
<TableCell>
{formatTimestamp(payment?.timestamp)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Card>
)}
</Box>
</Box> </Box>
</Box> </ClickAwayListener>
</DrawerUserLookup> </DrawerUserLookup>
); );
}; };

View File

@ -207,7 +207,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
if (isLoading) return null; if (isLoading) return null;
return ( return (
<div> <Box>
{wallets?.length === 0 || !wallets ? ( {wallets?.length === 0 || !wallets ? (
<> <>
<Typography> <Typography>
@ -469,7 +469,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
</Typography> </Typography>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
</div> </Box>
); );
}; };
@ -562,6 +562,8 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
<IconButton <IconButton
sx={{ sx={{
alignSelf: 'flex-start', alignSelf: 'flex-start',
bgcolor: theme.palette.background.default,
color: theme.palette.text.primary,
}} }}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();

View File

@ -116,7 +116,6 @@
"apps_official": "offizielle Apps", "apps_official": "offizielle Apps",
"attachment": "Anhang", "attachment": "Anhang",
"balance": "Gleichgewicht:", "balance": "Gleichgewicht:",
"basic_tabs_example": "Basic Tabs Beispiel",
"category": "Kategorie", "category": "Kategorie",
"category_other": "Kategorien", "category_other": "Kategorien",
"chat": "Chat", "chat": "Chat",
@ -256,6 +255,7 @@
"no_pinned_changes": "Sie haben derzeit keine Änderungen an Ihren angestellten Apps", "no_pinned_changes": "Sie haben derzeit keine Änderungen an Ihren angestellten Apps",
"no_results": "Keine Ergebnisse", "no_results": "Keine Ergebnisse",
"one_app_per_name": "Hinweis: Derzeit ist pro Namen nur eine App und Website zulässig.", "one_app_per_name": "Hinweis: Derzeit ist pro Namen nur eine App und Website zulässig.",
"ongoing_transactions": "laufende Transaktionen",
"opened": "geöffnet", "opened": "geöffnet",
"overwrite_qdn": "überschreibt zu QDN", "overwrite_qdn": "überschreibt zu QDN",
"password_confirm": "Bitte bestätigen Sie ein Passwort", "password_confirm": "Bitte bestätigen Sie ein Passwort",
@ -280,8 +280,9 @@
"settings": "Sie verwenden den Export-/Import -Weg zum Speichern von Einstellungen.", "settings": "Sie verwenden den Export-/Import -Weg zum Speichern von Einstellungen.",
"space_for_admins": "Entschuldigung, dieser Raum gilt nur für Administratoren.", "space_for_admins": "Entschuldigung, dieser Raum gilt nur für Administratoren.",
"unread_messages": "ungelesene Nachrichten unten", "unread_messages": "ungelesene Nachrichten unten",
"unsaved_changes": "Sie haben nicht gespeicherte Änderungen an Ihren angestellten Apps. Speichern Sie sie bei QDN.", "unsaved_changes": "sie haben nicht gespeicherte Änderungen an Ihren angestellten Apps. Speichern Sie sie bei QDN.",
"updating": "Aktualisierung" "updating": "aktualisierung",
"wait": "bitte warten"
}, },
"message": "Nachricht", "message": "Nachricht",
"promotion_text": "Promotionstext", "promotion_text": "Promotionstext",

View File

@ -116,7 +116,6 @@
"apps_official": "official Apps", "apps_official": "official Apps",
"attachment": "attachment", "attachment": "attachment",
"balance": "balance:", "balance": "balance:",
"basic_tabs_example": "basic tabs example",
"category": "category", "category": "category",
"category_other": "categories", "category_other": "categories",
"chat": "chat", "chat": "chat",
@ -220,6 +219,7 @@
"benefits_qort": "benefits of having QORT", "benefits_qort": "benefits of having QORT",
"building": "building", "building": "building",
"building_app": "building app", "building_app": "building app",
"confirmed": "confirmed",
"created_by": "created by {{ owner }}", "created_by": "created by {{ owner }}",
"buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>", "buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>",
"buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>", "buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>",
@ -256,6 +256,7 @@
"no_pinned_changes": "you currently do not have any changes to your pinned apps", "no_pinned_changes": "you currently do not have any changes to your pinned apps",
"no_results": "no results", "no_results": "no results",
"one_app_per_name": "note: Currently, only one App and Website is allowed per Name.", "one_app_per_name": "note: Currently, only one App and Website is allowed per Name.",
"ongoing_transactions": "ongoing transactions",
"opened": "opened", "opened": "opened",
"overwrite_qdn": "overwrite to QDN", "overwrite_qdn": "overwrite to QDN",
"password_confirm": "please confirm a password", "password_confirm": "please confirm a password",
@ -281,7 +282,8 @@
"space_for_admins": "sorry, this space is only for Admins.", "space_for_admins": "sorry, this space is only for Admins.",
"unread_messages": "unread messages below", "unread_messages": "unread messages below",
"unsaved_changes": "you have unsaved changes to your pinned apps. Save them to QDN.", "unsaved_changes": "you have unsaved changes to your pinned apps. Save them to QDN.",
"updating": "updating" "updating": "updating",
"wait": "please wait"
}, },
"message": "message", "message": "message",
"promotion_text": "promotion text", "promotion_text": "promotion text",

View File

@ -116,7 +116,6 @@
"apps_official": "aplicaciones oficiales", "apps_official": "aplicaciones oficiales",
"attachment": "adjunto", "attachment": "adjunto",
"balance": "balance:", "balance": "balance:",
"basic_tabs_example": "Ejemplo de pestañas básicas",
"category": "categoría", "category": "categoría",
"category_other": "categorías", "category_other": "categorías",
"chat": "charlar", "chat": "charlar",
@ -219,7 +218,8 @@
"avatar_size": "{{ size }} KB max. for GIFS", "avatar_size": "{{ size }} KB max. for GIFS",
"benefits_qort": "beneficios de tener Qort", "benefits_qort": "beneficios de tener Qort",
"building": "edificio", "building": "edificio",
"building_app": "Aplicación de construcción", "building_app": "aplicación de construcción",
"confirmed": "confirmado",
"created_by": "created by {{ owner }}", "created_by": "created by {{ owner }}",
"buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>", "buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>",
"buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>", "buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>",
@ -256,6 +256,7 @@
"no_pinned_changes": "Actualmente no tiene ningún cambio en sus aplicaciones fijadas", "no_pinned_changes": "Actualmente no tiene ningún cambio en sus aplicaciones fijadas",
"no_results": "Sin resultados", "no_results": "Sin resultados",
"one_app_per_name": "Nota: Actualmente, solo se permite una aplicación y un sitio web por nombre.", "one_app_per_name": "Nota: Actualmente, solo se permite una aplicación y un sitio web por nombre.",
"ongoing_transactions": "transacciones en curso",
"opened": "abierto", "opened": "abierto",
"overwrite_qdn": "sobrescribir a QDN", "overwrite_qdn": "sobrescribir a QDN",
"password_confirm": "Confirme una contraseña", "password_confirm": "Confirme una contraseña",
@ -277,11 +278,12 @@
"select_image": "Seleccione una imagen para un logotipo", "select_image": "Seleccione una imagen para un logotipo",
"select_zip": "Seleccione el archivo .zip que contenga contenido estático:", "select_zip": "Seleccione el archivo .zip que contenga contenido estático:",
"sending": "envío...", "sending": "envío...",
"settings": "Está utilizando la forma de exportación/importación de la configuración de ahorro.", "settings": "está utilizando la forma de exportación/importación de la configuración de ahorro.",
"space_for_admins": "Lo siento, este espacio es solo para administradores.", "space_for_admins": "lo siento, este espacio es solo para administradores.",
"unread_messages": "Mensajes no leídos a continuación", "unread_messages": "mensajes no leídos a continuación",
"unsaved_changes": "Tiene cambios no salvos en sus aplicaciones fijadas. Guárdelos a QDN.", "unsaved_changes": "tiene cambios no salvos en sus aplicaciones fijadas. Guárdelos a QDN.",
"updating": "actualización" "updating": "actualización",
"wait": "espera por favor"
}, },
"message": "mensaje", "message": "mensaje",
"promotion_text": "texto de promoción", "promotion_text": "texto de promoción",
@ -393,4 +395,4 @@
}, },
"website": "sitio web", "website": "sitio web",
"welcome": "bienvenido" "welcome": "bienvenido"
} }

View File

@ -116,7 +116,6 @@
"apps_official": "Applications officielles", "apps_official": "Applications officielles",
"attachment": "pièce jointe", "attachment": "pièce jointe",
"balance": "équilibre:", "balance": "équilibre:",
"basic_tabs_example": "Exemple de base des onglets",
"category": "catégorie", "category": "catégorie",
"category_other": "catégories", "category_other": "catégories",
"chat": "chat", "chat": "chat",
@ -220,6 +219,7 @@
"benefits_qort": "Avantages d'avoir QORT", "benefits_qort": "Avantages d'avoir QORT",
"building": "bâtiment", "building": "bâtiment",
"building_app": "application de construction", "building_app": "application de construction",
"confirmed": "confirmé",
"created_by": "created by {{ owner }}", "created_by": "created by {{ owner }}",
"buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>", "buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>",
"buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>", "buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>",
@ -256,6 +256,7 @@
"no_pinned_changes": "Vous n'avez actuellement aucune modification à vos applications épinglées", "no_pinned_changes": "Vous n'avez actuellement aucune modification à vos applications épinglées",
"no_results": "Aucun résultat", "no_results": "Aucun résultat",
"one_app_per_name": "Remarque: Actuellement, une seule application et site Web est autorisée par nom.", "one_app_per_name": "Remarque: Actuellement, une seule application et site Web est autorisée par nom.",
"ongoing_transactions": "transactions en cours",
"opened": "ouvert", "opened": "ouvert",
"overwrite_qdn": "Écraser à QDN", "overwrite_qdn": "Écraser à QDN",
"password_confirm": "Veuillez confirmer un mot de passe", "password_confirm": "Veuillez confirmer un mot de passe",
@ -277,11 +278,12 @@
"select_image": "Veuillez sélectionner une image pour un logo", "select_image": "Veuillez sélectionner une image pour un logo",
"select_zip": "Sélectionnez un fichier .zip contenant du contenu statique:", "select_zip": "Sélectionnez un fichier .zip contenant du contenu statique:",
"sending": "envoi...", "sending": "envoi...",
"settings": "Vous utilisez la manière d'exportation / l'importation d'enregistrer les paramètres.", "settings": "vous utilisez la manière d'exportation / importation d'enregistrement des paramètres.",
"space_for_admins": "Désolé, cet espace est uniquement pour les administrateurs.", "space_for_admins": "désolé, cet espace est uniquement pour les administrateurs.",
"unread_messages": "Messages non lus ci-dessous", "unread_messages": "messages non lus ci-dessous",
"unsaved_changes": "Vous avez des modifications non enregistrées à vos applications épinglées. Enregistrez-les sur QDN.", "unsaved_changes": "vous avez des modifications non enregistrées à vos applications épinglées. Enregistrez-les sur QDN.",
"updating": "mise à jour" "updating": "mise à jour",
"wait": "attendez s'il vous plaît"
}, },
"message": "message", "message": "message",
"promotion_text": "texte de promotion", "promotion_text": "texte de promotion",
@ -393,4 +395,4 @@
}, },
"website": "site web", "website": "site web",
"welcome": "accueillir" "welcome": "accueillir"
} }

View File

@ -116,7 +116,6 @@
"apps_official": "app ufficiali", "apps_official": "app ufficiali",
"attachment": "allegato", "attachment": "allegato",
"balance": "bilancio:", "balance": "bilancio:",
"basic_tabs_example": "esempio di schede base",
"category": "categoria", "category": "categoria",
"category_other": "categorie", "category_other": "categorie",
"chat": "chat", "chat": "chat",
@ -220,6 +219,7 @@
"benefits_qort": "vantaggi di avere QORT", "benefits_qort": "vantaggi di avere QORT",
"building": "creazione", "building": "creazione",
"building_app": "creazione app", "building_app": "creazione app",
"confirmed": "confermato",
"created_by": "creato da {{ owner }}", "created_by": "creato da {{ owner }}",
"buy_order_request": "l'applicazione <br/><italic>{{hostname}}</italic> <br/><span>sta effettuando {{count}} ordine d'acquisto</span>.", "buy_order_request": "l'applicazione <br/><italic>{{hostname}}</italic> <br/><span>sta effettuando {{count}} ordine d'acquisto</span>.",
"buy_order_request_other": "l'applicazione <br/><italic>{{hostname}}</italic> <br/><span>sta effettuando {{count}} ordini d'acquisto</span>.", "buy_order_request_other": "l'applicazione <br/><italic>{{hostname}}</italic> <br/><span>sta effettuando {{count}} ordini d'acquisto</span>.",
@ -255,6 +255,7 @@
"no_pinned_changes": "per ora non ci sono modifiche alle app bloccate", "no_pinned_changes": "per ora non ci sono modifiche alle app bloccate",
"no_results": "nessun risultato", "no_results": "nessun risultato",
"one_app_per_name": "nota: per adesso sono consentiti solo un'app e un sito Web per nome.", "one_app_per_name": "nota: per adesso sono consentiti solo un'app e un sito Web per nome.",
"ongoing_transactions": "transazioni in corso",
"opened": "aperto", "opened": "aperto",
"overwrite_qdn": "sovrascrivi a QDN", "overwrite_qdn": "sovrascrivi a QDN",
"password_confirm": "si prega di confermare una password", "password_confirm": "si prega di confermare una password",
@ -280,7 +281,8 @@
"space_for_admins": "mi dispiace, questo spazio è solo per gli amministratori.", "space_for_admins": "mi dispiace, questo spazio è solo per gli amministratori.",
"unread_messages": "messaggi non letti qua sotto", "unread_messages": "messaggi non letti qua sotto",
"unsaved_changes": "hai cambiato modifiche alle app bloccate. Salvali su QDN.", "unsaved_changes": "hai cambiato modifiche alle app bloccate. Salvali su QDN.",
"updating": "aggiornamento" "updating": "aggiornamento",
"wait": "attendere per favore"
}, },
"message": "messaggio", "message": "messaggio",
"promotion_text": "testo di promozione", "promotion_text": "testo di promozione",

View File

@ -71,7 +71,7 @@
"block_delay_maximum": "ritardo massimo del blocco per le approvazioni delle transazioni di gruppo", "block_delay_maximum": "ritardo massimo del blocco per le approvazioni delle transazioni di gruppo",
"closed_group": "questo è un gruppo chiuso/privato, quindi occorre attendere fino a quando un amministratore accetta la richiesta", "closed_group": "questo è un gruppo chiuso/privato, quindi occorre attendere fino a quando un amministratore accetta la richiesta",
"descrypt_wallet": "decrittazione del wallet ...", "descrypt_wallet": "decrittazione del wallet ...",
"encryption_key": "la prima chiave di crittografia comune del gruppo è in fase di creazione. Si prega di attendere qualche minuto per essere recuperato dalla rete. Controllo ogni 2 minuti ...", "encryption_key": "la prima chiave di crittografia comune del gruppo è in fase di creazione. Si prega di attendere qualche minuto per il suo recupero dalla rete. Controllo ogni 2 minuti ...",
"group_announcement": "annunci di gruppo", "group_announcement": "annunci di gruppo",
"group_approval_threshold": "soglia di approvazione del gruppo (numero / percentuale di amministratori che devono approvare una transazione)", "group_approval_threshold": "soglia di approvazione del gruppo (numero / percentuale di amministratori che devono approvare una transazione)",
"group_encrypted": "gruppo crittografato", "group_encrypted": "gruppo crittografato",

View File

@ -116,7 +116,6 @@
"apps_official": "公式アプリ", "apps_official": "公式アプリ",
"attachment": "添付ファイル", "attachment": "添付ファイル",
"balance": "バランス:", "balance": "バランス:",
"basic_tabs_example": "基本的なタブの例",
"category": "カテゴリ", "category": "カテゴリ",
"category_other": "カテゴリ", "category_other": "カテゴリ",
"chat": "チャット", "chat": "チャット",
@ -220,6 +219,7 @@
"benefits_qort": "QORTを持つことの利点", "benefits_qort": "QORTを持つことの利点",
"building": "建物", "building": "建物",
"building_app": "ビルディングアプリ", "building_app": "ビルディングアプリ",
"confirmed": "確認済み",
"created_by": "created by {{ owner }}", "created_by": "created by {{ owner }}",
"buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>", "buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>",
"buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>", "buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>",
@ -256,6 +256,7 @@
"no_pinned_changes": "現在、ピン留めアプリに変更がありません", "no_pinned_changes": "現在、ピン留めアプリに変更がありません",
"no_results": "結果はありません", "no_results": "結果はありません",
"one_app_per_name": "注現在、名前ごとに1つのアプリとWebサイトのみが許可されています。", "one_app_per_name": "注現在、名前ごとに1つのアプリとWebサイトのみが許可されています。",
"ongoing_transactions": "継続中の取引",
"opened": "オープン", "opened": "オープン",
"overwrite_qdn": "QDNに上書きします", "overwrite_qdn": "QDNに上書きします",
"password_confirm": "パスワードを確認してください", "password_confirm": "パスワードを確認してください",
@ -281,7 +282,8 @@
"space_for_admins": "申し訳ありませんが、このスペースは管理者専用です。", "space_for_admins": "申し訳ありませんが、このスペースは管理者専用です。",
"unread_messages": "以下の未読メッセージ", "unread_messages": "以下の未読メッセージ",
"unsaved_changes": "ピン留めアプリに救われていない変更があります。それらをqdnに保存します。", "unsaved_changes": "ピン留めアプリに救われていない変更があります。それらをqdnに保存します。",
"updating": "更新" "updating": "更新",
"wait": "お待ちください"
}, },
"message": "メッセージ", "message": "メッセージ",
"promotion_text": "プロモーションテキスト", "promotion_text": "プロモーションテキスト",
@ -393,4 +395,4 @@
}, },
"website": "Webサイト", "website": "Webサイト",
"welcome": "いらっしゃいませ" "welcome": "いらっしゃいませ"
} }

View File

@ -116,7 +116,6 @@
"apps_official": "официальные приложения", "apps_official": "официальные приложения",
"attachment": "вложение", "attachment": "вложение",
"balance": "баланс:", "balance": "баланс:",
"basic_tabs_example": "Основные вкладки",
"category": "категория", "category": "категория",
"category_other": "категории", "category_other": "категории",
"chat": "чат", "chat": "чат",
@ -220,6 +219,7 @@
"benefits_qort": "Преимущества наличия qort", "benefits_qort": "Преимущества наличия qort",
"building": "здание", "building": "здание",
"building_app": "строительство приложения", "building_app": "строительство приложения",
"confirmed": "подтвержденный",
"created_by": "created by {{ owner }}", "created_by": "created by {{ owner }}",
"buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>", "buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>",
"buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>", "buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>",
@ -256,6 +256,7 @@
"no_pinned_changes": "В настоящее время у вас нет никаких изменений в ваших приложениях", "no_pinned_changes": "В настоящее время у вас нет никаких изменений в ваших приложениях",
"no_results": "Нет результатов", "no_results": "Нет результатов",
"one_app_per_name": "ПРИМЕЧАНИЕ. В настоящее время только одно приложение и веб -сайт разрешены для имени.", "one_app_per_name": "ПРИМЕЧАНИЕ. В настоящее время только одно приложение и веб -сайт разрешены для имени.",
"ongoing_transactions": "текущие операции",
"opened": "открыл", "opened": "открыл",
"overwrite_qdn": "перезаписать в QDN", "overwrite_qdn": "перезаписать в QDN",
"password_confirm": "Пожалуйста, подтвердите пароль", "password_confirm": "Пожалуйста, подтвердите пароль",
@ -281,7 +282,8 @@
"space_for_admins": "Извините, это пространство только для администраторов.", "space_for_admins": "Извините, это пространство только для администраторов.",
"unread_messages": "Непрочитанные сообщения ниже", "unread_messages": "Непрочитанные сообщения ниже",
"unsaved_changes": "У вас есть неспасенные изменения в ваши приложения. Сохраните их в QDN.", "unsaved_changes": "У вас есть неспасенные изменения в ваши приложения. Сохраните их в QDN.",
"updating": "обновление" "updating": "обновление",
"wait": "пожалуйста, подождите"
}, },
"message": "сообщение", "message": "сообщение",
"promotion_text": "Текст продвижения", "promotion_text": "Текст продвижения",

View File

@ -116,7 +116,6 @@
"apps_official": "官方应用程序", "apps_official": "官方应用程序",
"attachment": "依恋", "attachment": "依恋",
"balance": "平衡:", "balance": "平衡:",
"basic_tabs_example": "基本标签示例",
"category": "类别", "category": "类别",
"category_other": "类别", "category_other": "类别",
"chat": "聊天", "chat": "聊天",
@ -220,9 +219,10 @@
"benefits_qort": "Qort的好处", "benefits_qort": "Qort的好处",
"building": "建筑", "building": "建筑",
"building_app": "建筑应用", "building_app": "建筑应用",
"created_by": "created by {{ owner }}", "confirmed": "已确认",
"buy_order_request": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy order</span>", "created_by": "创建人 {{ owner }}",
"buy_order_request_other": "the Application <br/><italic>{{hostname}}</italic> <br/><span>is requesting {{count}} buy orders</span>", "buy_order_request": "应用程序<br/><italic>{{hostname}}</italic><br/><span>正在请求{{count}}购买订单</span>",
"buy_order_request_other": "应用程序<br/><italic>{{hostname}}</italic><br/><span>正在请求{{count}}购买订单</span>",
"devmode_local_node": "请使用您的本地节点进行开发模式!注销并使用本地节点。", "devmode_local_node": "请使用您的本地节点进行开发模式!注销并使用本地节点。",
"downloading": "下载", "downloading": "下载",
"downloading_decrypting_app": "下载和解密私人应用程序。", "downloading_decrypting_app": "下载和解密私人应用程序。",
@ -256,6 +256,7 @@
"no_pinned_changes": "您目前对固定应用程序没有任何更改", "no_pinned_changes": "您目前对固定应用程序没有任何更改",
"no_results": "没有结果", "no_results": "没有结果",
"one_app_per_name": "注意:目前,每个名称只允许一个应用程序和网站。", "one_app_per_name": "注意:目前,每个名称只允许一个应用程序和网站。",
"ongoing_transactions": "正在进行的交易",
"opened": "打开", "opened": "打开",
"overwrite_qdn": "覆盖为QDN", "overwrite_qdn": "覆盖为QDN",
"password_confirm": "请确认密码", "password_confirm": "请确认密码",
@ -281,7 +282,8 @@
"space_for_admins": "抱歉,这个空间仅适用于管理员。", "space_for_admins": "抱歉,这个空间仅适用于管理员。",
"unread_messages": "下面未读消息", "unread_messages": "下面未读消息",
"unsaved_changes": "您对固定应用程序有未保存的更改。将它们保存到QDN。", "unsaved_changes": "您对固定应用程序有未保存的更改。将它们保存到QDN。",
"updating": "更新" "updating": "更新",
"wait": "请稍等"
}, },
"message": "信息", "message": "信息",
"promotion_text": "促销文本", "promotion_text": "促销文本",
@ -393,4 +395,4 @@
}, },
"website": "网站", "website": "网站",
"welcome": "欢迎" "welcome": "欢迎"
} }

View File

@ -2463,7 +2463,6 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
const filename = data.filename; const filename = data.filename;
const blob = data.blob; const blob = data.blob;
const mimeType = blob.type || data.mimeType;
const resPermission = await getUserPermission( const resPermission = await getUserPermission(
{ {
text1: i18n.t('question:download_file', { text1: i18n.t('question:download_file', {
@ -2474,17 +2473,6 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
isFromExtension isFromExtension
); );
const { accepted } = resPermission; const { accepted } = resPermission;
if (!accepted) throw new Error('User declined to save file'); // TODO translate
showSaveFilePicker(
{
filename,
mimeType,
blob,
},
snackMethods
);
return true;
if (accepted) { if (accepted) {
const mimeType = blob.type || data.mimeType; const mimeType = blob.type || data.mimeType;

View File

@ -16,7 +16,7 @@ export const darkThemeOptions: ThemeOptions = {
background: { background: {
default: 'rgb(49, 51, 56)', default: 'rgb(49, 51, 56)',
surface: 'rgb(58, 60, 65)', surface: 'rgb(58, 60, 65)',
paper: 'rgb(62, 64, 68)', paper: 'rgb(77, 80, 85)',
}, },
text: { text: {
primary: 'rgb(255, 255, 255)', primary: 'rgb(255, 255, 255)',