diff --git a/src/App.tsx b/src/App.tsx index 87d8b37..04cd035 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1906,7 +1906,7 @@ function App() { textTransform: 'uppercase', }} > - {t('core:minting_status')} + {t('core:minting.status_title')} } placement="left" @@ -2370,11 +2370,7 @@ function App() { count: requestBuyOrder?.crosschainAtInfo?.length || 0, }} tOptions={{ postProcess: ['capitalizeFirstChar'] }} - > - The Application - {{ hostname }} - is requesting {{ count }} buy order - + > @@ -2481,11 +2477,7 @@ function App() { count: requestBuyOrder?.crosschainAtInfo?.length || 0, }} tOptions={{ postProcess: ['capitalizeFirstChar'] }} - > - The Application - {{ hostname }} - is requesting {{ count }} a payment - + > @@ -2964,10 +2956,7 @@ function App() { ), }} tOptions={{ postProcess: ['capitalizeFirstChar'] }} - > - A SEEDPHRASE has been randomly generated in - the background. - + > - Create your Qortal account by clicking NEXT{' '} - below. - + > diff --git a/src/common/BoundedNumericTextField.tsx b/src/common/BoundedNumericTextField.tsx index 803011e..b3c995e 100644 --- a/src/common/BoundedNumericTextField.tsx +++ b/src/common/BoundedNumericTextField.tsx @@ -135,6 +135,10 @@ export const BoundedNumericTextField = ({ changeValueWithIncDecButton(1)} + sx={{ + bgcolor: theme.palette.background.default, + color: theme.palette.text.primary, + }} > changeValueWithIncDecButton(-1)} + sx={{ + bgcolor: theme.palette.background.default, + color: theme.palette.text.primary, + }} > +) { + return ; +}); diff --git a/src/components/Apps/AppsDevModeNavBar.tsx b/src/components/Apps/AppsDevModeNavBar.tsx index 548f1d2..6a39c16 100644 --- a/src/components/Apps/AppsDevModeNavBar.tsx +++ b/src/components/Apps/AppsDevModeNavBar.tsx @@ -108,13 +108,10 @@ export const AppsDevModeNavBar = () => { { { } }; - const handleChange = (event: React.SyntheticEvent, newValue: number) => { + const handleChange = (event: SyntheticEvent, newValue: number) => { setValueTabPrivateApp(newValue); }; @@ -353,13 +354,10 @@ export const AppsPrivate = ({ myName, myAddress }) => { - - )} - + - + - + ); }; diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index f5cb3f6..dc018d9 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -206,7 +206,7 @@ export const ChatList = ({ width: '100%', }} > - - - - - + ); } return ( - - + ); })} - - - + + + {showScrollButton && ( scrollToBottom()} - style={{ + sx={{ backgroundColor: theme.palette.other.unread, border: 'none', borderRadius: '20px', @@ -428,15 +427,15 @@ export const ChatList = ({ {showScrollDownButton && !showScrollButton && ( scrollToBottom()} - style={{ + sx={{ backgroundColor: theme.palette.background.paper, - outline: `1px solid ${theme.palette.primary.light}`, border: 'none', borderRadius: '20px', bottom: 20, color: theme.palette.text.primary, cursor: 'pointer', fontSize: '16px', + outline: `1px solid ${theme.palette.primary.light}`, padding: '10px 20px', position: 'absolute', right: 20, @@ -449,7 +448,7 @@ export const ChatList = ({ })} )} - + {enableMentions && (hasSecretKey || isPrivate === false) && ( { - - + > ); diff --git a/src/components/ContextMenu.tsx b/src/components/ContextMenu.tsx index 3d1ede6..a05b8fe 100644 --- a/src/components/ContextMenu.tsx +++ b/src/components/ContextMenu.tsx @@ -185,4 +185,4 @@ export const ContextMenu = ({ children, groupId, getUserSettings }) => { ); -}; +}; // TODO translate diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index 9b5bd29..8752025 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -4,7 +4,7 @@ import syncedMintingImg from '../assets/syncStatus/synced_minting.webp'; import syncingImg from '../assets/syncStatus/syncing.webp'; import { getBaseApiReact } from '../App'; import '../styles/CoreSyncStatus.css'; -import { useTheme } from '@mui/material'; +import { Box, useTheme } from '@mui/material'; import { useTranslation } from 'react-i18next'; import { manifestData } from './NotAuthenticated'; @@ -82,31 +82,31 @@ export const CoreSyncStatus = () => { : ''; let imagePath = syncingImg; - let message = t('core:message.status.synchronizing', { + let message = t('core:minting.status.synchronizing', { postProcess: 'capitalizeFirstChar', }); if (isMintingPossible && !isUsingGateway) { imagePath = syncedMintingImg; - message = `${t(`core:message.status.${isSynchronizing ? 'synchronizing' : 'synchronized'}`, { postProcess: 'capitalizeFirstChar' })} ${t('core:message.status.minting')}`; + message = `${t(`core:minting.status.${isSynchronizing ? 'synchronizing' : 'synchronized'}`, { postProcess: 'capitalizeFirstChar' })} ${t('core:minting.status.minting')}`; } else if (isSynchronizing === true && syncPercent === 99) { imagePath = syncingImg; } else if (isSynchronizing && !isMintingPossible && syncPercent === 100) { imagePath = syncingImg; - message = `${t('core:message.status.synchronizing', { postProcess: 'capitalizeFirstChar' })} ${!isUsingGateway ? t('core:message.status.not_minting') : ''}`; + message = `${t('core:minting.status.synchronizing', { postProcess: 'capitalizeFirstChar' })} ${!isUsingGateway ? t('core:minting.status.not_minting') : ''}`; } else if (!isSynchronizing && !isMintingPossible && syncPercent === 100) { imagePath = syncedImg; - message = `${t('core:message.status.synchronized', { postProcess: 'capitalizeFirstChar' })} ${!isUsingGateway ? t('core:message.status.not_minting') : ''}`; + message = `${t('core:minting.status.synchronized', { postProcess: 'capitalizeFirstChar' })} ${!isUsingGateway ? t('core:minting.status.not_minting') : ''}`; } else if (isSynchronizing && isMintingPossible && syncPercent === 100) { imagePath = syncingImg; - message = `${t('core:message.status.synchronizing', { postProcess: 'capitalizeFirstChar' })} ${!isUsingGateway ? t('core:message.status.minting') : ''}`; + message = `${t('core:minting.status.synchronizing', { postProcess: 'capitalizeFirstChar' })} ${!isUsingGateway ? t('core:minting.status.minting') : ''}`; } else if (!isSynchronizing && isMintingPossible && syncPercent === 100) { imagePath = syncedMintingImg; - message = `${t('core:message.status.synchronized', { postProcess: 'capitalizeFirstChar' })} ${!isUsingGateway ? t('core:message.status.minting') : ''}`; + message = `${t('core:minting.status.synchronized', { postProcess: 'capitalizeFirstChar' })} ${!isUsingGateway ? t('core:minting.status.minting') : ''}`; } return ( - { /> - { {t('core:ui.version', { postProcess: 'capitalizeFirstChar' })}:{' '} {manifestData.version} - - + + ); }; - return {renderSyncStatusIcon()}; + return {renderSyncStatusIcon()}; }; diff --git a/src/components/Embeds/ImageEmbed.tsx b/src/components/Embeds/ImageEmbed.tsx index 9438166..f197f5b 100644 --- a/src/components/Embeds/ImageEmbed.tsx +++ b/src/components/Embeds/ImageEmbed.tsx @@ -269,11 +269,12 @@ export function ImageViewer({ src = null, alt = '' }) { diff --git a/src/components/Embeds/VideoPlayer.tsx b/src/components/Embeds/VideoPlayer.tsx index c5ea94d..b8caab4 100644 --- a/src/components/Embeds/VideoPlayer.tsx +++ b/src/components/Embeds/VideoPlayer.tsx @@ -57,27 +57,27 @@ const ControlsContainer = styled(Box)` `; interface VideoPlayerProps { - src?: string; - poster?: string; - name?: string; - identifier?: string; - service?: string; autoplay?: boolean; - from?: string | null; customStyle?: any; + from?: string | null; + identifier?: string; + name?: string; + poster?: string; + service?: string; + src?: string | null; user?: string; } // TODO translate and theme (optional) export const VideoPlayer: FC = ({ - poster, - name, - identifier, - service, autoplay = true, - from = null, customStyle = {}, + from = null, + identifier, + name, node, + poster, + service, }) => { const keyIdentifier = useMemo(() => { if (name && identifier && service) { @@ -549,22 +549,22 @@ export const VideoPlayer: FC = ({ )} {((!src && !isLoading) || !startPlay) && ( { togglePlay(); }} + position="absolute" + right={0} sx={{ cursor: 'pointer', }} + top={0} + zIndex={500} > = ({ -) { - return ; -}); - export const AddGroup = ({ address, open, setOpen }) => { const { show } = useContext(QORTAL_APP_CONTEXT); const setTxList = useSetAtom(txListAtom); @@ -229,13 +215,12 @@ export const AddGroup = ({ address, open, setOpen }) => { open={open} onClose={handleClose} slots={{ - transition: Transition, + transition: TransitionUp, }} > @@ -252,6 +237,10 @@ export const AddGroup = ({ address, open, setOpen }) => { color="inherit" edge="start" onClick={handleClose} + sx={{ + bgcolor: theme.palette.background.default, + color: theme.palette.text.primary, + }} > @@ -274,14 +263,11 @@ export const AddGroup = ({ address, open, setOpen }) => { { setOpenAdvance((prev) => !prev)} > diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index df48de7..628b6ac 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -406,10 +406,11 @@ export const BlockedUsersModal = () => { })} onClick={onCancel} sx={{ + bgcolor: theme.palette.background.default, + color: theme.palette.text.primary, position: 'absolute', right: 8, top: 8, - color: theme.palette.text.primary, }} > diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 0fde3d1..d2cc6ef 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1632,10 +1632,10 @@ export const Group = ({ const renderDirects = () => { return ( - - + {direct?.sender !== myAddress && direct?.timestamp && ((!timestampEnterData[direct?.address] && @@ -1844,10 +1847,10 @@ export const Group = ({ ))} - + - + {!isRunningPublicNode && ( { @@ -1888,8 +1892,8 @@ export const Group = ({ /> )} - - + + ); }; @@ -1936,7 +1940,7 @@ export const Group = ({ setInfo={setInfoSnack} /> - - + > ); }; diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index 8bad077..902bd73 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -173,6 +173,10 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { aria-label={t('core:comment_other', { postProcess: 'capitalizeFirstChar', })} + sx={{ + bgcolor: theme.palette.background.default, + color: theme.palette.text.primary, + }} > - ))} - + - {t('group:group.group', { postProcess: 'capitalizeFirstChar' })} @@ -213,14 +213,14 @@ export const GroupList = ({ > )} > - - + + ); }; @@ -294,20 +294,22 @@ const GroupItem = React.memo( ? 'no messages' : `last message: ${formatEmailDate(group?.timestamp)}` } - primaryTypographyProps={{ - style: { - color: - group?.groupId === selectedGroup?.groupId && - theme.palette.text.primary, - fontSize: '16px', + slotProps={{ + primary: { + style: { + color: + group?.groupId === selectedGroup?.groupId && + theme.palette.text.primary, + fontSize: '16px', + }, }, - }} - secondaryTypographyProps={{ - style: { - color: - group?.groupId === selectedGroup?.groupId && - theme.palette.text.primary, - fontSize: '12px', + secondary: { + style: { + color: + group?.groupId === selectedGroup?.groupId && + theme.palette.text.primary, + fontSize: '12px', + }, }, }} sx={{ diff --git a/src/components/Group/ListOfThreadPostsWatched.tsx b/src/components/Group/ListOfThreadPostsWatched.tsx index 8a3c3d2..ffb2fbd 100644 --- a/src/components/Group/ListOfThreadPostsWatched.tsx +++ b/src/components/Group/ListOfThreadPostsWatched.tsx @@ -5,7 +5,7 @@ import ListItemButton from '@mui/material/ListItemButton'; import ListItemText from '@mui/material/ListItemText'; import IconButton from '@mui/material/IconButton'; import { executeEvent } from '../../utils/events'; -import { Box, Typography } from '@mui/material'; +import { Box, Typography, useTheme } from '@mui/material'; import { Spacer } from '../../common/Spacer'; import { CustomLoader } from '../../common/CustomLoader'; import VisibilityIcon from '@mui/icons-material/Visibility'; @@ -14,6 +14,7 @@ import { useTranslation } from 'react-i18next'; export const ListOfThreadPostsWatched = () => { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); + const theme = useTheme(); const { t } = useTranslation([ 'auth', 'core', @@ -175,6 +176,10 @@ export const ListOfThreadPostsWatched = () => { aria-label={t('core:comment_other', { postProcess: 'capitalizeFirstChar', })} + sx={{ + bgcolor: theme.palette.background.default, + color: theme.palette.text.primary, + }} > @@ -247,14 +251,11 @@ export const ManageMembers = ({ ({ padding: 8, @@ -74,15 +75,6 @@ const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ }, })); -const Transition = forwardRef(function Transition( - props: TransitionProps & { - children: ReactElement; - }, - ref: Ref -) { - return ; -}); - export const Settings = ({ open, setOpen, rawWallet }) => { const [checked, setChecked] = useState(false); const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); @@ -163,7 +155,7 @@ export const Settings = ({ open, setOpen, rawWallet }) => { open={open} onClose={handleClose} slots={{ - transition: Transition, + transition: TransitionUp, }} > @@ -181,6 +173,10 @@ export const Settings = ({ open, setOpen, rawWallet }) => { aria-label={t('core:action.close', { postProcess: 'capitalizeFirstChar', })} + sx={{ + bgcolor: theme.palette.background.default, + color: theme.palette.text.primary, + }} > diff --git a/src/components/Language/LanguageSelector.tsx b/src/components/Language/LanguageSelector.tsx index a51c715..e15727b 100644 --- a/src/components/Language/LanguageSelector.tsx +++ b/src/components/Language/LanguageSelector.tsx @@ -30,25 +30,18 @@ const LanguageSelector = () => { return ( {!showSelect && ( - setShowSelect(true)} + style={{ + fontSize: '1.3rem', + }} + aria-label={t('core:current_language', { + language: name, postProcess: 'capitalizeFirstChar', })} > - setShowSelect(true)} - style={{ - fontSize: '1.3rem', - }} - aria-label={t('core:current_language', { - language: name, - postProcess: 'capitalizeFirstChar', - })} - > - {flag} - - + {flag} + )} {showSelect && ( diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 198b5e1..2d94344 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -1,21 +1,33 @@ import { Alert, + alpha, AppBar, Box, Button, Card, + Container, Dialog, DialogActions, DialogContent, DialogTitle, Divider, IconButton, + Paper, Snackbar, + Tab, + Tabs, Toolbar, Typography, useTheme, } 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 { getBaseApiReact } from '../../App'; import { @@ -29,7 +41,27 @@ import { FidgetSpinner } from 'react-loader-spinner'; import { useModal } from '../../hooks/useModal.tsx'; import { useAtom, useSetAtom } from 'jotai'; import { memberGroupsAtom, txListAtom } from '../../atoms/global'; -import { useTranslation } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; +import { TransitionUp } from '../../common/Transitions.tsx'; +import { + nextLevel, + averageBlockDay, + averageBlockTime, + dayReward, + levelUpBlocks, + levelUpDays, + mintingStatus, + countMintersInLevel, + currentTier, + tierPercent, + countReward, + countRewardDay, +} from './MintingStats.tsx'; + +export type AddressLevelEntry = { + level: number; + count: number; +}; export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const setTxList = useSetAtom(txListAtom); @@ -37,14 +69,16 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const [mintingAccounts, setMintingAccounts] = useState([]); const [accountInfo, setAccountInfo] = useState(null); - const [rewardSharePublicKey, setRewardSharePublicKey] = useState(''); const [mintingKey, setMintingKey] = useState(''); - const [rewardsharekey, setRewardsharekey] = useState(''); const [rewardShares, setRewardShares] = useState([]); - const [nodeInfos, setNodeInfos] = useState({}); + const [adminInfo, setAdminInfo] = useState({}); + const [nodeStatus, setNodeStatus] = useState({}); + const [addressLevel, setAddressLevel] = useState([]); + const [tier4Online, setTier4Online] = useState(0); const [openSnack, setOpenSnack] = useState(false); const [isLoading, setIsLoading] = useState(false); - const { show: showKey, message } = useModal(); + const [nodeHeightBlock, setNodeHeightBlock] = useState({}); + const [valueMintingTab, setValueMintingTab] = useState(0); const { isShow: isShowNext, onOk, show: showNext } = useModal(); const theme = useTheme(); const { t } = useTranslation([ @@ -86,9 +120,8 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const getName = async (address) => { try { - const response = await fetch( - `${getBaseApiReact()}/names/primary/${address}` - ); + const url = `${getBaseApiReact()}/names/primary/${address}`; + const response = await fetch(url); const nameData = await response.json(); if (nameData?.name) { setNames((prev) => { @@ -110,6 +143,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) => { try { if (!others) { @@ -140,6 +180,13 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { } }; + const daysToNextLevel = levelUpDays( + accountInfo, + adminInfo, + nodeHeightBlock, + nodeStatus + ); + const refreshRewardShare = () => { if (!myAddress) return; getRewardShares(myAddress); @@ -161,31 +208,61 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { 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 getAdminInfo = useCallback(async () => { + try { + const url = `${getBaseApiReact()}/admin/info`; + const response = await fetch(url); + const data = await response.json(); + setAdminInfo(data); + setTimeout(getAdminInfo, 30000); + } catch (error) { + console.log(error); + } + }, []); - const calculateBlocksRemainingToLevel1 = (address) => { - if (!address) return undefined; - if (!accountInfos[address]) return undefined; - return 7200 - accountInfos[address]?.blocksMinted || 0; - }; - - const getNodeInfos = async () => { + const getNodeStatus = useCallback(async () => { try { const url = `${getBaseApiReact()}/admin/status`; - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); + const response = await fetch(url); const data = await response.json(); - setNodeInfos(data); + setNodeStatus(data); + setTimeout(getNodeStatus, 30000); + } catch (error) { + console.error('Request failed', error); + } + }, []); + + useEffect(() => { + if (nodeStatus?.height) { + const getNodeHeightBlock = async () => { + try { + const nodeBlock = nodeStatus.height - 1440; + const url = `${getBaseApiReact()}/blocks/byheight/${nodeBlock}`; + const response = await fetch(url); + const data = await response.json(); + setNodeHeightBlock(data); + } catch (error) { + console.error('Request failed', error); + } + }; + + getNodeHeightBlock(); + } + }, [nodeStatus]); + + const getAddressLevel = async () => { + try { + const url = `${getBaseApiReact()}/addresses/online/levels`; + const response = await fetch(url); + const data: AddressLevelEntry[] = await response.json(); + if (Array.isArray(data)) { + setAddressLevel(data); + const level7 = data.find((entry) => entry.level === 7)?.count || 0; + const level8 = data.find((entry) => entry.level === 8)?.count || 0; + const tier4Count = + parseFloat(level7.toString()) + parseFloat(level8.toString()); + setTier4Online(tier4Count); + } } catch (error) { console.error('Request failed', error); } @@ -193,7 +270,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const getRewardShares = useCallback(async (address) => { try { - const url = `${getBaseApiReact()}/addresses/rewardshares?involving=${address}`; + const url = `${getBaseApiReact()}/addresses/rewardshares?involving=${address}`; // TODO check API (still useful?) const response = await fetch(url); if (!response.ok) { throw new Error('network error'); @@ -308,6 +385,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const createRewardShare = useCallback(async (publicKey, recipient) => { const fee = await getFee('REWARD_SHARE'); + await show({ message: t('core:message.question.perform_transaction', { action: 'REWARD_SHARE', @@ -315,6 +393,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { }), publishFee: fee.fee + ' QORT', }); + return await new Promise((res, rej) => { window .sendMessage('createRewardShare', { @@ -382,7 +461,6 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const waitUntilRewardShareIsConfirmed = async (timeoutMs = 600000) => { const pollingInterval = 30000; const startTime = Date.now(); - const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); while (Date.now() - startTime < timeoutMs) { @@ -424,9 +502,11 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { await showNext({ message: '', }); + const privateRewardShare = await getRewardSharePrivateKey( accountInfo?.publicKey ); + setShowWaitDialog(false); addMintingAccount(privateRewardShare); } @@ -446,98 +526,19 @@ 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(() => { - getNodeInfos(); + getAddressLevel(); + getAdminInfo(); getMintingAccounts(); + getNodeStatus(); }, []); useEffect(() => { if (!myAddress) return; getRewardShares(myAddress); - getAccountInfo(myAddress); }, [myAddress]); - const _blocksNeed = () => { - if (accountInfo?.level === 0) { - return 7200; // TODO manage these magic numbers in a proper location - } else if (accountInfo?.level === 1) { - return 72000; - } else if (accountInfo?.level === 2) { - return 201600; - } else if (accountInfo?.level === 3) { - return 374400; - } else if (accountInfo?.level === 4) { - return 618400; - } else if (accountInfo?.level === 5) { - return 964000; - } else if (accountInfo?.level === 6) { - return 1482400; - } else if (accountInfo?.level === 7) { - return 2173600; - } else if (accountInfo?.level === 8) { - return 3037600; - } else if (accountInfo?.level === 9) { - return 4074400; - } - }; - const handleClose = () => { setOpenSnack(false); setTimeout(() => { @@ -545,18 +546,26 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { }, 250); }; - const _levelUpBlocks = () => { - if ( - accountInfo?.blocksMinted === undefined || - nodeInfos?.height === undefined - ) - return null; - let countBlocks = - _blocksNeed() - - (accountInfo?.blocksMinted + accountInfo?.blocksMintedAdjustment); + const StatCard = ({ label, value }: { label: string; value: string }) => ( + + + + {label} + + {value} + + + ); - let countBlocksString = countBlocks.toString(); - return '' + countBlocksString; + const handleChange = (event: SyntheticEvent, newValue: number) => { + setValueMintingTab(newValue); }; return ( @@ -565,14 +574,8 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { maxWidth="lg" fullWidth fullScreen - sx={{ - '& .MuiDialog-paper': { - height: '100vh', - margin: 0, - maxWidth: '100%', - overflow: 'hidden', // Prevent scrollbars - width: '100%', - }, + slots={{ + transition: TransitionUp, }} > @@ -590,323 +593,617 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { aria-label={t('core:action.close', { postProcess: 'capitalizeFirstChar', })} + sx={{ + bgcolor: theme.palette.background.default, + color: theme.palette.text.primary, + }} > - - {isLoading && ( - - - - )} - - - {t('auth:account.account_one', { - postProcess: 'capitalizeFirstChar', - })} - : {handleNames(accountInfo?.address)} - - - - {t('core:level', { - postProcess: 'capitalizeFirstChar', - })} - : {accountInfo?.level} - - - - {t('group:message.generic.next_level', { - postProcess: 'capitalizeFirstChar', - })}{' '} - {_levelUpBlocks()} - - - - {t('group:message.generic.node_minting', { - postProcess: 'capitalizeFirstChar', - })}{' '} - {nodeInfos?.isMintingPossible?.toString()} - - - - - - {isPartOfMintingGroup && !accountIsMinting && ( - - { - startMinting(); - }} - disabled={mintingAccounts?.length > 1} + - {t('core:action.start_minting', { - postProcess: 'capitalizeFirstChar', + {...a11yProps(0)} + /> + - - {mintingAccounts?.length > 1 && ( - - {t('group:message.generic.minting_keys_per_node', { - postProcess: 'capitalizeFirstChar', - })} - - )} - - )} - - - - {mintingAccounts?.length > 0 && ( - - {t('group:message.generic.node_minting_account', { - postProcess: 'capitalizeFirstChar', - })} - - )} - - {accountIsMinting && ( - - - {t('group:message.generic.node_minting_key', { - postProcess: 'capitalizeFirstChar', - })} - - - )} - - - - {mintingAccounts?.map((acct) => ( - - - {t('group:message.generic.minting_account', { - postProcess: 'capitalizeFirstChar', - })}{' '} - {handleNames(acct?.mintingAccount)} - - - { - removeMintingAccount(acct.publicKey, acct); - }} - variant="contained" - > - {t('group:action.remove_minting_account', { - postProcess: 'capitalizeFirstChar', - })} - + }, + fontSize: '1rem', + }} + {...a11yProps(1)} + /> + + - - - - - ))} - - {mintingAccounts?.length > 1 && ( - - {t('group:message.generic.minting_keys_per_node_different', { - postProcess: 'capitalizeFirstChar', - })} - - )} - - - - - {!isPartOfMintingGroup && ( - - + - - {t('group:message.generic.minter_group', { - postProcess: 'capitalizeFirstChar', - })} - - - - {t('group:message.generic.mintership_app', { - postProcess: 'capitalizeFirstChar', - })} - - - - - { - executeEvent('addTab', { - data: { service: 'APP', name: 'q-mintership' }, - }); - executeEvent('open-apps-mode', {}); - setIsOpenMinting(false); - }} - variant="contained" > - {t('group:action.visit_q_mintership', { - postProcess: 'capitalizeFirstChar', - })} - - - + + alpha(theme.palette.background.paper, 0.5), + p: 3, + mb: 4, + borderRadius: '10px', + }} + > + + {t('core:minting.blockchain_statistics', { + postProcess: 'capitalizeEachFirstChar', + })} + + + + + + + + + + + + + alpha(theme.palette.background.paper, 0.5), + p: 3, + mb: 4, + borderRadius: '10px', + }} + > + + {t('core:minting.account_details', { + postProcess: 'capitalizeEachFirstChar', + })} + + + + + + + + + + + + , + }} + values={{ + level: nextLevel(accountInfo?.level), + count: daysToNextLevel?.toFixed(2), + }} + tOptions={{ postProcess: ['capitalizeFirstChar'] }} + > + + + + + + + alpha(theme.palette.background.paper, 0.5), + p: 3, + borderRadius: '10px', + }} + > + + {t('core:minting.rewards_info', { + postProcess: 'capitalizeEachFirstChar', + })} + + + + + + + + + + + + + > )} - {showWaitDialog && ( - - + - {isShowNext ? 'Confirmed' : 'Please Wait'} - + {isLoading && ( + + + + )} - - {!isShowNext && ( + - {t('group:message.success.rewardshare_creation', { + {t('auth:account.account_one', { + postProcess: 'capitalizeFirstChar', + })} + : {handleNames(accountInfo?.address)} + + + + {t('core:level', { + postProcess: 'capitalizeFirstChar', + })} + : {accountInfo?.level} + + + + + + {isPartOfMintingGroup && !accountIsMinting && ( + + { + 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', + })} + + + {mintingAccounts?.length > 1 && ( + + {t('group:message.generic.minting_keys_per_node', { + postProcess: 'capitalizeFirstChar', + })} + + )} + + )} + + + + {mintingAccounts?.length > 0 && ( + + {t('group:message.generic.node_minting_account', { postProcess: 'capitalizeFirstChar', })} )} - {isShowNext && ( - - {t('group:message.success.rewardshare_confirmed', { - postProcess: 'capitalizeFirstChar', - })} - + + {accountIsMinting && ( + + + {t('group:message.generic.node_minting_key', { + postProcess: 'capitalizeFirstChar', + })} + + + )} + + + + {mintingAccounts?.map((acct) => ( + + + {t('group:message.generic.minting_account', { + postProcess: 'capitalizeFirstChar', + })}{' '} + {handleNames(acct?.mintingAccount)} + + + { + removeMintingAccount(acct.publicKey, acct); + }} + variant="contained" + > + {t('group:action.remove_minting_account', { + postProcess: 'capitalizeFirstChar', + })} + + + + + + + ))} + + {mintingAccounts?.length > 1 && ( + + {t( + 'group:message.generic.minting_keys_per_node_different', + { + postProcess: 'capitalizeFirstChar', + } + )} + + )} + + + + + {!isPartOfMintingGroup && ( + + + + {t('group:message.generic.minter_group', { + postProcess: 'capitalizeFirstChar', + })} + + + + {t('group:message.generic.mintership_app', { + postProcess: 'capitalizeFirstChar', + })} + + + + + { + executeEvent('addTab', { + data: { service: 'APP', name: 'q-mintership' }, + }); + executeEvent('open-apps-mode', {}); + setIsOpenMinting(false); + }} + variant="contained" + > + {t('group:action.visit_q_mintership', { + postProcess: 'capitalizeFirstChar', + })} + + + + )} + + {showWaitDialog && ( + + + {isShowNext + ? t('core:message.generic.confirmed', { + postProcess: 'capitalizeFirstChar', + }) + : t('core:message.generic.wait', { + postProcess: 'capitalizeFirstChar', + })} + + + + {!isShowNext && ( + + {t('group:message.success.rewardshare_creation', { + postProcess: 'capitalizeFirstChar', + })} + + )} + + {isShowNext && ( + + {t('group:message.success.rewardshare_confirmed', { + postProcess: 'capitalizeFirstChar', + })} + + )} + + + + + {t('core:page.next', { + postProcess: 'capitalizeFirstChar', + })} + + + )} - - - - {t('core:page.next', { postProcess: 'capitalizeFirstChar' })} - - - + > )} - + { + if (level === 0) { + return 7200; + } else if (level === 1) { + return 72000; + } else if (level === 2) { + return 201600; + } else if (level === 3) { + return 374400; + } else if (level === 4) { + return 618400; + } else if (level === 5) { + return 964000; + } else if (level === 6) { + return 1482400; + } else if (level === 7) { + return 2173600; + } else if (level === 8) { + return 3037600; + } else if (level === 9) { + return 4074400; + } else { + return undefined; // fallback: should never reach this point + } +}; + +export const nextLevel = (level: number): number | undefined => { + if (level === 0) { + return 1; + } else if (level === 1) { + return 2; + } else if (level === 2) { + return 3; + } else if (level === 3) { + return 4; + } else if (level === 4) { + return 5; + } else if (level === 5) { + return 6; + } else if (level === 6) { + return 7; + } else if (level === 7) { + return 8; + } else if (level === 8) { + return 9; + } else if (level === 9) { + return 10; + } else { + return undefined; // fallback: should never reach this point + } +}; + +export const blockReward = (nodeStatus): number => { + if (nodeStatus.height < 259201) { + return 5.0; + } else if (nodeStatus.height < 518401) { + return 4.75; + } else if (nodeStatus.height < 777601) { + return 4.5; + } else if (nodeStatus.height < 1036801) { + return 4.25; + } else if (nodeStatus.height < 1296001) { + return 4.0; + } else if (nodeStatus.height < 1555201) { + return 3.75; + } else if (nodeStatus.height < 1814401) { + return 3.5; + } else if (nodeStatus.height < 2073601) { + return 3.25; + } else if (nodeStatus.height < 2332801) { + return 3.0; + } else if (nodeStatus.height < 2592001) { + return 2.75; + } else if (nodeStatus.height < 2851201) { + return 2.5; + } else if (nodeStatus.height < 3110401) { + return 2.25; + } else { + return 2.0; + } +}; + +export const currentTier = (level): [string, string] | undefined => { + if (level === 0) { + return ['0', '0']; + } else if (level === 1 || level === 2) { + return ['1', '1 + 2']; + } else if (level === 3 || level === 4) { + return ['2', '3 + 4']; + } else if (level === 5 || level === 6) { + return ['3', '5 + 6']; + } else if (level === 7 || level === 8) { + return ['4', '7 + 8']; + } else if (level === 9 || level === 10) { + return ['5', '9 + 10']; + } else { + return undefined; // fallback: should never reach this point + } +}; + +export const tierPercent = (accountInfo, tier4Online): number => { + if (accountInfo !== null) { + const level = accountInfo.level; + if (level === 0) { + return 0; + } else if (level === 1 || level === 2) { + return 6; + } else if (level === 3 || level === 4) { + return 13; + } else if (level === 5 || level === 6) { + if (tier4Online < 30) { + return 45; + } else { + return 19; + } + } else if (level === 7 || level === 8) { + if (tier4Online < 30) { + return 45; + } else { + return 26; + } + } else if (level === 9 || level === 10) { + return 32; + } + } + return 0; // fallback: should never reach this point +}; + +export const countMintersInLevel = ( + level: number, + addressLevel: AddressLevelEntry[], + tier4Online: number +): number | undefined => { + if (addressLevel && addressLevel.length > 0) { + if (level === 0) { + const countTier0 = addressLevel[0].count; + return countTier0; + } else if (level === 1 || level === 2) { + const countTier1 = addressLevel[1].count + addressLevel[2].count; + return countTier1; + } else if (level === 3 || level === 4) { + const countTier2 = addressLevel[3].count + addressLevel[4].count; + return countTier2; + } else if (level === 5 || level === 6) { + if (tier4Online < 30) { + const countTier3 = + addressLevel[5].count + + addressLevel[6].count + + addressLevel[7].count + + addressLevel[8].count; + return countTier3; + } else { + const countTier3 = addressLevel[5].count + addressLevel[6].count; + return countTier3; + } + } else if (level === 7 || level === 8) { + if (tier4Online < 30) { + const countTier4 = + addressLevel[5].count + + addressLevel[6].count + + addressLevel[7].count + + addressLevel[8].count; + return countTier4; + } else { + const countTier4 = addressLevel[7].count + addressLevel[8].count; + return countTier4; + } + } else if (level === 9 || level === 10) { + const countTier5 = addressLevel[9].count + addressLevel[10].count; + return countTier5; + } + } + + return undefined; // fallback: should never reach this point +}; + +export const countReward = ( + accountInfo, + addressLevel: AddressLevelEntry[], + nodeStatus, + tier4Online: number +): number => { + if (accountInfo != null && addressLevel && addressLevel.length > 0) { + const level = accountInfo.level; + if (level === 0) { + return 0; + } else if (level === 1 || level === 2) { + const countReward12: number = parseFloat( + ( + ((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[1].count + addressLevel[2].count) + ).toFixed(8) + ); + return countReward12; + } else if (level === 3 || level === 4) { + const countReward34 = parseFloat( + ( + ((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[3].count + addressLevel[4].count) + ).toFixed(8) + ); + return countReward34; + } else if (level === 5 || level === 6) { + if (tier4Online < 30) { + const countReward56 = parseFloat( + ( + ((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[5].count + + addressLevel[6].count + + addressLevel[7].count + + addressLevel[8].count) + ).toFixed(8) + ); + return countReward56; + } else { + const countReward56 = parseFloat( + ( + ((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[5].count + addressLevel[6].count) + ).toFixed(8) + ); + return countReward56; + } + } else if (level === 7 || level === 8) { + if (tier4Online < 30) { + const countReward78 = parseFloat( + ( + ((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[5].count + + addressLevel[6].count + + addressLevel[7].count + + addressLevel[8].count) + ).toFixed(8) + ); + return countReward78; + } else { + const countReward78 = parseFloat( + ( + ((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[7].count + addressLevel[8].count) + ).toFixed(8) + ); + return countReward78; + } + } else if (level === 9 || level === 10) { + const countReward910 = parseFloat( + ( + ((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[9].count + addressLevel[10].count) + ).toFixed(8) + ); + return countReward910; + } + } + return 0; // fallback: should never reach this point +}; + +export const countRewardDay = ( + accountInfo, + addressLevel: AddressLevelEntry[], + adminInfo, + nodeHeightBlock, + nodeStatus, + tier4Online: number +): number => { + if (accountInfo != null && addressLevel && addressLevel.length > 0) { + const level = accountInfo.level; + const timeCalc = averageBlockDay(adminInfo, nodeHeightBlock); + if (level === 0) { + return 0; + } else if (level === 1 || level === 2) { + const countRewardDay12 = parseFloat( + ( + (((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[1].count + addressLevel[2].count)) * + timeCalc + ).toFixed(8) + ); + return countRewardDay12; + } else if (level === 3 || level === 4) { + const countRewardDay34 = parseFloat( + ( + (((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[3].count + addressLevel[4].count)) * + timeCalc + ).toFixed(8) + ); + return countRewardDay34; + } else if (level === 5 || level === 6) { + if (this.tier4Online < 30) { + const countRewardDay56 = parseFloat( + ( + (((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[5].count + + addressLevel[6].count + + addressLevel[7].count + + addressLevel[8].count)) * + timeCalc + ).toFixed(8) + ); + return countRewardDay56; + } else { + const countRewardDay56 = parseFloat( + ( + (((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[5].count + addressLevel[6].count)) * + timeCalc + ).toFixed(8) + ); + return countRewardDay56; + } + } else if (level === 7 || level === 8) { + if (this.tier4Online < 30) { + const countRewardDay78 = parseFloat( + ( + (((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[5].count + + addressLevel[6].count + + addressLevel[7].count + + addressLevel[8].count)) * + timeCalc + ).toFixed(8) + ); + return countRewardDay78; + } else { + const countRewardDay78 = parseFloat( + ( + (((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[7].count + addressLevel[8].count)) * + timeCalc + ).toFixed(8) + ); + return countRewardDay78; + } + } else if (level === 9 || level === 10) { + const countRewardDay910 = parseFloat( + ( + (((blockReward(nodeStatus) / 100) * + tierPercent(accountInfo, tier4Online)) / + (addressLevel[9].count + addressLevel[10].count)) * + timeCalc + ).toFixed(8) + ); + return countRewardDay910; + } + } + return 0; // fallback: should never reach this point +}; + +export const mintingStatus = (nodeStatus): string => { + if ( + nodeStatus.isMintingPossible === true && + nodeStatus.isSynchronizing === true + ) { + // this.cssMinting = 'blue'; + return i18n.t('core:minting.status.minting', { + postProcess: 'capitalizeFirstChar', + }); + } else if ( + nodeStatus.isMintingPossible === true && + nodeStatus.isSynchronizing === false + ) { + // this.cssMinting = 'blue'; + return i18n.t('core:minting.status.minting', { + postProcess: 'capitalizeFirstChar', + }); + } else if ( + nodeStatus.isMintingPossible === false && + nodeStatus.isSynchronizing === true + ) { + // this.cssMinting = 'red'; + return i18n.t('core:minting.status.synchronizing', { + postProcess: 'capitalizeFirstChar', + }) + + nodeStatus.syncPercent !== + undefined + ? nodeStatus.syncPercent + '%' + : ''; + } else if ( + nodeStatus.isMintingPossible === false && + nodeStatus.isSynchronizing === false + ) { + // this.cssMinting = 'red'; + return i18n.t('core:minting.status.not_minting', { + postProcess: 'capitalizeFirstChar', + }); + } else { + return i18n.t('core:minting.status.no_status', { + postProcess: 'capitalizeFirstChar', + }); + } +}; + +export const averageBlockTime = (adminInfo, nodeHeightBlock) => { + const avgBlock = adminInfo.currentTimestamp - nodeHeightBlock.timestamp; + const averageTime = avgBlock / 1000 / 1440; + return averageTime; +}; + +export const averageBlockDay = (adminInfo, nodeHeightBlock) => { + const averageBlockDay = 86400 / averageBlockTime(adminInfo, nodeHeightBlock); + return averageBlockDay; +}; + +export const levelUpBlocks = (accountInfo, nodeStatus): number => { + if ( + accountInfo?.blocksMinted === undefined || + nodeStatus?.height === undefined || + accountTargetBlocks(accountInfo?.level) == undefined + ) + return 0; + + const nextBatch = 1000 - (nodeStatus.height % 1000); + const countBlocks = + accountTargetBlocks(accountInfo?.level)! - + (accountInfo?.blocksMinted + accountInfo?.blocksMintedAdjustment) + + 1000; + const countBlocksActual = countBlocks + nextBatch - (countBlocks % 1000); + return countBlocksActual; +}; + +export const levelUpDays = ( + accountInfo, + adminInfo, + nodeHeightBlock, + nodeStatus +): number | undefined => { + if ( + accountInfo?.blocksMinted === undefined || + nodeStatus?.height === undefined || + accountTargetBlocks(accountInfo?.level) == undefined + ) + return undefined; + + const nextBatch = 1000 - (nodeStatus.height % 1000); + const countBlocks = + accountTargetBlocks(accountInfo?.level)! - + (accountInfo?.blocksMinted + accountInfo?.blocksMintedAdjustment) + + 1000; + + const countBlocksActual = countBlocks + nextBatch - (countBlocks % 1000); + const countDays = + countBlocksActual / averageBlockDay(adminInfo, nodeHeightBlock); + return countDays; +}; + +export const dayReward = (adminInfo, nodeHeightBlock, nodeStatus) => { + const reward = + averageBlockDay(adminInfo, nodeHeightBlock) * blockReward(nodeStatus); + return reward; +}; diff --git a/src/components/NotAuthenticated.tsx b/src/components/NotAuthenticated.tsx index e0a06cf..c3ec233 100644 --- a/src/components/NotAuthenticated.tsx +++ b/src/components/NotAuthenticated.tsx @@ -73,7 +73,6 @@ export const NotAuthenticated = ({ const [mode, setMode] = useState('list'); const [customNodes, setCustomNodes] = useState(null); const [importedApiKey, setImportedApiKey] = useState(null); - //add and edit states const [url, setUrl] = useState('https://'); const [customApikey, setCustomApiKey] = useState(''); const [showSelectApiKey, setShowSelectApiKey] = useState(false); @@ -82,7 +81,13 @@ export const NotAuthenticated = ({ const { showTutorial, hasSeenGettingStarted } = useContext(QORTAL_APP_CONTEXT); const theme = useTheme(); - const { t } = useTranslation(['auth', 'core']); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const importedApiKeyRef = useRef(null); const currentNodeRef = useRef(null); @@ -515,9 +520,9 @@ export const NotAuthenticated = ({ <> @@ -782,24 +783,22 @@ export const NotAuthenticated = ({ - {t('auth:node.custom_many', { postProcess: 'capitalizeFirstChar' })} - : + {t('auth:node.custom_many', { postProcess: 'capitalizeAll' })} {mode === 'list' && ( @@ -819,7 +818,6 @@ export const NotAuthenticated = ({ > @@ -828,10 +826,10 @@ export const NotAuthenticated = ({ @@ -894,10 +891,10 @@ export const NotAuthenticated = ({ { const [ltcPerQort, setLtcPerQort] = useState(null); - const [supply, setSupply] = useState(null); - const [lastBlock, setLastBlock] = useState(null); + const [supply, setSupply] = useState(''); + const [lastBlock, setLastBlock] = useState(''); const [loading, setLoading] = useState(true); const { t } = useTranslation(['core', 'tutorial']); const theme = useTheme(); @@ -45,12 +47,10 @@ export const QortPrice = () => { const getPrice = useCallback(async () => { try { setLoading(true); - const response = await fetch( `${getBaseApiReact()}/crosschain/trades?foreignBlockchain=LITECOIN&minimumTimestamp=${getTwoWeeksAgoTimestamp()}&limit=20&reverse=true` ); const data = await response.json(); - setLtcPerQort(getAverageLtcPerQort(data)); } catch (error) { console.error(error); @@ -62,10 +62,8 @@ export const QortPrice = () => { const getLastBlock = useCallback(async () => { try { setLoading(true); - const response = await fetch(`${getBaseApiReact()}/blocks/last`); const data = await response.json(); - setLastBlock(data); } catch (error) { console.error(error); @@ -77,13 +75,11 @@ export const QortPrice = () => { const getSupplyInCirculation = useCallback(async () => { try { setLoading(true); - const response = await fetch( `${getBaseApiReact()}/stats/supply/circulating` ); const data = await response.text(); - formatWithCommasAndDecimals(data); - setSupply(formatWithCommasAndDecimals(data)); + setSupply(formatWithCommasAndDecimals(parseFloat(data))); } catch (error) { console.error(error); } finally { @@ -249,7 +245,7 @@ export const QortPrice = () => { fontSize: '1rem', }} > - {lastBlock?.height} + {formatWithCommasAndDecimals(lastBlock?.height)} )} diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index c6f20a4..faa6be5 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -293,9 +293,9 @@ export const RegisterName = ({ {isNameAvailable === Availability.AVAILABLE && ( { const [memberGroups] = useAtom(memberGroupsAtom); - const [txList, setTxList] = useAtom(txListAtom); - const [open, setOpen] = React.useState(false); const intervals = useRef({}); const theme = useTheme(); + const { t } = useTranslation([ + 'auth', + 'core', + 'group', + 'question', + 'tutorial', + ]); const handleClick = () => { setOpen((prev) => !prev); @@ -199,7 +205,11 @@ export const TaskManager = ({ getUserInfo }) => { )} - + {open ? : } diff --git a/src/components/Theme/ThemeContext.tsx b/src/components/Theme/ThemeContext.tsx index 8ce2c6a..3044045 100644 --- a/src/components/Theme/ThemeContext.tsx +++ b/src/components/Theme/ThemeContext.tsx @@ -41,21 +41,16 @@ export const ThemeProvider = ({ children }) => { userThemes.find((theme) => theme.id === currentThemeId) || defaultTheme; const muiTheme = useMemo(() => { - if (themeMode === 'light') { - return createTheme({ - ...lightThemeOptions, - palette: { - ...currentTheme.light, - }, - }); - } else { - return createTheme({ - ...lightThemeOptions, - palette: { - ...currentTheme.dark, - }, - }); - } + const baseThemeOptions = + themeMode === 'light' ? lightThemeOptions : darkThemeOptions; + + const palette = + themeMode === 'light' ? currentTheme.light : currentTheme.dark; + + return createTheme({ + ...baseThemeOptions, + palette, + }); }, [themeMode, currentTheme]); const saveSettings = ( diff --git a/src/components/Theme/ThemeManager.tsx b/src/components/Theme/ThemeManager.tsx index d5e3087..f5d89f3 100644 --- a/src/components/Theme/ThemeManager.tsx +++ b/src/components/Theme/ThemeManager.tsx @@ -306,7 +306,6 @@ export default function ThemeManager() { { return ( - - - {themeMode === 'dark' ? : } - - + {themeMode === 'dark' ? : } + ); }; diff --git a/src/components/Tutorials/Tutorials.tsx b/src/components/Tutorials/Tutorials.tsx index 4075b67..6b41586 100644 --- a/src/components/Tutorials/Tutorials.tsx +++ b/src/components/Tutorials/Tutorials.tsx @@ -41,15 +41,12 @@ export const Tutorials = () => { > setMultiNumber(value)} - aria-label={t('core:basic_tabs_example', { - postProcess: 'capitalizeFirstChar', - })} > {openTutorialModal?.multi?.map((item, index) => { return ( @@ -75,10 +72,11 @@ export const Tutorials = () => { })} onClick={handleClose} sx={{ + bgcolor: theme.palette.background.default, + color: theme.palette.text.primary, position: 'absolute', right: 8, top: 8, - color: theme.palette.text.primary, }} > @@ -123,10 +121,11 @@ export const Tutorials = () => { })} onClick={handleClose} sx={{ + bgcolor: theme.palette.background.default, + color: theme.palette.text.primary, position: 'absolute', right: 8, top: 8, - color: theme.palette.text.primary, }} > diff --git a/src/components/UserLookup.tsx/UserLookup.tsx b/src/components/UserLookup.tsx/UserLookup.tsx index b58d616..bab5370 100644 --- a/src/components/UserLookup.tsx/UserLookup.tsx +++ b/src/components/UserLookup.tsx/UserLookup.tsx @@ -18,6 +18,8 @@ import { CircularProgress, useTheme, Autocomplete, + IconButton, + ClickAwayListener, } from '@mui/material'; import { getAddressInfo, @@ -28,7 +30,7 @@ import { getNameInfo } from '../Group/Group'; import AccountCircleIcon from '@mui/icons-material/AccountCircle'; import { Spacer } from '../../common/Spacer'; import { formatTimestamp } from '../../utils/time'; -import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen'; +import CloseIcon from '@mui/icons-material/Close'; import { executeEvent, subscribeToEvent, @@ -160,489 +162,481 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => { return ( - - - { - 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) => ( - { - if (e.key === 'Enter' && nameOrAddress) { - lookupFunc(inputValue); - } - }} - /> - )} - /> - - { - onClose(); - }} - > - - - - + - {!isLoadingUser && errorMessage && ( - + { + if (!newValue) { + setNameOrAddress(''); + return; + } + setNameOrAddress(newValue); + lookupFunc(newValue); }} - > - {errorMessage} - - )} - - {isLoadingUser && ( - { + setInputValue(newInputValue); }} - > - - - )} + id="controllable-states-demo" + loading={isLoading} + noOptionsText={t('core:option_no', { + postProcess: 'capitalizeFirstChar', + })} + options={options} + sx={{ flexGrow: 1 }} + renderInput={(params) => ( + { + if (e.key === 'Enter' && nameOrAddress) { + lookupFunc(inputValue); + } + }} + /> + )} + /> + - {!isLoadingUser && addressInfo && ( - <> - + + {!isLoadingUser && errorMessage && ( - {errorMessage} + + )} + + {isLoadingUser && ( + + + + )} + + {!isLoadingUser && addressInfo && ( + <> + + - - {addressInfo?.name ?? - t('auth:message.error.name_not_registered', { - postProcess: 'capitalizeFirstChar', - })} - + + {addressInfo?.name ?? + t('auth:message.error.name_not_registered', { + postProcess: 'capitalizeFirstChar', + })} + - + - - {addressInfo?.name ? ( - + + {addressInfo?.name ? ( + + + + ) : ( - - ) : ( - - )} - + )} + - + - - {t('core:level', { postProcess: 'capitalizeFirstChar' })}{' '} - {addressInfo?.level} - - - - - + {t('core:level', { postProcess: 'capitalizeFirstChar' })}{' '} + {addressInfo?.level} + + + + + + + {t('auth:address', { + postProcess: 'capitalizeFirstChar', + })} + + + + + {t('auth:action.copy_address', { + postProcess: 'capitalizeFirstChar', + })} + + } + 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, + }, + }, + }} + > + { + navigator.clipboard.writeText(addressInfo?.address); + }} + > + + {addressInfo?.address} + + + + + + - {t('auth:address', { + {t('core:balance', { postProcess: 'capitalizeFirstChar', })} + + {addressInfo?.balance} - - {t('auth:action.copy_address', { - postProcess: 'capitalizeFirstChar', - })} - - } - 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, - }, - }, + + + { + executeEvent('openPaymentInternal', { + address: addressInfo?.address, + name: addressInfo?.name, + }); }} > - { - navigator.clipboard.writeText(addressInfo?.address); - }} - > - - {addressInfo?.address} - - - - + {t('core:action.send_qort', { + postProcess: 'capitalizeFirstChar', + })} + + + + > + )} + + + {isLoadingPayments && ( + + + + )} + + {!isLoadingPayments && addressInfo && ( + + + {t('core:message.generic.most_recent_payment', { + count: 20, + postProcess: 'capitalizeFirstChar', + })} + + + + + {!isLoadingPayments && payments?.length === 0 && ( - {t('core:balance', { + {t('core:message.generic.no_payments', { postProcess: 'capitalizeFirstChar', })} - - {addressInfo?.balance} + )} - - - { - executeEvent('openPaymentInternal', { - address: addressInfo?.address, - name: addressInfo?.name, - }); - }} - > - {t('core:action.send_qort', { - postProcess: 'capitalizeFirstChar', - })} - - - - > - )} - - - - {isLoadingPayments && ( - - - - )} - - {!isLoadingPayments && addressInfo && ( - - - {t('core:message.generic.most_recent_payment', { - count: 20, - postProcess: 'capitalizeFirstChar', - })} - - - - - {!isLoadingPayments && payments?.length === 0 && ( - - - {t('core:message.generic.no_payments', { - postProcess: 'capitalizeFirstChar', - })} - - - )} - - - - - - {t('core:sender', { postProcess: 'capitalizeFirstChar' })} - - - {t('core:receiver', { - postProcess: 'capitalizeFirstChar', - })} - - - {t('core:amount', { postProcess: 'capitalizeFirstChar' })} - - - {t('core:time.time', { - postProcess: 'capitalizeFirstChar', - })} - - - - - - {payments.map((payment, index) => ( - + + + - - {t('auth:action.copy_address', { - postProcess: 'capitalizeFirstChar', - })} - - } - 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, - }, - }, - }} - > - { - navigator.clipboard.writeText( - payment?.creatorAddress - ); - }} - > - {formatAddress(payment?.creatorAddress)} - - + {t('core:sender', { + postProcess: 'capitalizeFirstChar', + })} - - - {t('auth:action.copy_address', { - postProcess: 'capitalizeFirstChar', - })} - - } - 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, - }, - }, - }} - > - { - navigator.clipboard.writeText(payment?.recipient); - }} - > - {formatAddress(payment?.recipient)} - - + {t('core:receiver', { + postProcess: 'capitalizeFirstChar', + })} - - {payment?.amount} - - {formatTimestamp(payment?.timestamp)} + {t('core:amount', { + postProcess: 'capitalizeFirstChar', + })} + + + {t('core:time.time', { + postProcess: 'capitalizeFirstChar', + })} - ))} - - - - )} + + + + {payments.map((payment, index) => ( + + + + {t('auth:action.copy_address', { + postProcess: 'capitalizeFirstChar', + })} + + } + 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, + }, + }, + }} + > + { + navigator.clipboard.writeText( + payment?.creatorAddress + ); + }} + > + {formatAddress(payment?.creatorAddress)} + + + + + + + {t('auth:action.copy_address', { + postProcess: 'capitalizeFirstChar', + })} + + } + 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, + }, + }, + }} + > + { + navigator.clipboard.writeText( + payment?.recipient + ); + }} + > + {formatAddress(payment?.recipient)} + + + + + {payment?.amount} + + + {formatTimestamp(payment?.timestamp)} + + + ))} + + + + )} + - + ); }; diff --git a/src/components/Wallets.tsx b/src/components/Wallets.tsx index aef22b3..c058677 100644 --- a/src/components/Wallets.tsx +++ b/src/components/Wallets.tsx @@ -207,7 +207,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { if (isLoading) return null; return ( - + {wallets?.length === 0 || !wallets ? ( <> @@ -469,7 +469,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { - + ); }; @@ -562,6 +562,8 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { { e.stopPropagation(); diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 1e79aab..122b8fd 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -4,6 +4,7 @@ import HttpBackend from 'i18next-http-backend'; import LanguageDetector from 'i18next-browser-languagedetector'; import { capitalizeAll, + capitalizeEachFirstChar, capitalizeFirstChar, capitalizeFirstWord, } from './processors'; @@ -42,6 +43,7 @@ i18n .use(initReactI18next) .use(LanguageDetector) .use(capitalizeAll as any) + .use(capitalizeEachFirstChar as any) .use(capitalizeFirstChar as any) .use(capitalizeFirstWord as any) .init({ diff --git a/src/i18n/locales/de/core.json b/src/i18n/locales/de/core.json index 8ae5243..51eb668 100644 --- a/src/i18n/locales/de/core.json +++ b/src/i18n/locales/de/core.json @@ -116,7 +116,6 @@ "apps_official": "offizielle Apps", "attachment": "Anhang", "balance": "Gleichgewicht:", - "basic_tabs_example": "Basic Tabs Beispiel", "category": "Kategorie", "category_other": "Kategorien", "chat": "Chat", @@ -256,6 +255,7 @@ "no_pinned_changes": "Sie haben derzeit keine Änderungen an Ihren angestellten Apps", "no_results": "Keine Ergebnisse", "one_app_per_name": "Hinweis: Derzeit ist pro Namen nur eine App und Website zulässig.", + "ongoing_transactions": "laufende Transaktionen", "opened": "geöffnet", "overwrite_qdn": "überschreibt zu QDN", "password_confirm": "Bitte bestätigen Sie ein Passwort", @@ -280,8 +280,9 @@ "settings": "Sie verwenden den Export-/Import -Weg zum Speichern von Einstellungen.", "space_for_admins": "Entschuldigung, dieser Raum gilt nur für Administratoren.", "unread_messages": "ungelesene Nachrichten unten", - "unsaved_changes": "Sie haben nicht gespeicherte Änderungen an Ihren angestellten Apps. Speichern Sie sie bei QDN.", - "updating": "Aktualisierung" + "unsaved_changes": "sie haben nicht gespeicherte Änderungen an Ihren angestellten Apps. Speichern Sie sie bei QDN.", + "updating": "aktualisierung", + "wait": "bitte warten" }, "message": "Nachricht", "promotion_text": "Promotionstext", @@ -302,12 +303,6 @@ "reset_qdn": "Mögen Sie Ihre aktuellen lokalen Änderungen nicht? Möchten Sie auf Ihre gespeicherten QDN -Apps zurücksetzen?", "transfer_qort": "would you like to transfer {{ amount }} QORT" }, - "status": { - "minting": "(Prägung)", - "not_minting": "(nicht punktieren)", - "synchronized": "synchronisiert", - "synchronizing": "Synchronisierung" - }, "success": { "order_submitted": "Ihre Kaufbestellung wurde eingereicht", "published": "erfolgreich veröffentlicht. Bitte warten Sie ein paar Minuten, bis das Netzwerk die Änderungen vorschreibt.", @@ -318,7 +313,34 @@ "voted": "erfolgreich abgestimmt. Bitte warten Sie ein paar Minuten, bis das Netzwerk die Änderungen vorschreibt." } }, - "minting_status": "Münzstatus", + "minting": { + "account_details": "Minting-Kontodetails", + "actions": "Minting-Aktionen", + "average_blocktime": "durchschnittliche Qortal-Blockzeit", + "average_blocks_per_day": "durchschnittliche Blöcke pro Tag", + "average_created_qorts_per_day": "durchschnittlich erstellte QORTs pro Tag", + "blockchain_statistics": "Blockchain-Statistiken", + "blocks_next_level": "Blöcke bis zum nächsten Level", + "current_level": "aktuelles Level", + "current_status": "aktueller Status", + "current_tier": "aktuelle Stufe", + "current_tier_content": "{{ tier }} (Stufe {{ levels }})", + "details": "Minting-Details", + "next_level": "Mit 24/7-Minting erreichst du Level {{ level }} in {{ count }} Tagen", + "rewards_info": "Informationen zu Minting-Belohnungen", + "reward_per_block": "geschätzte Belohnung pro Block", + "reward_per_day": "geschätzte Belohnung pro Tag", + "status": { + "minting": "(Prägung)", + "not_minting": "(nicht punktieren)", + "no_status": "kein Status", + "synchronized": "synchronisiert", + "synchronizing": "Synchronisierung..." + }, + "status_title": "Münzstatus", + "tier_share_per_block": "Stufenanteil pro Block", + "total_minter_in_tier": "Gesamtanzahl der Minter in dieser Stufe" + }, "name": "Name", "name_app": "Name/App", "new_post_in": "new post in {{ title }}", diff --git a/src/i18n/locales/de/group.json b/src/i18n/locales/de/group.json index 314adb4..344b492 100644 --- a/src/i18n/locales/de/group.json +++ b/src/i18n/locales/de/group.json @@ -91,8 +91,6 @@ "minting_account": "Minting-Konto:", "minting_keys_per_node": "Nur 2 Minting-Schlüssel pro Node erlaubt. Bitte entferne einen.", "minting_keys_per_node_different": "Nur 2 Minting-Schlüssel pro Node erlaubt. Entferne einen, um einen anderen hinzuzufügen.", - "next_level": "Verbleibende Blöcke bis zum nächsten Level:", - "node_minting": "Dieser Node mintet:", "node_minting_account": "Minting-Konten dieses Nodes", "node_minting_key": "Du hast bereits einen Minting-Schlüssel für dieses Konto auf diesem Node", "no_announcement": "Keine Ankündigungen", diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index a5ca275..1ef7b47 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -116,7 +116,6 @@ "apps_official": "official Apps", "attachment": "attachment", "balance": "balance:", - "basic_tabs_example": "basic tabs example", "category": "category", "category_other": "categories", "chat": "chat", @@ -218,25 +217,26 @@ "already_voted": "you've already voted.", "avatar_size": "{{ size }} KB max. for GIFS", "benefits_qort": "benefits of having QORT", - "building": "building", "building_app": "building app", - "created_by": "created by {{ owner }}", + "building": "building", "buy_order_request": "the Application {{hostname}} is requesting {{count}} buy order", "buy_order_request_other": "the Application {{hostname}} is requesting {{count}} buy orders", + "confirmed": "confirmed", + "created_by": "created by {{ owner }}", "devmode_local_node": "please use your local node for dev mode! Logout and use Local node.", - "downloading": "downloading", "downloading_decrypting_app": "downloading and decrypting private app.", + "downloading": "downloading", "edited": "edited", "editing_message": "editing message", - "encrypted": "encrypted", "encrypted_not": "not encrypted", + "encrypted": "encrypted", "fee_qort": "fee: {{ message }} QORT", "fetching_data": "fetching app data", "foreign_fee": "foreign fee: {{ message }}", "get_qort_trade_portal": "get QORT using Qortal's crosschain trade portal", - "minimal_qort_balance": "having at least {{ quantity }} QORT in your balance (4 qort balance for chat, 1.25 for name, 0.75 for some transactions)", "mentioned": "mentioned", "message_with_image": "this message already has an image", + "minimal_qort_balance": "having at least {{ quantity }} QORT in your balance (4 qort balance for chat, 1.25 for name, 0.75 for some transactions)", "most_recent_payment": "{{ count }} most recent payment", "name_available": "{{ name }} is available", "name_benefits": "benefits of a name", @@ -256,6 +256,7 @@ "no_pinned_changes": "you currently do not have any changes to your pinned apps", "no_results": "no results", "one_app_per_name": "note: Currently, only one App and Website is allowed per Name.", + "ongoing_transactions": "ongoing transactions", "opened": "opened", "overwrite_qdn": "overwrite to QDN", "password_confirm": "please confirm a password", @@ -281,7 +282,8 @@ "space_for_admins": "sorry, this space is only for Admins.", "unread_messages": "unread messages below", "unsaved_changes": "you have unsaved changes to your pinned apps. Save them to QDN.", - "updating": "updating" + "updating": "updating", + "wait": "please wait" }, "message": "message", "promotion_text": "promotion text", @@ -302,12 +304,7 @@ "reset_qdn": "don't like your current local changes? Would you like to reset to your saved QDN pinned apps?", "transfer_qort": "would you like to transfer {{ amount }} QORT" }, - "status": { - "minting": "(minting)", - "not_minting": "(not minting)", - "synchronized": "synchronized", - "synchronizing": "synchronizing" - }, + "success": { "order_submitted": "your buy order was submitted", "published": "successfully published. Please wait a couple minutes for the network to propogate the changes.", @@ -318,7 +315,34 @@ "voted": "successfully voted. Please wait a couple minutes for the network to propogate the changes." } }, - "minting_status": "minting status", + "minting": { + "account_details": "minting account details", + "actions": "minting actions", + "average_blocktime": "average qortal blocktime", + "average_blocks_per_day": "average blocks per day", + "average_created_qorts_per_day": "average created QORT per day", + "blockchain_statistics": "blockchain statistics", + "blocks_next_level": "blocks to nexl level", + "current_level": "current level", + "current_status": "current status", + "current_tier": "current tier", + "current_tier_content": "{{ tier }} (Levels {{ levels }})", + "details": "minting details", + "next_level": "with a 24/7 minting you will reach level {{ level }} in {{ count }} days", + "rewards_info": "minting rewards info", + "reward_per_block": "estimated reward per block", + "reward_per_day": "estimated reward per day", + "status": { + "minting": "(minting)", + "not_minting": "(not minting)", + "no_status": "no status", + "synchronized": "synchronized", + "synchronizing": "synchronizing..." + }, + "status_title": "minting status", + "tier_share_per_block": "tier share per block", + "total_minter_in_tier": "total minters in the tier" + }, "name": "name", "name_app": "name/App", "new_post_in": "new post in {{ title }}", diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index 34731c1..4dfc636 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -91,8 +91,6 @@ "minting_account": "minting account:", "minting_keys_per_node": "only 2 minting keys are allowed per node. Please remove one if you would like to mint with this account.", "minting_keys_per_node_different": "only 2 minting keys are allowed per node. Please remove one if you would like to add a different account.", - "next_level": "blocks remaining until next level:", - "node_minting": "this node is minting:", "node_minting_account": "node's minting accounts", "node_minting_key": "you currently have a minting key for this account attached to this node", "no_announcement": "no announcements", diff --git a/src/i18n/locales/es/core.json b/src/i18n/locales/es/core.json index c87b5ab..2fe2500 100644 --- a/src/i18n/locales/es/core.json +++ b/src/i18n/locales/es/core.json @@ -116,7 +116,6 @@ "apps_official": "aplicaciones oficiales", "attachment": "adjunto", "balance": "balance:", - "basic_tabs_example": "Ejemplo de pestañas básicas", "category": "categoría", "category_other": "categorías", "chat": "charlar", @@ -219,7 +218,8 @@ "avatar_size": "{{ size }} KB max. for GIFS", "benefits_qort": "beneficios de tener Qort", "building": "edificio", - "building_app": "Aplicación de construcción", + "building_app": "aplicación de construcción", + "confirmed": "confirmado", "created_by": "created by {{ owner }}", "buy_order_request": "the Application {{hostname}} is requesting {{count}} buy order", "buy_order_request_other": "the Application {{hostname}} is requesting {{count}} buy orders", @@ -256,6 +256,7 @@ "no_pinned_changes": "Actualmente no tiene ningún cambio en sus aplicaciones fijadas", "no_results": "Sin resultados", "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", "overwrite_qdn": "sobrescribir a QDN", "password_confirm": "Confirme una contraseña", @@ -277,11 +278,12 @@ "select_image": "Seleccione una imagen para un logotipo", "select_zip": "Seleccione el archivo .zip que contenga contenido estático:", "sending": "envío...", - "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.", - "unread_messages": "Mensajes no leídos a continuación", - "unsaved_changes": "Tiene cambios no salvos en sus aplicaciones fijadas. Guárdelos a QDN.", - "updating": "actualización" + "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.", + "unread_messages": "mensajes no leídos a continuación", + "unsaved_changes": "tiene cambios no salvos en sus aplicaciones fijadas. Guárdelos a QDN.", + "updating": "actualización", + "wait": "espera por favor" }, "message": "mensaje", "promotion_text": "texto de promoción", @@ -302,12 +304,6 @@ "reset_qdn": "¿No te gustan tus cambios locales actuales? ¿Le gustaría restablecer a sus aplicaciones guardadas de QDN guardadas?", "transfer_qort": "would you like to transfer {{ amount }} QORT" }, - "status": { - "minting": "(acuñado)", - "not_minting": "(no acuñar)", - "synchronized": "sincronizado", - "synchronizing": "sincronización" - }, "success": { "order_submitted": "Su pedido de compra fue enviado", "published": "publicado con éxito. Espere un par de minutos para que la red propoque los cambios.", @@ -318,7 +314,34 @@ "voted": "votado con éxito. Espere un par de minutos para que la red propoque los cambios." } }, - "minting_status": "estado de acuñación", + "minting": { + "account_details": "detalles de la cuenta de minting", + "actions": "acciones de minting", + "average_blocktime": "tiempo promedio por bloque de Qortal", + "average_blocks_per_day": "promedio de bloques por día", + "average_created_qorts_per_day": "promedio de QORT creados por día", + "blockchain_statistics": "estadísticas de la blockchain", + "blocks_next_level": "bloques para el siguiente nivel", + "current_level": "nivel actual", + "current_status": "estado actual", + "current_tier": "nivel actual", + "current_tier_content": "{{ tier }} (Nivels {{ levels }})", + "details": "detalles de minting", + "next_level": "Con minting 24/7 alcanzarás el nivel {{ level }} en {{ count }} días", + "rewards_info": "información sobre recompensas de minting", + "reward_per_block": "recompensa estimada por bloque", + "reward_per_day": "recompensa estimada por día", + "status": { + "minting": "(acuñado)", + "not_minting": "(no acuñar)", + "no_status": "sin estado", + "synchronized": "sincronizado", + "synchronizing": "sincronización..." + }, + "status_title": "estado de acuñación", + "tier_share_per_block": "porción del nivel por bloque", + "total_minter_in_tier": "total de minters en el nivel" + }, "name": "nombre", "name_app": "nombre/aplicación", "new_post_in": "new post in {{ title }}", @@ -393,4 +416,4 @@ }, "website": "sitio web", "welcome": "bienvenido" -} \ No newline at end of file +} diff --git a/src/i18n/locales/es/group.json b/src/i18n/locales/es/group.json index a5165bc..a5b49b2 100644 --- a/src/i18n/locales/es/group.json +++ b/src/i18n/locales/es/group.json @@ -91,8 +91,6 @@ "minting_account": "cuenta de minteo:", "minting_keys_per_node": "solo se permiten 2 claves de minteo por nodo. Elimina una para usar esta cuenta.", "minting_keys_per_node_different": "solo se permiten 2 claves de minteo por nodo. Elimina una para añadir otra cuenta.", - "next_level": "bloques restantes para el siguiente nivel:", - "node_minting": "este nodo está minteando:", "node_minting_account": "cuentas de minteo del nodo", "node_minting_key": "ya tienes una clave de minteo para esta cuenta en este nodo", "no_announcement": "no hay anuncios", diff --git a/src/i18n/locales/fr/core.json b/src/i18n/locales/fr/core.json index da8ad93..de469e4 100644 --- a/src/i18n/locales/fr/core.json +++ b/src/i18n/locales/fr/core.json @@ -116,7 +116,6 @@ "apps_official": "Applications officielles", "attachment": "pièce jointe", "balance": "équilibre:", - "basic_tabs_example": "Exemple de base des onglets", "category": "catégorie", "category_other": "catégories", "chat": "chat", @@ -220,6 +219,7 @@ "benefits_qort": "Avantages d'avoir QORT", "building": "bâtiment", "building_app": "application de construction", + "confirmed": "confirmé", "created_by": "created by {{ owner }}", "buy_order_request": "the Application {{hostname}} is requesting {{count}} buy order", "buy_order_request_other": "the Application {{hostname}} is requesting {{count}} buy orders", @@ -256,6 +256,7 @@ "no_pinned_changes": "Vous n'avez actuellement aucune modification à vos applications épinglées", "no_results": "Aucun résultat", "one_app_per_name": "Remarque: Actuellement, une seule application et site Web est autorisée par nom.", + "ongoing_transactions": "transactions en cours", "opened": "ouvert", "overwrite_qdn": "Écraser à QDN", "password_confirm": "Veuillez confirmer un mot de passe", @@ -277,11 +278,12 @@ "select_image": "Veuillez sélectionner une image pour un logo", "select_zip": "Sélectionnez un fichier .zip contenant du contenu statique:", "sending": "envoi...", - "settings": "Vous utilisez la manière d'exportation / l'importation d'enregistrer les paramètres.", - "space_for_admins": "Désolé, cet espace est uniquement pour les administrateurs.", - "unread_messages": "Messages non lus ci-dessous", - "unsaved_changes": "Vous avez des modifications non enregistrées à vos applications épinglées. Enregistrez-les sur QDN.", - "updating": "mise à jour" + "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.", + "unread_messages": "messages non lus ci-dessous", + "unsaved_changes": "vous avez des modifications non enregistrées à vos applications épinglées. Enregistrez-les sur QDN.", + "updating": "mise à jour", + "wait": "attendez s'il vous plaît" }, "message": "message", "promotion_text": "texte de promotion", @@ -302,12 +304,6 @@ "reset_qdn": "Vous n'aimez pas vos changements locaux actuels? Souhaitez-vous réinitialiser avec vos applications QDN enregistrées?", "transfer_qort": "would you like to transfer {{ amount }} QORT" }, - "status": { - "minting": "(Coupure)", - "not_minting": "(pas de la frappe)", - "synchronized": "synchronisé", - "synchronizing": "synchronisation" - }, "success": { "order_submitted": "Votre commande d'achat a été soumise", "published": "publié avec succès. Veuillez patienter quelques minutes pour que le réseau propage les modifications.", @@ -318,7 +314,34 @@ "voted": "voté avec succès. Veuillez patienter quelques minutes pour que le réseau propage les modifications." } }, - "minting_status": "statut de frappe", + "minting": { + "account_details": "détails du compte de minting", + "actions": "actions de minting", + "average_blocktime": "temps moyen par bloc Qortal", + "average_blocks_per_day": "blocs moyens par jour", + "average_created_qorts_per_day": "QORT créés en moyenne par jour", + "blockchain_statistics": "statistiques de la blockchain", + "blocks_next_level": "blocs jusqu'au niveau suivant", + "current_level": "niveau actuel", + "current_status": "statut actuel", + "current_tier": "niveau actuel", + "current_tier_content": "{{ tier }} (Niveaux {{ levels }})", + "details": "détails du minting", + "next_level": "Avec un minting 24/7, vous atteindrez le niveau {{ level }} en {{ count }} jours", + "rewards_info": "informations sur les récompenses de minting", + "reward_per_block": "récompense estimée par bloc", + "reward_per_day": "récompense estimée par jour", + "status": { + "minting": "(Frappe)", + "not_minting": "(pas de la frappe)", + "no_status": "pas de statut", + "synchronized": "synchronisé", + "synchronizing": "synchronisation..." + }, + "status_title": "statut de frappe", + "tier_share_per_block": "part du niveau par bloc", + "total_minter_in_tier": "nombre total de minters dans ce niveau" + }, "name": "nom", "name_app": "nom / application", "new_post_in": "new post in {{ title }}", @@ -393,4 +416,4 @@ }, "website": "site web", "welcome": "accueillir" -} \ No newline at end of file +} diff --git a/src/i18n/locales/fr/group.json b/src/i18n/locales/fr/group.json index 1f1dcba..d4edb9b 100644 --- a/src/i18n/locales/fr/group.json +++ b/src/i18n/locales/fr/group.json @@ -91,8 +91,6 @@ "minting_account": "compte de minage :", "minting_keys_per_node": "2 clés de minage max par nœud. Veuillez en retirer une.", "minting_keys_per_node_different": "2 clés de minage max par nœud. Retirez-en une pour ajouter un autre compte.", - "next_level": "blocs restants avant le niveau suivant :", - "node_minting": "ce nœud est en train de miner :", "node_minting_account": "comptes de minage du nœud", "node_minting_key": "vous avez déjà une clé de minage attachée à ce nœud", "no_announcement": "aucune annonce", diff --git a/src/i18n/locales/it/auth.json b/src/i18n/locales/it/auth.json index de4f857..e5b1f6e 100644 --- a/src/i18n/locales/it/auth.json +++ b/src/i18n/locales/it/auth.json @@ -92,7 +92,7 @@ "seedphrase_notice": "È stato generato una SEED PHRASE in background.", "turn_local_node": "si prega di attivare il nodo locale", "type_seed": "digita o incolla la seed phrase", - "your_accounts": "i tuoi conti salvati" + "your_accounts": "i tuoi account salvati" }, "success": { "reencrypted_secret_key": "chiave segreta recriptata con successo. Potrebbero essere necessari un paio di minuti per propagare le modifiche. Aggiorna il gruppo fra 5 minuti." diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 10e1706..8010bdc 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -116,7 +116,6 @@ "apps_official": "app ufficiali", "attachment": "allegato", "balance": "bilancio:", - "basic_tabs_example": "esempio di schede base", "category": "categoria", "category_other": "categorie", "chat": "chat", @@ -220,6 +219,7 @@ "benefits_qort": "vantaggi di avere QORT", "building": "creazione", "building_app": "creazione app", + "confirmed": "confermato", "created_by": "creato da {{ owner }}", "buy_order_request": "l'applicazione {{hostname}} sta effettuando {{count}} ordine d'acquisto.", "buy_order_request_other": "l'applicazione {{hostname}} sta effettuando {{count}} ordini d'acquisto.", @@ -255,6 +255,7 @@ "no_pinned_changes": "per ora non ci sono modifiche alle app bloccate", "no_results": "nessun risultato", "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", "overwrite_qdn": "sovrascrivi a QDN", "password_confirm": "si prega di confermare una password", @@ -280,7 +281,8 @@ "space_for_admins": "mi dispiace, questo spazio è solo per gli amministratori.", "unread_messages": "messaggi non letti qua sotto", "unsaved_changes": "hai cambiato modifiche alle app bloccate. Salvali su QDN.", - "updating": "aggiornamento" + "updating": "aggiornamento", + "wait": "attendere per favore" }, "message": "messaggio", "promotion_text": "testo di promozione", @@ -301,12 +303,6 @@ "reset_qdn": "non ti piacciono le attuali modifiche locali? Vorresti ripristinare le app QDN salvate?", "transfer_qort": "vuoi trasferire {{ amount }} QORT?" }, - "status": { - "minting": "(minting)", - "not_minting": "(non minting)", - "synchronized": "sincronizzato", - "synchronizing": "sincronizzazione" - }, "success": { "order_submitted": "l'ordine di acquisto è stato inviato", "published": "pubblicato con successo. Si prega di attendere un paio di minuti affinché la rete propaghi le modifiche.", @@ -317,7 +313,34 @@ "voted": "votato con successo. Si prega di attendere un paio di minuti affinché la rete propaghi le modifiche." } }, - "minting_status": "stato minting", + "minting": { + "account_details": "dettagli dell'account di minting", + "actions": "azioni sul minting", + "average_blocktime": "tempo medio per blocco di Qortal", + "average_blocks_per_day": "blocchi medi al giorno", + "average_created_qorts_per_day": "QORT medi creati al giorno", + "blockchain_statistics": "statistiche della blockchain", + "blocks_next_level": "blocchi al prossimo livello", + "current_level": "livello attuale", + "current_status": "stato attuale", + "current_tier": "fascia attuale", + "current_tier_content": "{{ tier }} (Livelli {{ levels }})", + "details": "dettagli sul minting", + "next_level": "Con un minting 24/7 raggiungerai il livello {{ level }} in {{ count }} giorni", + "rewards_info": "informazioni sulle ricompense del minting", + "reward_per_block": "ricompensa stimata per blocco", + "reward_per_day": "ricompensa stimata al giorno", + "status": { + "minting": "(minting)", + "not_minting": "(non minting)", + "no_status": "nessun stato", + "synchronized": "sincronizzato", + "synchronizing": "sincronizzazione..." + }, + "status_title": "stato minting", + "tier_share_per_block": "quota della fascia per blocco", + "total_minter_in_tier": "minter totali in questa fascia" + }, "name": "nome", "name_app": "nome/app", "new_post_in": "nuovo post in {{ title }}", @@ -361,7 +384,7 @@ "default": "tema di default", "light": "chiaro", "light_mode": "modalità chiara", - "manager": "manager tema", + "manager": "gestore del tema", "name": "nome tema" }, "thread": "thread", diff --git a/src/i18n/locales/it/group.json b/src/i18n/locales/it/group.json index 8047970..f0a4229 100644 --- a/src/i18n/locales/it/group.json +++ b/src/i18n/locales/it/group.json @@ -71,7 +71,7 @@ "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", "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_approval_threshold": "soglia di approvazione del gruppo (numero / percentuale di amministratori che devono approvare una transazione)", "group_encrypted": "gruppo crittografato", @@ -91,8 +91,6 @@ "minting_account": "account di minting:", "minting_keys_per_node": "sono ammesse solo 2 chiavi di minting per nodo. Rimuoverne una se si desidera fare minting con questo account.", "minting_keys_per_node_different": "sono ammesse solo 2 chiavi di minting per nodo. Rimuovine una se desideri aggiungere un account diverso.", - "next_level": "blocchi mancanti al livello successivo:", - "node_minting": "questo nodo sta coniando:", "node_minting_account": "account minting del nodo", "node_minting_key": "hai una chiave di minting per questo account collegata al nodo", "no_announcement": "nessun annuncio", diff --git a/src/i18n/locales/ja/core.json b/src/i18n/locales/ja/core.json index 7c478dd..e58e29a 100644 --- a/src/i18n/locales/ja/core.json +++ b/src/i18n/locales/ja/core.json @@ -116,7 +116,6 @@ "apps_official": "公式アプリ", "attachment": "添付ファイル", "balance": "バランス:", - "basic_tabs_example": "基本的なタブの例", "category": "カテゴリ", "category_other": "カテゴリ", "chat": "チャット", @@ -220,6 +219,7 @@ "benefits_qort": "QORTを持つことの利点", "building": "建物", "building_app": "ビルディングアプリ", + "confirmed": "確認済み", "created_by": "created by {{ owner }}", "buy_order_request": "the Application {{hostname}} is requesting {{count}} buy order", "buy_order_request_other": "the Application {{hostname}} is requesting {{count}} buy orders", @@ -256,6 +256,7 @@ "no_pinned_changes": "現在、ピン留めアプリに変更がありません", "no_results": "結果はありません", "one_app_per_name": "注:現在、名前ごとに1つのアプリとWebサイトのみが許可されています。", + "ongoing_transactions": "継続中の取引", "opened": "オープン", "overwrite_qdn": "QDNに上書きします", "password_confirm": "パスワードを確認してください", @@ -281,7 +282,8 @@ "space_for_admins": "申し訳ありませんが、このスペースは管理者専用です。", "unread_messages": "以下の未読メッセージ", "unsaved_changes": "ピン留めアプリに救われていない変更があります。それらをqdnに保存します。", - "updating": "更新" + "updating": "更新", + "wait": "お待ちください" }, "message": "メッセージ", "promotion_text": "プロモーションテキスト", @@ -302,12 +304,6 @@ "reset_qdn": "あなたの現在のローカルの変更が気に入らない?保存したQDNピン留めアプリにリセットしますか?", "transfer_qort": "would you like to transfer {{ amount }} QORT" }, - "status": { - "minting": "(鋳造)", - "not_minting": "(造りではありません)", - "synchronized": "同期", - "synchronizing": "同期" - }, "success": { "order_submitted": "購入注文が提出されました", "published": "正常に公開されました。ネットワークが変更を提案するまで数分待ってください。", @@ -318,7 +314,34 @@ "voted": "正常に投票しました。ネットワークが変更を提案するまで数分待ってください。" } }, - "minting_status": "ミントステータス", + "minting": { + "account_details": "ミンティングアカウントの詳細", + "actions": "ミンティングアクション", + "average_blocktime": "Qortalの平均ブロック時間", + "average_blocks_per_day": "1日あたりの平均ブロック数", + "average_created_qorts_per_day": "1日あたりに作成された平均QORT数", + "blockchain_statistics": "ブロックチェーン統計", + "blocks_next_level": "次のレベルまでのブロック数", + "current_level": "現在のレベル", + "current_status": "現在のステータス", + "current_tier": "現在のティア", + "current_tier_content": "{{ tier }}(レベル {{ levels }})", + "details": "ミンティングの詳細", + "next_level": "24時間年中無休のミンティングで、{{ count }}日以内にレベル{{ level }}に到達します", + "rewards_info": "ミンティング報酬情報", + "reward_per_block": "ブロックごとの推定報酬", + "reward_per_day": "1日あたりの推定報酬", + "status": { + "minting": "(鋳造)", + "not_minting": "(造りではありません)", + "no_status": "ステータスなし", + "synchronized": "同期", + "synchronizing": "同期..." + }, + "status_title": "ミントステータス", + "tier_share_per_block": "ブロックごとのティアシェア", + "total_minter_in_tier": "このティアの合計ミンター数" + }, "name": "名前", "name_app": "名前/アプリ", "new_post_in": "new post in {{ title }}", @@ -393,4 +416,4 @@ }, "website": "Webサイト", "welcome": "いらっしゃいませ" -} \ No newline at end of file +} diff --git a/src/i18n/locales/ja/group.json b/src/i18n/locales/ja/group.json index 98f1b05..94dfac2 100644 --- a/src/i18n/locales/ja/group.json +++ b/src/i18n/locales/ja/group.json @@ -91,8 +91,6 @@ "minting_account": "マイニングアカウント:", "minting_keys_per_node": "ノードごとに最大2つのマイニング鍵が許可されています。削除してから追加してください。", "minting_keys_per_node_different": "他のアカウントを追加するには、既存の鍵を削除してください。", - "next_level": "次のレベルまでの残りブロック数:", - "node_minting": "このノードはマイニング中:", "node_minting_account": "ノードのマイニングアカウント", "node_minting_key": "このノードに現在接続されているアカウントのマイニング鍵があります", "no_announcement": "お知らせはありません", diff --git a/src/i18n/locales/ru/core.json b/src/i18n/locales/ru/core.json index 43f3943..07d4331 100644 --- a/src/i18n/locales/ru/core.json +++ b/src/i18n/locales/ru/core.json @@ -116,7 +116,6 @@ "apps_official": "официальные приложения", "attachment": "вложение", "balance": "баланс:", - "basic_tabs_example": "Основные вкладки", "category": "категория", "category_other": "категории", "chat": "чат", @@ -220,6 +219,7 @@ "benefits_qort": "Преимущества наличия qort", "building": "здание", "building_app": "строительство приложения", + "confirmed": "подтвержденный", "created_by": "created by {{ owner }}", "buy_order_request": "the Application {{hostname}} is requesting {{count}} buy order", "buy_order_request_other": "the Application {{hostname}} is requesting {{count}} buy orders", @@ -256,6 +256,7 @@ "no_pinned_changes": "В настоящее время у вас нет никаких изменений в ваших приложениях", "no_results": "Нет результатов", "one_app_per_name": "ПРИМЕЧАНИЕ. В настоящее время только одно приложение и веб -сайт разрешены для имени.", + "ongoing_transactions": "текущие операции", "opened": "открыл", "overwrite_qdn": "перезаписать в QDN", "password_confirm": "Пожалуйста, подтвердите пароль", @@ -281,7 +282,8 @@ "space_for_admins": "Извините, это пространство только для администраторов.", "unread_messages": "Непрочитанные сообщения ниже", "unsaved_changes": "У вас есть неспасенные изменения в ваши приложения. Сохраните их в QDN.", - "updating": "обновление" + "updating": "обновление", + "wait": "пожалуйста, подождите" }, "message": "сообщение", "promotion_text": "Текст продвижения", @@ -302,12 +304,6 @@ "reset_qdn": "Не нравятся ваши текущие локальные изменения? Хотели бы вы сбросить в сохраненные приложения QDN?", "transfer_qort": "would you like to transfer {{ amount }} QORT" }, - "status": { - "minting": "(добыча)", - "not_minting": "(не шахта)", - "synchronized": "синхронизированный", - "synchronizing": "синхронизация" - }, "success": { "order_submitted": "Ваш заказ на покупку был отправлен", "published": "успешно опубликовано. Пожалуйста, подождите пару минут, пока сеть прокатит изменения.", @@ -318,10 +314,37 @@ "voted": "успешно проголосовал. Пожалуйста, подождите пару минут, пока сеть прокатит изменения." } }, - "minting_status": "Статус майтинга", + "minting": { + "account_details": "детали аккаунта майнинга", + "actions": "действия по майнингу", + "average_blocktime": "среднее время блока Qortal", + "average_blocks_per_day": "среднее количество блоков в день", + "average_created_qorts_per_day": "среднее количество создаваемых QORT в день", + "blockchain_statistics": "статистика блокчейна", + "blocks_next_level": "блоков до следующего уровня", + "current_level": "текущий уровень", + "current_status": "текущий статус", + "current_tier": "текущий уровень", + "current_tier_content": "{{ tier }} (Уровни {{ levels }})", + "details": "детали майнинга", + "next_level": "При майнинге 24/7 вы достигнете {{ level }} уровня за {{ count }} дней", + "rewards_info": "информация о наградах за майнинг", + "reward_per_block": "предполагаемая награда за блок", + "reward_per_day": "предполагаемая награда в день", + "status": { + "minting": "(добыча)", + "not_minting": "(не шахта)", + "no_status": "нет статуса", + "synchronized": "синхронизированный", + "synchronizing": "синхронизация..." + }, + "status_title": "Статус майтинга", + "tier_share_per_block": "доля уровня за блок", + "total_minter_in_tier": "всего майнеров на этом уровне" + }, "name": "имя", "name_app": "имя/приложение", - "new_post_in": "new post in {{ title }}", + "new_post_in": "новые сообщения в {{ title }}", "none": "никто", "note": "примечание", "option": "вариант", diff --git a/src/i18n/locales/ru/group.json b/src/i18n/locales/ru/group.json index ed5ca33..5c8cb2e 100644 --- a/src/i18n/locales/ru/group.json +++ b/src/i18n/locales/ru/group.json @@ -91,8 +91,6 @@ "minting_account": "аккаунт майнинга:", "minting_keys_per_node": "максимум 2 ключа майнинга на узел. Удалите один, чтобы продолжить.", "minting_keys_per_node_different": "максимум 2 ключа майнинга на узел. Удалите ключ для добавления другого.", - "next_level": "блоков до следующего уровня:", - "node_minting": "этот узел майнит:", "node_minting_account": "аккаунты майнинга узла", "node_minting_key": "этот узел уже содержит ключ майнинга для этой учетной записи", "no_announcement": "нет объявлений", diff --git a/src/i18n/locales/zh/core.json b/src/i18n/locales/zh/core.json index d5f8c8d..52e0c2e 100644 --- a/src/i18n/locales/zh/core.json +++ b/src/i18n/locales/zh/core.json @@ -116,7 +116,6 @@ "apps_official": "官方应用程序", "attachment": "依恋", "balance": "平衡:", - "basic_tabs_example": "基本标签示例", "category": "类别", "category_other": "类别", "chat": "聊天", @@ -220,9 +219,10 @@ "benefits_qort": "Qort的好处", "building": "建筑", "building_app": "建筑应用", - "created_by": "created by {{ owner }}", - "buy_order_request": "the Application {{hostname}} is requesting {{count}} buy order", - "buy_order_request_other": "the Application {{hostname}} is requesting {{count}} buy orders", + "confirmed": "已确认", + "created_by": "创建人 {{ owner }}", + "buy_order_request": "应用程序{{hostname}}正在请求{{count}}购买订单", + "buy_order_request_other": "应用程序{{hostname}}正在请求{{count}}购买订单", "devmode_local_node": "请使用您的本地节点进行开发模式!注销并使用本地节点。", "downloading": "下载", "downloading_decrypting_app": "下载和解密私人应用程序。", @@ -256,6 +256,7 @@ "no_pinned_changes": "您目前对固定应用程序没有任何更改", "no_results": "没有结果", "one_app_per_name": "注意:目前,每个名称只允许一个应用程序和网站。", + "ongoing_transactions": "正在进行的交易", "opened": "打开", "overwrite_qdn": "覆盖为QDN", "password_confirm": "请确认密码", @@ -281,7 +282,8 @@ "space_for_admins": "抱歉,这个空间仅适用于管理员。", "unread_messages": "下面未读消息", "unsaved_changes": "您对固定应用程序有未保存的更改。将它们保存到QDN。", - "updating": "更新" + "updating": "更新", + "wait": "请稍等" }, "message": "信息", "promotion_text": "促销文本", @@ -302,12 +304,6 @@ "reset_qdn": "不喜欢您当前的本地更改吗?您想重置保存的QDN固定应用吗?", "transfer_qort": "would you like to transfer {{ amount }} QORT" }, - "status": { - "minting": "(铸造)", - "not_minting": "(不是铸造)", - "synchronized": "同步", - "synchronizing": "同步" - }, "success": { "order_submitted": "您的买入订单已提交", "published": "成功出版。请等待几分钟,以使网络传播更改。", @@ -318,7 +314,34 @@ "voted": "成功投票。请等待几分钟,以使网络传播更改。" } }, - "minting_status": "铸造状态", + "minting": { + "account_details": "铸造账户详情", + "actions": "铸造操作", + "average_blocktime": "Qortal平均区块时间", + "average_blocks_per_day": "每日平均区块数", + "average_created_qorts_per_day": "每日平均生成的QORT数量", + "blockchain_statistics": "区块链统计", + "blocks_next_level": "距离下一级的区块数", + "current_level": "当前等级", + "current_status": "当前状态", + "current_tier": "当前等级层级", + "current_tier_content": "{{ tier }}(等级 {{ levels }})", + "details": "铸造详情", + "next_level": "全天候铸造将使你在{{ count }}天内达到{{ level }}级", + "rewards_info": "铸造奖励信息", + "reward_per_block": "每个区块的预计奖励", + "reward_per_day": "每日预计奖励", + "status": { + "minting": "(铸造)", + "not_minting": "(不是铸造)", + "no_status": "没有状态", + "synchronized": "同步", + "synchronizing": "同步..." + }, + "status_title": "铸造状态", + "tier_share_per_block": "每个区块的等级份额", + "total_minter_in_tier": "该等级层级中的总铸造者数" + }, "name": "姓名", "name_app": "名称/应用", "new_post_in": "new post in {{ title }}", @@ -393,4 +416,4 @@ }, "website": "网站", "welcome": "欢迎" -} \ No newline at end of file +} diff --git a/src/i18n/locales/zh/group.json b/src/i18n/locales/zh/group.json index 999c6c9..9ef565e 100644 --- a/src/i18n/locales/zh/group.json +++ b/src/i18n/locales/zh/group.json @@ -91,8 +91,6 @@ "minting_account": "铸币账户:", "minting_keys_per_node": "每个节点最多允许 2 个铸币密钥。请先移除一个", "minting_keys_per_node_different": "每个节点最多允许 2 个铸币密钥。请先移除再添加其他账户", - "next_level": "距离下一级还需区块数:", - "node_minting": "当前节点正在铸币:", "node_minting_account": "节点铸币账户", "node_minting_key": "你已有该账户的铸币密钥与该节点绑定", "no_announcement": "暂无公告", diff --git a/src/i18n/processors.ts b/src/i18n/processors.ts index 16afecd..d7ae34c 100644 --- a/src/i18n/processors.ts +++ b/src/i18n/processors.ts @@ -4,6 +4,28 @@ export const capitalizeAll = { process: (value: string) => value.toUpperCase(), }; +export const capitalizeEachFirstChar = { + type: 'postProcessor', + name: 'capitalizeEachFirstChar', + process: (value: string) => { + if (!value?.trim()) return value; + + const leadingSpaces = value.match(/^\s*/)?.[0] || ''; + const trailingSpaces = value.match(/\s*$/)?.[0] || ''; + + const core = value + .trim() + .split(/\s+/) + .map( + (word) => + word.charAt(0).toLocaleUpperCase() + word.slice(1).toLocaleLowerCase() + ) + .join(' '); + + return leadingSpaces + core + trailingSpaces; + }, +}; + export const capitalizeFirstChar = { type: 'postProcessor', name: 'capitalizeFirstChar', diff --git a/src/qortal/get.ts b/src/qortal/get.ts index efc81fe..ad85a1d 100644 --- a/src/qortal/get.ts +++ b/src/qortal/get.ts @@ -2465,7 +2465,6 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => { const filename = data.filename; const blob = data.blob; - const mimeType = blob.type || data.mimeType; const resPermission = await getUserPermission( { text1: i18n.t('question:download_file', { @@ -2476,17 +2475,6 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => { isFromExtension ); const { accepted } = resPermission; - if (!accepted) throw new Error('User declined to save file'); // TODO translate - showSaveFilePicker( - { - filename, - mimeType, - blob, - }, - snackMethods - ); - - return true; if (accepted) { const mimeType = blob.type || data.mimeType; diff --git a/src/styles/App-styles.ts b/src/styles/App-styles.ts index 4f3548f..8061fa5 100644 --- a/src/styles/App-styles.ts +++ b/src/styles/App-styles.ts @@ -138,7 +138,10 @@ interface CustomButtonProps { customColor?: string; } -export const CustomButtonAccept = styled(Box)((props) => { +export const CustomButtonAccept = styled(Box, { + shouldForwardProp: (prop) => + prop !== 'customBgColor' && prop !== 'customColor', +})((props) => { const { customBgColor, customColor, theme } = props; return { alignItems: 'center', diff --git a/src/styles/index.css b/src/styles/index.css index ce338ba..2afd8fb 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -19,6 +19,13 @@ font-weight: 400; } +/* * { // TODO restore and check + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Roboto'; +} */ + .image-container { position: relative; } diff --git a/src/styles/theme-common.ts b/src/styles/theme-common.ts index aca2522..cb7ac59 100644 --- a/src/styles/theme-common.ts +++ b/src/styles/theme-common.ts @@ -73,6 +73,33 @@ const commonThemeOptions = { }, }, + MuiDialog: { + styleOverrides: { + paper: { + backgroundColor: 'background.paper', + color: 'text.primary', + }, + }, + }, + + MuiDialogTitle: { + styleOverrides: { + root: { + backgroundColor: 'inherit', + color: 'inherit', + }, + }, + }, + + MuiDialogContent: { + styleOverrides: { + root: { + backgroundColor: 'inherit', + color: 'inherit', + }, + }, + }, + MuiModal: { styleOverrides: { root: { @@ -80,6 +107,14 @@ const commonThemeOptions = { }, }, }, + + MuiPopover: { + styleOverrides: { + paper: { + backgroundImage: 'none', + }, + }, + }, }, }; diff --git a/src/styles/theme-dark.ts b/src/styles/theme-dark.ts index a519cd6..a4a8a3b 100644 --- a/src/styles/theme-dark.ts +++ b/src/styles/theme-dark.ts @@ -16,7 +16,7 @@ export const darkThemeOptions: ThemeOptions = { background: { default: 'rgb(49, 51, 56)', surface: 'rgb(58, 60, 65)', - paper: 'rgb(62, 64, 68)', + paper: 'rgb(77, 80, 85)', }, text: { primary: 'rgb(255, 255, 255)', @@ -114,18 +114,11 @@ export const darkThemeOptions: ThemeOptions = { }, }, - MuiDialog: { + MuiPaper: { styleOverrides: { - paper: { - backgroundImage: 'none', - }, - }, - }, - - MuiPopover: { - styleOverrides: { - paper: { - backgroundImage: 'none', + root: { + backgroundColor: 'rgb(77, 80, 85)', + color: 'rgb(255, 255, 255)', }, }, }, diff --git a/src/styles/theme-light.ts b/src/styles/theme-light.ts index 3cfbdae..7fc6a8b 100644 --- a/src/styles/theme-light.ts +++ b/src/styles/theme-light.ts @@ -116,18 +116,11 @@ export const lightThemeOptions: ThemeOptions = { }, }, - MuiDialog: { + MuiPaper: { styleOverrides: { - paper: { - backgroundImage: 'none', - }, - }, - }, - - MuiPopover: { - styleOverrides: { - paper: { - backgroundImage: 'none', + root: { + backgroundColor: 'rgb(220, 220, 220)', + color: 'rgba(0, 0, 0, 0.87)', }, }, },