From b946e192360760d60aea665ca07e245bb384833f Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 10 May 2025 18:49:59 +0200 Subject: [PATCH 01/47] Add translations --- src/Wallets.tsx | 2 +- src/components/Apps/AppsDevModeHome.tsx | 2 +- src/components/Apps/AppsPrivate.tsx | 3 +- src/components/Group/AddGroup.tsx | 2 +- src/components/Group/InviteMember.tsx | 17 +- .../Group/ListOfGroupPromotions.tsx | 302 +++++++++++------- src/components/Snackbar/LoadingSnackbar.tsx | 20 +- src/components/Snackbar/Snackbar.tsx | 1 - src/components/TaskManager/TaskManager.tsx | 2 + src/i18n/locales/en/core.json | 3 +- src/i18n/locales/en/group.json | 14 +- 11 files changed, 224 insertions(+), 144 deletions(-) diff --git a/src/Wallets.tsx b/src/Wallets.tsx index 281e07e..20fb5a6 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -322,7 +322,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { sx={{ display: 'flex', flexDirection: 'column', - }} + }} // TODO translate > { const object64 = await objectToBase64(objectToSave); const decryptedData = await window.sendMessage( 'ENCRYPT_QORTAL_GROUP_DATA', - { base64: object64, groupId: selectedGroup, @@ -313,7 +312,7 @@ export const AppsPrivate = ({ myName }) => { display: 'flex', flexDirection: 'column', gap: '5px', - }} + }} // TODO translate > diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index e9e9515..f9cbbf8 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -371,7 +371,6 @@ export const AddGroup = ({ address, open, setOpen }) => { }} > + setValue(e.target.value)} /> + + + + + { const [promotionTimeInterval, setPromotionTimeInterval] = useAtom( promotionTimeIntervalAtom ); - const [isExpanded, setIsExpanded] = React.useState(false); - const [openSnack, setOpenSnack] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const [fee, setFee] = useState(null); @@ -99,7 +96,6 @@ export const ListOfGroupPromotions = () => { const [isLoadingPublish, setIsLoadingPublish] = useState(false); const { show } = useContext(MyContext); const setTxList = useSetAtom(txListAtom); - const theme = useTheme(); const { t } = useTranslation(['core', 'group']); const listRef = useRef(null); @@ -250,8 +246,9 @@ export const ListOfGroupPromotions = () => { }); setInfoSnack({ type: 'success', - message: - 'Successfully published promotion. It may take a couple of minutes for the promotion to appear', + message: t('group:message.success.group_promotion', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); setText(''); @@ -261,7 +258,10 @@ export const ListOfGroupPromotions = () => { setInfoSnack({ type: 'error', message: - error?.message || 'Error publishing the promotion. Please try again', + error?.message || + t('group:message.error.group_promotion', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); } finally { @@ -290,8 +290,9 @@ export const ListOfGroupPromotions = () => { if (!response?.error) { setInfoSnack({ type: 'success', - message: - 'Successfully requested to join group. It may take a couple of minutes for the changes to propagate', + message: t('group:message.success.group_join', { + postProcess: 'capitalize', + }), }); if (isOpen) { @@ -299,8 +300,14 @@ export const ListOfGroupPromotions = () => { { ...response, type: 'joined-group', - label: `Joined Group ${group?.groupName}: awaiting confirmation`, - labelDone: `Joined Group ${group?.groupName}: success!`, + label: t('group:message.success.group_join_label', { + group_name: group?.groupName, + postProcess: 'capitalize', + }), + labelDone: t('group:message.success.group_join_label', { + group_name: group?.groupName, + postProcess: 'capitalize', + }), done: false, groupId, }, @@ -311,15 +318,20 @@ export const ListOfGroupPromotions = () => { { ...response, type: 'joined-group-request', - label: `Requested to join Group ${group?.groupName}: awaiting confirmation`, - labelDone: `Requested to join Group ${group?.groupName}: success!`, + label: t('group:message.success.group_join_request', { + group_name: group?.groupName, + postProcess: 'capitalize', + }), + labelDone: t('group:message.success.group_join_outcome', { + group_name: group?.groupName, + postProcess: 'capitalize', + }), done: false, groupId, }, ...prev, ]); } - setOpenSnack(true); handlePopoverClose(); res(response); @@ -336,7 +348,9 @@ export const ListOfGroupPromotions = () => { .catch((error) => { setInfoSnack({ type: 'error', - message: error.message || 'An error occurred', + message: + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }), }); setOpenSnack(true); rej(error); @@ -385,7 +399,7 @@ export const ListOfGroupPromotions = () => { fontSize: '1rem', }} > - Group promotions{' '} + {t('group:group.promotions', { postProcess: 'capitalize' })}{' '} {promotions.length > 0 && ` (${promotions.length})`} @@ -444,7 +458,7 @@ export const ListOfGroupPromotions = () => { fontSize: '12px', }} > - Add Promotion + {t('group.action.add_promotion', { postProcess: 'capitalize' })} @@ -490,7 +504,9 @@ export const ListOfGroupPromotions = () => { color: 'rgba(255, 255, 255, 0.2)', }} > - Nothing to display + {t('group.message.generic.no_display', { + postProcess: 'capitalize', + })} )} @@ -537,23 +553,25 @@ export const ListOfGroupPromotions = () => { ref={rowVirtualizer.measureElement} //measure dynamic row height key={promotion?.identifier} style={{ - position: 'absolute', - top: 0, - left: '50%', // Move to the center horizontally - transform: `translateY(${virtualRow.start}px) translateX(-50%)`, // Adjust for centering - width: '100%', // Control width (90% of the parent) - padding: '10px 0', - display: 'flex', alignItems: 'center', - overscrollBehavior: 'none', + display: 'flex', flexDirection: 'column', gap: '5px', + left: '50%', // Move to the center horizontally + overscrollBehavior: 'none', + padding: '10px 0', + position: 'absolute', + top: 0, + transform: `translateY(${virtualRow.start}px) translateX(-50%)`, // Adjust for centering + width: '100%', // Control width (90% of the parent) }} > - Error loading content: Invalid Data + {t('group.message.generic.invalid_data', { + postProcess: 'capitalize', + })} } > @@ -568,7 +586,7 @@ export const ListOfGroupPromotions = () => { { + onClose={(reason) => { if (reason === 'backdropClick') { // Prevent closing on backdrop click return; @@ -603,7 +621,10 @@ export const ListOfGroupPromotions = () => { fontWeight: 600, }} > - Group name: {` ${promotion?.groupName}`} + {t('group:group.name', { + postProcess: 'capitalize', + })} + : {` ${promotion?.groupName}`} { fontWeight: 600, }} > - Number of members:{' '} - {` ${promotion?.memberCount}`} + {t('group:group.member_number', { + postProcess: 'capitalize', + })} + : {` ${promotion?.memberCount}`} {promotion?.description && ( @@ -634,9 +657,9 @@ export const ListOfGroupPromotions = () => { fontWeight: 600, }} > - *This is a closed/private group, so you - will need to wait until an admin accepts - your request + {t('group:message.generic.closed_group', { + postProcess: 'capitalize', + })} )} @@ -657,7 +680,9 @@ export const ListOfGroupPromotions = () => { variant="contained" onClick={handlePopoverClose} > - Close + {t('core:action.close', { + postProcess: 'capitalize', + })} { ) } > - Join + {t('core:action.join', { + postProcess: 'capitalize', + })} @@ -755,8 +782,12 @@ export const ListOfGroupPromotions = () => { }} > {promotion?.isOpen - ? 'Public group' - : 'Private group'} + ? t('group:group.public', { + postProcess: 'capitalize', + }) + : t('group:group.private', { + postProcess: 'capitalize', + })} @@ -790,7 +821,10 @@ export const ListOfGroupPromotions = () => { color: theme.palette.text.primary, }} > - Join Group: {` ${promotion?.groupName}`} + {t('group:action.join_group', { + postProcess: 'capitalize', + })} + : {` ${promotion?.groupName}`} @@ -810,90 +844,114 @@ export const ListOfGroupPromotions = () => { - {isShowModal && ( - - - {'Promote your group to non-members'} - - - - Only the latest promotion from the week will be shown for your - group. - - - Max 200 characters. Publish Fee: {fee && fee} {' QORT'} - - - + + {t('group:action.promote_group', { postProcess: 'capitalize' })} + + + + + {t('group:message.generic.latest_promotion', { + postProcess: 'capitalize', + })} + + + + {t('group:message.generic.max_chars', { + postProcess: 'capitalize', + })} + : {fee && fee} {' QORT'} + + + + + + + + + + setSelectedGroup(e.target.value)} - variant="outlined" - > - {myGroupsWhereIAmAdmin?.map((group) => { - return ( - - {group?.groupName} - - ); - })} - - - - setText(e.target.value)} - inputProps={{ - maxLength: 200, - }} - multiline={true} - sx={{ - '& .MuiFormLabel-root': { - color: theme.palette.text.primary, - }, - '& .MuiFormLabel-root.Mui-focused': { - color: theme.palette.text.primary, - }, - }} - /> - - - - - - - )} + {myGroupsWhereIAmAdmin?.map((group) => { + return ( + + {group?.groupName} + + ); + })} + + + + + + setText(e.target.value)} + inputProps={{ + maxLength: 200, + }} + multiline={true} + sx={{ + '& .MuiFormLabel-root': { + color: theme.palette.text.primary, + }, + '& .MuiFormLabel-root.Mui-focused': { + color: theme.palette.text.primary, + }, + }} + /> + + + + + + + + { - +export const LoadingSnackbar = ({ open, info }) => { return (
- - + + {info?.message}
); -} \ No newline at end of file +}; diff --git a/src/components/Snackbar/Snackbar.tsx b/src/components/Snackbar/Snackbar.tsx index d79677e..d4315ed 100644 --- a/src/components/Snackbar/Snackbar.tsx +++ b/src/components/Snackbar/Snackbar.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar'; import Alert from '@mui/material/Alert'; diff --git a/src/components/TaskManager/TaskManager.tsx b/src/components/TaskManager/TaskManager.tsx index d49077e..d34bb5e 100644 --- a/src/components/TaskManager/TaskManager.tsx +++ b/src/components/TaskManager/TaskManager.tsx @@ -198,9 +198,11 @@ export const TaskManager = ({ getUserInfo }) => { /> )} + {open ? : } + {txList.map((item) => ( diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index c819a38..91b7f48 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -27,7 +27,8 @@ }, "notify": "notify", "post": "post", - "post_message": "post message" + "post_message": "post message", + "publish": "publish" }, "admin": "admin", "core": { diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index 6fba714..5e0db18 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -1,5 +1,6 @@ { "action": { + "add_promotion": "add promotion", "ban": "ban member from group", "cancel_ban": "cancel ban", "copy_private_key": "copy private key", @@ -16,9 +17,11 @@ "load_members": "load members with names", "make_admin": "make an admin", "manage_members": "manage members", + "promote_group": "promote your group to non-members", "refetch_page": "refetch page", "remove_admin": "remove as admin", - "return_to_thread": "return to threads" + "return_to_thread": "return to threads", + "select_group": "select a group" }, "advanced_options": "advanced options", "approval_threshold": "group Approval Threshold (number / percentage of Admins that must approve a transaction)", @@ -36,6 +39,9 @@ "member_number": "number of members", "name": "group name", "open": "open (public)", + "private": "private group", + "promotions": "group promotions", + "public": "public group", "type": "group type" }, "invitation_expiry": "invitation Expiry Time", @@ -46,12 +52,16 @@ "latest_mails": "latest Q-Mails", "message": { "generic": { + "admin_only": "only groups where you are an admin will be shown", "already_in_group": "you are already in this group!", "closed_group": "this is a closed/private group, so you will need to wait until an admin accepts your request", "descrypt_wallet": "decrypting wallet...", "encryption_key": "the group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes...", "group_invited_you": "{{group}} has invited you", + "invalid_data": "error loading content: Invalid Data", + "latest_promotion": "only the latest promotion from the week will be shown for your group.", "loading_members": "loading member list with names... please wait.", + "max_chars": " Max 200 characters. Publish Fee", "no_display": "nothing to display", "no_selection": "no group selected", "not_part_group": "you are not part of the encrypted group of members. Wait until an admin re-encrypts the keys.", @@ -66,6 +76,7 @@ "descrypt_wallet": "error decrypting wallet {{ :errorMessage }}", "description_required": "please provide a description", "group_info": "cannot access group information", + "group_promotion": "error publishing the promotion. Please try again", "group_secret_key": "cannot get group secret key", "name_required": "please provide a name", "notify_admins": "try notifying an admin from the list of admins below:", @@ -87,6 +98,7 @@ "group_leave_name": "left group {{group_name}}: awaiting confirmation", "group_leave_label": "left group {{name}}: success!", "group_member_admin": "successfully made member an admin. It may take a couple of minutes for the changes to propagate", + "group_promotion": "successfully published promotion. It may take a couple of minutes for the promotion to appear", "group_remove_member": "successfully removed member as an admin. It may take a couple of minutes for the changes to propagate", "invitation_cancellation": "successfully canceled invitation. It may take a couple of minutes for the changes to propagate", "invitation_request": "accepted join request: awaiting confirmation", From a0bddf338d9e6dad63ef277b6fcea049df8c12b2 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 10 May 2025 19:02:38 +0200 Subject: [PATCH 02/47] Add comment --- src/components/Apps/AppInfo.tsx | 3 +-- src/components/Apps/AppInfoSnippet.tsx | 2 +- src/components/Apps/AppPublish.tsx | 1 + src/components/Apps/AppRating.tsx | 1 + src/components/Apps/Apps-styles.tsx | 2 +- src/components/Apps/AppsCategoryDesktop.tsx | 20 ++------------------ src/components/Apps/AppsDesktop.tsx | 13 +++++-------- src/components/Apps/AppsDevMode.tsx | 5 ++++- src/components/Apps/AppsDevModeNavBar.tsx | 6 +++--- src/components/Apps/AppsHomeDesktop.tsx | 2 +- src/components/Apps/AppsLibraryDesktop.tsx | 2 +- 11 files changed, 21 insertions(+), 36 deletions(-) diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index 6dc8c37..abca186 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -1,4 +1,3 @@ -import React, { useEffect, useMemo, useState } from 'react'; import { AppCircle, AppCircleContainer, @@ -172,7 +171,7 @@ export const AppInfo = ({ app, myName }) => { }} > - {isSelectedAppPinned + {isSelectedAppPinned // TODO translate ? 'Unpin from dashboard' : 'Pin to dashboard'} diff --git a/src/components/Apps/AppInfoSnippet.tsx b/src/components/Apps/AppInfoSnippet.tsx index 22903d9..ba71164 100644 --- a/src/components/Apps/AppInfoSnippet.tsx +++ b/src/components/Apps/AppInfoSnippet.tsx @@ -166,7 +166,7 @@ export const AppInfoSnippet = ({ sx={{ backgroundColor: theme.palette.background.paper, opacity: isSelectedAppPinned ? 0.6 : 1, - }} + }} // TODO translate > {' '} diff --git a/src/components/Apps/AppPublish.tsx b/src/components/Apps/AppPublish.tsx index 486fd8a..8875c0b 100644 --- a/src/components/Apps/AppPublish.tsx +++ b/src/components/Apps/AppPublish.tsx @@ -185,6 +185,7 @@ export const AppPublish = ({ names, categories }) => { const fee = await getFee('ARBITRARY'); await show({ + // TODO translate message: 'Would you like to publish this app?', publishFee: fee.fee + ' QORT', }); diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index 191650d..881cfec 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -106,6 +106,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { const fee = await getFee('CREATE_POLL'); await show({ + // TODO translate message: `Would you like to rate this app a rating of ${newValue}?. It will create a POLL tx.`, publishFee: fee.fee + ' QORT', }); diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index dd10e36..abbbaba 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -351,7 +351,7 @@ export const PublishQAppChoseFile = styled(ButtonBase)(({ theme }) => ({ justifyContent: 'center', width: '120px', '&:hover': { - backgroundColor: 'action.hover', // background on hover + backgroundColor: 'action.hover', }, })); diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index 39f12ee..7fc853a 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -16,20 +16,6 @@ import { Spacer } from '../../common/Spacer'; import { AppInfoSnippet } from './AppInfoSnippet'; import { Virtuoso } from 'react-virtuoso'; -const ScrollerStyled = styled('div')({ - // Hide scrollbar for WebKit browsers (Chrome, Safari) - '::-webkit-scrollbar': { - width: '0px', - height: '0px', - }, - - // Hide scrollbar for Firefox - scrollbarWidth: 'none', - - // Hide scrollbar for IE and older Edge - msOverflowStyle: 'none', -}); - const StyledVirtuosoContainer = styled('div')({ position: 'relative', width: '100%', @@ -99,7 +85,8 @@ export const AppsCategoryDesktop = ({ }, [debouncedValue, categoryList]); const rowRenderer = (index) => { - let app = searchedList[index]; + const app = searchedList[index]; + return ( diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index 80f57b9..d1268fb 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -17,7 +17,6 @@ import { AppsCategoryDesktop } from './AppsCategoryDesktop'; import { AppsNavBarDesktop } from './AppsNavBarDesktop'; import { Box, ButtonBase, useTheme } from '@mui/material'; import { HomeIcon } from '../../assets/Icons/HomeIcon'; -import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; import { Save } from '../Save/Save'; import { IconWrapper } from '../Desktop/DesktopFooter'; import { enabledDevModeAtom } from '../../atoms/global'; @@ -98,8 +97,6 @@ export const AppsDesktop = ({ setCategories(responseData); } catch (error) { console.log(error); - } finally { - // dispatch(setIsLoadingGlobal(false)) } }, []); @@ -135,8 +132,6 @@ export const AppsDesktop = ({ setAvailableQapps(combine); } catch (error) { console.log(error); - } finally { - // dispatch(setIsLoadingGlobal(false)) } }, []); useEffect(() => { @@ -338,13 +333,13 @@ export const AppsDesktop = ({ + {isEnabledDevMode && ( @@ -515,6 +511,7 @@ export const AppsDesktop = ({ }} > + + + { setDesktopViewMode('dev'); @@ -342,10 +344,10 @@ export const AppsDevMode = ({ @@ -387,6 +389,7 @@ export const AppsDevMode = ({ }} > + { return ( Apps Dashboard diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 17b9b71..64f1007 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -273,7 +273,7 @@ export const AppsLibraryDesktop = ({ }} onClick={() => { executeEvent('navigateBack', {}); - }} + }} // TODO translate > Return to Apps Dashboard From cbd784db0438258a59308bf6dc0798ad2cfa819e Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 10 May 2025 23:35:20 +0200 Subject: [PATCH 03/47] Add translation to wallets --- src/Wallets.tsx | 46 ++++++++++++++++++++++++++--------- src/i18n/locales/en/auth.json | 9 +++++++ 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/Wallets.tsx b/src/Wallets.tsx index 20fb5a6..c3f290a 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from 'react'; +import { Fragment, useContext, useEffect, useState } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import Divider from '@mui/material/Divider'; @@ -32,6 +32,7 @@ import { LoadingButton } from '@mui/lab'; import { PasswordField } from './components'; import { HtmlTooltip } from './ExtStates/NotAuthenticated'; import { MyContext } from './App'; +import { useTranslation } from 'react-i18next'; const parsefilenameQortal = (filename) => { return filename.startsWith('qortal_backup_') ? filename.slice(14) : filename; @@ -44,11 +45,11 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const [seedName, setSeedName] = useState(''); const [seedError, setSeedError] = useState(''); const { hasSeenGettingStarted } = useContext(MyContext); - const [password, setPassword] = useState(''); const [isOpenSeedModal, setIsOpenSeedModal] = useState(false); const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false); const theme = useTheme(); + const { t } = useTranslation(['core', 'auth']); const { isShow, onCancel, onOk, show } = useModal(); const { getRootProps, getInputProps } = useDropzone({ @@ -83,7 +84,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { } let error: any = null; - let uniqueInitialMap = new Map(); + const uniqueInitialMap = new Map(); // Only add a message if it doesn't already exist in the Map importedWallets.forEach((wallet) => { @@ -92,7 +93,9 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { uniqueInitialMap.set(wallet?.address0, wallet); } }); + const data = Array.from(uniqueInitialMap.values()); + if (data && data?.length > 0) { const uniqueNewWallets = data.filter( (newWallet) => @@ -148,10 +151,19 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { setPassword(''); setSeedError(''); } else { - setSeedError('Could not create account.'); + setSeedError( + t('auth:message.error.account_creation', { + postProcess: 'capitalize', + }) + ); } } catch (error) { - setSeedError(error?.message || 'Could not create account.'); + setSeedError( + error?.message || + t('auth:message.error.account_creation', { + postProcess: 'capitalize', + }) + ); } finally { setIsLoadingEncryptSeed(false); } @@ -189,19 +201,29 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
{wallets?.length === 0 || !wallets ? ( <> - No accounts saved + + {t('auth:message.generic.no_account', { + postProcess: 'capitalize', + })} + + ) : ( <> - Your saved accounts + + {t('auth:message.generic.your_accounts', { + postProcess: 'capitalize', + })} + + )} {rawWallet && ( - Selected Account: + Selected Account: // TODO translate {rawWallet?.name && {rawWallet.name}} {rawWallet?.address0 && ( {rawWallet?.address0} @@ -249,7 +271,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { + { here to access it. This phrase is one of the ways to recover your account. - + } > { + { already made, in order to login with them afterwards. You will need access to your backup JSON file in order to do so. - + } > Date: Sat, 10 May 2025 22:27:56 +0300 Subject: [PATCH 04/47] added event for UI language to qapp --- src/components/Apps/AppViewer.tsx | 43 ++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 06a64c5..7ffeecd 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -5,6 +5,7 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { useFrame } from 'react-frame-component'; import { useQortalMessageListener } from './useQortalMessageListener'; import { useThemeContext } from '../Theme/ThemeContext'; +import { useTranslation } from 'react-i18next'; export const AppViewer = React.forwardRef( ({ app, hide, isDevMode, skipAuth }, iframeRef) => { @@ -22,11 +23,13 @@ export const AppViewer = React.forwardRef( ); const [url, setUrl] = useState(''); const { themeMode } = useThemeContext(); + const { i18n } = useTranslation(['core']); + const currentLang = i18n.language; useEffect(() => { if (app?.isPreview) return; if (isDevMode) { - setUrl(app?.url); + setUrl(app?.url + `?theme=${themeMode}&lang=${currentLang}`); return; } let hasQueryParam = false; @@ -35,14 +38,14 @@ export const AppViewer = React.forwardRef( } setUrl( - `${getBaseApiReact()}/render/${app?.service}/${app?.name}${app?.path != null ? `/${app?.path}` : ''}${hasQueryParam ? '&' : '?'}theme=${themeMode}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}` + `${getBaseApiReact()}/render/${app?.service}/${app?.name}${app?.path != null ? `/${app?.path}` : ''}${hasQueryParam ? '&' : '?'}theme=${themeMode}&lang=${currentLang}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}` ); }, [app?.service, app?.name, app?.identifier, app?.path, app?.isPreview]); useEffect(() => { if (app?.isPreview && app?.url) { resetHistory(); - setUrl(app.url); + setUrl(app.url + `&theme=${themeMode}&lang=${currentLang}`); } }, [app?.url, app?.isPreview]); const defaultUrl = useMemo(() => { @@ -55,11 +58,14 @@ export const AppViewer = React.forwardRef( if (isDevMode) { resetHistory(); if (!app?.isPreview || app?.isPrivate) { - setUrl(app?.url + `?time=${Date.now()}`); + setUrl( + app?.url + + `?time=${Date.now()}&theme=${themeMode}&lang=${currentLang}` + ); } return; } - const constructUrl = `${getBaseApiReact()}/render/${app?.service}/${app?.name}${path != null ? path : ''}?theme=${themeMode}&identifier=${app?.identifier != null ? app?.identifier : ''}&time=${new Date().getMilliseconds()}`; + const constructUrl = `${getBaseApiReact()}/render/${app?.service}/${app?.name}${path != null ? path : ''}?theme=${themeMode}&lang=${currentLang}&identifier=${app?.identifier != null ? app?.identifier : ''}&time=${new Date().getMilliseconds()}`; setUrl(constructUrl); } }; @@ -70,7 +76,7 @@ export const AppViewer = React.forwardRef( return () => { unsubscribeFromEvent('refreshApp', refreshAppFunc); }; - }, [app, path, isDevMode]); + }, [app, path, isDevMode, themeMode, currentLang]); useEffect(() => { const iframe = iframeRef?.current; @@ -87,6 +93,25 @@ export const AppViewer = React.forwardRef( } }, [themeMode]); + useEffect(() => { + const iframe = iframeRef?.current; + if (!iframe) return; + + try { + const targetOrigin = new URL(iframe.src).origin; + iframe.contentWindow?.postMessage( + { + action: 'LANGUAGE_CHANGED', + language: currentLang, + requestedHandler: 'UI', + }, + targetOrigin + ); + } catch (err) { + console.error('Failed to send theme change to iframe:', err); + } + }, [currentLang]); + const removeTrailingSlash = (str) => str.replace(/\/$/, ''); const copyLinkFunc = (e) => { @@ -181,12 +206,12 @@ export const AppViewer = React.forwardRef( } catch (error) { if (isDevMode) { setUrl( - `${url}${previousPath != null ? previousPath : ''}?theme=${themeMode}&time=${new Date().getMilliseconds()}&isManualNavigation=false` + `${url}${previousPath != null ? previousPath : ''}?theme=${themeMode}&lang=${currentLang}&time=${new Date().getMilliseconds()}&isManualNavigation=false` ); return; } setUrl( - `${getBaseApiReact()}/render/${app?.service}/${app?.name}${previousPath != null ? previousPath : ''}?theme=${themeMode}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}&time=${new Date().getMilliseconds()}&isManualNavigation=false` + `${getBaseApiReact()}/render/${app?.service}/${app?.name}${previousPath != null ? previousPath : ''}?theme=${themeMode}&lang=${currentLang}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}&time=${new Date().getMilliseconds()}&isManualNavigation=false` ); // iframeRef.current.contentWindow.location.href = previousPath; // Fallback URL update } @@ -209,7 +234,7 @@ export const AppViewer = React.forwardRef( navigateBackAppFunc ); }; - }, [app, history]); + }, [app, history, themeMode, currentLang]); // Function to navigate back in iframe const navigateForwardInIframe = async () => { From ccd348100e6135383ed11a72adcaba84016189b6 Mon Sep 17 00:00:00 2001 From: "nico.benaz" <52411515+nbenaglia@users.noreply.github.com> Date: Sat, 10 May 2025 23:09:18 +0200 Subject: [PATCH 05/47] Update src/components/Apps/AppViewer.tsx --- src/components/Apps/AppViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 7ffeecd..87f88e8 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -108,7 +108,7 @@ export const AppViewer = React.forwardRef( targetOrigin ); } catch (err) { - console.error('Failed to send theme change to iframe:', err); + console.error('Failed to send language change to iframe:', err); } }, [currentLang]); From 5820f8123ca9060ba7864f28208a2967cf83eb71 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 11 May 2025 00:00:36 +0200 Subject: [PATCH 06/47] Organize code --- src/MessageQueueContext.tsx | 142 ++++++++++++--------- src/hooks/useHandlePaymentNotification.tsx | 7 +- src/hooks/useNameSearch.tsx | 2 + 3 files changed, 88 insertions(+), 63 deletions(-) diff --git a/src/MessageQueueContext.tsx b/src/MessageQueueContext.tsx index 7104520..11f356f 100644 --- a/src/MessageQueueContext.tsx +++ b/src/MessageQueueContext.tsx @@ -1,12 +1,17 @@ -import React, { createContext, useContext, useState, useCallback, useRef } from 'react'; -import ShortUniqueId from "short-unique-id"; +import { + createContext, + useContext, + useState, + useCallback, + useRef, +} from 'react'; +import ShortUniqueId from 'short-unique-id'; const MessageQueueContext = createContext(null); +const uid = new ShortUniqueId({ length: 8 }); export const useMessageQueue = () => useContext(MessageQueueContext); -const uid = new ShortUniqueId({ length: 8 }); - export const MessageQueueProvider = ({ children }) => { const messageQueueRef = useRef([]); const [queueChats, setQueueChats] = useState({}); // Stores chats and status for display @@ -21,39 +26,41 @@ export const MessageQueueProvider = ({ children }) => { const processingPromiseRef = useRef(Promise.resolve()); // Function to add a message to the queue - const addToQueue = useCallback((sendMessageFunc, messageObj, type, groupDirectId) => { - const tempId = uid.rnd(); - const chatData = { - ...messageObj, - type, - groupDirectId, - signature: uid.rnd(), - identifier: tempId, - retries: 0, // Retry count for display purposes - status: 'pending' // Initial status is 'pending' - }; + const addToQueue = useCallback( + (sendMessageFunc, messageObj, type, groupDirectId) => { + const tempId = uid.rnd(); + const chatData = { + ...messageObj, + type, + groupDirectId, + signature: uid.rnd(), + identifier: tempId, + retries: 0, // Retry count for display purposes + status: 'pending', // Initial status is 'pending' + }; - // Add chat data to queueChats for status tracking - setQueueChats((prev) => ({ - ...prev, - [groupDirectId]: [...(prev[groupDirectId] || []), chatData] - })); + // Add chat data to queueChats for status tracking + setQueueChats((prev) => ({ + ...prev, + [groupDirectId]: [...(prev[groupDirectId] || []), chatData], + })); - // Add the message to the global messageQueueRef - messageQueueRef.current.push({ - func: sendMessageFunc, - identifier: tempId, - groupDirectId, - specialId: messageObj?.message?.specialId - }); + // Add the message to the global messageQueueRef + messageQueueRef.current.push({ + func: sendMessageFunc, + identifier: tempId, + groupDirectId, + specialId: messageObj?.message?.specialId, + }); - // Start processing the queue - processQueue([], groupDirectId); - }, []); + // Start processing the queue + processQueue([], groupDirectId); + }, + [] + ); // Function to process the message queue const processQueue = useCallback((newMessages = [], groupDirectId) => { - processingPromiseRef.current = processingPromiseRef.current .then(() => processQueueInternal(newMessages, groupDirectId)) .catch((err) => console.error('Error in processQueue:', err)); @@ -62,7 +69,7 @@ export const MessageQueueProvider = ({ children }) => { // Internal function to handle queue processing const processQueueInternal = async (newMessages, groupDirectId) => { // Remove any messages from the queue that match the specialId from newMessages - + // If the queue is empty, no need to process if (messageQueueRef.current.length === 0) return; @@ -92,7 +99,6 @@ export const MessageQueueProvider = ({ children }) => { // Remove the message from the queue after successful sending messageQueueRef.current.shift(); - } catch (error) { console.error('Message sending failed', error); @@ -110,7 +116,8 @@ export const MessageQueueProvider = ({ children }) => { updatedChats[groupDirectId][chatIndex].status = 'failed'; } else { // Max retries reached, set status to 'failed-permanent' - updatedChats[groupDirectId][chatIndex].status = 'failed-permanent'; + updatedChats[groupDirectId][chatIndex].status = + 'failed-permanent'; // Remove the message from the queue after max retries messageQueueRef.current.shift(); @@ -127,33 +134,39 @@ export const MessageQueueProvider = ({ children }) => { // Method to process with new messages and groupDirectId const processWithNewMessages = (newMessages, groupDirectId) => { - let updatedNewMessages = newMessages + let updatedNewMessages = newMessages; if (newMessages.length > 0) { // Remove corresponding entries in queueChats for the provided groupDirectId setQueueChats((prev) => { const updatedChats = { ...prev }; if (updatedChats[groupDirectId]) { - - updatedNewMessages = newMessages?.map((msg)=> { - const findTempMsg = updatedChats[groupDirectId]?.find((msg2)=> msg2?.message?.specialId === msg?.specialId) - if(findTempMsg){ + updatedNewMessages = newMessages?.map((msg) => { + const findTempMsg = updatedChats[groupDirectId]?.find( + (msg2) => msg2?.message?.specialId === msg?.specialId + ); + if (findTempMsg) { return { ...msg, - tempSignature: findTempMsg?.signature - } + tempSignature: findTempMsg?.signature, + }; } - return msg - }) - - - updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => { - return !newMessages.some(newMsg => newMsg?.specialId === chat?.message?.specialId); + return msg; }); + updatedChats[groupDirectId] = updatedChats[groupDirectId].filter( + (chat) => { + return !newMessages.some( + (newMsg) => newMsg?.specialId === chat?.message?.specialId + ); + } + ); + // Remove messages with status 'failed-permanent' - updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => { - return chat?.status !== 'failed-permanent'; - }); + updatedChats[groupDirectId] = updatedChats[groupDirectId].filter( + (chat) => { + return chat?.status !== 'failed-permanent'; + } + ); // If no more chats for this group, delete the groupDirectId entry if (updatedChats[groupDirectId].length === 0) { @@ -162,27 +175,36 @@ export const MessageQueueProvider = ({ children }) => { } return updatedChats; }); - } setTimeout(() => { - if(!messageQueueRef.current.find((msg) => msg?.groupDirectId === groupDirectId)){ + if ( + !messageQueueRef.current.find( + (msg) => msg?.groupDirectId === groupDirectId + ) + ) { setQueueChats((prev) => { const updatedChats = { ...prev }; if (updatedChats[groupDirectId]) { - delete updatedChats[groupDirectId] + delete updatedChats[groupDirectId]; } - - return updatedChats - } - ) + + return updatedChats; + }); } }, 300); - - return updatedNewMessages + + return updatedNewMessages; }; return ( - + {children} ); diff --git a/src/hooks/useHandlePaymentNotification.tsx b/src/hooks/useHandlePaymentNotification.tsx index 9ca9ff5..7a13072 100644 --- a/src/hooks/useHandlePaymentNotification.tsx +++ b/src/hooks/useHandlePaymentNotification.tsx @@ -7,11 +7,9 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events'; import { useAtom } from 'jotai'; export const useHandlePaymentNotification = (address) => { - const [latestTx, setLatestTx] = useState(null); - const nameAddressOfSender = useRef({}); const isFetchingName = useRef({}); - + const [latestTx, setLatestTx] = useState(null); const [lastEnteredTimestampPayment, setLastEnteredTimestampPayment] = useAtom( lastPaymentSeenTimestampAtom ); @@ -63,6 +61,7 @@ export const useHandlePaymentNotification = (address) => { const key = `last-seen-payment-${address}`; const res = await getData(key).catch(() => null); + if (res) { setLastEnteredTimestampPayment(res); } @@ -76,6 +75,7 @@ export const useHandlePaymentNotification = (address) => { const latestTx = responseData.filter( (tx) => tx?.creatorAddress !== address && tx?.recipient === address )[0]; + if (!latestTx) { return; // continue to the next group } @@ -128,6 +128,7 @@ export const useHandlePaymentNotification = (address) => { ); }; }, [setLastEnteredTimestampPaymentEventFunc]); + return { latestTx, getNameOrAddressOfSenderMiddle, diff --git a/src/hooks/useNameSearch.tsx b/src/hooks/useNameSearch.tsx index 6bc86ec..e00fec1 100644 --- a/src/hooks/useNameSearch.tsx +++ b/src/hooks/useNameSearch.tsx @@ -5,6 +5,7 @@ interface NameListItem { name: string; address: string; } + export const useNameSearch = (value: string, limit = 20) => { const [nameList, setNameList] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -48,6 +49,7 @@ export const useNameSearch = (value: string, limit = 20) => { clearTimeout(handler); }; }, [value, limit, checkIfNameExisits]); + return { isLoading, results: nameList, From 42f870b466f365df06d374d4b8aa03c0f4d8f543 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sun, 11 May 2025 00:02:02 +0200 Subject: [PATCH 07/47] Sort --- src/atoms/global.ts | 51 ++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/atoms/global.ts b/src/atoms/global.ts index e0b4156..baa96d5 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -18,37 +18,36 @@ export const sortablePinnedAppsAtom = atomWithReset([ { name: 'Q-Nodecontrol', service: 'APP' }, ]); -export const canSaveSettingToQdnAtom = atomWithReset(false); -export const settingsQDNLastUpdatedAtom = atomWithReset(-100); -export const settingsLocalLastUpdatedAtom = atomWithReset(0); -export const oldPinnedAppsAtom = atomWithReset([]); -export const isUsingImportExportSettingsAtom = atomWithReset(null); -export const fullScreenAtom = atomWithReset(false); -export const hasSettingsChangedAtom = atomWithReset(false); -export const navigationControllerAtom = atomWithReset({}); -export const enabledDevModeAtom = atomWithReset(false); -export const myGroupsWhereIAmAdminAtom = atomWithReset([]); -export const promotionTimeIntervalAtom = atomWithReset(0); -export const promotionsAtom = atomWithReset([]); -export const resourceDownloadControllerAtom = atomWithReset({}); -export const blobControllerAtom = atomWithReset({}); -export const selectedGroupIdAtom = atomWithReset(null); export const addressInfoControllerAtom = atomWithReset({}); +export const blobControllerAtom = atomWithReset({}); +export const canSaveSettingToQdnAtom = atomWithReset(false); +export const enabledDevModeAtom = atomWithReset(false); +export const fullScreenAtom = atomWithReset(false); +export const groupAnnouncementsAtom = atomWithReset({}); +export const groupChatTimestampsAtom = atomWithReset({}); +export const groupsOwnerNamesAtom = atomWithReset({}); +export const groupsPropertiesAtom = atomWithReset({}); +export const hasSettingsChangedAtom = atomWithReset(false); export const isDisabledEditorEnterAtom = atomWithReset(false); -export const qMailLastEnteredTimestampAtom = atomWithReset(null); +export const isOpenBlockedModalAtom = atomWithReset(false); +export const isRunningPublicNodeAtom = atomWithReset(false); +export const isUsingImportExportSettingsAtom = atomWithReset(null); export const lastPaymentSeenTimestampAtom = atomWithReset(null); export const mailsAtom = atomWithReset([]); -export const groupsPropertiesAtom = atomWithReset({}); -export const groupsOwnerNamesAtom = atomWithReset({}); -export const isOpenBlockedModalAtom = atomWithReset(false); -export const groupAnnouncementsAtom = atomWithReset({}); -export const mutedGroupsAtom = atomWithReset([]); -export const groupChatTimestampsAtom = atomWithReset({}); -export const timestampEnterDataAtom = atomWithReset({}); - -export const txListAtom = atomWithReset([]); export const memberGroupsAtom = atomWithReset([]); -export const isRunningPublicNodeAtom = atomWithReset(false); +export const mutedGroupsAtom = atomWithReset([]); +export const myGroupsWhereIAmAdminAtom = atomWithReset([]); +export const navigationControllerAtom = atomWithReset({}); +export const oldPinnedAppsAtom = atomWithReset([]); +export const promotionsAtom = atomWithReset([]); +export const promotionTimeIntervalAtom = atomWithReset(0); +export const qMailLastEnteredTimestampAtom = atomWithReset(null); +export const resourceDownloadControllerAtom = atomWithReset({}); +export const selectedGroupIdAtom = atomWithReset(null); +export const settingsLocalLastUpdatedAtom = atomWithReset(0); +export const settingsQDNLastUpdatedAtom = atomWithReset(-100); +export const timestampEnterDataAtom = atomWithReset({}); +export const txListAtom = atomWithReset([]); // Atom Families (replacing selectorFamily) export const resourceKeySelector = atomFamily((key) => From 8b22e65646fb97a5d80d7fdfe442bfde1d81ed9a Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 12 May 2025 08:19:05 +0200 Subject: [PATCH 08/47] Add auth action --- src/App.tsx | 10 ++- src/ExtStates/NotAuthenticated.tsx | 6 +- src/Wallets.tsx | 26 +++--- src/components/Auth/DownloadWallet.tsx | 6 +- src/components/Home/NewUsersCTA.tsx | 2 +- src/i18n/locales/en/auth.json | 17 ++-- src/i18n/locales/en/core.json | 6 +- src/utils/queue/queue.ts | 110 +++++++++++++------------ 8 files changed, 98 insertions(+), 85 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index d24a4ae..07b915c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2513,7 +2513,7 @@ function App() { setExtstate('create-wallet'); }} > - {t('auth:create_account', { postProcess: 'capitalize' })} + {t('auth:action.create_account', { postProcess: 'capitalize' })} )} @@ -2610,7 +2610,7 @@ function App() { fontWeight: 600, }} > - {t('auth:authenticate', { postProcess: 'capitalize' })} + {t('auth:action.authenticate', { postProcess: 'capitalize' })} @@ -2662,7 +2662,7 @@ function App() { - {t('auth:authenticate', { postProcess: 'capitalize' })} + {t('auth:action.authenticate', { postProcess: 'capitalize' })} {walletToBeDecryptedError} @@ -2930,7 +2930,9 @@ function App() { - {t('auth:create_account', { postProcess: 'capitalize' })} + {t('auth:action.create_account', { + postProcess: 'capitalize', + })} {walletToBeDownloadedError} diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 693cdf0..2a6b090 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -572,7 +572,7 @@ export const NotAuthenticated = ({ }, }} > - {t('auth:create_account', { postProcess: 'capitalize' })} + {t('auth:action.create_account', { postProcess: 'capitalize' })} @@ -953,7 +953,9 @@ export const NotAuthenticated = ({ setCustomNodeToSaveIndex(null); }} > - {t('auth:return_to_list', { postProcess: 'capitalize' })} + {t('auth:action.return_to_list', { + postProcess: 'capitalize', + })} { }} autoFocus > - Add + {t('core:action.add', { + postProcess: 'capitalize', + })} { const [note, setNote] = useState(''); const [isEdit, setIsEdit] = useState(false); const theme = useTheme(); + const { t } = useTranslation(['core', 'auth']); useEffect(() => { if (wallet?.name) { @@ -463,6 +485,7 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { + { marginTop: '5px', }} > - Login + {t('core:action.login', { + postProcess: 'capitalize', + })} } @@ -519,7 +544,11 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { padding: '8px', }} > - + { - + { variant="contained" onClick={() => setIsEdit(false)} > - Close + {t('core:action.close', { + postProcess: 'capitalize', + })} diff --git a/src/i18n/locales/en/auth.json b/src/i18n/locales/en/auth.json index 09aae5f..8a514fa 100644 --- a/src/i18n/locales/en/auth.json +++ b/src/i18n/locales/en/auth.json @@ -6,9 +6,13 @@ "selected": "selected account" }, "action": { - "add_seed_phrase": "add seed-phrase", + "add": { + "account": "add account", + "seed_phrase": "add seed-phrase" + }, "authenticate": "authenticate", "create_account": "create account", + "choose_password": "choose new password", "download_account": "download account", "return_to_list": "return to list" }, @@ -29,9 +33,11 @@ "generic": { "no_account": "No accounts saved", "keep_secure": "keep your account file secure", + "type_seed": "type or paste in your seed-phrase", "your_accounts": "your saved accounts" } }, + "name": "name", "node": { "choose": "choose custom node", "custom_many": "custom nodes", @@ -40,9 +46,12 @@ "using": "using node", "using_public": "using public node" }, + "note": "note", "password": "password", "password_confirmation": "confirm password", + "seed": "seed phrase", "tips": { + "additional_wallet": "use this option to connect additional Qortal wallets you've already made, in order to login with them afterwards. You will need access to your backup JSON file in order to do so.", "digital_id": "your wallet is like your digital ID on Qortal, and is how you will login to the Qortal User Interface. It holds your public address and the Qortal name you will eventually choose. Every transaction you make is linked to your ID, and this is where you manage all your QORT and other tradeable cryptocurrencies on Qortal.", "existing_account": "already have a Qortal account? Enter your secret backup phrase here to access it. This phrase is one of the ways to recover your account.", "new_account": "creating an account means creating a new wallet and digital ID to start using Qortal. Once you have made your account, you can start doing things like obtaining some QORT, buying a name and avatar, publishing videos and blogs, and much more.", diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 6114abc..41ff3c0 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -20,6 +20,7 @@ "import": "import", "invite": "invite", "join": "join", + "login": "login", "logout": "logout", "new": { "post": "new post", @@ -28,7 +29,9 @@ "notify": "notify", "post": "post", "post_message": "post message", - "publish": "publish" + "publish": "publish", + "remove": "remove", + "save": "save" }, "admin": "admin", "core": { From 035142504b14b2711c01d69879896cd56ed4896c Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Mon, 12 May 2025 09:27:24 +0200 Subject: [PATCH 10/47] Move file --- src/components/Chat/AdminSpaceInner.tsx | 2 +- src/components/{ => Chat}/GroupAvatar.tsx | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) rename src/components/{ => Chat}/GroupAvatar.tsx (96%) diff --git a/src/components/Chat/AdminSpaceInner.tsx b/src/components/Chat/AdminSpaceInner.tsx index 9a1cffd..1d1dc26 100644 --- a/src/components/Chat/AdminSpaceInner.tsx +++ b/src/components/Chat/AdminSpaceInner.tsx @@ -15,7 +15,7 @@ import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; import { formatTimestampForum } from '../../utils/time'; import { Spacer } from '../../common/Spacer'; -import { GroupAvatar } from '../GroupAvatar'; +import { GroupAvatar } from './GroupAvatar'; export const getPublishesFromAdminsAdminSpace = async ( admins: string[], diff --git a/src/components/GroupAvatar.tsx b/src/components/Chat/GroupAvatar.tsx similarity index 96% rename from src/components/GroupAvatar.tsx rename to src/components/Chat/GroupAvatar.tsx index 3244d5c..1533dbe 100644 --- a/src/components/GroupAvatar.tsx +++ b/src/components/Chat/GroupAvatar.tsx @@ -1,6 +1,10 @@ import { useCallback, useContext, useEffect, useState } from 'react'; import Logo2 from '../assets/svgs/Logo2.svg'; -import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from '../App'; +import { + MyContext, + getArbitraryEndpointReact, + getBaseApiReact, +} from '../../App'; import { Avatar, Box, @@ -10,10 +14,10 @@ import { Typography, useTheme, } from '@mui/material'; -import { Spacer } from '../common/Spacer'; -import ImageUploader from '../common/ImageUploader'; -import { getFee } from '../background'; -import { fileToBase64 } from '../utils/fileReading'; +import { Spacer } from '../../common/Spacer'; +import ImageUploader from '../../common/ImageUploader'; +import { getFee } from '../../background'; +import { fileToBase64 } from '../../utils/fileReading'; import { LoadingButton } from '@mui/lab'; import ErrorIcon from '@mui/icons-material/Error'; From 452c3a0894576c6f3b521a0c72f5a7f6ff55b7ad Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 13 May 2025 06:15:26 +0200 Subject: [PATCH 11/47] Format code --- src/components/Chat/GroupAvatar.tsx | 2 +- src/utils/chromeStorage.ts | 63 +++++--- src/utils/decode.ts | 54 +++---- src/utils/decryptChatMessage.ts | 75 +++++---- src/utils/decryptWallet.ts | 57 +++---- src/utils/events.ts | 21 +-- src/utils/helpers.ts | 48 +++--- src/utils/indexedDB.ts | 169 +++++++++++--------- src/utils/memeTypes.ts | 230 ++++++++++++++-------------- src/utils/time.ts | 101 ++++++------ src/utils/utils.ts | 102 ++++++------ src/utils/validateAddress.ts | 29 ++-- 12 files changed, 502 insertions(+), 449 deletions(-) diff --git a/src/components/Chat/GroupAvatar.tsx b/src/components/Chat/GroupAvatar.tsx index 1533dbe..8e00911 100644 --- a/src/components/Chat/GroupAvatar.tsx +++ b/src/components/Chat/GroupAvatar.tsx @@ -1,5 +1,5 @@ import { useCallback, useContext, useEffect, useState } from 'react'; -import Logo2 from '../assets/svgs/Logo2.svg'; +import Logo2 from '../../assets/svgs/Logo2.svg'; import { MyContext, getArbitraryEndpointReact, diff --git a/src/utils/chromeStorage.ts b/src/utils/chromeStorage.ts index ebe7578..f2b227c 100644 --- a/src/utils/chromeStorage.ts +++ b/src/utils/chromeStorage.ts @@ -7,22 +7,25 @@ const keysToEncrypt = ['keyPair']; async function initializeKeyAndIV() { if (!inMemoryKey) { - inMemoryKey = await generateKey(); // Generates the key in memory + inMemoryKey = await generateKey(); // Generates the key in memory } } async function generateKey(): Promise { return await crypto.subtle.generateKey( { - name: "AES-GCM", - length: 256 + name: 'AES-GCM', + length: 256, }, true, - ["encrypt", "decrypt"] + ['encrypt', 'decrypt'] ); } -async function encryptData(data: string, key: CryptoKey): Promise<{ iv: Uint8Array; encryptedData: ArrayBuffer }> { +async function encryptData( + data: string, + key: CryptoKey +): Promise<{ iv: Uint8Array; encryptedData: ArrayBuffer }> { const encoder = new TextEncoder(); const encodedData = encoder.encode(data); @@ -31,8 +34,8 @@ async function encryptData(data: string, key: CryptoKey): Promise<{ iv: Uint8Arr const encryptedData = await crypto.subtle.encrypt( { - name: "AES-GCM", - iv: iv + name: 'AES-GCM', + iv: iv, }, key, encodedData @@ -41,11 +44,15 @@ async function encryptData(data: string, key: CryptoKey): Promise<{ iv: Uint8Arr return { iv, encryptedData }; } -async function decryptData(encryptedData: ArrayBuffer, key: CryptoKey, iv: Uint8Array): Promise { +async function decryptData( + encryptedData: ArrayBuffer, + key: CryptoKey, + iv: Uint8Array +): Promise { const decryptedData = await crypto.subtle.decrypt( { - name: "AES-GCM", - iv: iv + name: 'AES-GCM', + iv: iv, }, key, encryptedData @@ -83,7 +90,10 @@ export const storeData = async (key: string, payload: any): Promise => { const { iv, encryptedData } = await encryptData(base64Data, inMemoryKey); // Combine IV and encrypted data into a single Uint8Array - const combinedData = new Uint8Array([...iv, ...new Uint8Array(encryptedData)]); + const combinedData = new Uint8Array([ + ...iv, + ...new Uint8Array(encryptedData), + ]); const encryptedBase64Data = btoa(String.fromCharCode(...combinedData)); await SecureStoragePlugin.set({ key, value: encryptedBase64Data }); } else { @@ -91,10 +101,9 @@ export const storeData = async (key: string, payload: any): Promise => { await SecureStoragePlugin.set({ key, value: base64Data }); } - return "Data saved successfully"; + return 'Data saved successfully'; }; - export const getData = async (key: string): Promise => { await initializeKeyAndIV(); @@ -105,13 +114,17 @@ export const getData = async (key: string): Promise => { if (keysToEncrypt.includes(key) && inMemoryKey) { // Decode the Base64-encoded encrypted data const combinedData = atob(storedDataBase64.value) - .split("") + .split('') .map((c) => c.charCodeAt(0)); const iv = new Uint8Array(combinedData.slice(0, 12)); // First 12 bytes are the IV const encryptedData = new Uint8Array(combinedData.slice(12)).buffer; - const decryptedBase64Data = await decryptData(encryptedData, inMemoryKey, iv); + const decryptedBase64Data = await decryptData( + encryptedData, + inMemoryKey, + iv + ); return base64ToJson(decryptedBase64Data); } else { // Decode non-encrypted data @@ -121,21 +134,21 @@ export const getData = async (key: string): Promise => { return null; } } catch (error) { - return null + return null; } }; - - - - // Remove keys from storage and log out -export async function removeKeysAndLogout(keys: string[], event: MessageEvent, request: any) { +export async function removeKeysAndLogout( + keys: string[], + event: MessageEvent, + request: any +) { try { for (const key of keys) { try { await SecureStoragePlugin.remove({ key }); - await SecureStoragePlugin.remove({ key: `${key}_iv` }); // Remove associated IV + await SecureStoragePlugin.remove({ key: `${key}_iv` }); // Remove associated IV } catch (error) { console.warn(`Key not found: ${key}`); } @@ -144,13 +157,13 @@ export async function removeKeysAndLogout(keys: string[], event: MessageEvent, r event.source.postMessage( { requestId: request.requestId, - action: "logout", + action: 'logout', payload: true, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); } catch (error) { - console.error("Error removing keys:", error); + console.error('Error removing keys:', error); } } diff --git a/src/utils/decode.ts b/src/utils/decode.ts index 06fdb2b..ba8d87d 100644 --- a/src/utils/decode.ts +++ b/src/utils/decode.ts @@ -1,31 +1,31 @@ export function decodeIfEncoded(input) { - try { - // Check if input is URI-encoded by encoding and decoding - const encoded = encodeURIComponent(decodeURIComponent(input)); - if (encoded === input) { - // Input is URI-encoded, so decode it - return decodeURIComponent(input); - } - } catch (e) { - // decodeURIComponent throws an error if input is not encoded - console.error("Error decoding URI:", e); + try { + // Check if input is URI-encoded by encoding and decoding + const encoded = encodeURIComponent(decodeURIComponent(input)); + if (encoded === input) { + // Input is URI-encoded, so decode it + return decodeURIComponent(input); } - - // Return input as-is if not URI-encoded - return input; + } catch (e) { + // decodeURIComponent throws an error if input is not encoded + console.error('Error decoding URI:', e); } - export const isValidBase64 = (str: string): boolean => { - if (typeof str !== "string" || str.length % 4 !== 0) return false; - - const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/; - return base64Regex.test(str); - }; - - export const isValidBase64WithDecode = (str: string): boolean => { - try { - return isValidBase64(str) && Boolean(atob(str)); - } catch { - return false; - } - }; \ No newline at end of file + // Return input as-is if not URI-encoded + return input; +} + +export const isValidBase64 = (str: string): boolean => { + if (typeof str !== 'string' || str.length % 4 !== 0) return false; + + const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/; + return base64Regex.test(str); +}; + +export const isValidBase64WithDecode = (str: string): boolean => { + try { + return isValidBase64(str) && Boolean(atob(str)); + } catch { + return false; + } +}; diff --git a/src/utils/decryptChatMessage.ts b/src/utils/decryptChatMessage.ts index c50bb45..39085e8 100644 --- a/src/utils/decryptChatMessage.ts +++ b/src/utils/decryptChatMessage.ts @@ -1,38 +1,59 @@ // @ts-nocheck -import Base58 from '../deps/Base58' -import ed2curve from '../deps/ed2curve' -import nacl from '../deps/nacl-fast' -import {Sha256} from 'asmcrypto.js' +import Base58 from '../deps/Base58'; +import ed2curve from '../deps/ed2curve'; +import nacl from '../deps/nacl-fast'; +import { Sha256 } from 'asmcrypto.js'; +export const decryptChatMessage = ( + encryptedMessage, + privateKey, + recipientPublicKey, + lastReference +) => { + const test = encryptedMessage; + let _encryptedMessage = atob(encryptedMessage); + const binaryLength = _encryptedMessage.length; + const bytes = new Uint8Array(binaryLength); -export const decryptChatMessage = (encryptedMessage, privateKey, recipientPublicKey, lastReference) => { - const test = encryptedMessage - let _encryptedMessage = atob(encryptedMessage) - const binaryLength = _encryptedMessage.length - const bytes = new Uint8Array(binaryLength) + for (let i = 0; i < binaryLength; i++) { + bytes[i] = _encryptedMessage.charCodeAt(i); + } - for (let i = 0; i < binaryLength; i++) { - bytes[i] = _encryptedMessage.charCodeAt(i) - } + const _base58RecipientPublicKey = + recipientPublicKey instanceof Uint8Array + ? Base58.encode(recipientPublicKey) + : recipientPublicKey; + const _recipientPublicKey = Base58.decode(_base58RecipientPublicKey); - - const _base58RecipientPublicKey = recipientPublicKey instanceof Uint8Array ? Base58.encode(recipientPublicKey) : recipientPublicKey - const _recipientPublicKey = Base58.decode(_base58RecipientPublicKey) + const _lastReference = + lastReference instanceof Uint8Array + ? lastReference + : Base58.decode(lastReference); - const _lastReference = lastReference instanceof Uint8Array ? lastReference : Base58.decode(lastReference) + const convertedPrivateKey = ed2curve.convertSecretKey(privateKey); + const convertedPublicKey = ed2curve.convertPublicKey(_recipientPublicKey); + const sharedSecret = new Uint8Array(32); + nacl.lowlevel.crypto_scalarmult( + sharedSecret, + convertedPrivateKey, + convertedPublicKey + ); - const convertedPrivateKey = ed2curve.convertSecretKey(privateKey) - const convertedPublicKey = ed2curve.convertPublicKey(_recipientPublicKey) - const sharedSecret = new Uint8Array(32); - nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedPublicKey) + const _chatEncryptionSeed = new Sha256() + .process(sharedSecret) + .finish().result; + const _decryptedMessage = nacl.secretbox.open( + bytes, + _lastReference.slice(0, 24), + _chatEncryptionSeed + ); - const _chatEncryptionSeed = new Sha256().process(sharedSecret).finish().result - const _decryptedMessage = nacl.secretbox.open(bytes, _lastReference.slice(0, 24), _chatEncryptionSeed) + let decryptedMessage = ''; - let decryptedMessage = '' + _decryptedMessage === false + ? decryptedMessage + : (decryptedMessage = new TextDecoder('utf-8').decode(_decryptedMessage)); - _decryptedMessage === false ? decryptedMessage : decryptedMessage = new TextDecoder('utf-8').decode(_decryptedMessage) - - return decryptedMessage -} \ No newline at end of file + return decryptedMessage; +}; diff --git a/src/utils/decryptWallet.ts b/src/utils/decryptWallet.ts index b091639..11394a2 100644 --- a/src/utils/decryptWallet.ts +++ b/src/utils/decryptWallet.ts @@ -1,33 +1,38 @@ // @ts-nocheck -import { crypto } from '../constants/decryptWallet' -import Base58 from '../deps/Base58' -import {AES_CBC, HmacSha512} from 'asmcrypto.js' -import { doInitWorkers, kdf } from '../deps/kdf' - +import { crypto } from '../constants/decryptWallet'; +import Base58 from '../deps/Base58'; +import { AES_CBC, HmacSha512 } from 'asmcrypto.js'; +import { doInitWorkers, kdf } from '../deps/kdf'; export const decryptStoredWallet = async (password, wallet) => { - const threads = doInitWorkers(crypto.kdfThreads) - const encryptedSeedBytes = Base58.decode(wallet.encryptedSeed) - const iv = Base58.decode(wallet.iv) - const salt = Base58.decode(wallet.salt) - - const key = await kdf(password, salt, threads) - const encryptionKey = key.slice(0, 32) - const macKey = key.slice(32, 63) - const mac = new HmacSha512(macKey).process(encryptedSeedBytes).finish().result - if (Base58.encode(mac) !== wallet.mac) { - throw new Error("Incorrect password") - } - const decryptedBytes = AES_CBC.decrypt(encryptedSeedBytes, encryptionKey, false, iv) - return decryptedBytes -} + const threads = doInitWorkers(crypto.kdfThreads); + const encryptedSeedBytes = Base58.decode(wallet.encryptedSeed); + const iv = Base58.decode(wallet.iv); + const salt = Base58.decode(wallet.salt); + + const key = await kdf(password, salt, threads); + const encryptionKey = key.slice(0, 32); + const macKey = key.slice(32, 63); + const mac = new HmacSha512(macKey) + .process(encryptedSeedBytes) + .finish().result; + if (Base58.encode(mac) !== wallet.mac) { + throw new Error('Incorrect password'); + } + const decryptedBytes = AES_CBC.decrypt( + encryptedSeedBytes, + encryptionKey, + false, + iv + ); + return decryptedBytes; +}; export const decryptStoredWalletFromSeedPhrase = async (password) => { - const threads = doInitWorkers(crypto.kdfThreads) - const salt = new Uint8Array(void 0) + const threads = doInitWorkers(crypto.kdfThreads); + const salt = new Uint8Array(void 0); - - const seed = await kdf(password, salt, threads) - return seed -} \ No newline at end of file + const seed = await kdf(password, salt, threads); + return seed; +}; diff --git a/src/utils/events.ts b/src/utils/events.ts index 94d1c31..5145a93 100644 --- a/src/utils/events.ts +++ b/src/utils/events.ts @@ -1,11 +1,12 @@ -export const executeEvent = (eventName: string, data: any)=> { - const event = new CustomEvent(eventName, {detail: data}) - document.dispatchEvent(event) -} -export const subscribeToEvent = (eventName: string, listener: any)=> { - document.addEventListener(eventName, listener) -} +export const executeEvent = (eventName: string, data: any) => { + const event = new CustomEvent(eventName, { detail: data }); + document.dispatchEvent(event); +}; -export const unsubscribeFromEvent = (eventName: string, listener: any)=> { - document.removeEventListener(eventName, listener) -} \ No newline at end of file +export const subscribeToEvent = (eventName: string, listener: any) => { + document.addEventListener(eventName, listener); +}; + +export const unsubscribeFromEvent = (eventName: string, listener: any) => { + document.removeEventListener(eventName, listener); +}; diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index ff7997c..a60a523 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,34 +1,28 @@ -import moment from "moment"; - -export const delay = (time: number) => new Promise((_, reject) => +export const delay = (time: number) => + new Promise((_, reject) => setTimeout(() => reject(new Error('Request timed out')), time) -); - -// const originalHtml = `

---------- Forwarded message ---------

From: Alex

Date: Mon, Jun 9 2014 9:32 PM

Subject: Batteries

To: Jessica



`; - - -// export function updateMessageDetails(newFrom: string, newDateMillis: number, newTo: string) { -// let htmlString = originalHtml -// // Use Moment.js to format the date from milliseconds -// const formattedDate = moment(newDateMillis).format('ddd, MMM D YYYY h:mm A'); - -// // Replace the From, Date, and To fields in the HTML string -// htmlString = htmlString.replace(/

From:.*?<\/p>/, `

From: ${newFrom}

`); -// htmlString = htmlString.replace(/

Date:.*?<\/p>/, `

Date: ${formattedDate}

`); -// htmlString = htmlString.replace(/

To:.*?<\/p>/, `

To: ${newTo}

`); - -// return htmlString; -// } + ); const originalHtml = `

---------- Forwarded message ---------

From: Alex

Subject: Batteries

To: Jessica



`; +export function updateMessageDetails( + newFrom: string, + newSubject: string, + newTo: string +) { + let htmlString = originalHtml; -export function updateMessageDetails(newFrom: string, newSubject: string, newTo: string) { - let htmlString = originalHtml + htmlString = htmlString.replace( + /

From:.*?<\/p>/, + `

From: ${newFrom}

` + ); - htmlString = htmlString.replace(/

From:.*?<\/p>/, `

From: ${newFrom}

`); - htmlString = htmlString.replace(/

Subject:.*?<\/p>/, `

Subject: ${newSubject}

`); - htmlString = htmlString.replace(/

To:.*?<\/p>/, `

To: ${newTo}

`); + htmlString = htmlString.replace( + /

Subject:.*?<\/p>/, + `

Subject: ${newSubject}

` + ); - return htmlString; -} \ No newline at end of file + htmlString = htmlString.replace(/

To:.*?<\/p>/, `

To: ${newTo}

`); + + return htmlString; +} diff --git a/src/utils/indexedDB.ts b/src/utils/indexedDB.ts index 0364570..3066b6b 100644 --- a/src/utils/indexedDB.ts +++ b/src/utils/indexedDB.ts @@ -1,90 +1,107 @@ -import { openIndexedDB } from "../components/Apps/useQortalMessageListener"; -import { fileToBase64 } from "./fileReading"; +import { openIndexedDB } from '../components/Apps/useQortalMessageListener'; +import { fileToBase64 } from './fileReading'; export async function handleGetFileFromIndexedDB(event) { - try { - const { fileId, requestId } = event.data; - const db = await openIndexedDB(); - const transaction = db.transaction(["files"], "readonly"); - const objectStore = transaction.objectStore("files"); - - const getRequest = objectStore.get(fileId); - - getRequest.onsuccess = async function (event) { - if (getRequest.result) { - const file = getRequest.result.data; - - try { - const base64String = await fileToBase64(file); - - // Create a new transaction to delete the file - const deleteTransaction = db.transaction(["files"], "readwrite"); - const deleteObjectStore = deleteTransaction.objectStore("files"); - const deleteRequest = deleteObjectStore.delete(fileId); - - deleteRequest.onsuccess = function () { - try { - const targetOrigin = window.location.origin; + try { + const { fileId, requestId } = event.data; + const db = await openIndexedDB(); + const transaction = db.transaction(['files'], 'readonly'); + const objectStore = transaction.objectStore('files'); - window.postMessage( - { action: "getFileFromIndexedDBResponse", requestId, result: base64String }, - targetOrigin - ); - } catch (error) { - console.log('error', error) - } - }; - - deleteRequest.onerror = function () { - console.error(`Error deleting file with ID ${fileId} from IndexedDB`); - - }; - } catch (error) { - console.error("Error converting file to Base64:", error); - event.ports[0].postMessage({ - result: null, - error: "Failed to convert file to Base64", - }); - const targetOrigin = window.location.origin; + const getRequest = objectStore.get(fileId); - window.postMessage( - { action: "getFileFromIndexedDBResponse", requestId, result: null, - error: "Failed to convert file to Base64" - }, - targetOrigin + getRequest.onsuccess = async function (event) { + if (getRequest.result) { + const file = getRequest.result.data; + + try { + const base64String = await fileToBase64(file); + + // Create a new transaction to delete the file + const deleteTransaction = db.transaction(['files'], 'readwrite'); + const deleteObjectStore = deleteTransaction.objectStore('files'); + const deleteRequest = deleteObjectStore.delete(fileId); + + deleteRequest.onsuccess = function () { + try { + const targetOrigin = window.location.origin; + + window.postMessage( + { + action: 'getFileFromIndexedDBResponse', + requestId, + result: base64String, + }, + targetOrigin + ); + } catch (error) { + console.log('error', error); + } + }; + + deleteRequest.onerror = function () { + console.error( + `Error deleting file with ID ${fileId} from IndexedDB` ); - } - } else { - console.error(`File with ID ${fileId} not found in IndexedDB`); + }; + } catch (error) { + console.error('Error converting file to Base64:', error); + event.ports[0].postMessage({ + result: null, + error: 'Failed to convert file to Base64', + }); const targetOrigin = window.location.origin; window.postMessage( - { action: "getFileFromIndexedDBResponse", requestId, result: null, - error: 'File not found in IndexedDB' - }, - targetOrigin + { + action: 'getFileFromIndexedDBResponse', + requestId, + result: null, + error: 'Failed to convert file to Base64', + }, + targetOrigin ); } - }; - - getRequest.onerror = function () { - console.error(`Error retrieving file with ID ${fileId} from IndexedDB`); - - event.source.postMessage( - { action: "getFileFromIndexedDBResponse", requestId, result: null, - error: 'Error retrieving file from IndexedDB' - }, - event.origin + } else { + console.error(`File with ID ${fileId} not found in IndexedDB`); + const targetOrigin = window.location.origin; + + window.postMessage( + { + action: 'getFileFromIndexedDBResponse', + requestId, + result: null, + error: 'File not found in IndexedDB', + }, + targetOrigin ); - }; - } catch (error) { - const { requestId } = event.data; - console.error("Error opening IndexedDB:", error); + } + }; + + getRequest.onerror = function () { + console.error(`Error retrieving file with ID ${fileId} from IndexedDB`); + event.source.postMessage( - { action: "getFileFromIndexedDBResponse", requestId, result: null, - error: 'Error opening IndexedDB' - }, + { + action: 'getFileFromIndexedDBResponse', + requestId, + result: null, + error: 'Error retrieving file from IndexedDB', + }, event.origin ); - } - } \ No newline at end of file + }; + } catch (error) { + const { requestId } = event.data; + console.error('Error opening IndexedDB:', error); + event.source.postMessage( + { + action: 'getFileFromIndexedDBResponse', + requestId, + result: null, + error: 'Error opening IndexedDB', + }, + event.origin + ); + } +} diff --git a/src/utils/memeTypes.ts b/src/utils/memeTypes.ts index 2bd8ac0..f87277f 100644 --- a/src/utils/memeTypes.ts +++ b/src/utils/memeTypes.ts @@ -1,123 +1,125 @@ export const mimeToExtensionMap = { - // Documents - "application/pdf": ".pdf", - "application/msword": ".doc", - "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx", - "application/vnd.ms-excel": ".xls", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx", - "application/vnd.ms-powerpoint": ".ppt", - "application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx", - "application/vnd.oasis.opendocument.text": ".odt", - "application/vnd.oasis.opendocument.spreadsheet": ".ods", - "application/vnd.oasis.opendocument.presentation": ".odp", - "text/plain": ".txt", - "text/csv": ".csv", - "application/xhtml+xml": ".xhtml", - "application/xml": ".xml", - "application/rtf": ".rtf", - "application/vnd.apple.pages": ".pages", - "application/vnd.google-apps.document": ".gdoc", - "application/vnd.google-apps.spreadsheet": ".gsheet", - "application/vnd.google-apps.presentation": ".gslides", + // Documents + 'application/pdf': '.pdf', + 'application/msword': '.doc', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': + '.docx', + 'application/vnd.ms-excel': '.xls', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx', + 'application/vnd.ms-powerpoint': '.ppt', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation': + '.pptx', + 'application/vnd.oasis.opendocument.text': '.odt', + 'application/vnd.oasis.opendocument.spreadsheet': '.ods', + 'application/vnd.oasis.opendocument.presentation': '.odp', + 'text/plain': '.txt', + 'text/csv': '.csv', + 'application/xhtml+xml': '.xhtml', + 'application/xml': '.xml', + 'application/rtf': '.rtf', + 'application/vnd.apple.pages': '.pages', + 'application/vnd.google-apps.document': '.gdoc', + 'application/vnd.google-apps.spreadsheet': '.gsheet', + 'application/vnd.google-apps.presentation': '.gslides', - // Images - "image/jpeg": ".jpg", - "image/png": ".png", - "image/gif": ".gif", - "image/webp": ".webp", - "image/svg+xml": ".svg", - "image/tiff": ".tif", - "image/bmp": ".bmp", - "image/x-icon": ".ico", - "image/heic": ".heic", - "image/heif": ".heif", - "image/apng": ".apng", - "image/avif": ".avif", + // Images + 'image/jpeg': '.jpg', + 'image/png': '.png', + 'image/gif': '.gif', + 'image/webp': '.webp', + 'image/svg+xml': '.svg', + 'image/tiff': '.tif', + 'image/bmp': '.bmp', + 'image/x-icon': '.ico', + 'image/heic': '.heic', + 'image/heif': '.heif', + 'image/apng': '.apng', + 'image/avif': '.avif', - // Audio - "audio/mpeg": ".mp3", - "audio/ogg": ".ogg", - "audio/wav": ".wav", - "audio/webm": ".weba", - "audio/aac": ".aac", - "audio/flac": ".flac", - "audio/x-m4a": ".m4a", - "audio/x-ms-wma": ".wma", - "audio/midi": ".midi", - "audio/x-midi": ".mid", + // Audio + 'audio/mpeg': '.mp3', + 'audio/ogg': '.ogg', + 'audio/wav': '.wav', + 'audio/webm': '.weba', + 'audio/aac': '.aac', + 'audio/flac': '.flac', + 'audio/x-m4a': '.m4a', + 'audio/x-ms-wma': '.wma', + 'audio/midi': '.midi', + 'audio/x-midi': '.mid', - // Video - "video/mp4": ".mp4", - "video/webm": ".webm", - "video/ogg": ".ogv", - "video/x-msvideo": ".avi", - "video/quicktime": ".mov", - "video/x-ms-wmv": ".wmv", - "video/mpeg": ".mpeg", - "video/3gpp": ".3gp", - "video/3gpp2": ".3g2", - "video/x-matroska": ".mkv", - "video/x-flv": ".flv", - "video/x-ms-asf": ".asf", + // Video + 'video/mp4': '.mp4', + 'video/webm': '.webm', + 'video/ogg': '.ogv', + 'video/x-msvideo': '.avi', + 'video/quicktime': '.mov', + 'video/x-ms-wmv': '.wmv', + 'video/mpeg': '.mpeg', + 'video/3gpp': '.3gp', + 'video/3gpp2': '.3g2', + 'video/x-matroska': '.mkv', + 'video/x-flv': '.flv', + 'video/x-ms-asf': '.asf', - // Archives - "application/zip": ".zip", - "application/x-rar-compressed": ".rar", - "application/x-tar": ".tar", - "application/x-7z-compressed": ".7z", - "application/x-gzip": ".gz", - "application/x-bzip2": ".bz2", - "application/x-apple-diskimage": ".dmg", - "application/vnd.android.package-archive": ".apk", - "application/x-iso9660-image": ".iso", + // Archives + 'application/zip': '.zip', + 'application/x-rar-compressed': '.rar', + 'application/x-tar': '.tar', + 'application/x-7z-compressed': '.7z', + 'application/x-gzip': '.gz', + 'application/x-bzip2': '.bz2', + 'application/x-apple-diskimage': '.dmg', + 'application/vnd.android.package-archive': '.apk', + 'application/x-iso9660-image': '.iso', - // Code Files - "text/javascript": ".js", - "text/css": ".css", - "text/html": ".html", - "application/json": ".json", - "text/xml": ".xml", - "application/x-sh": ".sh", - "application/x-csh": ".csh", - "text/x-python": ".py", - "text/x-java-source": ".java", - "application/java-archive": ".jar", - "application/vnd.microsoft.portable-executable": ".exe", - "application/x-msdownload": ".msi", - "text/x-c": ".c", - "text/x-c++": ".cpp", - "text/x-go": ".go", - "application/x-perl": ".pl", - "text/x-php": ".php", - "text/x-ruby": ".rb", - "text/x-sql": ".sql", - "application/x-httpd-php": ".php", - "application/x-python-code": ".pyc", + // Code Files + 'text/javascript': '.js', + 'text/css': '.css', + 'text/html': '.html', + 'application/json': '.json', + 'text/xml': '.xml', + 'application/x-sh': '.sh', + 'application/x-csh': '.csh', + 'text/x-python': '.py', + 'text/x-java-source': '.java', + 'application/java-archive': '.jar', + 'application/vnd.microsoft.portable-executable': '.exe', + 'application/x-msdownload': '.msi', + 'text/x-c': '.c', + 'text/x-c++': '.cpp', + 'text/x-go': '.go', + 'application/x-perl': '.pl', + 'text/x-php': '.php', + 'text/x-ruby': '.rb', + 'text/x-sql': '.sql', + 'application/x-httpd-php': '.php', + 'application/x-python-code': '.pyc', - // ROM Files - "application/x-nintendo-nes-rom": ".nes", - "application/x-snes-rom": ".smc", - "application/x-gameboy-rom": ".gb", - "application/x-gameboy-advance-rom": ".gba", - "application/x-n64-rom": ".n64", - "application/x-sega-genesis-rom": ".gen", - "application/x-sega-master-system-rom": ".sms", - "application/x-psx-rom": ".iso", // PlayStation ROMs - "application/x-bios-rom": ".rom", - "application/x-flash-rom": ".bin", - "application/x-eeprom": ".eep", - "application/x-c64-rom": ".prg", + // ROM Files + 'application/x-nintendo-nes-rom': '.nes', + 'application/x-snes-rom': '.smc', + 'application/x-gameboy-rom': '.gb', + 'application/x-gameboy-advance-rom': '.gba', + 'application/x-n64-rom': '.n64', + 'application/x-sega-genesis-rom': '.gen', + 'application/x-sega-master-system-rom': '.sms', + 'application/x-psx-rom': '.iso', // PlayStation ROMs + 'application/x-bios-rom': '.rom', + 'application/x-flash-rom': '.bin', + 'application/x-eeprom': '.eep', + 'application/x-c64-rom': '.prg', - // Miscellaneous - "application/octet-stream": ".bin", // General binary files - "application/x-shockwave-flash": ".swf", - "application/x-silverlight-app": ".xap", - "application/x-ms-shortcut": ".lnk", - "application/vnd.ms-fontobject": ".eot", - "font/woff": ".woff", - "font/woff2": ".woff2", - "font/ttf": ".ttf", - "font/otf": ".otf", - "application/vnd.visio": ".vsd", - "application/vnd.ms-project": ".mpp", + // Miscellaneous + 'application/octet-stream': '.bin', // General binary files + 'application/x-shockwave-flash': '.swf', + 'application/x-silverlight-app': '.xap', + 'application/x-ms-shortcut': '.lnk', + 'application/vnd.ms-fontobject': '.eot', + 'font/woff': '.woff', + 'font/woff2': '.woff2', + 'font/ttf': '.ttf', + 'font/otf': '.otf', + 'application/vnd.visio': '.vsd', + 'application/vnd.ms-project': '.mpp', }; diff --git a/src/utils/time.ts b/src/utils/time.ts index c89c1cd..e434cfb 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -1,56 +1,57 @@ -import moment from "moment" +import moment from 'moment'; export function formatTimestamp(timestamp: number): string { - const now = moment() - const timestampMoment = moment(timestamp) - const elapsedTime = now.diff(timestampMoment, 'minutes') - - if (elapsedTime < 1) { - return 'Just now' - } else if (elapsedTime < 60) { - return `${elapsedTime}m ago` - } else if (elapsedTime < 1440) { - return `${Math.floor(elapsedTime / 60)}h ago` - } else { - return timestampMoment.format('MMM D, YYYY') - } + const now = moment(); + const timestampMoment = moment(timestamp); + const elapsedTime = now.diff(timestampMoment, 'minutes'); + + if (elapsedTime < 1) { + return 'Just now'; + } else if (elapsedTime < 60) { + return `${elapsedTime}m ago`; + } else if (elapsedTime < 1440) { + return `${Math.floor(elapsedTime / 60)}h ago`; + } else { + return timestampMoment.format('MMM D, YYYY'); } - export function formatTimestampForum(timestamp: number): string { - const now = moment(); - const timestampMoment = moment(timestamp); - const elapsedTime = now.diff(timestampMoment, 'minutes'); - - if (elapsedTime < 1) { - return `Just now - ${timestampMoment.format('h:mm A')}`; - } else if (elapsedTime < 60) { - return `${elapsedTime}m ago - ${timestampMoment.format('h:mm A')}`; - } else if (elapsedTime < 1440) { - return `${Math.floor(elapsedTime / 60)}h ago - ${timestampMoment.format('h:mm A')}`; - } else { - return timestampMoment.format('MMM D, YYYY - h:mm A'); - } } - export const formatDate = (unixTimestamp: number): string => { - const date = moment(unixTimestamp, 'x').fromNow() - - return date - } +export function formatTimestampForum(timestamp: number): string { + const now = moment(); + const timestampMoment = moment(timestamp); + const elapsedTime = now.diff(timestampMoment, 'minutes'); - export function sortArrayByTimestampAndGroupName(array) { - return array.sort((a, b) => { - if (a.timestamp && b.timestamp) { - // Both have timestamp, sort by timestamp descending - return b.timestamp - a.timestamp; - } else if (a.timestamp) { - // Only `a` has timestamp, it comes first - return -1; - } else if (b.timestamp) { - // Only `b` has timestamp, it comes first - return 1; - } else { - // Neither has timestamp, sort alphabetically by groupName - return a.groupName.localeCompare(b.groupName); - } - }); - } \ No newline at end of file + if (elapsedTime < 1) { + return `Just now - ${timestampMoment.format('h:mm A')}`; + } else if (elapsedTime < 60) { + return `${elapsedTime}m ago - ${timestampMoment.format('h:mm A')}`; + } else if (elapsedTime < 1440) { + return `${Math.floor(elapsedTime / 60)}h ago - ${timestampMoment.format('h:mm A')}`; + } else { + return timestampMoment.format('MMM D, YYYY - h:mm A'); + } +} + +export const formatDate = (unixTimestamp: number): string => { + const date = moment(unixTimestamp, 'x').fromNow(); + + return date; +}; + +export function sortArrayByTimestampAndGroupName(array) { + return array.sort((a, b) => { + if (a.timestamp && b.timestamp) { + // Both have timestamp, sort by timestamp descending + return b.timestamp - a.timestamp; + } else if (a.timestamp) { + // Only `a` has timestamp, it comes first + return -1; + } else if (b.timestamp) { + // Only `b` has timestamp, it comes first + return 1; + } else { + // Neither has timestamp, sort alphabetically by groupName + return a.groupName.localeCompare(b.groupName); + } + }); +} diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 49912ae..a981fe0 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,55 +1,55 @@ // @ts-nocheck const utils = { - int32ToBytes (word) { - var byteArray = [] - for (var b = 0; b < 32; b += 8) { - byteArray.push((word >>> (24 - b % 32)) & 0xFF) - } - return byteArray - }, - - stringtoUTF8Array (message) { - if (typeof message === 'string') { - var s = unescape(encodeURIComponent(message)) // UTF-8 - message = new Uint8Array(s.length) - for (var i = 0; i < s.length; i++) { - message[i] = s.charCodeAt(i) & 0xff - } - } - return message - }, - // ...buffers then buffers.foreach and append to buffer1 - appendBuffer (buffer1, buffer2) { - buffer1 = new Uint8Array(buffer1) - buffer2 = new Uint8Array(buffer2) - const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength) - tmp.set(buffer1, 0) - tmp.set(buffer2, buffer1.byteLength) - return tmp - }, - - int64ToBytes (int64) { - // we want to represent the input as a 8-bytes array - var byteArray = [0, 0, 0, 0, 0, 0, 0, 0] - - for (var index = 0; index < byteArray.length; index++) { - var byte = int64 & 0xff - byteArray[byteArray.length - index - 1] = byte - int64 = (int64 - byte) / 256 - } - - return byteArray - }, - - equal (buf1, buf2) { - if (buf1.byteLength != buf2.byteLength) return false - var dv1 = new Uint8Array(buf1) - var dv2 = new Uint8Array(buf2) - for (var i = 0; i != buf1.byteLength; i++) { - if (dv1[i] != dv2[i]) return false - } - return true + int32ToBytes(word) { + var byteArray = []; + for (var b = 0; b < 32; b += 8) { + byteArray.push((word >>> (24 - (b % 32))) & 0xff); } -} + return byteArray; + }, -export default utils + stringtoUTF8Array(message) { + if (typeof message === 'string') { + var s = unescape(encodeURIComponent(message)); // UTF-8 + message = new Uint8Array(s.length); + for (var i = 0; i < s.length; i++) { + message[i] = s.charCodeAt(i) & 0xff; + } + } + return message; + }, + // ...buffers then buffers.foreach and append to buffer1 + appendBuffer(buffer1, buffer2) { + buffer1 = new Uint8Array(buffer1); + buffer2 = new Uint8Array(buffer2); + const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength); + tmp.set(buffer1, 0); + tmp.set(buffer2, buffer1.byteLength); + return tmp; + }, + + int64ToBytes(int64) { + // we want to represent the input as a 8-bytes array + var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]; + + for (var index = 0; index < byteArray.length; index++) { + var byte = int64 & 0xff; + byteArray[byteArray.length - index - 1] = byte; + int64 = (int64 - byte) / 256; + } + + return byteArray; + }, + + equal(buf1, buf2) { + if (buf1.byteLength != buf2.byteLength) return false; + var dv1 = new Uint8Array(buf1); + var dv2 = new Uint8Array(buf2); + for (var i = 0; i != buf1.byteLength; i++) { + if (dv1[i] != dv2[i]) return false; + } + return true; + }, +}; + +export default utils; diff --git a/src/utils/validateAddress.ts b/src/utils/validateAddress.ts index 8b359bf..bbab210 100644 --- a/src/utils/validateAddress.ts +++ b/src/utils/validateAddress.ts @@ -1,21 +1,20 @@ // @ts-nocheck -import Base58 from "../deps/Base58" +import Base58 from '../deps/Base58'; export const validateAddress = (address) => { - let isAddress = false - try { - const decodePubKey = Base58.decode(address) + let isAddress = false; + try { + const decodePubKey = Base58.decode(address); - if (!(decodePubKey instanceof Uint8Array && decodePubKey.length == 25)) { - isAddress = false - } else { - isAddress = true - } - - } catch (error) { - - } + if (!(decodePubKey instanceof Uint8Array && decodePubKey.length == 25)) { + isAddress = false; + } else { + isAddress = true; + } + } catch (error) { + console.log(error); + } - return isAddress -} + return isAddress; +}; From 9adddce9fc7a863918e9f4151666d8fd392d99a6 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 13 May 2025 06:39:29 +0200 Subject: [PATCH 12/47] Add minting translations --- src/components/Minting/Minting.tsx | 186 +++++++++++++++++++---------- src/i18n/locales/en/core.json | 2 + src/i18n/locales/en/group.json | 9 +- 3 files changed, 132 insertions(+), 65 deletions(-) diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index fe05c5b..c84ffdb 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -27,6 +27,7 @@ import { FidgetSpinner } from 'react-loader-spinner'; import { useModal } from '../../common/useModal'; import { useAtom, useSetAtom } from 'jotai'; import { memberGroupsAtom, txListAtom } from '../../atoms/global'; +import { useTranslation } from 'react-i18next'; export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const setTxList = useSetAtom(txListAtom); @@ -44,7 +45,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const { show: showKey, message } = useModal(); const { isShow: isShowNext, onOk, show: showNext } = useModal(); const theme = useTheme(); - + const { t } = useTranslation(['core', 'auth', 'group']); const [info, setInfo] = useState(null); const [names, setNames] = useState({}); const [accountInfos, setAccountInfos] = useState({}); @@ -223,13 +224,21 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || 'An error occurred' }); + rej({ + message: + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }), + }); }); }); } catch (error) { setInfo({ type: 'error', - message: error?.message || 'Unable to add minting account', + message: + error?.message || + t('core:message.error.minting_account_add', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); } finally { @@ -263,13 +272,21 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || 'An error occurred' }); + rej({ + message: + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }), + }); }); }); } catch (error) { setInfo({ type: 'error', - message: error?.message || 'Unable to remove minting account', + message: + error?.message || + t('core:message.error.minting_account_remove', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); } finally { @@ -278,9 +295,13 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { }, []); const createRewardShare = useCallback(async (publicKey, recipient) => { - const fee = await getFee('REWARD_SHARE'); // TODO translate + const fee = await getFee('REWARD_SHARE'); await show({ - message: 'Would you like to perform an REWARD_SHARE transaction?', + message: t('group:question.perform_transaction', { + // TODO move from group into core namespace + action: 'REWARD_SHARE', + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); return await new Promise((res, rej) => { @@ -295,8 +316,12 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { recipient, ...response, type: 'add-rewardShare', - label: `Add rewardshare: awaiting confirmation`, - labelDone: `Add rewardshare: success!`, + label: t('group:message.success.rewardshare_add', { + postProcess: 'capitalize', + }), + labelDone: t('group:message.success.rewardshare_add_label', { + postProcess: 'capitalize', + }), done: false, }, ...prev, @@ -307,7 +332,11 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || 'An error occurred' }); + rej({ + message: + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }), + }); }); }); }, []); @@ -326,7 +355,11 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || 'An error occurred' }); + rej({ + message: + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }), + }); }); }); }, []); @@ -350,7 +383,9 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { await sleep(pollingInterval); // Wait before the next poll } - throw new Error('Timeout waiting for reward share confirmation'); + throw new Error( + t('group:message.error.timeout_reward', { postProcess: 'capitalize' }) + ); }; const startMinting = async () => { @@ -382,7 +417,11 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { setShowWaitDialog(false); setInfo({ type: 'error', - message: error?.message || 'Unable to start minting', + message: + error?.message || + t('group:message.error.unable_minting', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); } finally { @@ -420,8 +459,12 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { ...rewardShare, ...response, type: 'remove-rewardShare', - label: `Remove rewardshare: awaiting confirmation`, - labelDone: `Remove rewardshare: success!`, + label: t('group:message.success.rewardshare_remove', { + postProcess: 'capitalize', + }), + labelDone: t('group:message.success.rewardshare_remove_label', { + postProcess: 'capitalize', + }), done: false, }, ...prev, @@ -431,59 +474,65 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || 'An error occurred' }); + rej({ + message: + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }), + }); }); }); }, []); - const handleRemoveRewardShare = async (rewardShare) => { - try { - setIsLoading(true); + // TODO unused functions. Remove?? - const privateRewardShare = await removeRewardShare(rewardShare); - } catch (error) { - setInfo({ - type: 'error', - message: error?.message || 'Unable to remove reward share', - }); - setOpenSnack(true); - } finally { - setIsLoading(false); - } - }; + // const handleRemoveRewardShare = async (rewardShare) => { + // try { + // setIsLoading(true); - const createRewardShareForPotentialMinter = async (receiver) => { - try { - setIsLoading(true); - const confirmReceiver = await getNameOrAddress(receiver); - if (confirmReceiver.error) - throw new Error('Invalid receiver address or name'); - const isInMinterGroup = await checkIfMinterGroup(confirmReceiver); - if (!isInMinterGroup) throw new Error('Account not in Minter Group'); - const publicKey = await getPublicKeyFromAddress(confirmReceiver); - const findRewardShare = rewardShares?.find( - (item) => - item?.recipient === confirmReceiver && - item?.mintingAccount === myAddress - ); - if (findRewardShare) { - const privateRewardShare = await getRewardSharePrivateKey(publicKey); - setRewardsharekey(privateRewardShare); - } else { - await createRewardShare(publicKey, confirmReceiver); - const privateRewardShare = await getRewardSharePrivateKey(publicKey); - setRewardsharekey(privateRewardShare); - } - } catch (error) { - setInfo({ - type: 'error', - message: error?.message || 'Unable to create reward share', - }); - setOpenSnack(true); - } finally { - setIsLoading(false); - } - }; + // const privateRewardShare = await removeRewardShare(rewardShare); + // } catch (error) { + // setInfo({ + // type: 'error', + // message: error?.message || 'Unable to remove reward share', + // }); + // setOpenSnack(true); + // } finally { + // setIsLoading(false); + // } + // }; + + // const createRewardShareForPotentialMinter = async (receiver) => { + // try { + // setIsLoading(true); + // const confirmReceiver = await getNameOrAddress(receiver); + // if (confirmReceiver.error) + // throw new Error('Invalid receiver address or name'); + // const isInMinterGroup = await checkIfMinterGroup(confirmReceiver); + // if (!isInMinterGroup) throw new Error('Account not in Minter Group'); + // const publicKey = await getPublicKeyFromAddress(confirmReceiver); + // const findRewardShare = rewardShares?.find( + // (item) => + // item?.recipient === confirmReceiver && + // item?.mintingAccount === myAddress + // ); + // if (findRewardShare) { + // const privateRewardShare = await getRewardSharePrivateKey(publicKey); + // setRewardsharekey(privateRewardShare); + // } else { + // await createRewardShare(publicKey, confirmReceiver); + // const privateRewardShare = await getRewardSharePrivateKey(publicKey); + // setRewardsharekey(privateRewardShare); + // } + // } catch (error) { + // setInfo({ + // type: 'error', + // message: error?.message || 'Unable to create reward share', + // }); + // setOpenSnack(true); + // } finally { + // setIsLoading(false); + // } + // }; useEffect(() => { getNodeInfos(); @@ -558,7 +607,12 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { }, }} > - {'Manage your minting'} + + {t('group:message.generic.manage_minting', { + postProcess: 'capitalize', + })} + + { sx={{ backgroundColor: theme.palette.background.default, padding: '10px', - }} + }} // TODO translate > Account: {handleNames(accountInfo?.address)} @@ -618,7 +672,9 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { This node is minting: {nodeInfos?.isMintingPossible?.toString()} + + {isPartOfMintingGroup && !accountIsMinting && ( { )} )} + + {mintingAccounts?.length > 0 && ( Node's minting accounts )} diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 41ff3c0..df7eb80 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -67,6 +67,8 @@ "error": { "generic": "an error occurred", "incorrect_password": "incorrect password", + "minting_account_add": "unable to add minting account", + "minting_account_remove": "unable to remove minting account", "missing_field": "missing: {{ field }}", "save_qdn": "unable to save to QDN" }, diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index 5e0db18..6e0c8c7 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -62,6 +62,7 @@ "latest_promotion": "only the latest promotion from the week will be shown for your group.", "loading_members": "loading member list with names... please wait.", "max_chars": " Max 200 characters. Publish Fee", + "manage_minting": "manage your minting", "no_display": "nothing to display", "no_selection": "no group selected", "not_part_group": "you are not part of the encrypted group of members. Wait until an admin re-encrypts the keys.", @@ -80,7 +81,9 @@ "group_secret_key": "cannot get group secret key", "name_required": "please provide a name", "notify_admins": "try notifying an admin from the list of admins below:", - "thread_id": "unable to locate thread Id" + "timeout_reward": "timeout waiting for reward share confirmation", + "thread_id": "unable to locate thread Id", + "unable_minting": "unable to start minting" }, "success": { "group_ban": "successfully banned member from group. It may take a couple of minutes for the changes to propagate", @@ -104,6 +107,10 @@ "invitation_request": "accepted join request: awaiting confirmation", "loading_threads": "loading threads... please wait.", "post_creation": "successfully created post. It may take some time for the publish to propagate", + "rewardshare_add": "add rewardshare: awaiting confirmation", + "rewardshare_add_label": "add rewardshare: success!", + "rewardshare_remove": "remove rewardshare: awaiting confirmation", + "rewardshare_remove_label": "remove rewardshare: success!", "thread_creation": "successfully created thread. It may take some time for the publish to propagate", "unbanned_user": "successfully unbanned user. It may take a couple of minutes for the changes to propagate", "user_joined": "user successfully joined!" From c5ff69b2f7d3137107b721efecae35337993312e Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 14 May 2025 21:47:39 +0200 Subject: [PATCH 13/47] Add minting translations --- src/App.tsx | 1 + src/components/Group/AddGroup.tsx | 2 +- src/components/Minting/Minting.tsx | 46 ++++++++++++++++++++++-------- src/i18n/locales/en/core.json | 4 ++- src/i18n/locales/en/group.json | 3 ++ 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 07b915c..17dea23 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1778,6 +1778,7 @@ function App() { + {extState === 'authenticated' && ( )} diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index f9cbbf8..8b91ebe 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -536,7 +536,7 @@ export const AddGroup = ({ address, open, setOpen }) => { {t('core:time.hour', { count: 1 })} - 3{t('core:time.hour', { count: 3 })} + {t('core:time.hour', { count: 3 })} {t('core:time.hour', { count: 5 })} diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index c84ffdb..2ee2508 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -21,7 +21,7 @@ import { subscribeToEvent, unsubscribeFromEvent, } from '../../utils/events'; -import { getFee, getNameOrAddress } from '../../background'; +import { getFee } from '../../background'; import { Spacer } from '../../common/Spacer'; import { FidgetSpinner } from 'react-loader-spinner'; import { useModal } from '../../common/useModal'; @@ -658,18 +658,34 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { sx={{ backgroundColor: theme.palette.background.default, padding: '10px', - }} // TODO translate + }} > - Account: {handleNames(accountInfo?.address)} - - Level: {accountInfo?.level} - - blocks remaining until next level: {_levelUpBlocks()} + {t('auth:account.account_one', { + postProcess: 'capitalize', + })} + : {handleNames(accountInfo?.address)} - This node is minting: {nodeInfos?.isMintingPossible?.toString()} + {t('core:level', { + postProcess: 'capitalize', + })} + : {accountInfo?.level} + + + + {t('group:message.generic.next_level', { + postProcess: 'capitalize', + })}{' '} + {_levelUpBlocks()} + + + + {t('group:message.generic.node_minting', { + postProcess: 'capitalize', + })}{' '} + {nodeInfos?.isMintingPossible?.toString()} @@ -706,12 +722,16 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { }} variant="contained" > - Start minting + {t('core:action.start_minting', { + postProcess: 'capitalize', + })} + {mintingAccounts?.length > 1 && ( - Only 2 minting keys are allowed per node. Please remove one if - you would like to mint with this account. + {t('group:message.generic.minting_keys_per_node', { + postProcess: 'capitalize', + })} )} @@ -720,7 +740,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { {mintingAccounts?.length > 0 && ( - Node's minting accounts + Node's minting accounts // TODO translate )} { )} + + {mintingAccounts?.map((acct) => ( Date: Thu, 15 May 2025 08:23:40 +0200 Subject: [PATCH 14/47] Translations for minting file --- src/components/Minting/Minting.tsx | 57 ++++++++++++++++++++++-------- src/i18n/locales/en/group.json | 14 ++++++-- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 2ee2508..d7a8d88 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -740,7 +740,11 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { {mintingAccounts?.length > 0 && ( - Node's minting accounts // TODO translate + + {t('group:message.generic.node_minting_account', { + postProcess: 'capitalize', + })} + )} { }} > - You currently have a minting key for this account attached to - this node + {t('group:message.generic.node_minting_key', { + postProcess: 'capitalize', + })} )} @@ -775,8 +780,12 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { }} > - Minting account: {handleNames(acct?.mintingAccount)} + {t('group:message.generic.minting_account', { + postProcess: 'capitalize', + })}{' '} + {handleNames(acct?.mintingAccount)} + @@ -808,13 +819,15 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { {mintingAccounts?.length > 1 && ( - Only 2 minting keys are allowed per node. Please remove one if you - would like to add a different account. + {t('group:message.generic.minting_keys_per_node_different', { + postProcess: 'capitalize', + })} )} + {!isPartOfMintingGroup && ( { }} > - You are currently not part of the MINTER group + {t('group:message.generic.minter_group', { + postProcess: 'capitalize', + })} + - Visit the Q-Mintership app to apply to be a minter + {t('group:message.generic.mintership_app', { + postProcess: 'capitalize', + })} + + @@ -880,13 +902,16 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { {!isShowNext && ( - Confirming creation of rewardshare on chain. Please be - patient, this could take up to 90 seconds. + {t('group:message.success.rewardshare_creation', { + postProcess: 'capitalize', + })} )} {isShowNext && ( - Rewardshare confirmed. Please click Next. + {t('group:message.success.rewardshare_confirmed', { + postProcess: 'capitalize', + })} )} @@ -898,21 +923,23 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { onClick={onOk} autoFocus > - Next + {t('core:page.next', { postProcess: 'capitalize' })} )} + + Date: Thu, 15 May 2025 08:55:57 +0200 Subject: [PATCH 15/47] Translate register_name page --- src/components/RegisterName.tsx | 100 ++++++++++++++++++++++++++------ src/i18n/locales/en/core.json | 13 +++++ src/i18n/locales/en/group.json | 6 +- 3 files changed, 99 insertions(+), 20 deletions(-) diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index adb8198..f11e9ee 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -25,6 +25,7 @@ import CheckIcon from '@mui/icons-material/Check'; import ErrorIcon from '@mui/icons-material/Error'; import { useSetAtom } from 'jotai'; import { txListAtom } from '../atoms/global'; +import { useTranslation } from 'react-i18next'; enum Availability { NULL = 'null', @@ -50,6 +51,7 @@ export const RegisterName = ({ ); const [nameFee, setNameFee] = useState(null); const theme = useTheme(); + const { t } = useTranslation(['core', 'auth', 'group']); const checkIfNameExisits = async (name) => { if (!name?.trim()) { setIsNameAvailable(Availability.NULL); @@ -110,12 +112,24 @@ export const RegisterName = ({ const registerName = async () => { try { - if (!userInfo?.address) throw new Error('Your address was not found'); - if (!registerNameValue) throw new Error('Enter a name'); + if (!userInfo?.address) + throw new Error( + t('core:message.error.address_not_found', { + postProcess: 'capitalize', + }) + ); + if (!registerNameValue) + throw new Error( + t('core:action.enter_name', { + postProcess: 'capitalize', + }) + ); const fee = await getFee('REGISTER_NAME'); await show({ - message: 'Would you like to register this name?', + message: t('group:question.register_name', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); setIsLoadingRegisterName(true); @@ -130,8 +144,9 @@ export const RegisterName = ({ setIsLoadingRegisterName(false); setInfoSnack({ type: 'success', - message: - 'Successfully registered. It may take a couple of minutes for the changes to propagate', + message: t('group:message.success.registered_name', { + postProcess: 'capitalize', + }), }); setIsOpen(false); setRegisterNameValue(''); @@ -140,8 +155,15 @@ export const RegisterName = ({ { ...response, type: 'register-name', - label: `Registered name: awaiting confirmation. This may take a couple minutes.`, - labelDone: `Registered name: success!`, + label: t('group:message.success.registered_name_label', { + postProcess: 'capitalize', + }), + labelDone: t( + 'group:message.success.registered_name_success', + { + postProcess: 'capitalize', + } + ), done: false, }, ...prev.filter((item) => !item.done), @@ -158,7 +180,9 @@ export const RegisterName = ({ .catch((error) => { setInfoSnack({ type: 'error', - message: error.message || 'An error occurred', + message: + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }), }); setOpenSnack(true); rej(error); @@ -199,7 +223,9 @@ export const RegisterName = ({ width: '400px', }} > - // TODO: translate + + - Your balance is {balance ?? 0} QORT. A name registration - requires a {nameFee} QORT fee + {t('core:message.generic.name_registration', { + balance: balance ?? 0, + fee: { nameFee }, + postProcess: 'capitalize', + })} )} + + {isNameAvailable === Availability.AVAILABLE && ( - {registerNameValue} is available + + {t('core:message.generic.name_available', { + name: registerNameValue, + postProcess: 'capitalize', + })} + )} + {isNameAvailable === Availability.NOT_AVAILABLE && ( - {registerNameValue} is unavailable + + {t('core:message.generic.name_unavailable', { + name: registerNameValue, + postProcess: 'capitalize', + })} + )} + {isNameAvailable === Availability.LOADING && ( - Checking if name already existis + + + {t('core:message.generic.name_checking', { + postProcess: 'capitalize', + })} + )} + + - Benefits of a name + {t('core:message.generic.name_benefits', { + postProcess: 'capitalize', + })} + - + @@ -307,7 +365,11 @@ export const RegisterName = ({ }} /> - + @@ -322,7 +384,7 @@ export const RegisterName = ({ setRegisterNameValue(''); }} > - Close + {t('core:action.close', { postProcess: 'capitalize' })} diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 2b0d173..b33a4bf 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -13,9 +13,11 @@ "continue": "continue", "continue_logout": "continue to logout", "create_thread": "create thread", + "choose_name": "choose a name", "decline": "decline", "decrypt": "decrypt", "edit": "edit", + "enter_name": "enter a name", "export": "export", "import": "import", "invite": "invite", @@ -30,6 +32,7 @@ "post": "post", "post_message": "post message", "publish": "publish", + "register_name": "register name", "remove": "remove", "save": "save", "start_minting": "start minting" @@ -67,6 +70,7 @@ "message_us": "please message us on Telegram or Discord if you need 4 QORT to start chatting without any limitations", "message": { "error": { + "address_not_found": "your address was not found", "generic": "an error occurred", "incorrect_password": "incorrect password", "minting_account_add": "unable to add minting account", @@ -74,6 +78,15 @@ "missing_field": "missing: {{ field }}", "save_qdn": "unable to save to QDN" }, + "generic": { + "name_available": "{{ name }} is available", + "name_benefits": "benefits of a name", + "name_checking": "checking if name already exists", + "name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee", + "name_unavailable": "{{ name }} is unavailable", + "publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!", + "secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party." + }, "question": { "new_user": "are you a new user?" }, diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index f8a7d41..7a8cc23 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -118,6 +118,9 @@ "invitation_request": "accepted join request: awaiting confirmation", "loading_threads": "loading threads... please wait.", "post_creation": "successfully created post. It may take some time for the publish to propagate", + "registered_name": "successfully registered. It may take a couple of minutes for the changes to propagate", + "registered_name_label": "registered name: awaiting confirmation. This may take a couple minutes.", + "registered_name_success": "registered name: success!", "rewardshare_add": "add rewardshare: awaiting confirmation", "rewardshare_add_label": "add rewardshare: success!", "rewardshare_creation": "confirming creation of rewardshare on chain. Please be patient, this could take up to 90 seconds.", @@ -131,7 +134,8 @@ }, "question": { "perform_transaction": "would you like to perform a {{action}} transaction?", - "provide_thread": "please provide a thread title" + "provide_thread": "please provide a thread title", + "register_name": "would you like to register this name?" }, "thread_posts": "new thread posts" } From 23c552a2b3189e7039994e6d57be22c91a5f1c7c Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 15 May 2025 09:01:01 +0200 Subject: [PATCH 16/47] Add translation --- src/components/Apps/AppInfo.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index abca186..adf9c15 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -27,8 +27,8 @@ import { sortablePinnedAppsAtom, } from '../../atoms/global'; import { saveToLocalStorage } from './AppsNavBarDesktop'; - import { useAtom, useSetAtom } from 'jotai'; +import { useTranslation } from 'react-i18next'; export const AppInfo = ({ app, myName }) => { const isInstalled = app?.status?.status === 'READY'; @@ -37,6 +37,7 @@ export const AppInfo = ({ app, myName }) => { ); const theme = useTheme(); + const { t } = useTranslation(['core', 'auth', 'group']); const isSelectedAppPinned = !!sortablePinnedApps?.find( (item) => item?.name === app?.name && item?.service === app?.service From f6dccdfb2d6413f2abfcc91263ca5d00ad4c63c6 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 15 May 2025 14:05:22 +0200 Subject: [PATCH 17/47] Add translations to apps --- src/components/Apps/AppInfo.tsx | 41 +++++++++++++++++++++----- src/components/Apps/AppInfoSnippet.tsx | 21 ++++++++++--- src/components/Apps/AppPublish.tsx | 26 +++++----------- src/components/QMailStatus.tsx | 2 +- src/i18n/locales/en/core.json | 17 +++++++++-- 5 files changed, 74 insertions(+), 33 deletions(-) diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index adf9c15..b36799a 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -172,9 +172,13 @@ export const AppInfo = ({ app, myName }) => { }} > - {isSelectedAppPinned // TODO translate - ? 'Unpin from dashboard' - : 'Pin to dashboard'} + {isSelectedAppPinned + ? t('core:action.unpin_from_dashboard', { + postProcess: 'capitalize', + }) + : t('core:action.pin_from_dashboard', { + postProcess: 'capitalize', + })} @@ -194,7 +198,13 @@ export const AppInfo = ({ app, myName }) => { }} > - {isInstalled ? 'Open' : 'Download'} + {isInstalled + ? t('core:action.open', { + postProcess: 'capitalize', + }) + : t('core:action.download', { + postProcess: 'capitalize', + })} @@ -217,25 +227,40 @@ export const AppInfo = ({ app, myName }) => { - Category: + + {t('core:category', { + postProcess: 'capitalize', + })} + : + - {app?.metadata?.categoryName || 'none'} + {app?.metadata?.categoryName || + t('core:none', { + postProcess: 'capitalize', + })} - About this Q-App + + {t('core:q_apps.about', { + postProcess: 'capitalize', + })} + - {app?.metadata?.description || 'No description'} + {app?.metadata?.description || + t('core:message.generic.no_description', { + postProcess: 'capitalize', + })} diff --git a/src/components/Apps/AppInfoSnippet.tsx b/src/components/Apps/AppInfoSnippet.tsx index ba71164..c027bb0 100644 --- a/src/components/Apps/AppInfoSnippet.tsx +++ b/src/components/Apps/AppInfoSnippet.tsx @@ -23,6 +23,7 @@ import { } from '../../atoms/global'; import { saveToLocalStorage } from './AppsNavBarDesktop'; import { useAtom, useSetAtom } from 'jotai'; +import { useTranslation } from 'react-i18next'; export const AppInfoSnippet = ({ app, @@ -41,6 +42,7 @@ export const AppInfoSnippet = ({ ); const theme = useTheme(); + const { t } = useTranslation(['core', 'auth', 'group']); return ( - {' '} - {isSelectedAppPinned ? 'Unpin' : 'Pin'} + {isSelectedAppPinned + ? t('core:action.unpin', { + postProcess: 'capitalize', + }) + : t('core:action.pin', { + postProcess: 'capitalize', + })} @@ -187,7 +194,13 @@ export const AppInfoSnippet = ({ }} > - {isInstalled ? 'Open' : 'Download'} + {isInstalled + ? t('core:action.open', { + postProcess: 'capitalize', + }) + : t('core:action.download', { + postProcess: 'capitalize', + })} diff --git a/src/components/Apps/AppPublish.tsx b/src/components/Apps/AppPublish.tsx index 8875c0b..5a181cd 100644 --- a/src/components/Apps/AppPublish.tsx +++ b/src/components/Apps/AppPublish.tsx @@ -1,20 +1,8 @@ import React, { useContext, useEffect, useState } from 'react'; import { - AppCircle, - AppCircleContainer, - AppCircleLabel, - AppDownloadButton, - AppDownloadButtonText, - AppInfoAppName, - AppInfoSnippetContainer, - AppInfoSnippetLeft, - AppInfoSnippetMiddle, - AppInfoSnippetRight, - AppInfoUserName, AppLibrarySubTitle, AppPublishTagsContainer, AppsLibraryContainer, - AppsParent, AppsWidthLimiter, PublishQAppCTAButton, PublishQAppChoseFile, @@ -28,10 +16,7 @@ import { useTheme, } from '@mui/material'; import { styled } from '@mui/system'; -import UnfoldMoreRoundedIcon from '@mui/icons-material/UnfoldMoreRounded'; -import { Add } from '@mui/icons-material'; import { MyContext, getBaseApiReact } from '../../App'; -import LogoSelected from '../../assets/svgs/LogoSelected.svg'; import { Spacer } from '../../common/Spacer'; import { executeEvent } from '../../utils/events'; import { useDropzone } from 'react-dropzone'; @@ -39,6 +24,7 @@ import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { getFee } from '../../background'; import { fileToBase64 } from '../../utils/fileReading'; +import { useTranslation } from 'react-i18next'; const CustomSelect = styled(Select)({ border: '0.5px solid var(--50-white, #FFFFFF80)', @@ -81,6 +67,7 @@ export const AppPublish = ({ names, categories }) => { const [file, setFile] = useState(null); const { show } = useContext(MyContext); const theme = useTheme(); + const { t } = useTranslation(['core', 'auth', 'group']); const [tag1, setTag1] = useState(''); const [tag2, setTag2] = useState(''); const [tag3, setTag3] = useState(''); @@ -106,9 +93,11 @@ export const AppPublish = ({ names, categories }) => { errors.forEach((error) => { if (error.code === 'file-too-large') { console.error( - `File ${file.name} is too large. Max size allowed is ${ - maxFileSize / (1024 * 1024) - } MB.` + t('core:message.error.file_too_large', { + filename: file.name, + size: maxFileSize / (1024 * 1024), + postProcess: 'capitalize', + }) ); } }); @@ -142,6 +131,7 @@ export const AppPublish = ({ names, categories }) => { setTag5(myApp?.metadata?.tags[4] || ''); } } catch (error) { + console.log(error); } finally { setIsLoading(''); } diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index deac6db..f88adbb 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -65,7 +65,7 @@ export const QMailStatus = () => { textTransform: 'uppercase', }} > - {t('core:q_mail', { + {t('core:q_apps.q_mail', { postProcess: 'capitalize', })} diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index b33a4bf..757350d 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -16,6 +16,7 @@ "choose_name": "choose a name", "decline": "decline", "decrypt": "decrypt", + "download": "download", "edit": "edit", "enter_name": "enter a name", "export": "export", @@ -29,15 +30,21 @@ "thread": "new thread" }, "notify": "notify", + "open": "open", + "pin": "pin", + "pin_from_dashboard": "pin from dashboard", "post": "post", "post_message": "post message", "publish": "publish", "register_name": "register name", "remove": "remove", "save": "save", - "start_minting": "start minting" + "start_minting": "start minting", + "unpin": "unpin", + "unpin_from_dashboard": "unpin from dashboard" }, "admin": "admin", + "category": "category", "core": { "block_height": "block height", "information": "core information", @@ -71,6 +78,7 @@ "message": { "error": { "address_not_found": "your address was not found", + "file_too_large": "file {{ filename }} is too large. Max size allowed is {{ size }} MB.", "generic": "an error occurred", "incorrect_password": "incorrect password", "minting_account_add": "unable to add minting account", @@ -84,6 +92,7 @@ "name_checking": "checking if name already exists", "name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee", "name_unavailable": "{{ name }} is unavailable", + "no_description": "no description", "publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!", "secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party." }, @@ -104,6 +113,7 @@ } }, "minting_status": "minting status", + "none": "none", "page": { "last": "last", "first": "first", @@ -112,7 +122,10 @@ }, "payment_notification": "payment notification", "price": "price", - "q_mail": "q-mail", + "q_apps": { + "about": "about this Q-App", + "q_mail": "q-mail" + }, "save_options": { "no_pinned_changes": "you currently do not have any changes to your pinned apps", "overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?", From 3e6d2f5e4b0faaab8a8eb39df560d1d20202ffe7 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 15 May 2025 19:02:12 +0200 Subject: [PATCH 18/47] Update translations --- src/components/Apps/AppPublish.tsx | 97 ++++++++++++++++++------ src/components/Group/Forum/NewThread.tsx | 2 +- src/components/MainAvatar.tsx | 1 + src/components/Save/Save.tsx | 6 +- src/i18n/locales/en/core.json | 22 +++++- 5 files changed, 98 insertions(+), 30 deletions(-) diff --git a/src/components/Apps/AppPublish.tsx b/src/components/Apps/AppPublish.tsx index 5a181cd..1425ba6 100644 --- a/src/components/Apps/AppPublish.tsx +++ b/src/components/Apps/AppPublish.tsx @@ -169,17 +169,25 @@ export const AppPublish = ({ names, categories }) => { }); if (missingFields.length > 0) { const missingFieldsString = missingFields.join(', '); - const errorMsg = `Missing fields: ${missingFieldsString}`; + const errorMsg = t('core:message.error.missing_fields', { + fields: missingFieldsString, + postProcess: 'capitalize', + }); throw new Error(errorMsg); } const fee = await getFee('ARBITRARY'); await show({ - // TODO translate - message: 'Would you like to publish this app?', + message: t('core:save_options.publish_app', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); - setIsLoading('Publishing... Please wait.'); + setIsLoading( + t('core:message.generic.publishing', { + postProcess: 'capitalize', + }) + ); const fileBase64 = await fileToBase64(file); await new Promise((res, rej) => { window @@ -204,13 +212,17 @@ export const AppPublish = ({ names, categories }) => { rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }) + ); }); }); setInfoSnack({ type: 'success', - message: - 'Successfully published. Please wait a couple minutes for the network to propogate the changes.', + message: t('core:message.success.published', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); const dataObj = { @@ -229,7 +241,11 @@ export const AppPublish = ({ names, categories }) => { } catch (error) { setInfoSnack({ type: 'error', - message: error?.message || 'Unable to publish app', + message: + error?.message || + t('core:message.error.publish_app', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); } finally { @@ -250,18 +266,27 @@ export const AppPublish = ({ names, categories }) => { width: 'auto', }} > - Create Apps! + + {t('core:action.create_apps', { + postProcess: 'capitalize', + })} + ! + - Note: Currently, only one App and Website is allowed per Name. + {t('core:message.generic.one_app_per_name', { + postProcess: 'capitalize', + })} - Name/App + {t('core:name_app', { + postProcess: 'capitalize', + })} { color: theme.palette.text.secondary, }} > - Select Name/App + {t('core:action.select_name_app', { + postProcess: 'capitalize', + })} {/* This is the placeholder item */} @@ -288,7 +315,9 @@ export const AppPublish = ({ names, categories }) => { - App service type + {t('core:app_service_type', { + postProcess: 'capitalize', + })} { color: theme.palette.text.secondary, }} > - Select App Type + {t('core:action.select_app_type', { + postProcess: 'capitalize', + })} - App - Website + + {t('core:app', { + postProcess: 'capitalize', + })} + + + {t('core:website', { + postProcess: 'capitalize', + })} + - Title + {t('core:title', { + postProcess: 'capitalize', + })} { - Description + {t('core:description', { + postProcess: 'capitalize', + })} { - Category + {t('core:category', { + postProcess: 'capitalize', + })} { color: theme.palette.text.secondary, }} > - Select Category + {t('core:action.select_category', { + postProcess: 'capitalize', + })} {categories?.map((category) => { @@ -393,7 +440,9 @@ export const AppPublish = ({ names, categories }) => { - Tags + {t('core:tags', { + postProcess: 'capitalize', + })} @@ -487,7 +536,7 @@ export const AppPublish = ({ names, categories }) => { - Select .zip file containing static content:{' '} + {t('core:message.generic.select_zip', { postProcess: 'capitalize' })} @@ -507,7 +556,7 @@ export const AppPublish = ({ names, categories }) => { {' '} - Choose File + {t('core:action.choose_file', { postProcess: 'capitalize' })} @@ -518,7 +567,7 @@ export const AppPublish = ({ names, categories }) => { }} onClick={publishApp} > - Publish + {t('core:action.publish', { postProcess: 'capitalize' })} diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 1a1e5df..0212702 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -202,7 +202,7 @@ export const NewThread = ({ // if (!description) missingFields.push('subject') if (missingFields.length > 0) { const missingFieldsString = missingFields.join(', '); - const errMsg = t('group:message.error.missing_field', { + const errMsg = t('core:message.error.missing_fields', { field: missingFieldsString, postProcess: 'capitalize', }); diff --git a/src/components/MainAvatar.tsx b/src/components/MainAvatar.tsx index 10370de..60a32b8 100644 --- a/src/components/MainAvatar.tsx +++ b/src/components/MainAvatar.tsx @@ -65,6 +65,7 @@ export const MainAvatar = ({ myName, balance, setOpenSnack, setInfoSnack }) => { const publishAvatar = async () => { try { + // TODO translate const fee = await getFee('ARBITRARY'); if (+balance < +fee.fee) throw new Error(`Publishing an Avatar requires ${fee.fee}`); diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index d67780a..23032ee 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -155,7 +155,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { const fee = await getFee('ARBITRARY'); await show({ - message: t('core:save.publish_qnd', { postProcess: 'capitalize' }), + message: t('core:save_options.publish_qnd', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); const response = await new Promise((res, rej) => { @@ -184,7 +186,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { setSettingsQdnLastUpdated(Date.now()); setInfoSnack({ type: 'success', - message: t('core:message.success.publish_qdn', { + message: t('core:message.success.published_qdn', { postProcess: 'capitalize', }), }); diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 757350d..22c1c25 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -12,6 +12,8 @@ "close": "close", "continue": "continue", "continue_logout": "continue to logout", + "create_apps": "create apps", + "create_file": "create file", "create_thread": "create thread", "choose_name": "choose a name", "decline": "decline", @@ -39,11 +41,16 @@ "register_name": "register name", "remove": "remove", "save": "save", + "select_app_type": "select App Type", + "select_category": "select Category", + "select_name_app": "select Name/App", "start_minting": "start minting", "unpin": "unpin", "unpin_from_dashboard": "unpin from dashboard" }, "admin": "admin", + "app": "app", + "app_service_type": "app service type", "category": "category", "core": { "block_height": "block height", @@ -83,7 +90,8 @@ "incorrect_password": "incorrect password", "minting_account_add": "unable to add minting account", "minting_account_remove": "unable to remove minting account", - "missing_field": "missing: {{ field }}", + "missing_fields": "missing: {{ fields }}", + "publish_app": "unable to publish app", "save_qdn": "unable to save to QDN" }, "generic": { @@ -93,8 +101,11 @@ "name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee", "name_unavailable": "{{ name }} is unavailable", "no_description": "no description", + "one_app_per_name": "note: Currently, only one App and Website is allowed per Name.", "publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!", - "secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party." + "publishing": "publishing... Please wait.", + "secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party.", + "select_zip": "select .zip file containing static content:" }, "question": { "new_user": "are you a new user?" @@ -107,12 +118,14 @@ }, "success": { "order_submitted": "your buy order was submitted", - "publish_qdn": "successfully published to QDN", + "published": "successfully published. Please wait a couple minutes for the network to propogate the changes.", + "published_qdn": "successfully published to QDN", "request_read": "I have read this request", "transfer": "the transfer was succesful!" } }, "minting_status": "minting status", + "name_app": "name/App", "none": "none", "page": { "last": "last", @@ -130,6 +143,7 @@ "no_pinned_changes": "you currently do not have any changes to your pinned apps", "overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?", "overwrite_qdn": "overwrite to QDN", + "publish_app": "would you like to publish this app?", "publish_qdn": "would you like to publish your settings to QDN (encrypted)?", "qdn": "use QDN saving", "register_name": "you need a registered Qortal name to save your pinned apps to QDN.", @@ -144,6 +158,7 @@ }, "settings": "settings", "supply": "supply", + "tags": "tags", "theme": { "dark": "dark mode", "light": "light mode" @@ -163,5 +178,6 @@ "wallet": "wallet", "wallet_other": "wallets" }, + "website": "website", "welcome": "welcome" } From e94ff9ef09a9b1fd40bd1d72c1921b25f909c581 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 15 May 2025 19:26:43 +0200 Subject: [PATCH 19/47] Add translations to appRating --- src/components/Apps/AppRating.tsx | 25 +++++++++++++++---- src/components/Apps/useHandlePrivateApps.tsx | 5 ++-- .../Apps/useQortalMessageListener.tsx | 4 +-- src/components/GeneralNotifications.tsx | 2 +- src/components/Save/Save.tsx | 4 +-- src/i18n/locales/en/core.json | 5 +++- 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index 881cfec..8894f3c 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -7,6 +7,7 @@ import { StarFilledIcon } from '../../assets/Icons/StarFilled'; import { StarEmptyIcon } from '../../assets/Icons/StarEmpty'; import { AppInfoUserName } from './Apps-styles'; import { Spacer } from '../../common/Spacer'; +import { useTranslation } from 'react-i18next'; export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { const [value, setValue] = useState(0); @@ -19,6 +20,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { const [openSnack, setOpenSnack] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const hasCalledRef = useRef(false); + const { t } = useTranslation(['core', 'group']); const getRating = useCallback(async (name, service) => { try { @@ -101,26 +103,39 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { const rateFunc = async (event, chosenValue, currentValue) => { try { const newValue = chosenValue || currentValue; - if (!myName) throw new Error('You need a name to rate.'); + if (!myName) + throw new Error( + t('core:message.generic.name_rate', { + postProcess: 'capitalize', + }) + ); if (!app?.name) return; const fee = await getFee('CREATE_POLL'); await show({ - // TODO translate - message: `Would you like to rate this app a rating of ${newValue}?. It will create a POLL tx.`, + message: t('core:message.error.generic', { + rate: newValue, + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); if (hasPublishedRating === false) { const pollName = `app-library-${app.service}-rating-${app.name}`; const pollOptions = [`1, 2, 3, 4, 5, initialValue-${newValue}`]; + const pollDescription = t('core:message.error.generic', { + name: app.name, + service: app.service, + postProcess: 'capitalize', + }); + await new Promise((res, rej) => { window .sendMessage( 'createPoll', { pollName: pollName, - pollDescription: `Rating for ${app.service} ${app.name}`, + pollDescription: pollDescription, pollOptions: pollOptions, pollOwnerAddress: myName, }, @@ -137,7 +152,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { message: 'Successfully rated. Please wait a couple minutes for the network to propogate the changes.', }); - setOpenSnack(true); + setOpenSnack(true); // TODO translate } }) .catch((error) => { diff --git a/src/components/Apps/useHandlePrivateApps.tsx b/src/components/Apps/useHandlePrivateApps.tsx index 9a324bb..f8dd2a1 100644 --- a/src/components/Apps/useHandlePrivateApps.tsx +++ b/src/components/Apps/useHandlePrivateApps.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from 'react'; +import { useContext, useState } from 'react'; import { executeEvent } from '../../utils/events'; import { getBaseApiReact, MyContext } from '../../App'; import { createEndpoint } from '../../background'; @@ -7,10 +7,9 @@ import { sortablePinnedAppsAtom, } from '../../atoms/global'; import { saveToLocalStorage } from './AppsNavBarDesktop'; -import { base64ToBlobUrl } from '../../utils/fileReading'; import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; -import { useAtom, useSetAtom } from 'jotai'; +import { useSetAtom } from 'jotai'; export const useHandlePrivateApps = () => { const [status, setStatus] = useState(''); diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index b1736a2..e2bb3c6 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -1,7 +1,7 @@ -import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import { useCallback, useContext, useEffect, useState } from 'react'; import { executeEvent } from '../../utils/events'; import { navigationControllerAtom } from '../../atoms/global'; -import { Filesystem, Directory, Encoding } from '@capacitor/filesystem'; +import { Filesystem, Directory } from '@capacitor/filesystem'; import { saveFile } from '../../qortalRequests/get'; import { mimeToExtensionMap } from '../../utils/memeTypes'; import { MyContext } from '../../App'; diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx index c7a6072..b88000e 100644 --- a/src/components/GeneralNotifications.tsx +++ b/src/components/GeneralNotifications.tsx @@ -111,7 +111,7 @@ export const GeneralNotifications = ({ address }) => { userSelect: 'none', }} > - No new notifications + {t('core:message.generic.no_notifications')} )} {hasNewPayment && ( diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index 23032ee..36edbdd 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -233,7 +233,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { {isDesktop ? ( { onClick={saveToQdn} variant="contained" > - {t('core:save_options.save_qdn', { + {t('core:action.save_qdn', { postProcess: 'capitalize', })} diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 22c1c25..23c5fce 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -98,12 +98,15 @@ "name_available": "{{ name }} is available", "name_benefits": "benefits of a name", "name_checking": "checking if name already exists", + "name_rate": "you need a name to rate.", "name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee", "name_unavailable": "{{ name }} is unavailable", "no_description": "no description", + "no_notifications": "no new notifications", "one_app_per_name": "note: Currently, only one App and Website is allowed per Name.", "publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!", "publishing": "publishing... Please wait.", + "rating": "rating for {{ service }} {{ name }}", "secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party.", "select_zip": "select .zip file containing static content:" }, @@ -146,13 +149,13 @@ "publish_app": "would you like to publish this app?", "publish_qdn": "would you like to publish your settings to QDN (encrypted)?", "qdn": "use QDN saving", + "rate_app": "would you like to rate this app a rating of {{ rate }}?. It will create a POLL tx.", "register_name": "you need a registered Qortal name to save your pinned apps to QDN.", "reset_pinned": "don't like your current local changes? Would you like to reset to the default pinned apps?", "reset_qdn": "don't like your current local changes? Would you like to reset to your saved QDN pinned apps?", "revert_default": "revert to default", "revert_qdn": "revert to QDN", "save_qdn": "save to QDN", - "save": "save", "settings": "you are using the export/import way of saving settings.", "unsaved_changes": " you have unsaved changes to your pinned apps. Save them to QDN." }, From d93262fc92b3af943de785e3fddeded278b73e82 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 16 May 2025 07:46:38 +0200 Subject: [PATCH 20/47] Translation for appRating --- src/components/Apps/AppRating.tsx | 29 +++++++++++++++++++++-------- src/i18n/locales/en/core.json | 5 ++++- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index 8894f3c..7252c1f 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -149,10 +149,11 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { res(response); setInfoSnack({ type: 'success', - message: - 'Successfully rated. Please wait a couple minutes for the network to propogate the changes.', + message: t('core:message.success.rated_app', { + postProcess: 'capitalize', + }), }); - setOpenSnack(true); // TODO translate + setOpenSnack(true); } }) .catch((error) => { @@ -166,7 +167,11 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { (option) => +option.optionName === +newValue ); if (isNaN(optionIndex) || optionIndex === -1) - throw new Error('Cannot find rating option'); + throw new Error( + t('core:message.error.rating_option', { + postProcess: 'capitalize', + }) + ); await new Promise((res, rej) => { window .sendMessage( @@ -185,8 +190,9 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { res(response); setInfoSnack({ type: 'success', - message: - 'Successfully rated. Please wait a couple minutes for the network to propogate the changes.', + message: t('core:message.success.rated_app', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); } @@ -200,7 +206,11 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { console.log('error', error); setInfoSnack({ type: 'error', - message: error?.message || 'Unable to rate', + message: + error?.message || + t('core:message.error.unable_rate', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); } @@ -210,8 +220,8 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => {
@@ -222,8 +232,11 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { (votesInfo?.voteCounts?.length === 6 ? 1 : 0)}{' '} {' RATINGS'} + + {value?.toFixed(1)} + )} diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 23c5fce..90f00b6 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -92,7 +92,9 @@ "minting_account_remove": "unable to remove minting account", "missing_fields": "missing: {{ fields }}", "publish_app": "unable to publish app", - "save_qdn": "unable to save to QDN" + "rating_option": "cannot find rating option", + "save_qdn": "unable to save to QDN", + "unable_rate": "unable to rate" }, "generic": { "name_available": "{{ name }} is available", @@ -123,6 +125,7 @@ "order_submitted": "your buy order was submitted", "published": "successfully published. Please wait a couple minutes for the network to propogate the changes.", "published_qdn": "successfully published to QDN", + "rated_app": "successfully rated. Please wait a couple minutes for the network to propogate the changes.", "request_read": "I have read this request", "transfer": "the transfer was succesful!" } From 45e5e9b66048da356fe00d398d0bd79822b91483 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 16 May 2025 08:03:41 +0200 Subject: [PATCH 21/47] CapitalizeFirst and CapitalizeAll --- docs/i18n_languages.md | 2 +- src/App.tsx | 56 +++++++------ src/ExtStates/NotAuthenticated.tsx | 81 +++++++++++-------- src/Wallets.tsx | 46 ++++++----- src/components/Apps/AppInfo.tsx | 16 ++-- src/components/Apps/AppInfoSnippet.tsx | 8 +- src/components/Apps/AppPublish.tsx | 50 ++++++------ src/components/Apps/AppRating.tsx | 14 ++-- src/components/Apps/AppsDesktop.tsx | 6 +- src/components/Auth/DownloadWallet.tsx | 22 ++--- src/components/Chat/GroupAnnouncements.tsx | 2 +- src/components/CoreSyncStatus.tsx | 26 +++--- src/components/Explore/Explore.tsx | 10 ++- src/components/GlobalActions/JoinGroup.tsx | 32 ++++---- src/components/Group/AddGroup.tsx | 52 ++++++------ src/components/Group/AddGroupList.tsx | 16 ++-- src/components/Group/Forum/GroupMail.tsx | 10 ++- src/components/Group/Forum/NewThread.tsx | 32 ++++---- src/components/Group/Forum/Thread.tsx | 26 +++--- src/components/Group/Group.tsx | 32 +++++--- src/components/Group/GroupInvites.tsx | 6 +- src/components/Group/GroupJoinRequests.tsx | 4 +- src/components/Group/HomeDesktop.tsx | 6 +- src/components/Group/InviteMember.tsx | 14 ++-- src/components/Group/ListOfBans.tsx | 8 +- .../Group/ListOfGroupPromotions.tsx | 64 ++++++++------- src/components/Group/ListOfInvites.tsx | 10 +-- src/components/Group/ListOfJoinRequests.tsx | 18 +++-- src/components/Group/ListOfMembers.tsx | 44 +++++----- .../Group/ListOfThreadPostsWatched.tsx | 8 +- src/components/Group/ManageMembers.tsx | 38 +++++---- src/components/Group/QMailMessages.tsx | 8 +- src/components/Group/Settings.tsx | 30 +++---- src/components/Group/ThingsToDoInitial.tsx | 8 +- src/components/Group/UserListOfInvites.tsx | 14 ++-- src/components/Home/NewUsersCTA.tsx | 6 +- src/components/Home/QortPrice.tsx | 6 +- src/components/Language/LanguageSelector.tsx | 2 +- src/components/Minting/Minting.tsx | 78 ++++++++++-------- src/components/QMailStatus.tsx | 2 +- src/components/RegisterName.tsx | 36 +++++---- src/components/Save/Save.tsx | 40 ++++----- src/components/Theme/ThemeSelector.tsx | 4 +- src/components/Tutorials/Tutorials.tsx | 4 +- .../Tutorials/useHandleTutorials.tsx | 12 +-- src/i18n/i18n.ts | 13 ++- 46 files changed, 573 insertions(+), 449 deletions(-) diff --git a/docs/i18n_languages.md b/docs/i18n_languages.md index 7447193..9fcf779 100644 --- a/docs/i18n_languages.md +++ b/docs/i18n_languages.md @@ -7,4 +7,4 @@ In JSON file: In GUI: -- If the first letter of the translation must be uppercase, use the postProcess, for example: `{t_auth('advanced_users', { postProcess: 'capitalize' })}` +- If the first letter of the translation must be uppercase, use the postProcess, for example: `{t_auth('advanced_users', { postProcess: 'capitalizeFirst' })}` diff --git a/src/App.tsx b/src/App.tsx index 17dea23..e5f9cc0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2514,7 +2514,9 @@ function App() { setExtstate('create-wallet'); }} > - {t('auth:action.create_account', { postProcess: 'capitalize' })} + {t('auth:action.create_account', { + postProcess: 'capitalizeFirst', + })} )} @@ -2611,7 +2613,9 @@ function App() { fontWeight: 600, }} > - {t('auth:action.authenticate', { postProcess: 'capitalize' })} + {t('auth:action.authenticate', { + postProcess: 'capitalizeFirst', + })} @@ -2619,7 +2623,7 @@ function App() { <> - {t('auth:wallet.password', { postProcess: 'capitalize' })} + {t('auth:wallet.password', { postProcess: 'capitalizeFirst' })} @@ -2643,7 +2647,7 @@ function App() { fontSize: '12px', }} > - {t('auth:node.using', { postProcess: 'capitalize' })}:{' '} + {t('auth:node.using', { postProcess: 'capitalizeFirst' })}:{' '} {currentNode?.url} @@ -2655,7 +2659,9 @@ function App() { fontSize: '12px', }} > - {t('auth:node.using_public', { postProcess: 'capitalize' })} + {t('auth:node.using_public', { + postProcess: 'capitalizeFirst', + })} )} @@ -2663,7 +2669,9 @@ function App() { - {t('auth:action.authenticate', { postProcess: 'capitalize' })} + {t('auth:action.authenticate', { + postProcess: 'capitalizeFirst', + })} {walletToBeDecryptedError} @@ -2932,7 +2940,7 @@ function App() { {t('auth:action.create_account', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -2978,7 +2986,7 @@ function App() { }} > {t('core:action.backup_account', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -3008,7 +3016,7 @@ function App() { }} > {t('core:message.success.transfer', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -3019,7 +3027,7 @@ function App() { }} > - {t('core:action.continue', { postProcess: 'capitalize' })} + {t('core:action.continue', { postProcess: 'capitalizeFirst' })} @@ -3036,7 +3044,7 @@ function App() { }} > {t('core:message.success.transfer', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -3045,7 +3053,7 @@ function App() { window.close(); }} > - {t('core:action.continue', { postProcess: 'capitalize' })} + {t('core:action.continue', { postProcess: 'capitalizeFirst' })} )} @@ -3061,7 +3069,7 @@ function App() { }} > {t('core:message.success.order_submitted', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -3070,7 +3078,7 @@ function App() { window.close(); }} > - {t('core:action.close', { postProcess: 'capitalize' })} + {t('core:action.close', { postProcess: 'capitalizeFirst' })} )} @@ -3119,7 +3127,7 @@ function App() { {message?.paymentFee && ( {t('core:fee.payment', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} : {message.paymentFee} @@ -3127,7 +3135,7 @@ function App() { {message?.publishFee && ( {t('core:fee.publish', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} : {message.publishFee} @@ -3151,7 +3159,7 @@ function App() { autoFocus > {t('core:action.accept', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -3193,7 +3201,7 @@ function App() { @@ -3214,7 +3222,7 @@ function App() { @@ -3502,7 +3510,7 @@ function App() { {t('core:message.success.request_read', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -3552,7 +3560,7 @@ function App() { }} > {t('core:action.accept', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} onCancelQortalRequestExtension()} > {t('core:action.decline', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 2a6b090..c2f6e74 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -351,7 +351,8 @@ export const NotAuthenticated = ({ .catch((error) => { console.error( 'Failed to set API key:', - error.message || t('core:error', { postProcess: 'capitalize' }) + error.message || + t('core:error', { postProcess: 'capitalizeFirst' }) ); }); } else { @@ -361,7 +362,7 @@ export const NotAuthenticated = ({ setInfoSnack({ type: 'error', message: t('auth:apikey.select_valid', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -387,7 +388,7 @@ export const NotAuthenticated = ({ 'Failed to set API key:', error.message || t('core:error', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) ); }); @@ -399,7 +400,7 @@ export const NotAuthenticated = ({ message: error?.message || t('auth:apikey.select_valid', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -481,7 +482,7 @@ export const NotAuthenticated = ({ fontSize: '18px', }} > - {t('auth:welcome', { postProcess: 'capitalize' })} + {t('auth:welcome', { postProcess: 'capitalizeFirst' })} - {t('auth:tips.digital_id', { postProcess: 'capitalize' })} + {t('auth:tips.digital_id', { postProcess: 'capitalizeFirst' })} } > setExtstate('wallets')}> - {t('auth:account.account_many', { postProcess: 'capitalize' })} + {t('auth:account.account_many', { postProcess: 'capitalizeFirst' })} @@ -542,7 +543,7 @@ export const NotAuthenticated = ({ fontSize: '18px', }} > - {t('auth:tips.new_users', { postProcess: 'capitalize' })} + {t('auth:tips.new_users', { postProcess: 'capitalizeFirst' })} - {t('auth:tips.new_account', { postProcess: 'capitalize' })} + {t('auth:tips.new_account', { postProcess: 'capitalizeFirst' })} } @@ -572,7 +573,9 @@ export const NotAuthenticated = ({ }, }} > - {t('auth:action.create_account', { postProcess: 'capitalize' })} + {t('auth:action.create_account', { + postProcess: 'capitalizeFirst', + })} @@ -585,7 +588,7 @@ export const NotAuthenticated = ({ visibility: !useLocalNode && 'hidden', }} > - {t('auth:node.using', { postProcess: 'capitalize' })}:{' '} + {t('auth:node.using', { postProcess: 'capitalizeFirst' })}:{' '} {currentNode?.url} @@ -613,7 +616,7 @@ export const NotAuthenticated = ({ textDecoration: 'underline', }} > - {t('auth:advanced_users', { postProcess: 'capitalize' })} + {t('auth:advanced_users', { postProcess: 'capitalizeFirst' })} @@ -676,8 +683,12 @@ export const NotAuthenticated = ({ component="label" > {apiKey - ? t('auth:node.use_local', { postProcess: 'capitalize' }) - : t('auth:apikey.import', { postProcess: 'capitalize' })} + ? t('auth:node.use_local', { + postProcess: 'capitalizeFirst', + }) + : t('auth:apikey.import', { + postProcess: 'capitalizeFirst', + })} - {t('auth:apikey.key', { postProcess: 'capitalize' })}:{' '} + {t('auth:apikey.key', { postProcess: 'capitalizeFirst' })}:{' '} {importedApiKey} @@ -698,7 +709,7 @@ export const NotAuthenticated = ({ variant="contained" component="label" > - {t('auth:node.choose', { postProcess: 'capitalize' })} + {t('auth:node.choose', { postProcess: 'capitalizeFirst' })} - {t('auth:build_version', { postProcess: 'capitalize' })}: + {t('auth:build_version', { postProcess: 'capitalizeFirst' })}: {manifestData?.version} @@ -728,7 +739,7 @@ export const NotAuthenticated = ({ > {' '} - {t('auth:node.custom_many', { postProcess: 'capitalize' })}: + {t('auth:node.custom_many', { postProcess: 'capitalizeFirst' })}: - {t('core:action.choose', { postProcess: 'capitalize' })} + {t('core:action.choose', { + postProcess: 'capitalizeFirst', + })} @@ -858,7 +871,7 @@ export const NotAuthenticated = ({ variant="contained" > {t('core:action.choose', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -873,7 +886,7 @@ export const NotAuthenticated = ({ variant="contained" > {t('core:action.edit', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -887,7 +900,9 @@ export const NotAuthenticated = ({ }} variant="contained" > - {t('core:remove', { postProcess: 'capitalize' })} + {t('core:remove', { + postProcess: 'capitalizeFirst', + })} @@ -926,7 +941,7 @@ export const NotAuthenticated = ({ {mode === 'list' && ( )} @@ -939,7 +954,7 @@ export const NotAuthenticated = ({ }} autoFocus > - {t('core:action.close', { postProcess: 'capitalize' })} + {t('core:action.close', { postProcess: 'capitalizeFirst' })} )} @@ -954,7 +969,7 @@ export const NotAuthenticated = ({ }} > {t('auth:action.return_to_list', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -964,7 +979,7 @@ export const NotAuthenticated = ({ onClick={() => saveCustomNodes(customNodes)} autoFocus > - {t('core:save', { postProcess: 'capitalize' })} + {t('core:save', { postProcess: 'capitalizeFirst' })} )} @@ -979,7 +994,7 @@ export const NotAuthenticated = ({ aria-describedby="alert-dialog-description" > - {t('auth:apikey.enter', { postProcess: 'capitalize' })} + {t('auth:apikey.enter', { postProcess: 'capitalizeFirst' })} - {t('auth:apikey.alternative', { postProcess: 'capitalize' })} + {t('auth:apikey.alternative', { + postProcess: 'capitalizeFirst', + })} - {t('core:save', { postProcess: 'capitalize' })} + {t('core:save', { postProcess: 'capitalizeFirst' })} diff --git a/src/Wallets.tsx b/src/Wallets.tsx index 9fb153b..bd41369 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -152,7 +152,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { } else { setSeedError( t('auth:message.error.account_creation', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) ); } @@ -160,7 +160,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { setSeedError( error?.message || t('auth:message.error.account_creation', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) ); } finally { @@ -202,7 +202,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { <> {t('auth:message.generic.no_account', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -212,7 +212,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { <> {t('auth:message.generic.your_accounts', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -224,7 +224,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { {t('auth:account.selected', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} : @@ -282,7 +282,9 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { fontSize: '16px', }} > - {t('auth:tips.existing_account', { postProcess: 'capitalize' })} + {t('auth:tips.existing_account', { + postProcess: 'capitalizeFirst', + })} } @@ -294,7 +296,9 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { display: 'inline', }} > - {t('auth:action.add.seed_phrase', { postProcess: 'capitalize' })} + {t('auth:action.add.seed_phrase', { + postProcess: 'capitalizeFirst', + })} @@ -309,7 +313,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { }} > {t('auth:tips.additional_wallet', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -323,7 +327,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { > {t('auth:action.add.account', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -341,7 +345,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { > {t('auth:message.generic.type_seed', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -354,7 +358,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { > { { { }} > {t('core:action.close', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} { autoFocus > {t('core:action.add', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} { }} > {t('core:action.login', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -546,7 +550,7 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { > { { onClick={() => setIsEdit(false)} > {t('core:action.close', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/components/Apps/AppInfo.tsx b/src/components/Apps/AppInfo.tsx index b36799a..6e8e274 100644 --- a/src/components/Apps/AppInfo.tsx +++ b/src/components/Apps/AppInfo.tsx @@ -174,10 +174,10 @@ export const AppInfo = ({ app, myName }) => { {isSelectedAppPinned ? t('core:action.unpin_from_dashboard', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) : t('core:action.pin_from_dashboard', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -200,10 +200,10 @@ export const AppInfo = ({ app, myName }) => { {isInstalled ? t('core:action.open', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) : t('core:action.download', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -229,7 +229,7 @@ export const AppInfo = ({ app, myName }) => { {t('core:category', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} : @@ -239,7 +239,7 @@ export const AppInfo = ({ app, myName }) => { {app?.metadata?.categoryName || t('core:none', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -249,7 +249,7 @@ export const AppInfo = ({ app, myName }) => { {t('core:q_apps.about', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -259,7 +259,7 @@ export const AppInfo = ({ app, myName }) => { {app?.metadata?.description || t('core:message.generic.no_description', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/components/Apps/AppInfoSnippet.tsx b/src/components/Apps/AppInfoSnippet.tsx index c027bb0..1629b5e 100644 --- a/src/components/Apps/AppInfoSnippet.tsx +++ b/src/components/Apps/AppInfoSnippet.tsx @@ -173,10 +173,10 @@ export const AppInfoSnippet = ({ {isSelectedAppPinned ? t('core:action.unpin', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) : t('core:action.pin', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -196,10 +196,10 @@ export const AppInfoSnippet = ({ {isInstalled ? t('core:action.open', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) : t('core:action.download', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/components/Apps/AppPublish.tsx b/src/components/Apps/AppPublish.tsx index 1425ba6..6ab1525 100644 --- a/src/components/Apps/AppPublish.tsx +++ b/src/components/Apps/AppPublish.tsx @@ -96,7 +96,7 @@ export const AppPublish = ({ names, categories }) => { t('core:message.error.file_too_large', { filename: file.name, size: maxFileSize / (1024 * 1024), - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) ); } @@ -171,7 +171,7 @@ export const AppPublish = ({ names, categories }) => { const missingFieldsString = missingFields.join(', '); const errorMsg = t('core:message.error.missing_fields', { fields: missingFieldsString, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }); throw new Error(errorMsg); } @@ -179,13 +179,13 @@ export const AppPublish = ({ names, categories }) => { await show({ message: t('core:save_options.publish_app', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', }); setIsLoading( t('core:message.generic.publishing', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) ); const fileBase64 = await fileToBase64(file); @@ -214,14 +214,16 @@ export const AppPublish = ({ names, categories }) => { .catch((error) => { rej( error.message || - t('core:message.error.generic', { postProcess: 'capitalize' }) + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) ); }); }); setInfoSnack({ type: 'success', message: t('core:message.success.published', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -244,7 +246,7 @@ export const AppPublish = ({ names, categories }) => { message: error?.message || t('core:message.error.publish_app', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -268,7 +270,7 @@ export const AppPublish = ({ names, categories }) => { > {t('core:action.create_apps', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} ! @@ -277,7 +279,7 @@ export const AppPublish = ({ names, categories }) => { {t('core:message.generic.one_app_per_name', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -285,7 +287,7 @@ export const AppPublish = ({ names, categories }) => { {t('core:name_app', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -302,7 +304,7 @@ export const AppPublish = ({ names, categories }) => { }} > {t('core:action.select_name_app', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} {/* This is the placeholder item */} @@ -316,7 +318,7 @@ export const AppPublish = ({ names, categories }) => { {t('core:app_service_type', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -333,18 +335,18 @@ export const AppPublish = ({ names, categories }) => { }} > {t('core:action.select_app_type', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} {t('core:app', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} {t('core:website', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -353,7 +355,7 @@ export const AppPublish = ({ names, categories }) => { {t('core:title', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -380,7 +382,7 @@ export const AppPublish = ({ names, categories }) => { {t('core:description', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -407,7 +409,7 @@ export const AppPublish = ({ names, categories }) => { {t('core:category', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -424,7 +426,7 @@ export const AppPublish = ({ names, categories }) => { }} > {t('core:action.select_category', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -441,7 +443,7 @@ export const AppPublish = ({ names, categories }) => { {t('core:tags', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -536,7 +538,9 @@ export const AppPublish = ({ names, categories }) => { - {t('core:message.generic.select_zip', { postProcess: 'capitalize' })} + {t('core:message.generic.select_zip', { + postProcess: 'capitalizeFirst', + })} @@ -556,7 +560,7 @@ export const AppPublish = ({ names, categories }) => { {' '} - {t('core:action.choose_file', { postProcess: 'capitalize' })} + {t('core:action.choose_file', { postProcess: 'capitalizeFirst' })} @@ -567,7 +571,7 @@ export const AppPublish = ({ names, categories }) => { }} onClick={publishApp} > - {t('core:action.publish', { postProcess: 'capitalize' })} + {t('core:action.publish', { postProcess: 'capitalizeFirst' })} diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index 7252c1f..34bdee0 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -106,7 +106,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { if (!myName) throw new Error( t('core:message.generic.name_rate', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) ); if (!app?.name) return; @@ -115,7 +115,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { await show({ message: t('core:message.error.generic', { rate: newValue, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', }); @@ -126,7 +126,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { const pollDescription = t('core:message.error.generic', { name: app.name, service: app.service, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }); await new Promise((res, rej) => { @@ -150,7 +150,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { setInfoSnack({ type: 'success', message: t('core:message.success.rated_app', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -169,7 +169,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { if (isNaN(optionIndex) || optionIndex === -1) throw new Error( t('core:message.error.rating_option', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) ); await new Promise((res, rej) => { @@ -191,7 +191,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { setInfoSnack({ type: 'success', message: t('core:message.success.rated_app', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -209,7 +209,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { message: error?.message || t('core:message.error.unable_rate', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index d1268fb..56c4985 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -24,6 +24,7 @@ import { AppsIcon } from '../../assets/Icons/AppsIcon'; import { CoreSyncStatus } from '../CoreSyncStatus'; import { MessagingIconFilled } from '../../assets/Icons/MessagingIconFilled'; import { useAtom } from 'jotai'; +import { useTranslation } from 'react-i18next'; const uid = new ShortUniqueId({ length: 8 }); @@ -47,9 +48,9 @@ export const AppsDesktop = ({ const [categories, setCategories] = useState([]); const iframeRefs = useRef({}); const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); - const { showTutorial } = useContext(MyContext); const theme = useTheme(); + const { t } = useTranslation(['core', 'group']); const myApp = useMemo(() => { return availableQapps.find( @@ -104,7 +105,6 @@ export const AppsDesktop = ({ try { let apps = []; let websites = []; - // dispatch(setIsLoadingGlobal(true)) const url = `${getBaseApiReact()}/arbitrary/resources/search?service=APP&mode=ALL&limit=0&includestatus=true&includemetadata=true`; const response = await fetch(url, { @@ -113,6 +113,7 @@ export const AppsDesktop = ({ 'Content-Type': 'application/json', }, }); + if (!response?.ok) return; const responseData = await response.json(); const urlWebsites = `${getBaseApiReact()}/arbitrary/resources/search?service=WEBSITE&mode=ALL&limit=0&includestatus=true&includemetadata=true`; @@ -123,6 +124,7 @@ export const AppsDesktop = ({ 'Content-Type': 'application/json', }, }); + if (!responseWebsites?.ok) return; const responseDataWebsites = await responseWebsites.json(); diff --git a/src/components/Auth/DownloadWallet.tsx b/src/components/Auth/DownloadWallet.tsx index f96dbb6..eb85a20 100644 --- a/src/components/Auth/DownloadWallet.tsx +++ b/src/components/Auth/DownloadWallet.tsx @@ -76,14 +76,16 @@ export const DownloadWallet = ({ if (!keepCurrentPassword && !newPassword) { setWalletToBeDownloadedError( t('auth:wallet.error.missing_new_password', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) ); return; } if (!walletToBeDownloadedPassword) { setWalletToBeDownloadedError( - t('auth:wallet.error.missing_password', { postProcess: 'capitalize' }) + t('auth:wallet.error.missing_password', { + postProcess: 'capitalizeFirst', + }) ); return; } @@ -157,7 +159,9 @@ export const DownloadWallet = ({ fontWeight: 600, }} > - {t('auth:action.download_account', { postProcess: 'capitalize' })} + {t('auth:action.download_account', { + postProcess: 'capitalizeFirst', + })} @@ -167,7 +171,7 @@ export const DownloadWallet = ({ <> {t('auth:wallet.password_confirmation', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -206,7 +210,7 @@ export const DownloadWallet = ({ {t('auth:wallet.keep_password', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -217,7 +221,7 @@ export const DownloadWallet = ({ <> {t('auth:wallet.new_password', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -233,7 +237,7 @@ export const DownloadWallet = ({ {t('auth:password_confirmation', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -248,13 +252,13 @@ export const DownloadWallet = ({ await saveFileToDiskFunc(); await showInfo({ message: t('auth:message.generic.keep_secure', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); }} > {t('auth:action.download_account', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index c91bfaa..af92b71 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -291,7 +291,7 @@ export const GroupAnnouncements = ({ await show({ message: t('group:question.perform_transaction', { action: 'ARBITRARY', - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', }); diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index 79e22d2..3e782c4 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -77,26 +77,26 @@ export const CoreSyncStatus = () => { let imagePath = syncingImg; let message = t('core:message.status.synchronizing', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }); if (isMintingPossible && !isUsingGateway) { imagePath = syncedMintingImg; - message = `${t(`core:message.status.${isSynchronizing ? 'synchronizing' : 'synchronized'}`, { postProcess: 'capitalize' })} ${t('core:message.status.minting')}`; + message = `${t(`core:message.status.${isSynchronizing ? 'synchronizing' : 'synchronized'}`, { postProcess: 'capitalizeFirst' })} ${t('core:message.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: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.not_minting') : ''}`; + message = `${t('core:message.status.synchronizing', { postProcess: 'capitalizeFirst' })} ${!isUsingGateway ? t('core:message.status.not_minting') : ''}`; } else if (!isSynchronizing && !isMintingPossible && syncPercent === 100) { imagePath = syncedImg; - message = `${t('core:message.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.not_minting') : ''}`; + message = `${t('core:message.status.synchronized', { postProcess: 'capitalizeFirst' })} ${!isUsingGateway ? t('core:message.status.not_minting') : ''}`; } else if (isSynchronizing && isMintingPossible && syncPercent === 100) { imagePath = syncingImg; - message = `${t('core:message.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.minting') : ''}`; + message = `${t('core:message.status.synchronizing', { postProcess: 'capitalizeFirst' })} ${!isUsingGateway ? t('core:message.status.minting') : ''}`; } else if (!isSynchronizing && isMintingPossible && syncPercent === 100) { imagePath = syncedMintingImg; - message = `${t('core:message.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.minting') : ''}`; + message = `${t('core:message.status.synchronized', { postProcess: 'capitalizeFirst' })} ${!isUsingGateway ? t('core:message.status.minting') : ''}`; } return ( @@ -121,36 +121,38 @@ export const CoreSyncStatus = () => { top: '10px', }} > -

{t('core:core.information', { postProcess: 'capitalize' })}

+

+ {t('core:core.information', { postProcess: 'capitalizeFirst' })} +

- {t('core:core.version', { postProcess: 'capitalize' })}:{' '} + {t('core:core.version', { postProcess: 'capitalizeFirst' })}:{' '} {buildVersion}

{message}

- {t('core:core.block_height', { postProcess: 'capitalize' })}:{' '} + {t('core:core.block_height', { postProcess: 'capitalizeFirst' })}:{' '} {height || ''}

- {t('core:core.peers', { postProcess: 'capitalize' })}:{' '} + {t('core:core.peers', { postProcess: 'capitalizeFirst' })}:{' '} {numberOfConnections || ''}

- {t('auth:node.using_public', { postProcess: 'capitalize' })}:{' '} + {t('auth:node.using_public', { postProcess: 'capitalizeFirst' })}:{' '} {isUsingGateway?.toString()}

- {t('core:ui.version', { postProcess: 'capitalize' })}:{' '} + {t('core:ui.version', { postProcess: 'capitalizeFirst' })}:{' '} {manifestData.version}

diff --git a/src/components/Explore/Explore.tsx b/src/components/Explore/Explore.tsx index 3da3cf5..33c7876 100644 --- a/src/components/Explore/Explore.tsx +++ b/src/components/Explore/Explore.tsx @@ -46,7 +46,7 @@ export const Explore = ({ setDesktopViewMode }) => { fontSize: '1rem', }} > - {t('tutorial:initial.trade_qort', { postProcess: 'capitalize' })} + {t('tutorial:initial.trade_qort', { postProcess: 'capitalizeFirst' })} @@ -73,7 +73,7 @@ export const Explore = ({ setDesktopViewMode }) => { fontSize: '1rem', }} > - {t('tutorial:initial.see_apps', { postProcess: 'capitalize' })} + {t('tutorial:initial.see_apps', { postProcess: 'capitalizeFirst' })} @@ -102,7 +102,9 @@ export const Explore = ({ setDesktopViewMode }) => { fontSize: '1rem', }} > - {t('tutorial:initial.general_chat', { postProcess: 'capitalize' })} + {t('tutorial:initial.general_chat', { + postProcess: 'capitalizeFirst', + })} @@ -129,7 +131,7 @@ export const Explore = ({ setDesktopViewMode }) => { fontSize: '1rem', }} > - {t('core:wallet.wallet_other', { postProcess: 'capitalize' })} + {t('core:wallet.wallet_other', { postProcess: 'capitalizeFirst' })} diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx index a56d642..a6ec98f 100644 --- a/src/components/GlobalActions/JoinGroup.tsx +++ b/src/components/GlobalActions/JoinGroup.tsx @@ -72,7 +72,7 @@ export const JoinGroup = () => { await show({ message: t('group:question.perform_transaction', { action: 'JOIN_GROUP', - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', }); @@ -89,7 +89,7 @@ export const JoinGroup = () => { setInfoSnack({ type: 'success', message: t('group:message.success.group_join', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); @@ -100,11 +100,11 @@ export const JoinGroup = () => { type: 'joined-group', label: t('group:message.success.group_join_label', { group_name: group?.groupName, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), labelDone: t('group:message.success.group_join_label', { group_name: group?.groupName, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), done: false, groupId, @@ -118,11 +118,11 @@ export const JoinGroup = () => { type: 'joined-group-request', label: t('group:message.success.group_join_request', { group_name: group?.groupName, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), labelDone: t('group:message.success.group_join_outcome', { group_name: group?.groupName, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), done: false, groupId, @@ -147,7 +147,9 @@ export const JoinGroup = () => { type: 'error', message: error.message || - t('core:message.error.generic', { postProcess: 'capitalize' }), + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }), }); setOpenSnack(true); rej(error); @@ -205,7 +207,7 @@ export const JoinGroup = () => { fontWeight: 600, }} > - {t('group:group.name', { postProcess: 'capitalize' })}:{' '} + {t('group:group.name', { postProcess: 'capitalizeFirst' })}:{' '} {` ${groupInfo?.groupName}`} @@ -215,8 +217,10 @@ export const JoinGroup = () => { fontWeight: 600, }} > - {t('group:group.member_number', { postProcess: 'capitalize' })}:{' '} - {` ${groupInfo?.memberCount}`} + {t('group:group.member_number', { + postProcess: 'capitalizeFirst', + })} + : {` ${groupInfo?.memberCount}`} {groupInfo?.description && ( @@ -237,7 +241,7 @@ export const JoinGroup = () => { }} > {t('group:message.generic.already_in_group', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} )} @@ -249,7 +253,7 @@ export const JoinGroup = () => { }} > {t('group:message.generic.closed_group', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} )} @@ -276,7 +280,7 @@ export const JoinGroup = () => { }} > {t('core:action.join', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -291,7 +295,7 @@ export const JoinGroup = () => { onClick={() => setIsOpen(false)} > {t('core:action.close', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 8b91ebe..b1da7e5 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -105,13 +105,13 @@ export const AddGroup = ({ address, open, setOpen }) => { if (!name) throw new Error( t('group:message.error.name_required', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) ); if (!description) throw new Error( t('group:message.error.description_required', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) ); @@ -120,7 +120,7 @@ export const AddGroup = ({ address, open, setOpen }) => { await show({ message: t('group:question.perform_transaction', { action: 'CREATE_GROUP', - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', }); @@ -140,7 +140,7 @@ export const AddGroup = ({ address, open, setOpen }) => { setInfoSnack({ type: 'success', message: t('group:message.success.group_creation', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -150,11 +150,11 @@ export const AddGroup = ({ address, open, setOpen }) => { type: 'created-group', label: t('group:message.success.group_creation_name', { group_name: name, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), labelDone: t('group:message.success.group_creation_label', { group_name: name, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), done: false, }, @@ -172,7 +172,9 @@ export const AddGroup = ({ address, open, setOpen }) => { rej({ message: error.message || - t('core:message.error.generic', { postProcess: 'capitalize' }), + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }), }); }); }); @@ -225,7 +227,7 @@ export const AddGroup = ({ address, open, setOpen }) => { > - {t('group:group.management', { postProcess: 'capitalize' })} + {t('group:group.management', { postProcess: 'capitalizeFirst' })} { > { /> { /> { > setName(e.target.value)} @@ -350,13 +352,13 @@ export const AddGroup = ({ address, open, setOpen }) => { > setDescription(e.target.value)} @@ -372,7 +374,7 @@ export const AddGroup = ({ address, open, setOpen }) => { > @@ -385,12 +387,12 @@ export const AddGroup = ({ address, open, setOpen }) => { > {t('group:group.open', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} {t('group:group.closed', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -407,7 +409,7 @@ export const AddGroup = ({ address, open, setOpen }) => { > {t('group:advanced_options', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -424,7 +426,7 @@ export const AddGroup = ({ address, open, setOpen }) => { > @@ -437,12 +439,12 @@ export const AddGroup = ({ address, open, setOpen }) => { > {t('core:count.none', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} {t('core:count.one', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} 20% @@ -462,7 +464,7 @@ export const AddGroup = ({ address, open, setOpen }) => { > @@ -521,7 +523,7 @@ export const AddGroup = ({ address, open, setOpen }) => { > @@ -582,7 +584,7 @@ export const AddGroup = ({ address, open, setOpen }) => { onClick={handleCreateGroup} > {t('group:action.create_group', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index 49dce3d..5c577f1 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -115,7 +115,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { await show({ message: t('group:question.perform_transaction', { action: 'JOIN_GROUP', - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', }); @@ -131,7 +131,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { setInfoSnack({ type: 'success', message: t('group:message.success.join_group', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); @@ -142,11 +142,11 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { type: 'joined-group', label: t('group:message.success.group_join_label', { group_name: group?.groupName, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), labelDone: t('group:message.success.group_join_label', { group_name: group?.groupName, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), done: false, groupId, @@ -160,11 +160,11 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { type: 'joined-group-request', label: t('group:message.success.group_join_request', { group_name: group?.groupName, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), labelDone: t('group:message.success.group_join_outcome', { group_name: group?.groupName, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), done: false, groupId, @@ -243,7 +243,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { }} > - {t('core:action.join', { postProcess: 'capitalize' })}{' '} + {t('core:action.join', { postProcess: 'capitalizeFirst' })}{' '} {group?.groupName} @@ -257,7 +257,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { onClick={() => handleJoinGroup(group, group?.isOpen)} > {t('group:action.join_group', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 41f75ef..c1718a5 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -170,7 +170,9 @@ export const GroupMail = ({ .catch((error) => { rej( error.message || - t('core:message.error.generic', { postProcess: 'capitalize' }) + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) ); }); }); @@ -754,7 +756,7 @@ export const GroupMail = ({ {t('group:last_message', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} :{' '} @@ -791,7 +793,7 @@ export const GroupMail = ({ }} > {t('core:page.last', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 0212702..4a561f5 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -183,19 +183,19 @@ export const NewThread = ({ if (!isMessage && !threadTitle) { errorMsg = t('group:question.provide_thread', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }); } if (!name) { errorMsg = t('group:message.error.access_name', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }); } if (!groupInfo) { errorMsg = t('group:message.error.group_info', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }); } @@ -204,7 +204,7 @@ export const NewThread = ({ const missingFieldsString = missingFields.join(', '); const errMsg = t('core:message.error.missing_fields', { field: missingFieldsString, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }); errorMsg = errMsg; } @@ -217,7 +217,7 @@ export const NewThread = ({ if (!htmlContent?.trim() || htmlContent?.trim() === '

') { const errMsg = t('group:message.generic.provide_message', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }); throw new Error(errMsg); } @@ -231,7 +231,7 @@ export const NewThread = ({ await show({ message: t('group:question.perform_transaction', { action: 'ARBITRARY', - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), publishFee: feeToShow + ' QORT', }); @@ -257,7 +257,7 @@ export const NewThread = ({ isPrivate === false ? null : await getSecretKey(false, true); if (!secretKey && isPrivate) { const errMsg = t('group:message.error.group_secret_key', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }); throw new Error(errMsg); } @@ -320,7 +320,7 @@ export const NewThread = ({ setInfoSnack({ type: 'success', message: t('group:message.success.thread_creation', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -332,7 +332,7 @@ export const NewThread = ({ } else { if (!currentThread) { const errMsg = t('group:message.error.thread_id', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }); throw new Error(errMsg); } @@ -360,7 +360,7 @@ export const NewThread = ({ setInfoSnack({ type: 'success', message: t('group:message.success.post_creation', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -404,10 +404,10 @@ export const NewThread = ({ {currentThread ? t('core:action.new.post', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) : t('core:action.new.thread', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -437,10 +437,10 @@ export const NewThread = ({ {isMessage ? t('core:action.post_message', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) : t('core:action.new.thread', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -562,10 +562,10 @@ export const NewThread = ({ {isMessage ? t('core:action.post', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }) : t('core:action.create_thread', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index a6c2d0f..447200e 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -609,7 +609,7 @@ export const Thread = ({ {t('group:action.return_to_thread', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -688,7 +688,7 @@ export const Thread = ({ disabled={!hasFirstPage} variant="contained" > - {t('core:page.first', { postProcess: 'capitalize' })} + {t('core:page.first', { postProcess: 'capitalizeFirst' })} @@ -929,7 +929,9 @@ export const Thread = ({ fontSize: '18px', }} > - {t('core:downloading_qdn', { postProcess: 'capitalize' })} + {t('core:downloading_qdn', { + postProcess: 'capitalizeFirst', + })} @@ -961,7 +963,7 @@ export const Thread = ({ }} > {t('group:action.refetch_page', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -1000,7 +1002,7 @@ export const Thread = ({ disabled={!hasFirstPage} variant="contained" > - {t('core:page.first', { postProcess: 'capitalize' })} + {t('core:page.first', { postProcess: 'capitalizeFirst' })} @@ -1071,7 +1073,7 @@ export const Thread = ({ diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index a7e330d..b524697 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -514,7 +514,9 @@ export const Group = ({ .catch((error) => { rej( error.message || - t('core:message.error.generic', { postProcess: 'capitalize' }) + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) ); }); }); @@ -543,7 +545,9 @@ export const Group = ({ .catch((error) => { rej( error.message || - t('core:message.error.generic', { postProcess: 'capitalize' }) + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) ); }); }); @@ -575,7 +579,9 @@ export const Group = ({ .catch((error) => { rej( error.message || - t('core:message.error.generic', { postProcess: 'capitalize' }) + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) ); }); }); @@ -1098,7 +1104,9 @@ export const Group = ({ .catch((error) => { rej( error.message || - t('core:message.error.generic', { postProcess: 'capitalize' }) + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) ); }); }); @@ -1995,7 +2003,7 @@ export const Group = ({ }} > {t('group:message.generic.no_selection', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -2094,7 +2102,7 @@ export const Group = ({ {' '} {t('group:message.generic.encryption_key', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })}
@@ -2120,21 +2128,21 @@ export const Group = ({ {' '} {t('group:message.generic.not_part_group', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} {t('group:message.generic.only_encrypted', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} {t('group:message.generic.notify_admins', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} @@ -2156,7 +2164,7 @@ export const Group = ({ onClick={() => notifyAdmin(admin)} > {t('core:action.notify', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })}
@@ -2381,7 +2389,7 @@ export const Group = ({ message: isLoadingGroupMessage || t('group:message.generic.setting_group', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }} /> @@ -2390,7 +2398,7 @@ export const Group = ({ open={isLoadingGroups} info={{ message: t('group:message.generic.setting_group', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }} /> diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index ca3f32c..f3da8a0 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -71,7 +71,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { fontSize: '1rem', }} > - {t('group:group.invites', { postProcess: 'capitalize' })}{' '} + {t('group:group.invites', { postProcess: 'capitalizeFirst' })}{' '} {groupsWithJoinRequests?.length > 0 && ` (${groupsWithJoinRequests?.length})`} @@ -131,7 +131,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { }} > {t('group:message.generic.no_display', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })}
@@ -181,7 +181,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { }} primary={t('group:message.generic.group_invited_you', { group: group?.groupName, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} /> diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 15304f5..0991edb 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -144,7 +144,7 @@ export const GroupJoinRequests = ({ fontSize: '1rem', }} > - {t('group:join_requests', { postProcess: 'capitalize' })}{' '} + {t('group:join_requests', { postProcess: 'capitalizeFirst' })}{' '} {filteredJoinRequests?.filter((group) => group?.data?.length > 0) ?.length > 0 && ` (${filteredJoinRequests?.filter((group) => group?.data?.length > 0)?.length})`} @@ -207,7 +207,7 @@ export const GroupJoinRequests = ({ }} > {t('group:message.generic.no_display', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', })} diff --git a/src/components/Group/HomeDesktop.tsx b/src/components/Group/HomeDesktop.tsx index 83f8828..c270df0 100644 --- a/src/components/Group/HomeDesktop.tsx +++ b/src/components/Group/HomeDesktop.tsx @@ -85,7 +85,7 @@ export const HomeDesktop = ({ padding: '10px', }} > - {t('core:welcome', { postProcess: 'capitalize' })} + {t('core:welcome', { postProcess: 'capitalizeFirst' })} {userInfo?.name ? ( - {t('tutorial:initial.explore', { postProcess: 'capitalize' })} + {t('tutorial:initial.explore', { + postProcess: 'capitalizeFirst', + })} {' '} diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index 24ff960..94414ba 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -19,7 +19,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { await show({ message: t('group:question.perform_transaction', { action: 'GROUP_INVITE', - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', }); @@ -40,7 +40,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { type: 'success', message: t('group:message.success.group_invite', { value: value, - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -61,7 +61,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { message: error?.message || t('core:message.error.generic', { - postProcess: 'capitalize', + postProcess: 'capitalizeFirst', }), }); setOpenSnack(true); @@ -86,7 +86,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { flexDirection: 'column', }} > - {t('group:action.invite_member', { postProcess: 'capitalize' })} + {t('group:action.invite_member', { postProcess: 'capitalizeFirst' })} @@ -99,14 +99,14 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { setDomain(e.target.value)} /> + - + setPort(e.target.value)} /> @@ -474,15 +517,20 @@ export const AppsDevModeHome = ({ + diff --git a/src/components/Apps/AppsDevModeNavBar.tsx b/src/components/Apps/AppsDevModeNavBar.tsx index 5a632cd..e011ded 100644 --- a/src/components/Apps/AppsDevModeNavBar.tsx +++ b/src/components/Apps/AppsDevModeNavBar.tsx @@ -23,7 +23,6 @@ export const AppsDevModeNavBar = () => { const [navigationController, setNavigationController] = useAtom( navigationControllerAtom ); - const theme = useTheme(); const [isNewTabWindow, setIsNewTabWindow] = useState(false); const tabsRef = useRef(null); diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index 21d9d7d..b2c67cf 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -16,6 +16,7 @@ import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward'; import { AppsPrivate } from './AppsPrivate'; import ThemeSelector from '../Theme/ThemeSelector'; import LanguageSelector from '../Language/LanguageSelector'; +import { useTranslation } from 'react-i18next'; export const AppsHomeDesktop = ({ setMode, @@ -26,6 +27,7 @@ export const AppsHomeDesktop = ({ }) => { const [qortalUrl, setQortalUrl] = useState(''); const theme = useTheme(); + const { t } = useTranslation(['core', 'group']); const openQortalUrl = () => { try { @@ -41,6 +43,7 @@ export const AppsHomeDesktop = ({ console.log(error); } }; + return ( <> - Apps Dashboard + {t('core:apps_dashboard', { postProcess: 'capitalizeFirst' })} @@ -66,14 +69,14 @@ export const AppsHomeDesktop = ({ > - Library + + {t('core:library', { postProcess: 'capitalizeFirst' })} + diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 64f1007..ebe0ddd 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -41,6 +41,7 @@ import { Virtuoso } from 'react-virtuoso'; import { executeEvent } from '../../utils/events'; import { ComposeP, ShowMessageReturnButton } from '../Group/Forum/Mail-styles'; import { ReturnIcon } from '../../assets/Icons/ReturnIcon.tsx'; +import { useTranslation } from 'react-i18next'; const officialAppList = [ 'q-tube', @@ -104,6 +105,7 @@ export const AppsLibraryDesktop = ({ const [searchValue, setSearchValue] = useState(''); const virtuosoRef = useRef(null); const theme = useTheme(); + const { t } = useTranslation(['core', 'group']); const officialApps = useMemo(() => { return availableQapps.filter( @@ -210,9 +212,13 @@ export const AppsLibraryDesktop = ({ ml: 1, paddingLeft: '12px', }} - placeholder="Search for apps" + placeholder={t('core:action.search_apps', { + postProcess: 'capitalizeFirst', + })} inputProps={{ - 'aria-label': 'Search for apps', + 'aria-label': t('core:action.search_apps', { + postProcess: 'capitalizeFirst', + }), fontSize: '16px', fontWeight: 400, }} @@ -273,10 +279,14 @@ export const AppsLibraryDesktop = ({ }} onClick={() => { executeEvent('navigateBack', {}); - }} // TODO translate + }} > - Return to Apps Dashboard + + {t('core:action.return_apps_dashboard', { + postProcess: 'capitalizeFirst', + })} + @@ -302,7 +312,11 @@ export const AppsLibraryDesktop = ({ ) : searchedList?.length === 0 && debouncedValue ? ( - No results + + {t('core:message.generic.no_results', { + postProcess: 'capitalizeFirst', + })} + ) : ( <> @@ -311,7 +325,7 @@ export const AppsLibraryDesktop = ({ fontSize: '30px', }} > - Official Apps + {t('core:apps_official', { postProcess: 'capitalizeFirst' })} @@ -396,7 +410,13 @@ export const AppsLibraryDesktop = ({ textAlign: 'start', }} > - {hasPublishApp ? 'Update your app' : 'Publish your app'} + {hasPublishApp + ? t('core:action.update_app', { + postProcess: 'capitalizeFirst', + }) + : t('core:action.publish_app', { + postProcess: 'capitalizeFirst', + })} @@ -422,7 +442,13 @@ export const AppsLibraryDesktop = ({ }} > - {hasPublishApp ? 'Update' : 'Publish'} + {hasPublishApp + ? t('core:action.update', { + postProcess: 'capitalizeFirst', + }) + : t('core:action.publish', { + postProcess: 'capitalizeFirst', + })} @@ -441,7 +467,9 @@ export const AppsLibraryDesktop = ({ fontSize: '30px', }} > - Categories + {t('core:category_other', { + postProcess: 'capitalizeFirst', + })} @@ -480,7 +508,7 @@ export const AppsLibraryDesktop = ({ }, }} > - All + {t('core:all', { postProcess: 'capitalizeFirst' })} diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index 8d45604..f294834 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -32,6 +32,7 @@ import { sortablePinnedAppsAtom, } from '../../atoms/global'; import { useAtom, useSetAtom } from 'jotai'; +import { useTranslation } from 'react-i18next'; export function saveToLocalStorage(key, subKey, newValue) { try { @@ -75,7 +76,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { ); const theme = useTheme(); - + const { t } = useTranslation(['core', 'group']); const [isNewTabWindow, setIsNewTabWindow] = useState(false); const tabsRef = useRef(null); const [anchorEl, setAnchorEl] = useState(null); @@ -238,6 +239,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { }} /> + { if (!selectedTab) return; @@ -274,9 +276,9 @@ export const AppsNavBarDesktop = ({ disableBack }) => { paper: { sx: { backgroundColor: theme.palette.background.default, + borderRadius: '5px', color: theme.palette.text.primary, width: '148px', - borderRadius: '5px', }, }, }} @@ -375,9 +377,18 @@ export const AppsNavBarDesktop = ({ disableBack }) => { : theme.palette.text.primary, }, }} - primary={`${isSelectedAppPinned ? 'Unpin app' : 'Pin app'}`} + primary={`${ + isSelectedAppPinned + ? t('core:action.unpin_app', { + postProcess: 'capitalizeFirst', + }) + : t('core:action.pin_app', { + postProcess: 'capitalizeFirst', + }) + }}`} /> + { if (selectedTab?.refreshFunc) { @@ -404,6 +415,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => { }} /> + { color: theme.palette.text.primary, }, }} - primary="Copy link" + primary={t('core:action.copy_link', { + postProcess: 'capitalizeFirst', + })} /> )} diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index cddbc12..fb0a4dd 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -36,6 +36,7 @@ import { fileToBase64 } from '../../utils/fileReading'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import { getFee } from '../../background'; import { useAtom } from 'jotai'; +import { useTranslation } from 'react-i18next'; const maxFileSize = 50 * 1024 * 1024; // 50MB @@ -62,6 +63,7 @@ export const AppsPrivate = ({ myName }) => { const [memberGroups] = useAtom(memberGroupsAtom); const theme = useTheme(); + const { t } = useTranslation(['core', 'group']); const myGroupsPrivate = useMemo(() => { return memberGroups?.filter( @@ -98,9 +100,11 @@ export const AppsPrivate = ({ myName }) => { errors.forEach((error) => { if (error.code === 'file-too-large') { console.error( - `File ${file.name} is too large. Max size allowed is ${ - maxFileSize / (1024 * 1024) - } MB.` + t('core:message.error.file_too_large', { + filename: file.name, + size: maxFileSize / (1024 * 1024), + postProcess: 'capitalizeFirst', + }) ); } }); @@ -111,7 +115,6 @@ export const AppsPrivate = ({ myName }) => { const addPrivateApp = async () => { try { if (privateAppValues?.groupId === 0) return; - await openApp(privateAppValues, true); } catch (error) { console.error(error); @@ -139,9 +142,28 @@ export const AppsPrivate = ({ myName }) => { const publishPrivateApp = async () => { try { if (selectedGroup === 0) return; - if (!logo) throw new Error('Please select an image for a logo'); - if (!myName) throw new Error('You need a Qortal name to publish'); - if (!newPrivateAppValues?.name) throw new Error('Your app needs a name'); + + if (!logo) + throw new Error( + t('core:message.generic.select_image', { + postProcess: 'capitalizeFirst', + }) + ); + + if (!myName) + throw new Error( + t('core:message.generic.name_publish', { + postProcess: 'capitalizeFirst', + }) + ); + + if (!newPrivateAppValues?.name) + throw new Error( + t('core:message.error.app_need_name', { + postProcess: 'capitalizeFirst', + }) + ); + const base64Logo = await fileToBase64(logo); const base64App = await fileToBase64(file); const objectToSave = { @@ -160,16 +182,22 @@ export const AppsPrivate = ({ myName }) => { if (decryptedData?.error) { throw new Error( - decryptedData?.error || 'Unable to encrypt app. App not published' + decryptedData?.error || + t('core:message.error.unable_encrypt_app', { + postProcess: 'capitalizeFirst', + }) ); } const fee = await getFee('ARBITRARY'); await show({ - message: 'Would you like to publish this app?', + message: t('core:save_options.publish_app', { + postProcess: 'capitalizeFirst', + }), publishFee: fee.fee + ' QORT', }); + await new Promise((res, rej) => { window .sendMessage('publishOnQDN', { @@ -185,7 +213,12 @@ export const AppsPrivate = ({ myName }) => { rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); @@ -203,7 +236,11 @@ export const AppsPrivate = ({ myName }) => { setOpenSnackGlobal(true); setInfoSnackCustom({ type: 'error', - message: error?.message || 'Unable to publish app', + message: + error?.message || + t('core:message.error.unable_publish_app', { + postProcess: 'capitalizeFirst', + }), }); } }; @@ -241,6 +278,7 @@ export const AppsPrivate = ({ myName }) => { Private + {isOpenPrivateModal && ( { display: 'flex', flexDirection: 'column', gap: '5px', - }} // TODO translate + }} > - - + + + + + { marginTop: '15px', }} > - + { } /> + { marginTop: '15px', }} > - + + setPrivateAppValues((prev) => { @@ -397,7 +458,7 @@ export const AppsPrivate = ({ myName }) => { setIsOpenPrivateModal(false); }} > - Close + {t('core:action.close', { postProcess: 'capitalizeFirst' })} @@ -424,7 +485,9 @@ export const AppsPrivate = ({ myName }) => { fontSize: '14px', }} > - Select .zip file containing static content:{' '} + {t('core:message.generic.select_zip', { + postProcess: 'capitalizeFirst', + })} @@ -435,10 +498,11 @@ export const AppsPrivate = ({ myName }) => { fontSize: '14px', }} >{` - 50mb MB maximum`} + 50mb MB max`} {file && ( <> + {`Selected: (${file?.name})`} )} @@ -454,7 +518,13 @@ export const AppsPrivate = ({ myName }) => { > {' '} - {file ? 'Change' : 'Choose'} File + {file + ? t('core:action.change_file', { + postProcess: 'capitalizeFirst', + }) + : t('core:action.choose_file', { + postProcess: 'capitalizeFirst', + })} @@ -466,10 +536,18 @@ export const AppsPrivate = ({ myName }) => { gap: '5px', }} > - + + + setNewPrivateAppValues((prev) => { @@ -525,9 +611,14 @@ export const AppsPrivate = ({ myName }) => { marginTop: '15px', }} > - + + setNewPrivateAppValues((prev) => { @@ -543,10 +634,15 @@ export const AppsPrivate = ({ myName }) => { setLogo(file)}> - + {logo?.name} + @@ -558,7 +654,7 @@ export const AppsPrivate = ({ myName }) => { clearFields(); }} > - Close + {t('core:action.close', { postProcess: 'capitalizeFirst' })} diff --git a/src/i18n/locales/en/auth.json b/src/i18n/locales/en/auth.json index 8a514fa..d212416 100644 --- a/src/i18n/locales/en/auth.json +++ b/src/i18n/locales/en/auth.json @@ -37,7 +37,6 @@ "your_accounts": "your saved accounts" } }, - "name": "name", "node": { "choose": "choose custom node", "custom_many": "custom nodes", diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 90f00b6..e78df4c 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -1,20 +1,26 @@ { "action": { "add": "add", + "add_custom_framework": "add custom framework", "accept": "accept", + "access": "access", "backup_account": "backup account", "backup_wallet": "backup wallet", "cancel": "cancel", "cancel_invitation": "cancel invitation", "change": "change", + "change_file": "change file", "change_language": "change language", "choose": "choose", + "choose_file": "choose file", "close": "close", "continue": "continue", "continue_logout": "continue to logout", + "copy_link": "copy link", "create_apps": "create apps", "create_file": "create file", "create_thread": "create thread", + "choose_logo": "choose a logo", "choose_name": "choose a name", "decline": "decline", "decrypt": "decrypt", @@ -34,30 +40,44 @@ "notify": "notify", "open": "open", "pin": "pin", + "pin_app": "pin app", "pin_from_dashboard": "pin from dashboard", "post": "post", "post_message": "post message", "publish": "publish", + "publish_app": "publish your app", "register_name": "register name", "remove": "remove", + "return_apps_dashboard": "return to Apps Dashboard", "save": "save", + "search_apps": "search for apps", "select_app_type": "select App Type", "select_category": "select Category", "select_name_app": "select Name/App", "start_minting": "start minting", "unpin": "unpin", - "unpin_from_dashboard": "unpin from dashboard" + "unpin_app": "unpin app", + "unpin_from_dashboard": "unpin from dashboard", + "update": "update", + "update_app": "update your app" }, "admin": "admin", + "all": "all", + "api": "API", "app": "app", + "app_name": "app name", "app_service_type": "app service type", + "apps_dashboard": "apps Dashboard", + "apps_official": "official Apps", "category": "category", + "category_other": "categories", "core": { "block_height": "block height", "information": "core information", "peers": "connected peers", "version": "core version" }, + "domain": "domain", "ui": { "version": "UI version" }, @@ -66,14 +86,18 @@ "one": "one" }, "description": "description", + "devmode_apps": "dev Mode Apps", + "directory": "directory", "downloading_qdn": "downloading from QDN", "fee": { "payment": "payment fee", "publish": "publish fee" }, "general_settings": "general settings", + "identifier": "identifier", "last_height": "last height", "level": "level", + "library": "library", "list": { "invite": "invite list", "join_request": "join request list", @@ -85,31 +109,41 @@ "message": { "error": { "address_not_found": "your address was not found", + "app_need_name": "your app needs a name", "file_too_large": "file {{ filename }} is too large. Max size allowed is {{ size }} MB.", "generic": "an error occurred", "incorrect_password": "incorrect password", + "invalid_zip": "invalid zip", "minting_account_add": "unable to add minting account", "minting_account_remove": "unable to remove minting account", "missing_fields": "missing: {{ fields }}", "publish_app": "unable to publish app", "rating_option": "cannot find rating option", "save_qdn": "unable to save to QDN", + "unable_encrypt_app": "unable to encrypt app. App not published'", + "unable_publish_app": "unable to publish app", "unable_rate": "unable to rate" }, "generic": { + "devmode_local_node": "please use your local node for dev mode! Logout and use Local node.", "name_available": "{{ name }} is available", "name_benefits": "benefits of a name", "name_checking": "checking if name already exists", + "name_preview": "you need a name to use preview", + "name_publish": "you need a Qortal name to publish", "name_rate": "you need a name to rate.", "name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee", "name_unavailable": "{{ name }} is unavailable", "no_description": "no description", "no_notifications": "no new notifications", + "no_results": "no results", "one_app_per_name": "note: Currently, only one App and Website is allowed per Name.", "publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!", "publishing": "publishing... Please wait.", "rating": "rating for {{ service }} {{ name }}", "secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party.", + "select_file": "please select a file", + "select_image": "please select an image for a logo", "select_zip": "select .zip file containing static content:" }, "question": { @@ -131,6 +165,7 @@ } }, "minting_status": "minting status", + "name": "name", "name_app": "name/App", "none": "none", "page": { @@ -140,10 +175,12 @@ "previous": "previous" }, "payment_notification": "payment notification", + "port": "port", "price": "price", "q_apps": { "about": "about this Q-App", - "q_mail": "q-mail" + "q_mail": "q-mail", + "q_sandbox": "q-Sandbox" }, "save_options": { "no_pinned_changes": "you currently do not have any changes to your pinned apps", @@ -162,6 +199,7 @@ "settings": "you are using the export/import way of saving settings.", "unsaved_changes": " you have unsaved changes to your pinned apps. Save them to QDN." }, + "server": "server", "settings": "settings", "supply": "supply", "tags": "tags", @@ -180,6 +218,7 @@ "title": "title", "tutorial": "tutorial", "user_lookup": "user lookup", + "zip": "zip", "wallet": { "wallet": "wallet", "wallet_other": "wallets" diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index 7a8cc23..009ab97 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -78,6 +78,7 @@ "no_selection": "no group selected", "not_part_group": "you are not part of the encrypted group of members. Wait until an admin re-encrypts the keys.", "only_encrypted": "only unencrypted messages will be displayed.", + "only_private_groups": "only private groups will be shown", "private_key_copied": "private key copied", "provide_message": "please provide a first message to the thread", "secure_place": "keep your private key in a secure place. Do not share!", From 7df70b21653fde95f42a511e82b1720e36104ab2 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 16 May 2025 09:13:44 +0200 Subject: [PATCH 23/47] Translate app --- src/components/Apps/AppViewer.tsx | 14 ++++++++++---- src/components/Apps/AppViewerContainer.tsx | 5 ++--- src/i18n/locales/en/core.json | 1 + 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/Apps/AppViewer.tsx b/src/components/Apps/AppViewer.tsx index 87f88e8..640d062 100644 --- a/src/components/Apps/AppViewer.tsx +++ b/src/components/Apps/AppViewer.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import { forwardRef, useEffect, useMemo, useState } from 'react'; import { Box } from '@mui/material'; import { getBaseApiReact } from '../../App'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; @@ -7,7 +7,7 @@ import { useQortalMessageListener } from './useQortalMessageListener'; import { useThemeContext } from '../Theme/ThemeContext'; import { useTranslation } from 'react-i18next'; -export const AppViewer = React.forwardRef( +export const AppViewer = forwardRef( ({ app, hide, isDevMode, skipAuth }, iframeRef) => { // const iframeRef = useRef(null); const { window: frameWindow } = useFrame(); @@ -23,7 +23,7 @@ export const AppViewer = React.forwardRef( ); const [url, setUrl] = useState(''); const { themeMode } = useThemeContext(); - const { i18n } = useTranslation(['core']); + const { i18n, t } = useTranslation(['core']); const currentLang = i18n.language; useEffect(() => { @@ -184,7 +184,13 @@ export const AppViewer = React.forwardRef( // Timeout after 200ms if no response setTimeout(() => { window.removeEventListener('message', handleNavigationSuccess); - reject(new Error('Navigation timeout')); + reject( + new Error( + t('core:message.error.navigation_timeout', { + postProcess: 'capitalizeFirst', + }) + ) + ); }, 200); const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index b159aa7..38c017b 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -1,9 +1,8 @@ -import React, { useContext } from 'react'; +import { forwardRef } from 'react'; import { AppViewer } from './AppViewer'; import Frame from 'react-frame-component'; -import { MyContext } from '../../App'; -const AppViewerContainer = React.forwardRef( +const AppViewerContainer = forwardRef( ({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => { return ( Date: Fri, 16 May 2025 09:23:23 +0200 Subject: [PATCH 24/47] Refactor json structure and move some from group into core --- src/components/Apps/AppPublish.tsx | 2 +- src/components/Apps/AppRating.tsx | 2 +- src/components/Apps/AppsPrivate.tsx | 2 +- src/components/Chat/GroupAnnouncements.tsx | 2 +- src/components/GlobalActions/JoinGroup.tsx | 2 +- src/components/Group/AddGroup.tsx | 2 +- src/components/Group/AddGroupList.tsx | 2 +- src/components/Group/Forum/NewThread.tsx | 4 ++-- src/components/Group/InviteMember.tsx | 2 +- src/components/Group/ListOfBans.tsx | 2 +- .../Group/ListOfGroupPromotions.tsx | 2 +- src/components/Group/ListOfInvites.tsx | 2 +- src/components/Group/ListOfJoinRequests.tsx | 2 +- src/components/Group/ListOfMembers.tsx | 8 +++---- src/components/Group/ManageMembers.tsx | 2 +- src/components/Group/UserListOfInvites.tsx | 2 +- src/components/Minting/Minting.tsx | 2 +- src/components/RegisterName.tsx | 2 +- src/components/Save/Save.tsx | 6 +++--- src/i18n/locales/en/core.json | 21 ++++++++++++------- src/i18n/locales/en/group.json | 5 ----- 21 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/components/Apps/AppPublish.tsx b/src/components/Apps/AppPublish.tsx index 6ab1525..c63cee8 100644 --- a/src/components/Apps/AppPublish.tsx +++ b/src/components/Apps/AppPublish.tsx @@ -178,7 +178,7 @@ export const AppPublish = ({ names, categories }) => { const fee = await getFee('ARBITRARY'); await show({ - message: t('core:save_options.publish_app', { + message: t('core:question.publish_app', { postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index 34bdee0..8ae7e99 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -113,7 +113,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { const fee = await getFee('CREATE_POLL'); await show({ - message: t('core:message.error.generic', { + message: t('core:question.rate_app', { rate: newValue, postProcess: 'capitalizeFirst', }), diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index fb0a4dd..d3af274 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -192,7 +192,7 @@ export const AppsPrivate = ({ myName }) => { const fee = await getFee('ARBITRARY'); await show({ - message: t('core:save_options.publish_app', { + message: t('core:question.publish_app', { postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index af92b71..abb4860 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -289,7 +289,7 @@ export const GroupAnnouncements = ({ const fee = await getFee('ARBITRARY'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'ARBITRARY', postProcess: 'capitalizeFirst', }), diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx index a6ec98f..fc77d85 100644 --- a/src/components/GlobalActions/JoinGroup.tsx +++ b/src/components/GlobalActions/JoinGroup.tsx @@ -70,7 +70,7 @@ export const JoinGroup = () => { const fee = await getFee('JOIN_GROUP'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'JOIN_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index b1da7e5..24ac2da 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -118,7 +118,7 @@ export const AddGroup = ({ address, open, setOpen }) => { const fee = await getFee('CREATE_GROUP'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'CREATE_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index 5c577f1..06113a4 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -113,7 +113,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const fee = await getFee('JOIN_GROUP'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'JOIN_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 4a561f5..802f4a4 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -182,7 +182,7 @@ export const NewThread = ({ const missingFields: string[] = []; if (!isMessage && !threadTitle) { - errorMsg = t('group:question.provide_thread', { + errorMsg = t('core:question.provide_thread', { postProcess: 'capitalizeFirst', }); } @@ -229,7 +229,7 @@ export const NewThread = ({ feeToShow = +feeToShow * 2; } await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'ARBITRARY', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index 94414ba..8d7bcf2 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -17,7 +17,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const fee = await getFee('GROUP_INVITE'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'GROUP_INVITE', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 596744d..650c418 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -88,7 +88,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { try { const fee = await getFee('CANCEL_GROUP_BAN'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'CANCEL_GROUP_BAN', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index ab92df4..4e4be16 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -276,7 +276,7 @@ export const ListOfGroupPromotions = () => { const groupId = group.groupId; const fee = await getFee('JOIN_GROUP'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'JOIN_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ListOfInvites.tsx b/src/components/Group/ListOfInvites.tsx index 64927a8..fc63464 100644 --- a/src/components/Group/ListOfInvites.tsx +++ b/src/components/Group/ListOfInvites.tsx @@ -94,7 +94,7 @@ export const ListOfInvites = ({ const fee = await getFee('CANCEL_GROUP_INVITE'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'CANCEL_GROUP_INVITE', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ListOfJoinRequests.tsx b/src/components/Group/ListOfJoinRequests.tsx index 85b3642..6d9c38a 100644 --- a/src/components/Group/ListOfJoinRequests.tsx +++ b/src/components/Group/ListOfJoinRequests.tsx @@ -97,7 +97,7 @@ export const ListOfJoinRequests = ({ const fee = await getFee('GROUP_INVITE'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'GROUP_INVITE', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ListOfMembers.tsx b/src/components/Group/ListOfMembers.tsx index b605758..bfdc11f 100644 --- a/src/components/Group/ListOfMembers.tsx +++ b/src/components/Group/ListOfMembers.tsx @@ -59,7 +59,7 @@ const ListOfMembers = ({ try { const fee = await getFee('GROUP_KICK'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'GROUP_KICK', postProcess: 'capitalizeFirst', }), @@ -117,7 +117,7 @@ const ListOfMembers = ({ const fee = await getFee('GROUP_BAN'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'GROUP_BAN', postProcess: 'capitalizeFirst', }), @@ -177,7 +177,7 @@ const ListOfMembers = ({ try { const fee = await getFee('ADD_GROUP_ADMIN'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'ADD_GROUP_ADMIN', postProcess: 'capitalizeFirst', }), @@ -234,7 +234,7 @@ const ListOfMembers = ({ try { const fee = await getFee('REMOVE_GROUP_ADMIN'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'REMOVE_GROUP_ADMIN', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index db2444a..d996184 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -84,7 +84,7 @@ export const ManageMembers = ({ setIsLoadingLeave(true); const fee = await getFee('LEAVE_GROUP'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'LEAVE_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/UserListOfInvites.tsx b/src/components/Group/UserListOfInvites.tsx index 38bc29d..fd22d58 100644 --- a/src/components/Group/UserListOfInvites.tsx +++ b/src/components/Group/UserListOfInvites.tsx @@ -99,7 +99,7 @@ export const UserListOfInvites = ({ const fee = await getFee('JOIN_GROUP'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { action: 'JOIN_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 0924656..9bffeb7 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -301,7 +301,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const createRewardShare = useCallback(async (publicKey, recipient) => { const fee = await getFee('REWARD_SHARE'); await show({ - message: t('group:question.perform_transaction', { + message: t('core:question.perform_transaction', { // TODO move from group into core namespace action: 'REWARD_SHARE', postProcess: 'capitalizeFirst', diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index c50f66a..85f4390 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -127,7 +127,7 @@ export const RegisterName = ({ const fee = await getFee('REGISTER_NAME'); await show({ - message: t('group:question.register_name', { + message: t('core:question.register_name', { postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index a75baeb..70faac8 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -424,7 +424,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - {t('core:save_options.reset_qdn', { + {t('core:question.reset_qdn', { postProcess: 'capitalizeFirst', })} @@ -461,7 +461,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { }} > {' '} - {t('core:save_options.reset_pinned', { + {t('core:question.reset_pinned', { postProcess: 'capitalizeFirst', })} @@ -495,7 +495,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - {t('core:save_options.overwrite_changes', { + {t('core:question.overwrite_changes', { postProcess: 'capitalizeFirst', })} diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 2bb7f90..424c8d0 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -183,22 +183,27 @@ "q_mail": "q-mail", "q_sandbox": "q-Sandbox" }, - "save_options": { - "no_pinned_changes": "you currently do not have any changes to your pinned apps", - "overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?", - "overwrite_qdn": "overwrite to QDN", + "question": { + "perform_transaction": "would you like to perform a {{action}} transaction?", + "provide_thread": "please provide a thread title", "publish_app": "would you like to publish this app?", "publish_qdn": "would you like to publish your settings to QDN (encrypted)?", - "qdn": "use QDN saving", + "overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?", "rate_app": "would you like to rate this app a rating of {{ rate }}?. It will create a POLL tx.", - "register_name": "you need a registered Qortal name to save your pinned apps to QDN.", + "register_name": "would you like to register this name?", "reset_pinned": "don't like your current local changes? Would you like to reset to the default pinned apps?", - "reset_qdn": "don't like your current local changes? Would you like to reset to your saved QDN pinned apps?", + "reset_qdn": "don't like your current local changes? Would you like to reset to your saved QDN pinned apps?" + }, + "save_options": { + "no_pinned_changes": "you currently do not have any changes to your pinned apps", + "overwrite_qdn": "overwrite to QDN", + "qdn": "use QDN saving", + "register_name": "you need a registered Qortal name to save your pinned apps to QDN.", "revert_default": "revert to default", "revert_qdn": "revert to QDN", "save_qdn": "save to QDN", "settings": "you are using the export/import way of saving settings.", - "unsaved_changes": " you have unsaved changes to your pinned apps. Save them to QDN." + "unsaved_changes": "you have unsaved changes to your pinned apps. Save them to QDN." }, "server": "server", "settings": "settings", diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index 009ab97..21cb914 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -133,10 +133,5 @@ "user_joined": "user successfully joined!" } }, - "question": { - "perform_transaction": "would you like to perform a {{action}} transaction?", - "provide_thread": "please provide a thread title", - "register_name": "would you like to register this name?" - }, "thread_posts": "new thread posts" } From 30b5d1e36bceaa0d90357455e33f2f2cdc2bbc37 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 16 May 2025 18:49:33 +0200 Subject: [PATCH 25/47] Add chat translations --- src/components/Chat/AdminSpace.tsx | 15 ++- src/components/Chat/AdminSpaceInner.tsx | 145 +++++++++++++++++------- src/i18n/locales/en/auth.json | 19 +++- src/i18n/locales/en/core.json | 5 +- src/i18n/locales/en/group.json | 1 + src/utils/decryptWallet.ts | 3 +- 6 files changed, 136 insertions(+), 52 deletions(-) diff --git a/src/components/Chat/AdminSpace.tsx b/src/components/Chat/AdminSpace.tsx index 49109aa..ca080d8 100644 --- a/src/components/Chat/AdminSpace.tsx +++ b/src/components/Chat/AdminSpace.tsx @@ -1,7 +1,7 @@ -import { useContext, useEffect, useState } from 'react'; -import { MyContext } from '../../App'; +import { useEffect, useState } from 'react'; import { Box, Typography } from '@mui/material'; import { AdminSpaceInner } from './AdminSpaceInner'; +import { useTranslation } from 'react-i18next'; export const AdminSpace = ({ selectedGroup, @@ -19,6 +19,8 @@ export const AdminSpace = ({ isOwner, }) => { const [isMoved, setIsMoved] = useState(false); + const { t } = useTranslation(['core', 'group']); + useEffect(() => { if (hide) { setTimeout(() => setIsMoved(true), 300); // Wait for the fade-out to complete before moving @@ -35,10 +37,10 @@ export const AdminSpace = ({ height: 'calc(100vh - 70px)', left: hide && '-1000px', opacity: hide ? 0 : 1, + overflow: 'auto', position: hide ? 'fixed' : 'relative', visibility: hide && 'hidden', width: '100%', - overflow: 'auto', }} > {!isAdmin && ( @@ -50,9 +52,14 @@ export const AdminSpace = ({ width: '100%', }} > - Sorry, this space is only for Admins. + + {t('core:message.generic.space_for_admins', { + postProcess: 'capitalizeFirst', + })} + )} + {isAdmin && ( { try { @@ -81,20 +84,24 @@ export const AdminSpaceInner = ({ selectedGroup ); if (getLatestPublish === false) return; - let data; const res = await fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${ getLatestPublish.name }/${getLatestPublish.identifier}?encoding=base64&rebuild=true` ); - data = await res.text(); + const data = await res.text(); const decryptedKey: any = await decryptResource(data); const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + if (!validateSecretKey(decryptedKeyToObject)) - throw new Error('SecretKey is not valid'); + throw new Error( + t('auth:message.error.invalid_secret_key', { + postProcess: 'capitalizeFirst', + }) + ); setAdminGroupSecretKey(decryptedKeyToObject); setAdminGroupSecretKeyPublishDetails(getLatestPublish); } catch (error) { @@ -125,7 +132,10 @@ export const AdminSpaceInner = ({ const fee = await getFee('ARBITRARY'); await show({ - message: 'Would you like to perform an ARBITRARY transaction?', + message: t('core:question.perform_transaction', { + action: 'ARBITRARY', + postProcess: 'capitalizeFirst', + }), publishFee: fee.fee + ' QORT', }); @@ -141,22 +151,31 @@ export const AdminSpaceInner = ({ if (!response?.error) { setInfoSnackCustom({ type: 'success', - message: - 'Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.', + message: t('auth:message.success.reencrypted_secret_key', { + postProcess: 'capitalizeFirst', + }), }); setOpenSnackGlobal(true); return; } setInfoSnackCustom({ type: 'error', - message: response?.error || 'unable to re-encrypt secret key', + message: + response?.error || + t('auth:message.error.unable_reencrypt_secret_key', { + postProcess: 'capitalizeFirst', + }), }); setOpenSnackGlobal(true); }) .catch((error) => { setInfoSnackCustom({ type: 'error', - message: error?.message || 'unable to re-encrypt secret key', + message: + error?.message || + t('auth:message.error.unable_reencrypt_secret_key', { + postProcess: 'capitalizeFirst', + }), }); setOpenSnackGlobal(true); }); @@ -184,10 +203,13 @@ export const AdminSpaceInner = ({ fontSize: '14px', }} > - Reminder: After publishing the key, it will take a couple of minutes for - it to appear. Please just wait. + {t('auth:message.error.publishing_key', { + postProcess: 'capitalizeFirst', + })} + + {isFetchingGroupSecretKey && ( - Fetching Group secret key publishes - )} - {!isFetchingGroupSecretKey && - groupSecretKeyPublishDetails === false && ( - No secret key published yet - )} - {groupSecretKeyPublishDetails && ( - Last encryption date:{' '} - {formatTimestampForum( - groupSecretKeyPublishDetails?.updated || - groupSecretKeyPublishDetails?.created - )}{' '} - {` by ${groupSecretKeyPublishDetails?.name}`} + {t('auth:message.generic.fetching_group_secret_key', { + postProcess: 'capitalizeFirst', + })} )} + + {!isFetchingGroupSecretKey && + groupSecretKeyPublishDetails === false && ( + + {t('auth:message.generic.no_secret_key_published', { + postProcess: 'capitalizeFirst', + })} + + )} + + {groupSecretKeyPublishDetails && ( + + {t('auth:message.generic.last_encryption_date', { + date: formatTimestampForum( + groupSecretKeyPublishDetails?.updated || + groupSecretKeyPublishDetails?.created + ), + name: groupSecretKeyPublishDetails?.name, + postProcess: 'capitalizeFirst', + })} + + )} + @@ -232,9 +269,9 @@ export const AdminSpaceInner = ({ fontSize: '14px', }} > - This key is to encrypt GROUP related content. This is the only one - used in this UI as of now. All group members will be able to see - content encrypted with this key. + {t('auth:tips.key_encrypt_group', { + postProcess: 'capitalizeFirst', + })} @@ -253,26 +290,41 @@ export const AdminSpaceInner = ({ }} > {isFetchingAdminGroupSecretKey && ( - Fetching Admins secret key - )} - {!isFetchingAdminGroupSecretKey && !adminGroupSecretKey && ( - No secret key published yet - )} - {adminGroupSecretKeyPublishDetails && ( - Last encryption date:{' '} - {formatTimestampForum( - adminGroupSecretKeyPublishDetails?.updated || - adminGroupSecretKeyPublishDetails?.created - )} + {t('auth:message.generic.fetching_admin_secret_key', { + postProcess: 'capitalizeFirst', + })} )} + + {!isFetchingAdminGroupSecretKey && !adminGroupSecretKey && ( + + {t('auth:message.generic.no_secret_key_published', { + postProcess: 'capitalizeFirst', + })} + + )} + + {adminGroupSecretKeyPublishDetails && ( + + {t('auth:message.generic.last_encryption_date', { + date: formatTimestampForum( + adminGroupSecretKeyPublishDetails?.updated || + adminGroupSecretKeyPublishDetails?.created + ), + postProcess: 'capitalizeFirst', + })} + + )} + @@ -282,11 +334,14 @@ export const AdminSpaceInner = ({ fontSize: '14px', }} > - This key is to encrypt ADMIN related content. Only admins would see - content encrypted with it. + {t('auth:tips.key_encrypt_admin', { + postProcess: 'capitalizeFirst', + })} + + {isOwner && ( - Group Avatar + + {t('group:group.avatar', { + postProcess: 'capitalizeFirst', + })} + { const threads = doInitWorkers(crypto.kdfThreads); @@ -18,7 +19,7 @@ export const decryptStoredWallet = async (password, wallet) => { .process(encryptedSeedBytes) .finish().result; if (Base58.encode(mac) !== wallet.mac) { - throw new Error('Incorrect password'); + throw new Error(i18n.t('auth:message.error.incorrect_password')); // TODO: i18n non-react integration } const decryptedBytes = AES_CBC.decrypt( encryptedSeedBytes, From 075999489107c52a289808052722346dfd1105c2 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 16 May 2025 19:03:08 +0200 Subject: [PATCH 26/47] Add translation for chat section --- .../Chat/AnnouncementDiscussion.tsx | 49 ++++++++++++------- src/components/Chat/AnnouncementItem.tsx | 19 ++++--- src/components/Chat/AnnouncementList.tsx | 23 +++------ src/i18n/locales/en/core.json | 4 ++ 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index e4c2538..3715215 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import TipTap from './TipTap'; import { AuthenticatedContainerInnerTop, @@ -8,7 +8,7 @@ import { Box, CircularProgress, useTheme } from '@mui/material'; import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import ShortUniqueId from 'short-unique-id'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; -import { getBaseApi, getFee } from '../../background'; +import { getFee } from '../../background'; import { decryptPublishes, getTempPublish, @@ -24,6 +24,7 @@ import { pauseAllQueues, resumeAllQueues, } from '../../App'; +import { useTranslation } from 'react-i18next'; const tempKey = 'accouncement-comment'; @@ -39,10 +40,10 @@ export const AnnouncementDiscussion = ({ isPrivate, }) => { const theme = useTheme(); + const { t } = useTranslation(['auth', 'core', 'group']); const [isSending, setIsSending] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isFocusedParent, setIsFocusedParent] = useState(false); - const [comments, setComments] = useState([]); const [tempPublishedList, setTempPublishedList] = useState([]); const firstMountRef = useRef(false); @@ -100,7 +101,12 @@ export const AnnouncementDiscussion = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -131,9 +137,13 @@ export const AnnouncementDiscussion = ({ pauseAllQueues(); const fee = await getFee('ARBITRARY'); await show({ - message: 'Would you like to perform a ARBITRARY transaction?', + message: t('core:question.perform_transaction', { + action: 'ARBITRARY', + postProcess: 'capitalizeFirst', + }), publishFee: fee.fee + ' QORT', }); + if (isSending) return; if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); @@ -155,10 +165,11 @@ export const AnnouncementDiscussion = ({ : await encryptChatMessage(message64, secretKeyObject); const randomUid = uid.rnd(); const identifier = `cm-${selectedAnnouncement.identifier}-${randomUid}`; + const res = await publishAnc({ encryptedData: encryptSingle, identifier, - }); + }); // TODO remove unused? const dataToSaveToStorage = { name: myName, @@ -168,12 +179,13 @@ export const AnnouncementDiscussion = ({ created: Date.now(), announcementId: selectedAnnouncement.identifier, }; + await saveTempPublish({ data: dataToSaveToStorage, key: tempKey }); setTempData(); clearEditorContent(); } - // send chat message + // TODO send chat message } catch (error) { console.error(error); } finally { @@ -182,14 +194,12 @@ export const AnnouncementDiscussion = ({ } }; - const getComments = React.useCallback( + const getComments = useCallback( async (selectedAnnouncement, isPrivate) => { try { setIsLoading(true); const offset = 0; - - // dispatch(setIsLoadingGlobal(true)) const identifier = `cm-${selectedAnnouncement.identifier}`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; const response = await fetch(url, { @@ -209,8 +219,6 @@ export const AnnouncementDiscussion = ({ console.log(error); } finally { setIsLoading(false); - - // dispatch(setIsLoadingGlobal(false)) } }, [secretKey] @@ -259,7 +267,7 @@ export const AnnouncementDiscussion = ({ return sortedList; }, [tempPublishedList, comments]); - React.useEffect(() => { + useEffect(() => { if (!secretKey && isPrivate) return; if (selectedAnnouncement && !firstMountRef.current && isPrivate !== null) { getComments(selectedAnnouncement, isPrivate); @@ -299,6 +307,7 @@ export const AnnouncementDiscussion = ({ + +
+ - {` Close`} + {t('core:action.close', { postProcess: 'capitalizeFirst' })} )} )} - {` Publish Comment`} + {t('core:action.publish_comment', { + postProcess: 'capitalizeFirst', + })} @@ -410,7 +423,9 @@ export const AnnouncementDiscussion = ({ diff --git a/src/components/Chat/AnnouncementItem.tsx b/src/components/Chat/AnnouncementItem.tsx index e6ad671..e8d8ced 100644 --- a/src/components/Chat/AnnouncementItem.tsx +++ b/src/components/Chat/AnnouncementItem.tsx @@ -1,14 +1,14 @@ -import React, { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { MessageDisplay } from './MessageDisplay'; import { Avatar, Box, Typography, useTheme } from '@mui/material'; import { formatTimestamp } from '../../utils/time'; import ChatBubbleIcon from '@mui/icons-material/ChatBubble'; import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; -import { getBaseApi } from '../../background'; import { requestQueueCommentCount } from './GroupAnnouncements'; import { CustomLoader } from '../../common/CustomLoader'; import { getArbitraryEndpointReact, getBaseApiReact } from '../../App'; import { WrapperUserAction } from '../WrapperUserAction'; +import { useTranslation } from 'react-i18next'; export const AnnouncementItem = ({ message, @@ -18,12 +18,12 @@ export const AnnouncementItem = ({ myName, }) => { const theme = useTheme(); + const { t } = useTranslation(['auth', 'core', 'group']); const [commentLength, setCommentLength] = useState(0); - const getNumberOfComments = React.useCallback(async () => { + + const getNumberOfComments = useCallback(async () => { try { const offset = 0; - - // dispatch(setIsLoadingGlobal(true)) const identifier = `cm-${message.identifier}`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=0&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; @@ -84,6 +84,7 @@ export const AnnouncementItem = ({ {message?.name?.charAt(0)} + + {!messageData?.decryptedData && ( )} + {messageData?.decryptedData?.message && ( <> {messageData?.type === 'notification' ? ( @@ -150,6 +153,7 @@ export const AnnouncementItem = ({
+ {!disableComment && ( - Leave comment + {t('core:action.leave_comment', { + postProcess: 'capitalizeFirst', + })} )} + { - const listRef = useRef(null); const [messages, setMessages] = useState(initialMessages); + const { t } = useTranslation(['auth', 'core', 'group']); useEffect(() => { cache.clearAll(); @@ -36,9 +37,9 @@ export const AnnouncementList = ({ flexDirection: 'column', flexGrow: 1, flexShrink: 1, + overflow: 'auto', position: 'relative', width: '100%', - overflow: 'auto', }} > {messages.map((message) => { @@ -69,19 +70,7 @@ export const AnnouncementList = ({ ); })} - {/* - {({ height, width }) => ( - - )} - */} + {showLoadMore && ( - Load older announcements + {t('core:action.load_announcements', { + postProcess: 'capitalizeFirst', + })} )} diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 57b37ed..bf92f0c 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -31,6 +31,8 @@ "import": "import", "invite": "invite", "join": "join", + "leave_comment": "leave comment", + "load_announcements": "load older announcements", "login": "login", "logout": "logout", "new": { @@ -46,6 +48,7 @@ "post_message": "post message", "publish": "publish", "publish_app": "publish your app", + "publish_comment": "publish comment", "register_name": "register name", "remove": "remove", "return_apps_dashboard": "return to Apps Dashboard", @@ -104,6 +107,7 @@ "member": "member list" }, "loading": "loading...", + "loading_comments": "loading comments... please wait.", "loading_posts": "loading posts... please wait.", "message_us": "please message us on Telegram or Discord if you need 4 QORT to start chatting without any limitations", "message": { From 4f35730db66fffefb2f79b3bd50c418ed6a5bf0a Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 16 May 2025 19:23:06 +0200 Subject: [PATCH 27/47] Refactor loading translations --- .../Chat/AnnouncementDiscussion.tsx | 2 +- src/components/Chat/ChatDirect.tsx | 75 ++++++++++++------- src/components/Group/Forum/Thread.tsx | 2 +- src/components/Group/ThingsToDoInitial.tsx | 2 +- src/i18n/locales/en/core.json | 12 ++- src/i18n/locales/en/group.json | 1 + 6 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index 3715215..3ce480c 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -423,7 +423,7 @@ export const AnnouncementDiscussion = ({ { const theme = useTheme(); + const { t } = useTranslation(['auth', 'core', 'group']); const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); const [isFocusedParent, setIsFocusedParent] = useState(false); const [onEditMessage, setOnEditMessage] = useState(null); - const [messages, setMessages] = useState([]); const [isSending, setIsSending] = useState(false); const [directToValue, setDirectToValue] = useState(''); const hasInitialized = useRef(false); const [isLoading, setIsLoading] = useState(false); - const [openSnack, setOpenSnack] = React.useState(false); - const [infoSnack, setInfoSnack] = React.useState(null); - const [publicKeyOfRecipient, setPublicKeyOfRecipient] = React.useState(''); + const [openSnack, setOpenSnack] = useState(false); + const [infoSnack, setInfoSnack] = useState(null); + const [publicKeyOfRecipient, setPublicKeyOfRecipient] = useState(''); const hasInitializedWebsocket = useRef(false); const [chatReferences, setChatReferences] = useState({}); - const editorRef = useRef(null); const socketRef = useRef(null); const timeoutIdRef = useRef(null); @@ -74,12 +65,8 @@ export const ChatDirect = ({ const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; - const [, forceUpdate] = useReducer((x) => x + 1, 0); - - const triggerRerender = () => { - forceUpdate(); // Trigger re-render by updating the state - }; const publicKeyOfRecipientRef = useRef(null); + const getPublicKeyFunc = async (address) => { try { const publicKey = await getPublicKey(address); @@ -229,7 +216,12 @@ export const ChatDirect = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -397,11 +389,20 @@ export const ChatDirect = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { - throw new Error(error); + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error(String(error)); + } } }; const clearEditorContent = () => { @@ -432,8 +433,14 @@ export const ChatDirect = ({ try { if (messageSize > 4000) return; + // TODO set magic number in a proper file if (+balance < 4) - throw new Error('You need at least 4 QORT to send a message'); + throw new Error( + t('group:message.error.qortals_required', { + quantity: 4, + postProcess: 'capitalizeFirst', + }) + ); if (isSending) return; if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); @@ -500,7 +507,10 @@ export const ChatDirect = ({ type: 'error', message: errorMsg === 'invalid signature' - ? 'You need at least 4 QORT to send a message' + ? t('group:message.error.qortals_required', { + quantity: 4, + postProcess: 'capitalizeFirst', + }) : errorMsg, }); setOpenSnack(true); @@ -566,13 +576,14 @@ export const ChatDirect = ({ fontSize: '14px', }} > - Close Direct Chat + {t('core:action.close_chat', { postProcess: 'capitalizeFirst' })} {isNewChat && ( <> + - 4000 ? theme.palette.other.danger : 'unset', }} - >{`Your message size is of ${messageSize} bytes out of a maximum of 4000`} + > + {t('core:message.error.message_size', { + maximum: 4000, + size: messageSize, + postProcess: 'capitalizeFirst', + })} + )} @@ -746,7 +763,7 @@ export const ChatDirect = ({ diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 447200e..3ab2862 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -1073,7 +1073,7 @@ export const Thread = ({ diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index f85099e..b57b9da 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -76,7 +76,7 @@ export const ThingsToDoInitial = ({ }} > {!isLoaded - ? t('core:loading', { postProcess: 'capitalizeFirst' }) + ? t('core:loading.generic', { postProcess: 'capitalizeFirst' }) : t('tutorial:initial.getting_started', { postProcess: 'capitalizeFirst', })} diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index bf92f0c..71afe1f 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -14,6 +14,7 @@ "choose": "choose", "choose_file": "choose file", "close": "close", + "close_chat": "close Direct Chat", "continue": "continue", "continue_logout": "continue to logout", "copy_link": "copy link", @@ -106,9 +107,12 @@ "join_request": "join request list", "member": "member list" }, - "loading": "loading...", - "loading_comments": "loading comments... please wait.", - "loading_posts": "loading posts... please wait.", + "loading": { + "generic": "loading...", + "chat": "loading chat... please wait.", + "comments": "loading comments... please wait.", + "posts": "loading posts... please wait." + }, "message_us": "please message us on Telegram or Discord if you need 4 QORT to start chatting without any limitations", "message": { "error": { @@ -116,7 +120,9 @@ "app_need_name": "your app needs a name", "file_too_large": "file {{ filename }} is too large. Max size allowed is {{ size }} MB.", "generic": "an error occurred", + "invalid_signature": "invalid signature", "invalid_zip": "invalid zip", + "message_size": "your message size is of {{ size }} bytes out of a maximum of {{ maximum }}", "minting_account_add": "unable to add minting account", "minting_account_remove": "unable to remove minting account", "missing_fields": "missing: {{ fields }}", diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index 3526791..e3b52ad 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -94,6 +94,7 @@ "group_secret_key": "cannot get group secret key", "name_required": "please provide a name", "notify_admins": "try notifying an admin from the list of admins below:", + "qortals_required": "you need at least {{ quantity }} QORT to send a message", "timeout_reward": "timeout waiting for reward share confirmation", "thread_id": "unable to locate thread Id", "unable_minting": "unable to start minting" From cb336133c66893b483024582fd700ae74f8d5a71 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 16 May 2025 19:48:07 +0200 Subject: [PATCH 28/47] Translation for chat pages --- src/components/Chat/ChatGroup.tsx | 114 +++++++++++++++------ src/components/Chat/ChatList.tsx | 5 +- src/components/Chat/ChatOptions.tsx | 1 + src/components/Chat/CreateCommonSecret.tsx | 8 +- src/components/Chat/GroupAnnouncements.tsx | 15 +-- src/components/Chat/GroupAvatar.tsx | 12 +++ src/components/Chat/MessageDisplay.tsx | 3 +- src/components/Chat/MessageItem.tsx | 7 +- src/components/Chat/TipTap.tsx | 4 +- src/i18n/locales/en/group.json | 3 + 10 files changed, 120 insertions(+), 52 deletions(-) diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index a4f6e5a..e7df961 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -1,4 +1,4 @@ -import React, { +import { useCallback, useContext, useEffect, @@ -50,6 +50,8 @@ import CloseIcon from '@mui/icons-material/Close'; import { throttle } from 'lodash'; import ImageIcon from '@mui/icons-material/Image'; import { messageHasImage } from '../../utils/chat'; +import { useTranslation } from 'react-i18next'; + const uid = new ShortUniqueId({ length: 5 }); const uidImages = new ShortUniqueId({ length: 12 }); @@ -75,8 +77,8 @@ export const ChatGroup = ({ const [isSending, setIsSending] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isMoved, setIsMoved] = useState(false); - const [openSnack, setOpenSnack] = React.useState(false); - const [infoSnack, setInfoSnack] = React.useState(null); + const [openSnack, setOpenSnack] = useState(false); + const [infoSnack, setInfoSnack] = useState(null); const hasInitialized = useRef(false); const [isFocusedParent, setIsFocusedParent] = useState(false); const [replyMessage, setReplyMessage] = useState(null); @@ -93,8 +95,8 @@ export const ChatGroup = ({ const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); const [, forceUpdate] = useReducer((x) => x + 1, 0); const lastReadTimestamp = useRef(null); - const handleUpdateRef = useRef(null); + const { t } = useTranslation(['auth', 'core', 'group']); const getTimestampEnterChat = async (selectedGroup) => { try { @@ -129,7 +131,12 @@ export const ChatGroup = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -154,9 +161,6 @@ export const ChatGroup = ({ return Array.from(uniqueMembers); }, [messages]); - const triggerRerender = () => { - forceUpdate(); // Trigger re-render by updating the state - }; const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; @@ -168,6 +172,7 @@ export const ChatGroup = ({ } return []; }, [selectedGroup, queueChats]); + const tempChatReferences = useMemo(() => { if (!selectedGroup) return []; if (queueChats[selectedGroup]) { @@ -184,12 +189,6 @@ export const ChatGroup = ({ } }, [secretKey]); - // const getEncryptedSecretKey = useCallback(()=> { - // const response = getResource() - // const decryptResponse = decryptResource() - // return - // }, []) - const checkForFirstSecretKeyNotification = (messages) => { messages?.forEach((message) => { try { @@ -250,7 +249,6 @@ export const ChatGroup = ({ const dataRemovedBlock = responseData?.filter((item) => { return !isUserBlocked(item?.sender, item?.senderName); }); - decryptMessages(dataRemovedBlock, false); } catch (error) { console.error(error); @@ -273,6 +271,7 @@ export const ChatGroup = ({ const filterUIMessages = encryptedMessages.filter( (item) => !isExtMsg(item.data) ); + const decodedUIMessages = decodeBase64ForUIChatMessages(filterUIMessages); @@ -280,6 +279,7 @@ export const ChatGroup = ({ ...decodedUIMessages, ...response, ]; + const combineUIAndExtensionMsgs = processWithNewMessages( combineUIAndExtensionMsgsBefore.map((item) => ({ ...item, @@ -287,16 +287,24 @@ export const ChatGroup = ({ })), selectedGroup ); + res(combineUIAndExtensionMsgs); if (isInitiated) { const formatted = combineUIAndExtensionMsgs .filter((rawItem) => !rawItem?.chatReference) .map((item) => { + const message = ( +

+ {t('group:message.generic.group_key_created', { + postProcess: 'capitalizeFirst', + })} +

+ ); const additionalFields = - item?.data === 'NDAwMQ==' + item?.data === 'NDAwMQ==' // TODO put magic string somewhere in a file ? { - text: '

First group key created.

', + text: message, } : {}; return { @@ -362,7 +370,9 @@ export const ChatGroup = ({ !newTimestamp ) { console.warn( - 'Invalid content, sender, or timestamp in reaction data', + t('group:message.generic.invalid_content', { + postProcess: 'capitalizeFirst', + }), item ); return; @@ -435,10 +445,17 @@ export const ChatGroup = ({ const formatted = combineUIAndExtensionMsgs .filter((rawItem) => !rawItem?.chatReference) .map((item) => { + const message = ( +

+ {t('group:message.generic.group_key_created', { + postProcess: 'capitalizeFirst', + })} +

+ ); const additionalFields = item?.data === 'NDAwMQ==' ? { - text: '

First group key created.

', + text: message, } : {}; const divide = @@ -510,7 +527,9 @@ export const ChatGroup = ({ !newTimestamp ) { console.warn( - 'Invalid content, sender, or timestamp in reaction data', + t('group:message.generic.invalid_content', { + postProcess: 'capitalizeFirst', + }), item ); return; @@ -583,7 +602,12 @@ export const ChatGroup = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -615,6 +639,7 @@ export const ChatGroup = ({ console.error('Error during ping:', error); } }; + const initWebsocketMessageGroup = () => { let socketLink = `${getBaseApiReactSocket()}/websockets/chat/messages?txGroupId=${selectedGroup}&encoding=BASE64&limit=100`; socketRef.current = new WebSocket(socketLink); @@ -709,7 +734,12 @@ export const ChatGroup = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -744,7 +774,12 @@ export const ChatGroup = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -760,12 +795,22 @@ export const ChatGroup = ({ const sendMessage = async () => { try { - if (messageSize > 4000) return; + if (messageSize > 4000) return; // TODO magic number if (isPrivate === null) - throw new Error('Unable to determine if group is private'); + throw new Error( + t('group:message.error.unable_determine_group_private', { + postProcess: 'capitalizeFirst', + }) + ); if (isSending) return; if (+balance < 4) - throw new Error('You need at least 4 QORT to send a message'); + // TODO magic number + throw new Error( + t('group:message.error.qortals_required', { + quantity: 4, + postProcess: 'capitalizeFirst', + }) + ); pauseAllQueues(); if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); @@ -792,13 +837,16 @@ export const ChatGroup = ({ const imagesToPublish = []; const deleteImage = onEditMessage && isDeleteImage && messageHasImage(onEditMessage); + if (deleteImage) { const fee = await getFee('ARBITRARY'); - + // TODO translate await show({ publishFee: fee.fee + ' QORT', message: 'Would you like to delete your previous chat image?', }); + + // TODO magic string await window.sendMessage('publishOnQDN', { data: 'RA==', identifier: onEditMessage?.images[0]?.identifier, @@ -811,6 +859,7 @@ export const ChatGroup = ({ const base64ToSave = isPrivate ? await encryptChatMessage(imageToSave, secretKeyObject) : imageToSave; + // 1 represents public group, 0 is private const identifier = `grp-q-manager_${isPrivate ? 0 : 1}_group_${selectedGroup}_${uidImages.rnd()}`; imagesToPublish.push({ @@ -822,7 +871,6 @@ export const ChatGroup = ({ const res = await window.sendMessage( 'PUBLISH_MULTIPLE_QDN_RESOURCES', - { resources: imagesToPublish, }, @@ -979,6 +1027,7 @@ export const ChatGroup = ({ .setContent(message?.messageText || message?.text) .run(); }, []); + const handleReaction = useCallback( async (reaction, chatMessage, reactionState = true) => { try { @@ -1033,12 +1082,6 @@ export const ChatGroup = ({ chatReference: chatMessage.signature, }; addToQueue(sendMessageFunc, messageObj, 'chat-reaction', selectedGroup); - // setTimeout(() => { - // executeEvent("sent-new-message-group", {}) - // }, 150); - // clearEditorContent() - // setReplyMessage(null) - // send chat message } catch (error) { const errorMsg = error?.message || error; @@ -1417,6 +1460,7 @@ export const ChatGroup = ({ }} > Q-Manager + { setIsOpenQManager(false); @@ -1429,7 +1473,9 @@ export const ChatGroup = ({ /> + +
+ {showScrollButton && ( + {avatarFile?.name} + + {!myName && ( + { } } } catch (error) { - //error + console.log(error); } + const res = extractComponents(url); if (res) { const { service, name, identifier, path } = res; diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index a29e417..7b72d79 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -1,4 +1,5 @@ -import React, { +import { + memo, useCallback, useContext, useEffect, @@ -77,7 +78,7 @@ const getBadgeImg = (level) => { } }; -const UserBadge = React.memo(({ userInfo }) => { +const UserBadge = memo(({ userInfo }) => { return ( { ); }); -export const MessageItem = React.memo( +export const MessageItem = memo( ({ message, onSeen, diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 5890736..94894ab 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef } from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { EditorProvider, useCurrentEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import { Color } from '@tiptap/extension-color'; @@ -42,7 +42,7 @@ function textMatcher(doc, from) { return { start, query }; } -const MenuBar = React.memo( +const MenuBar = memo( ({ setEditorRef, isChat, diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index e3b52ad..78292c4 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -61,6 +61,8 @@ "descrypt_wallet": "decrypting wallet...", "encryption_key": "the group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes...", "group_invited_you": "{{group}} has invited you", + "group_key_created": "first group key created.", + "invalid_content": "invalid content, sender, or timestamp in reaction data", "invalid_data": "error loading content: Invalid Data", "latest_promotion": "only the latest promotion from the week will be shown for your group.", "loading_members": "loading member list with names... please wait.", @@ -97,6 +99,7 @@ "qortals_required": "you need at least {{ quantity }} QORT to send a message", "timeout_reward": "timeout waiting for reward share confirmation", "thread_id": "unable to locate thread Id", + "unable_determine_group_private": "unable to determine if group is private", "unable_minting": "unable to start minting" }, "success": { From 55f8ea9843f1abe6f521806b26ad4698e6d6f45b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 16 May 2025 19:52:06 +0200 Subject: [PATCH 29/47] Add todo --- src/App.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App.tsx b/src/App.tsx index e5f9cc0..c5455a6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1029,6 +1029,7 @@ function App() { const logoutFunc = useCallback(async () => { try { if (extState === 'authenticated') { + // TODO translate await showUnsavedChanges({ message: 'Are you sure you would like to logout?', }); From b0274b4cb7e8f879965338e3f26017fbd965e32d Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 16 May 2025 23:54:59 +0200 Subject: [PATCH 30/47] Organize code (prettier) --- src/background-cases.ts | 1737 +++++++++-------- src/constants/constants.ts | 319 +-- src/transactions/AddGroupAdminTransaction.ts | 54 +- src/transactions/BuyNameTransacion.ts | 75 +- src/transactions/CancelGroupBanTransaction.ts | 54 +- .../CancelGroupInviteTransaction.ts | 55 +- src/transactions/CancelSellNameTransacion.ts | 48 +- src/transactions/ChatBase.ts | 265 +-- src/transactions/ChatTransaction.ts | 182 +- src/transactions/CreateGroupTransaction.ts | 113 +- src/transactions/CreatePollTransaction.ts | 127 +- src/transactions/DeployAtTransaction.ts | 142 +- src/transactions/GroupBanTransaction.ts | 86 +- src/transactions/GroupChatTransaction.ts | 121 +- src/transactions/GroupInviteTransaction.ts | 70 +- src/transactions/GroupKickTransaction.ts | 78 +- src/transactions/JoinGroupTransaction.ts | 55 +- src/transactions/LeaveGroupTransaction.ts | 53 +- src/transactions/PaymentTransaction.ts | 56 +- src/transactions/RegisterNameTransaction.ts | 70 +- .../RemoveGroupAdminTransaction.ts | 56 +- .../RemoveRewardShareTransaction.ts | 78 +- src/transactions/RewardShareTransaction.ts | 122 +- src/transactions/SellNameTransacion.ts | 64 +- src/transactions/TradeBotCreateRequest.ts | 92 +- src/transactions/TradeBotDeleteRequest.ts | 44 +- .../TradeBotRespondMultipleRequest.ts | 51 +- src/transactions/TradeBotRespondRequest.ts | 52 +- src/transactions/TransactionBase.ts | 309 +-- src/transactions/TransferAssetTransaction.ts | 26 +- src/transactions/UpdateGroupTransaction.ts | 116 +- src/transactions/UpdateNameTransaction.ts | 89 +- src/transactions/VoteOnPollTransaction.ts | 66 +- src/transactions/signChat.ts | 53 +- src/transactions/signTradeBotTransaction.ts | 45 +- src/transactions/transactions.ts | 119 +- src/utils/decode.ts | 1 - src/utils/generateWallet/generateWallet.ts | 208 +- src/utils/generateWallet/storeWallet.ts | 58 +- src/utils/generateWallet/verb-past-tense.ts | 62 +- src/utils/qortalLink/index.ts | 18 +- .../seedPhrase/RandomSentenceGenerator.ts | 263 +-- src/utils/seedPhrase/verb-past-tense.ts | 62 +- src/utils/validateAddress.ts | 1 + 44 files changed, 3020 insertions(+), 2795 deletions(-) diff --git a/src/background-cases.ts b/src/background-cases.ts index d4ab82a..831c032 100644 --- a/src/background-cases.ts +++ b/src/background-cases.ts @@ -52,26 +52,31 @@ import { sendChatGroup, sendChatNotification, sendCoin, - setChatHeads, setGroupData, updateThreadActivity, walletVersion, -} from "./background"; -import { decryptGroupEncryption, encryptAndPublishSymmetricKeyGroupChat, encryptAndPublishSymmetricKeyGroupChatForAdmins, publishGroupEncryptedResource, publishOnQDN } from "./backgroundFunctions/encryption"; -import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from "./constants/codes"; -import Base58 from "./deps/Base58"; -import { encryptSingle } from "./qdn/encryption/group-encryption"; -import { _createPoll, _voteOnPoll } from "./qortalRequests/get"; -import { createTransaction } from "./transactions/transactions"; -import { getData, storeData } from "./utils/chromeStorage"; +} from './background'; +import { + decryptGroupEncryption, + encryptAndPublishSymmetricKeyGroupChat, + encryptAndPublishSymmetricKeyGroupChatForAdmins, + publishGroupEncryptedResource, + publishOnQDN, +} from './backgroundFunctions/encryption'; +import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from './constants/codes'; +import Base58 from './deps/Base58'; +import { encryptSingle } from './qdn/encryption/group-encryption'; +import { _createPoll, _voteOnPoll } from './qortalRequests/get'; +import { createTransaction } from './transactions/transactions'; +import { getData, storeData } from './utils/chromeStorage'; export function versionCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "version", - payload: { version: "1.0" }, - type: "backgroundMessageResponse", + action: 'version', + payload: { version: '1.0' }, + type: 'backgroundMessageResponse', }, event.origin ); @@ -82,14 +87,14 @@ export async function getWalletInfoCase(request, event) { const response = await getKeyPair(); try { - const walletInfo = await getData('walletInfo').catch((error)=> null) - if(walletInfo){ + const walletInfo = await getData('walletInfo').catch((error) => null); + if (walletInfo) { event.source.postMessage( { requestId: request.requestId, - action: "getWalletInfo", + action: 'getWalletInfo', payload: { walletInfo, hasKeyPair: true }, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -97,36 +102,34 @@ export async function getWalletInfoCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getWalletInfo", - error: "No wallet info found", - type: "backgroundMessageResponse", + action: 'getWalletInfo', + error: 'No wallet info found', // TODO translate + type: 'backgroundMessageResponse', }, event.origin ); } - } catch (error) { event.source.postMessage( { requestId: request.requestId, - action: "getWalletInfo", - error: "No wallet info found", - type: "backgroundMessageResponse", + action: 'getWalletInfo', + error: 'No wallet info found', + type: 'backgroundMessageResponse', }, event.origin ); } - } catch (error) { try { - const walletInfo = await getData('walletInfo').catch((error)=> null) - if(walletInfo){ + const walletInfo = await getData('walletInfo').catch((error) => null); + if (walletInfo) { event.source.postMessage( { requestId: request.requestId, - action: "getWalletInfo", + action: 'getWalletInfo', payload: { walletInfo, hasKeyPair: false }, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -134,9 +137,9 @@ export async function getWalletInfoCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getWalletInfo", - error: "Wallet not authenticated", - type: "backgroundMessageResponse", + action: 'getWalletInfo', + error: 'Wallet not authenticated', + type: 'backgroundMessageResponse', }, event.origin ); @@ -145,14 +148,13 @@ export async function getWalletInfoCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getWalletInfo", - error: "Wallet not authenticated", - type: "backgroundMessageResponse", + action: 'getWalletInfo', + error: 'Wallet not authenticated', + type: 'backgroundMessageResponse', }, event.origin ); } - } } @@ -163,9 +165,9 @@ export async function validApiCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "validApi", + action: 'validApi', payload: usableApi, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -173,9 +175,9 @@ export async function validApiCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "validApi", + action: 'validApi', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -189,9 +191,9 @@ export async function nameCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "name", + action: 'name', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -199,9 +201,9 @@ export async function nameCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "name", + action: 'name', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -215,9 +217,9 @@ export async function userInfoCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "userInfo", + action: 'userInfo', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -225,9 +227,9 @@ export async function userInfoCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "userInfo", - error: "User not authenticated", - type: "backgroundMessageResponse", + action: 'userInfo', + error: 'User not authenticated', + type: 'backgroundMessageResponse', }, event.origin ); @@ -235,15 +237,19 @@ export async function userInfoCase(request, event) { } export async function decryptWalletCase(request, event) { - try { + try { const { password, wallet } = request.payload; - const response = await decryptWallet({password, wallet, walletVersion: wallet?.version || walletVersion}); + const response = await decryptWallet({ + password, + wallet, + walletVersion: wallet?.version || walletVersion, + }); event.source.postMessage( { requestId: request.requestId, - action: "decryptWallet", + action: 'decryptWallet', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -251,9 +257,9 @@ export async function decryptWalletCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "decryptWallet", + action: 'decryptWallet', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -267,9 +273,9 @@ export async function balanceCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "balance", + action: 'balance', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -277,9 +283,9 @@ export async function balanceCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "balance", + action: 'balance', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -292,9 +298,9 @@ export async function ltcBalanceCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "ltcBalance", + action: 'ltcBalance', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -302,9 +308,9 @@ export async function ltcBalanceCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "ltcBalance", + action: 'ltcBalance', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -319,9 +325,9 @@ export async function sendCoinCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "sendCoin", + action: 'sendCoin', error: res?.data?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -330,9 +336,9 @@ export async function sendCoinCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "sendCoin", + action: 'sendCoin', payload: true, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -340,9 +346,9 @@ export async function sendCoinCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "sendCoin", + action: 'sendCoin', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -361,9 +367,9 @@ export async function inviteToGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "inviteToGroup", + action: 'inviteToGroup', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -371,9 +377,9 @@ export async function inviteToGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "inviteToGroup", + action: 'inviteToGroup', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -388,9 +394,9 @@ export async function saveTempPublishCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "saveTempPublish", + action: 'saveTempPublish', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -398,9 +404,9 @@ export async function saveTempPublishCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "saveTempPublish", + action: 'saveTempPublish', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -414,9 +420,9 @@ export async function getTempPublishCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getTempPublish", + action: 'getTempPublish', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -424,9 +430,9 @@ export async function getTempPublishCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getTempPublish", + action: 'getTempPublish', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -455,9 +461,9 @@ export async function createGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "createGroup", + action: 'createGroup', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -465,9 +471,9 @@ export async function createGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "createGroup", + action: 'createGroup', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -482,9 +488,9 @@ export async function cancelInvitationToGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "cancelInvitationToGroup", + action: 'cancelInvitationToGroup', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -492,9 +498,9 @@ export async function cancelInvitationToGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "cancelInvitationToGroup", + action: 'cancelInvitationToGroup', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -509,9 +515,9 @@ export async function leaveGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "leaveGroup", + action: 'leaveGroup', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -519,9 +525,9 @@ export async function leaveGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "leaveGroup", + action: 'leaveGroup', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -536,9 +542,9 @@ export async function joinGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "joinGroup", + action: 'joinGroup', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -546,9 +552,9 @@ export async function joinGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "joinGroup", + action: 'joinGroup', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -567,9 +573,9 @@ export async function kickFromGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "kickFromGroup", + action: 'kickFromGroup', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -577,9 +583,9 @@ export async function kickFromGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "kickFromGroup", + action: 'kickFromGroup', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -599,9 +605,9 @@ export async function banFromGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "banFromGroup", + action: 'banFromGroup', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -609,9 +615,9 @@ export async function banFromGroupCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "banFromGroup", + action: 'banFromGroup', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -621,14 +627,14 @@ export async function banFromGroupCase(request, event) { export async function addDataPublishesCase(request, event) { try { const { data, groupId, type } = request.payload; - const response = await addDataPublishes( data, groupId, type ); + const response = await addDataPublishes(data, groupId, type); event.source.postMessage( { requestId: request.requestId, - action: "addDataPublishes", + action: 'addDataPublishes', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -636,9 +642,9 @@ export async function addDataPublishesCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "addDataPublishes", + action: 'addDataPublishes', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -648,14 +654,14 @@ export async function addDataPublishesCase(request, event) { export async function getDataPublishesCase(request, event) { try { const { groupId, type } = request.payload; - const response = await getDataPublishes(groupId, type ); + const response = await getDataPublishes(groupId, type); event.source.postMessage( { requestId: request.requestId, - action: "getDataPublishes", + action: 'getDataPublishes', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -663,9 +669,9 @@ export async function getDataPublishesCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getDataPublishes", + action: 'getDataPublishes', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -679,9 +685,9 @@ export async function addUserSettingsCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "addUserSettings", + action: 'addUserSettings', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -689,9 +695,9 @@ export async function addUserSettingsCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "addUserSettings", + action: 'addUserSettings', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -706,9 +712,9 @@ export async function getUserSettingsCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getUserSettings", + action: 'getUserSettings', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -716,9 +722,9 @@ export async function getUserSettingsCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getUserSettings", + action: 'getUserSettings', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -733,9 +739,9 @@ export async function cancelBanCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "cancelBan", + action: 'cancelBan', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -743,9 +749,9 @@ export async function cancelBanCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "cancelBan", + action: 'cancelBan', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -760,9 +766,9 @@ export async function registerNameCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "registerName", + action: 'registerName', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -770,9 +776,9 @@ export async function registerNameCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "registerName", + action: 'registerName', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -788,15 +794,15 @@ export async function createPollCase(request, event) { options: pollOptions, }, true, - true // skip permission + true // skip permission ); event.source.postMessage( { requestId: request.requestId, - action: "registerName", + action: 'registerName', payload: resCreatePoll, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -804,9 +810,9 @@ export async function createPollCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "registerName", + action: 'registerName', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -816,13 +822,12 @@ export async function voteOnPollCase(request, event) { try { const res = await _voteOnPoll(request.payload, true, true); - event.source.postMessage( { requestId: request.requestId, - action: "registerName", + action: 'registerName', payload: res, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -830,9 +835,9 @@ export async function voteOnPollCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "registerName", + action: 'registerName', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -847,9 +852,9 @@ export async function makeAdminCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "makeAdmin", + action: 'makeAdmin', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -857,9 +862,9 @@ export async function makeAdminCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "makeAdmin", + action: 'makeAdmin', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -874,9 +879,9 @@ export async function removeAdminCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "removeAdmin", + action: 'removeAdmin', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -884,9 +889,9 @@ export async function removeAdminCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "removeAdmin", + action: 'removeAdmin', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -895,14 +900,14 @@ export async function removeAdminCase(request, event) { export async function notificationCase(request, event) { try { - const notificationId = "chat_notification_" + Date.now(); // Create a unique ID + const notificationId = 'chat_notification_' + Date.now(); // Create a unique ID // chrome.notifications.create(notificationId, { // type: "basic", // iconUrl: "qort.png", // Add an appropriate icon for chat notifications // title: "New Group Message!", // message: "You have received a new message from one of your groups", - // priority: 2, // Use the maximum priority to ensure it's + // priority: 2, // Use the maximum priority to ensure it's // }); // Set a timeout to clear the notification after 'timeout' milliseconds // setTimeout(() => { @@ -922,9 +927,9 @@ export async function notificationCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "notification", - error: "Error displaying notifaction", - type: "backgroundMessageResponse", + action: 'notification', + error: 'Error displaying notifaction', + type: 'backgroundMessageResponse', }, event.origin ); @@ -939,9 +944,9 @@ export async function addTimestampEnterChatCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "addTimestampEnterChat", + action: 'addTimestampEnterChat', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -949,9 +954,9 @@ export async function addTimestampEnterChatCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "addTimestampEnterChat", + action: 'addTimestampEnterChat', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -961,13 +966,13 @@ export async function addTimestampEnterChatCase(request, event) { export async function setApiKeyCase(request, event) { try { const payload = request.payload; - storeData('apiKey', payload) + storeData('apiKey', payload); event.source.postMessage( { requestId: request.requestId, - action: "setApiKey", + action: 'setApiKey', payload: true, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -975,9 +980,9 @@ export async function setApiKeyCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "setApiKey", + action: 'setApiKey', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -986,14 +991,14 @@ export async function setApiKeyCase(request, event) { export async function setCustomNodesCase(request, event) { try { const nodes = request.payload; - storeData('customNodes', nodes) + storeData('customNodes', nodes); event.source.postMessage( { requestId: request.requestId, - action: "setCustomNodes", + action: 'setCustomNodes', payload: true, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1001,9 +1006,9 @@ export async function setCustomNodesCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "setCustomNodes", + action: 'setCustomNodes', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1017,9 +1022,9 @@ export async function getApiKeyCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getApiKey", + action: 'getApiKey', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1027,9 +1032,9 @@ export async function getApiKeyCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getApiKey", + action: 'getApiKey', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1043,9 +1048,9 @@ export async function getCustomNodesFromStorageCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getCustomNodesFromStorage", + action: 'getCustomNodesFromStorage', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1053,9 +1058,9 @@ export async function getCustomNodesFromStorageCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getCustomNodesFromStorage", + action: 'getCustomNodesFromStorage', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1073,9 +1078,9 @@ export async function notifyAdminRegenerateSecretKeyCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "notifyAdminRegenerateSecretKey", + action: 'notifyAdminRegenerateSecretKey', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1083,9 +1088,9 @@ export async function notifyAdminRegenerateSecretKeyCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "notifyAdminRegenerateSecretKey", + action: 'notifyAdminRegenerateSecretKey', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1098,15 +1103,15 @@ export async function addGroupNotificationTimestampCase(request, event) { const response = await addTimestampGroupAnnouncement({ groupId, timestamp, - seenTimestamp: true + seenTimestamp: true, }); event.source.postMessage( { requestId: request.requestId, - action: "addGroupNotificationTimestamp", + action: 'addGroupNotificationTimestamp', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1114,9 +1119,9 @@ export async function addGroupNotificationTimestampCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "addGroupNotificationTimestamp", + action: 'addGroupNotificationTimestamp', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1129,9 +1134,9 @@ export async function addEnteredQmailTimestampCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "addEnteredQmailTimestamp", + action: 'addEnteredQmailTimestamp', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1139,9 +1144,9 @@ export async function addEnteredQmailTimestampCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "addEnteredQmailTimestamp", + action: 'addEnteredQmailTimestamp', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1154,9 +1159,9 @@ export async function getEnteredQmailTimestampCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getEnteredQmailTimestamp", - payload: {timestamp: response}, - type: "backgroundMessageResponse", + action: 'getEnteredQmailTimestamp', + payload: { timestamp: response }, + type: 'backgroundMessageResponse', }, event.origin ); @@ -1164,9 +1169,9 @@ export async function getEnteredQmailTimestampCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getEnteredQmailTimestamp", + action: 'getEnteredQmailTimestamp', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1180,9 +1185,9 @@ export async function clearAllNotificationsCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "clearAllNotifications", + action: 'clearAllNotifications', payload: true, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1190,9 +1195,9 @@ export async function clearAllNotificationsCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "clearAllNotifications", + action: 'clearAllNotifications', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1213,9 +1218,9 @@ export async function setGroupDataCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "setGroupData", + action: 'setGroupData', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1223,9 +1228,9 @@ export async function setGroupDataCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "setGroupData", + action: 'setGroupData', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1240,9 +1245,9 @@ export async function getGroupDataSingleCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getGroupDataSingle", + action: 'getGroupDataSingle', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1250,9 +1255,9 @@ export async function getGroupDataSingleCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getGroupDataSingle", + action: 'getGroupDataSingle', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1266,9 +1271,9 @@ export async function getTimestampEnterChatCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getTimestampEnterChat", + action: 'getTimestampEnterChat', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1276,9 +1281,9 @@ export async function getTimestampEnterChatCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getTimestampEnterChat", + action: 'getTimestampEnterChat', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1288,66 +1293,66 @@ export async function getTimestampEnterChatCase(request, event) { export async function listActionsCase(request, event) { try { const { type, listName = '', items = [] } = request.payload; - let responseData + let responseData; - if(type === 'get'){ + if (type === 'get') { const url = await createEndpoint(`/lists/${listName}`); - const response = await fetch(url); - if (!response.ok) throw new Error("Failed to fetch"); - - responseData = await response.json(); - } else if(type === 'remove'){ - const url = await createEndpoint(`/lists/${listName}`); - const body = { - items: items , - }; - const bodyToString = JSON.stringify(body); - const response = await fetch(url, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - body: bodyToString, - }); + const response = await fetch(url); + if (!response.ok) throw new Error('Failed to fetch'); - if (!response.ok) throw new Error("Failed to remove from list"); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - responseData = res; - } else if(type === 'add'){ - const url = await createEndpoint(`/lists/${listName}`); - const body = { - items: items , - }; - const bodyToString = JSON.stringify(body); - const response = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: bodyToString, - }); - - if (!response.ok) throw new Error("Failed to add to list"); - let res; - try { - res = await response.clone().json(); - } catch (e) { - res = await response.text(); - } - responseData = res; - } + responseData = await response.json(); + } else if (type === 'remove') { + const url = await createEndpoint(`/lists/${listName}`); + const body = { + items: items, + }; + const bodyToString = JSON.stringify(body); + const response = await fetch(url, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + + if (!response.ok) throw new Error('Failed to remove from list'); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + responseData = res; + } else if (type === 'add') { + const url = await createEndpoint(`/lists/${listName}`); + const body = { + items: items, + }; + const bodyToString = JSON.stringify(body); + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: bodyToString, + }); + + if (!response.ok) throw new Error('Failed to add to list'); + let res; + try { + res = await response.clone().json(); + } catch (e) { + res = await response.text(); + } + responseData = res; + } event.source.postMessage( { requestId: request.requestId, - action: "listActions", + action: 'listActions', payload: responseData, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1355,9 +1360,9 @@ export async function listActionsCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "listActions", + action: 'listActions', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1371,9 +1376,9 @@ export async function getTimestampMentionCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getTimestampMention", + action: 'getTimestampMention', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1381,9 +1386,9 @@ export async function getTimestampMentionCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getTimestampMention", + action: 'getTimestampMention', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1398,9 +1403,9 @@ export async function addTimestampMentionCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "addTimestampMention", + action: 'addTimestampMention', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1408,9 +1413,9 @@ export async function addTimestampMentionCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "addTimestampMention", + action: 'addTimestampMention', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1424,9 +1429,9 @@ export async function getGroupNotificationTimestampCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getGroupNotificationTimestamp", + action: 'getGroupNotificationTimestamp', payload: response, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1434,9 +1439,9 @@ export async function getGroupNotificationTimestampCase(request, event) { event.source.postMessage( { requestId: request.requestId, - action: "getGroupNotificationTimestamp", + action: 'getGroupNotificationTimestamp', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1459,24 +1464,24 @@ export async function encryptAndPublishSymmetricKeyGroupChatCase( event.source.postMessage( { requestId: request.requestId, - action: "encryptAndPublishSymmetricKeyGroupChat", + action: 'encryptAndPublishSymmetricKeyGroupChat', payload: data, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); if (!previousData) { - try { - sendChatGroup({ - groupId, - typeMessage: undefined, - chatReference: undefined, - messageText: PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY, - }); - } catch (error) { - // error in sending chat message + try { + sendChatGroup({ + groupId, + typeMessage: undefined, + chatReference: undefined, + messageText: PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY, + }); + } catch (error) { + // error in sending chat message + } } - } try { sendChatNotification(data, groupId, previousData, numberOfMembers); } catch (error) { @@ -1486,9 +1491,9 @@ export async function encryptAndPublishSymmetricKeyGroupChatCase( event.source.postMessage( { requestId: request.requestId, - action: "encryptAndPublishSymmetricKeyGroupChat", + action: 'encryptAndPublishSymmetricKeyGroupChat', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1505,15 +1510,15 @@ export async function encryptAndPublishSymmetricKeyGroupChatForAdminsCase( await encryptAndPublishSymmetricKeyGroupChatForAdmins({ groupId, previousData, - admins + admins, }); event.source.postMessage( { requestId: request.requestId, - action: "encryptAndPublishSymmetricKeyGroupChatForAdmins", + action: 'encryptAndPublishSymmetricKeyGroupChatForAdmins', payload: data, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1521,9 +1526,9 @@ export async function encryptAndPublishSymmetricKeyGroupChatForAdminsCase( event.source.postMessage( { requestId: request.requestId, - action: "encryptAndPublishSymmetricKeyGroupChat", + action: 'encryptAndPublishSymmetricKeyGroupChat', error: error?.message, - type: "backgroundMessageResponse", + type: 'backgroundMessageResponse', }, event.origin ); @@ -1531,588 +1536,632 @@ export async function encryptAndPublishSymmetricKeyGroupChatForAdminsCase( } export async function publishGroupEncryptedResourceCase(request, event) { - try { - const {encryptedData, identifier} = request.payload; - const response = await publishGroupEncryptedResource({encryptedData, identifier}); - - event.source.postMessage( - { - requestId: request.requestId, - action: "publishGroupEncryptedResource", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "publishGroupEncryptedResource", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } - } + try { + const { encryptedData, identifier } = request.payload; + const response = await publishGroupEncryptedResource({ + encryptedData, + identifier, + }); - export async function publishOnQDNCase(request, event) { - try { - const {data, identifier, service, title, - description, - category, - tag1, - tag2, - tag3, - tag4, - tag5, uploadType} = request.payload; - const response = await publishOnQDN({data, identifier, service, title, - description, - category, - tag1, - tag2, - tag3, - tag4, - tag5, uploadType}); - - event.source.postMessage( - { - requestId: request.requestId, - action: "publishOnQDN", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "publishOnQDN", - error: error?.message || 'Unable to publish', - type: "backgroundMessageResponse", - }, - event.origin - ); - } + event.source.postMessage( + { + requestId: request.requestId, + action: 'publishGroupEncryptedResource', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'publishGroupEncryptedResource', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function handleActiveGroupDataFromSocketCase(request, event) { - try { - const {groups, directs} = request.payload; - const response = await handleActiveGroupDataFromSocket({groups, directs}); - - event.source.postMessage( - { - requestId: request.requestId, - action: "handleActiveGroupDataFromSocket", - payload: true, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "handleActiveGroupDataFromSocket", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +export async function publishOnQDNCase(request, event) { + try { + const { + data, + identifier, + service, + title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, + uploadType, + } = request.payload; + const response = await publishOnQDN({ + data, + identifier, + service, + title, + description, + category, + tag1, + tag2, + tag3, + tag4, + tag5, + uploadType, + }); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'publishOnQDN', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'publishOnQDN', + error: error?.message || 'Unable to publish', + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function getThreadActivityCase(request, event) { - try { - const response = await checkThreads(true) - - event.source.postMessage( - { - requestId: request.requestId, - action: "getThreadActivity", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "getThreadActivity", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +export async function handleActiveGroupDataFromSocketCase(request, event) { + try { + const { groups, directs } = request.payload; + const response = await handleActiveGroupDataFromSocket({ groups, directs }); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'handleActiveGroupDataFromSocket', + payload: true, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'handleActiveGroupDataFromSocket', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function updateThreadActivityCase(request, event) { - try { - const { threadId, qortalName, groupId, thread} = request.payload; - const response = await updateThreadActivity({ threadId, qortalName, groupId, thread }); - - event.source.postMessage( - { - requestId: request.requestId, - action: "updateThreadActivity", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "updateThreadActivity", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +export async function getThreadActivityCase(request, event) { + try { + const response = await checkThreads(true); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'getThreadActivity', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'getThreadActivity', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function decryptGroupEncryptionCase(request, event) { - try { - const { data} = request.payload; - const response = await decryptGroupEncryption({ data }); - event.source.postMessage( - { - requestId: request.requestId, - action: "decryptGroupEncryption", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "decryptGroupEncryption", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +export async function updateThreadActivityCase(request, event) { + try { + const { threadId, qortalName, groupId, thread } = request.payload; + const response = await updateThreadActivity({ + threadId, + qortalName, + groupId, + thread, + }); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'updateThreadActivity', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'updateThreadActivity', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function encryptSingleCase(request, event) { - try { - const { data, secretKeyObject, typeNumber} = request.payload; - const response = await encryptSingle({ data64: data, secretKeyObject, typeNumber }); - - event.source.postMessage( - { - requestId: request.requestId, - action: "encryptSingle", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "encryptSingle", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +export async function decryptGroupEncryptionCase(request, event) { + try { + const { data } = request.payload; + const response = await decryptGroupEncryption({ data }); + event.source.postMessage( + { + requestId: request.requestId, + action: 'decryptGroupEncryption', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'decryptGroupEncryption', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function decryptSingleCase(request, event) { - try { - const { data, secretKeyObject, skipDecodeBase64} = request.payload; - const response = await decryptSingleFunc({ messages: data, secretKeyObject, skipDecodeBase64 }); - event.source.postMessage( - { - requestId: request.requestId, - action: "decryptSingle", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "decryptSingle", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +export async function encryptSingleCase(request, event) { + try { + const { data, secretKeyObject, typeNumber } = request.payload; + const response = await encryptSingle({ + data64: data, + secretKeyObject, + typeNumber, + }); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'encryptSingle', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'encryptSingle', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function pauseAllQueuesCase(request, event) { - try { - await pauseAllQueues(); - - event.source.postMessage( - { - requestId: request.requestId, - action: "pauseAllQueues", - payload: true, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "pauseAllQueues", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +export async function decryptSingleCase(request, event) { + try { + const { data, secretKeyObject, skipDecodeBase64 } = request.payload; + const response = await decryptSingleFunc({ + messages: data, + secretKeyObject, + skipDecodeBase64, + }); + event.source.postMessage( + { + requestId: request.requestId, + action: 'decryptSingle', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'decryptSingle', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function resumeAllQueuesCase(request, event) { - try { - await resumeAllQueues(); - - event.source.postMessage( - { - requestId: request.requestId, - action: "resumeAllQueues", - payload: true, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "resumeAllQueues", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +export async function pauseAllQueuesCase(request, event) { + try { + await pauseAllQueues(); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'pauseAllQueues', + payload: true, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'pauseAllQueues', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } - export async function checkLocalCase(request, event) { - try { - const response = await checkLocalFunc() - event.source.postMessage( - { - requestId: request.requestId, - action: "pauseAllQueues", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "checkLocal", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +} + +export async function resumeAllQueuesCase(request, event) { + try { + await resumeAllQueues(); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'resumeAllQueues', + payload: true, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'resumeAllQueues', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } - - export async function decryptSingleForPublishesCase(request, event) { - try { - const { data, secretKeyObject, skipDecodeBase64} = request.payload; - const response = await decryptSingleForPublishes({ messages: data, secretKeyObject, skipDecodeBase64 }); - - event.source.postMessage( - { - requestId: request.requestId, - action: "decryptSingleForPublishes", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "decryptSingle", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +} +export async function checkLocalCase(request, event) { + try { + const response = await checkLocalFunc(); + event.source.postMessage( + { + requestId: request.requestId, + action: 'pauseAllQueues', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'checkLocal', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function decryptDirectCase(request, event) { - try { - const { data, involvingAddress} = request.payload; - const response = await decryptDirectFunc({ messages: data, involvingAddress }); - - event.source.postMessage( - { - requestId: request.requestId, - action: "decryptDirect", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "decryptDirect", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +export async function decryptSingleForPublishesCase(request, event) { + try { + const { data, secretKeyObject, skipDecodeBase64 } = request.payload; + const response = await decryptSingleForPublishes({ + messages: data, + secretKeyObject, + skipDecodeBase64, + }); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'decryptSingleForPublishes', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'decryptSingle', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } - export async function sendChatGroupCase(request, event) { - try { - const { groupId, - typeMessage = undefined, - chatReference = undefined, - messageText} = request.payload; - const response = await sendChatGroup({ groupId, typeMessage, chatReference, messageText }); - - event.source.postMessage( - { - requestId: request.requestId, - action: "sendChatGroup", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "sendChatGroup", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +} + +export async function decryptDirectCase(request, event) { + try { + const { data, involvingAddress } = request.payload; + const response = await decryptDirectFunc({ + messages: data, + involvingAddress, + }); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'decryptDirect', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'decryptDirect', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } - export async function sendChatDirectCase(request, event) { - try { - const { directTo, - typeMessage = undefined, - chatReference = undefined, - messageText, - publicKeyOfRecipient, - address, - otherData} = request.payload; - const response = await sendChatDirect({ directTo, - chatReference, - messageText, - typeMessage, - publicKeyOfRecipient, - address, - otherData }); - - event.source.postMessage( - { - requestId: request.requestId, - action: "sendChatDirect", - payload: response, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "sendChatDirect", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +} +export async function sendChatGroupCase(request, event) { + try { + const { + groupId, + typeMessage = undefined, + chatReference = undefined, + messageText, + } = request.payload; + const response = await sendChatGroup({ + groupId, + typeMessage, + chatReference, + messageText, + }); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'sendChatGroup', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'sendChatGroup', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} +export async function sendChatDirectCase(request, event) { + try { + const { + directTo, + typeMessage = undefined, + chatReference = undefined, + messageText, + publicKeyOfRecipient, + address, + otherData, + } = request.payload; + const response = await sendChatDirect({ + directTo, + chatReference, + messageText, + typeMessage, + publicKeyOfRecipient, + address, + otherData, + }); - export async function setupGroupWebsocketCase(request, event) { - try { - - checkNewMessages(); - checkThreads(); - event.source.postMessage( - { - requestId: request.requestId, - action: "sendChatDirect", - payload: true, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "sendChatDirect", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } + event.source.postMessage( + { + requestId: request.requestId, + action: 'sendChatDirect', + payload: response, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'sendChatDirect', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function createRewardShareCase(request, event) { - try { - const {recipientPublicKey} = request.payload; - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - let lastRef = await getLastRef(); - - const tx = await createTransaction(38, keyPair, { - recipientPublicKey, - percentageShare: 0, - lastReference: lastRef, - }); - - const signedBytes = Base58.encode(tx.signedBytes); - - const res = await processTransactionVersion2(signedBytes); - if (!res?.signature) - throw new Error("Transaction was not able to be processed"); - event.source.postMessage( - { - requestId: request.requestId, - action: "createRewardShare", - payload: res, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "createRewardShare", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } +export async function setupGroupWebsocketCase(request, event) { + try { + checkNewMessages(); + checkThreads(); + event.source.postMessage( + { + requestId: request.requestId, + action: 'sendChatDirect', + payload: true, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'sendChatDirect', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function removeRewardShareCase(request, event) { - try { - const {rewardShareKeyPairPublicKey, recipient, percentageShare} = request.payload; - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - let lastRef = await getLastRef(); - - const tx = await createTransaction(381, keyPair, { - rewardShareKeyPairPublicKey, - recipient, - percentageShare, - lastReference: lastRef, - }); +export async function createRewardShareCase(request, event) { + try { + const { recipientPublicKey } = request.payload; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + let lastRef = await getLastRef(); - const signedBytes = Base58.encode(tx.signedBytes); - - const res = await processTransactionVersion2(signedBytes); - if (!res?.signature) - throw new Error("Transaction was not able to be processed"); - event.source.postMessage( - { - requestId: request.requestId, - action: "removeRewardShare", - payload: res, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "removeRewardShare", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } + const tx = await createTransaction(38, keyPair, { + recipientPublicKey, + percentageShare: 0, + lastReference: lastRef, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error('Transaction was not able to be processed'); + event.source.postMessage( + { + requestId: request.requestId, + action: 'createRewardShare', + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'createRewardShare', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - export async function getRewardSharePrivateKeyCase(request, event) { - try { - const {recipientPublicKey} = request.payload; - const resKeyPair = await getKeyPair(); - const parsedData = resKeyPair; - const uint8PrivateKey = Base58.decode(parsedData.privateKey); - const uint8PublicKey = Base58.decode(parsedData.publicKey); - const keyPair = { - privateKey: uint8PrivateKey, - publicKey: uint8PublicKey, - }; - let lastRef = await getLastRef(); - - const tx = await createTransaction(38, keyPair, { - recipientPublicKey, - percentageShare: 0, - lastReference: lastRef, - }); +export async function removeRewardShareCase(request, event) { + try { + const { rewardShareKeyPairPublicKey, recipient, percentageShare } = + request.payload; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + let lastRef = await getLastRef(); - event.source.postMessage( - { - requestId: request.requestId, - action: "getRewardSharePrivateKey", - payload: tx?._base58RewardShareSeed, - type: "backgroundMessageResponse", - }, - event.origin - ); - } catch (error) { - event.source.postMessage( - { - requestId: request.requestId, - action: "getRewardSharePrivateKey", - error: error?.message, - type: "backgroundMessageResponse", - }, - event.origin - ); - } + const tx = await createTransaction(381, keyPair, { + rewardShareKeyPairPublicKey, + recipient, + percentageShare, + lastReference: lastRef, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error('Transaction was not able to be processed'); + event.source.postMessage( + { + requestId: request.requestId, + action: 'removeRewardShare', + payload: res, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'removeRewardShare', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); } +} - \ No newline at end of file +export async function getRewardSharePrivateKeyCase(request, event) { + try { + const { recipientPublicKey } = request.payload; + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + let lastRef = await getLastRef(); + + const tx = await createTransaction(38, keyPair, { + recipientPublicKey, + percentageShare: 0, + lastReference: lastRef, + }); + + event.source.postMessage( + { + requestId: request.requestId, + action: 'getRewardSharePrivateKey', + payload: tx?._base58RewardShareSeed, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: 'getRewardSharePrivateKey', + error: error?.message, + type: 'backgroundMessageResponse', + }, + event.origin + ); + } +} diff --git a/src/constants/constants.ts b/src/constants/constants.ts index 5b5e427..ee96c68 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -1,175 +1,186 @@ - - // Qortal TX types const TX_TYPES = { - 1: "Genesis", - 2: "Payment", - 3: "Name registration", - 4: "Name update", - 5: "Sell name", - 6: "Cancel sell name", - 7: "Buy name", - 8: "Create poll", - 9: "Vote in poll", - 10: "Arbitrary", - 11: "Issue asset", - 12: "Transfer asset", - 13: "Create asset order", - 14: "Cancel asset order", - 15: "Multi-payment transaction", - 16: "Deploy AT", - 17: "Message", - 18: "Chat", - 19: "Publicize", - 20: "Airdrop", - 21: "AT", - 22: "Create group", - 23: "Update group", - 24: "Add group admin", - 25: "Remove group admin", - 26: "Group ban", - 27: "Cancel group ban", - 28: "Group kick", - 29: "Group invite", - 30: "Cancel group invite", - 31: "Join group", - 32: "Leave group", - 33: "Group approval", - 34: "Set group", - 35: "Update asset", - 36: "Account flags", - 37: "Enable forging", - 38: "Reward share", - 39: "Account level", - 40: "Transfer privs", - 41: "Presence" -} + 1: 'Genesis', + 2: 'Payment', + 3: 'Name registration', + 4: 'Name update', + 5: 'Sell name', + 6: 'Cancel sell name', + 7: 'Buy name', + 8: 'Create poll', + 9: 'Vote in poll', + 10: 'Arbitrary', + 11: 'Issue asset', + 12: 'Transfer asset', + 13: 'Create asset order', + 14: 'Cancel asset order', + 15: 'Multi-payment transaction', + 16: 'Deploy AT', + 17: 'Message', + 18: 'Chat', + 19: 'Publicize', + 20: 'Airdrop', + 21: 'AT', + 22: 'Create group', + 23: 'Update group', + 24: 'Add group admin', + 25: 'Remove group admin', + 26: 'Group ban', + 27: 'Cancel group ban', + 28: 'Group kick', + 29: 'Group invite', + 30: 'Cancel group invite', + 31: 'Join group', + 32: 'Leave group', + 33: 'Group approval', + 34: 'Set group', + 35: 'Update asset', + 36: 'Account flags', + 37: 'Enable forging', + 38: 'Reward share', + 39: 'Account level', + 40: 'Transfer privs', + 41: 'Presence', +}; // Qortal error codes const ERROR_CODES = { - 1: "Valid OK", - 2: "Invalid address", - 3: "Negative amount", - 4: "Nagative fee", - 5: "No balance", - 6: "Invalid reference", - 7: "Invalid time length", - 8: "Invalid value length", - 9: "Name already registered", - 10: "Name does not exist", - 11: "Invalid name owner", - 12: "Name already for sale", - 13: "Name not for sale", - 14: "Name buyer already owner", - 15: "Invalid amount", - 16: "Invalid seller", - 17: "Name not lowercase", - 18: "Invalid description length", - 19: "Invalid options length", - 20: "Invalid option length", - 21: "Duplicate option", - 22: "Poll already created", - 23: "Poll already has votes", - 24: "Poll does not exist", - 25: "Option does not exist", - 26: "Already voted for that option", - 27: "Invalid data length", - 28: "Invalid quantity", - 29: "Asset does not exist", - 30: "Invalid return", - 31: "Have equals want", - 32: "Order does not exist", - 33: "Invalid order creator", - 34: "Invalid payments length", - 35: "Negative price", - 36: "Invalid creation bytes", - 37: "Invalid tags length", - 38: "Invalid type length", - 39: "Invalid AT transaction", - 40: "Insufficient fee", - 41: "Asset does not match AT", + 1: 'Valid OK', + 2: 'Invalid address', + 3: 'Negative amount', + 4: 'Nagative fee', + 5: 'No balance', + 6: 'Invalid reference', + 7: 'Invalid time length', + 8: 'Invalid value length', + 9: 'Name already registered', + 10: 'Name does not exist', + 11: 'Invalid name owner', + 12: 'Name already for sale', + 13: 'Name not for sale', + 14: 'Name buyer already owner', + 15: 'Invalid amount', + 16: 'Invalid seller', + 17: 'Name not lowercase', + 18: 'Invalid description length', + 19: 'Invalid options length', + 20: 'Invalid option length', + 21: 'Duplicate option', + 22: 'Poll already created', + 23: 'Poll already has votes', + 24: 'Poll does not exist', + 25: 'Option does not exist', + 26: 'Already voted for that option', + 27: 'Invalid data length', + 28: 'Invalid quantity', + 29: 'Asset does not exist', + 30: 'Invalid return', + 31: 'Have equals want', + 32: 'Order does not exist', + 33: 'Invalid order creator', + 34: 'Invalid payments length', + 35: 'Negative price', + 36: 'Invalid creation bytes', + 37: 'Invalid tags length', + 38: 'Invalid type length', + 39: 'Invalid AT transaction', + 40: 'Insufficient fee', + 41: 'Asset does not match AT', - 43: "Asset already exists", - 44: "Missing creator", - 45: "Timestamp too old", - 46: "Timestamp too new", - 47: "Too many unconfirmed", - 48: "Group already exists", - 49: "Group does not exist", - 50: "Invalid group owner", - 51: "Already group memeber", - 52: "Group owner can not leave", - 53: "Not group member", - 54: "Already group admin", - 55: "Not group admin", - 56: "Invalid lifetime", - 57: "Invite unknown", - 58: "Ban exists", - 59: "Ban unknown", - 60: "Banned from group", - 61: "Join request", - 62: "Invalid group approval threshold", - 63: "Group ID mismatch", - 64: "Invalid group ID", - 65: "Transaction unknown", - 66: "Transaction already confirmed", - 67: "Invalid TX group", - 68: "TX group ID mismatch", - 69: "Multiple names forbidden", - 70: "Invalid asset owner", - 71: "AT is finished", - 72: "No flag permission", - 73: "Not minting accout", + 43: 'Asset already exists', + 44: 'Missing creator', + 45: 'Timestamp too old', + 46: 'Timestamp too new', + 47: 'Too many unconfirmed', + 48: 'Group already exists', + 49: 'Group does not exist', + 50: 'Invalid group owner', + 51: 'Already group memeber', + 52: 'Group owner can not leave', + 53: 'Not group member', + 54: 'Already group admin', + 55: 'Not group admin', + 56: 'Invalid lifetime', + 57: 'Invite unknown', + 58: 'Ban exists', + 59: 'Ban unknown', + 60: 'Banned from group', + 61: 'Join request', + 62: 'Invalid group approval threshold', + 63: 'Group ID mismatch', + 64: 'Invalid group ID', + 65: 'Transaction unknown', + 66: 'Transaction already confirmed', + 67: 'Invalid TX group', + 68: 'TX group ID mismatch', + 69: 'Multiple names forbidden', + 70: 'Invalid asset owner', + 71: 'AT is finished', + 72: 'No flag permission', + 73: 'Not minting accout', - 77: "Invalid rewardshare percent", - 78: "Public key unknown", - 79: "Invalid public key", - 80: "AT unknown", - 81: "AT already exists", - 82: "Group approval not required", - 83: "Group approval decided", - 84: "Maximum reward shares", - 85: "Transaction already exists", - 86: "No blockchain lock", - 87: "Order already closed", - 88: "Clock not synced", - 89: "Asset not spendable", - 90: "Account can not reward share", - 91: "Self share exists", - 92: "Account already exists", - 93: "Invalid group block delay", - 94: "Incorrect nonce", - 95: "Ivalid timestamp signature", - 96: "Address blocked", - 97: "Name Blocked", - 98: "Group approval required", - 99: "Account not transferable", + 77: 'Invalid rewardshare percent', + 78: 'Public key unknown', + 79: 'Invalid public key', + 80: 'AT unknown', + 81: 'AT already exists', + 82: 'Group approval not required', + 83: 'Group approval decided', + 84: 'Maximum reward shares', + 85: 'Transaction already exists', + 86: 'No blockchain lock', + 87: 'Order already closed', + 88: 'Clock not synced', + 89: 'Asset not spendable', + 90: 'Account can not reward share', + 91: 'Self share exists', + 92: 'Account already exists', + 93: 'Invalid group block delay', + 94: 'Incorrect nonce', + 95: 'Ivalid timestamp signature', + 96: 'Address blocked', + 97: 'Name Blocked', + 98: 'Group approval required', + 99: 'Account not transferable', - 999: "Ivalid but ok", - 1000: "Not yet released." -} + 999: 'Ivalid but ok', + 1000: 'Not yet released.', +}; // Qortal 8 decimals -const QORT_DECIMALS = 1e8 +const QORT_DECIMALS = 1e8; // Q for Qortal -const ADDRESS_VERSION = 58 +const ADDRESS_VERSION = 58; // Proxy for api calls -const PROXY_URL = "/proxy/" +const PROXY_URL = '/proxy/'; // Chat reference timestamp -const CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP = 1674316800000 +const CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP = 1674316800000; // Dynamic fee timestamp -const DYNAMIC_FEE_TIMESTAMP = 1692118800000 - +const DYNAMIC_FEE_TIMESTAMP = 1692118800000; // Used as a salt for all Qora addresses. Salts used for storing your private keys in local storage will be randomly generated -const STATIC_SALT = new Uint8Array([54, 190, 201, 206, 65, 29, 123, 129, 147, 231, 180, 166, 171, 45, 95, 165, 78, 200, 208, 194, 44, 207, 221, 146, 45, 238, 68, 68, 69, 102, 62, 6]) -const BCRYPT_ROUNDS = 10 // Remember that the total work spent on key derivation is BCRYPT_ROUNDS * KDF_THREADS -const BCRYPT_VERSION = "2a" -const STATIC_BCRYPT_SALT = `$${BCRYPT_VERSION}$${BCRYPT_ROUNDS}$IxVE941tXVUD4cW0TNVm.O` -const KDF_THREADS = 16 +const STATIC_SALT = new Uint8Array([ + 54, 190, 201, 206, 65, 29, 123, 129, 147, 231, 180, 166, 171, 45, 95, 165, 78, + 200, 208, 194, 44, 207, 221, 146, 45, 238, 68, 68, 69, 102, 62, 6, +]); +const BCRYPT_ROUNDS = 10; // Remember that the total work spent on key derivation is BCRYPT_ROUNDS * KDF_THREADS +const BCRYPT_VERSION = '2a'; +const STATIC_BCRYPT_SALT = `$${BCRYPT_VERSION}$${BCRYPT_ROUNDS}$IxVE941tXVUD4cW0TNVm.O`; +const KDF_THREADS = 16; -export { TX_TYPES, ERROR_CODES, QORT_DECIMALS, PROXY_URL, STATIC_SALT, ADDRESS_VERSION, KDF_THREADS, STATIC_BCRYPT_SALT, CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP, DYNAMIC_FEE_TIMESTAMP } +export { + TX_TYPES, + ERROR_CODES, + QORT_DECIMALS, + PROXY_URL, + STATIC_SALT, + ADDRESS_VERSION, + KDF_THREADS, + STATIC_BCRYPT_SALT, + CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP, + DYNAMIC_FEE_TIMESTAMP, +}; diff --git a/src/transactions/AddGroupAdminTransaction.ts b/src/transactions/AddGroupAdminTransaction.ts index f66f730..dd65ec4 100644 --- a/src/transactions/AddGroupAdminTransaction.ts +++ b/src/transactions/AddGroupAdminTransaction.ts @@ -1,37 +1,35 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class AddGroupAdminTransaction extends TransactionBase { - constructor() { - super() - this.type = 24 - } + constructor() { + super(); + this.type = 24; + } - set rGroupId(rGroupId) { - this._rGroupId = rGroupId - this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId) - } + set rGroupId(rGroupId) { + this._rGroupId = rGroupId; + this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId); + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - this.theRecipient = recipient - } + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + this.theRecipient = recipient; + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - get params() { - const params = super.params - params.push( - this._rGroupIdBytes, - this._recipient, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push(this._rGroupIdBytes, this._recipient, this._feeBytes); + return params; + } } diff --git a/src/transactions/BuyNameTransacion.ts b/src/transactions/BuyNameTransacion.ts index 8c321d3..0664b1c 100644 --- a/src/transactions/BuyNameTransacion.ts +++ b/src/transactions/BuyNameTransacion.ts @@ -1,45 +1,48 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class BuyNameTransacion extends TransactionBase { - constructor() { - super() - this.type = 7 - } + constructor() { + super(); + this.type = 7; + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set name(name) { - this.nameText = name - this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) - this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) - } + set name(name) { + this.nameText = name; + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name); + this._nameLength = this.constructor.utils.int32ToBytes( + this._nameBytes.length + ); + } - set sellPrice(sellPrice) { - this._sellPrice = sellPrice * QORT_DECIMALS - this._sellPriceBytes = this.constructor.utils.int64ToBytes(this._sellPrice) - } + set sellPrice(sellPrice) { + this._sellPrice = sellPrice * QORT_DECIMALS; + this._sellPriceBytes = this.constructor.utils.int64ToBytes(this._sellPrice); + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - this.theRecipient = recipient - } + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + this.theRecipient = recipient; + } - get params() { - const params = super.params - params.push( - this._nameLength, - this._nameBytes, - this._sellPriceBytes, - this._recipient, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._nameLength, + this._nameBytes, + this._sellPriceBytes, + this._recipient, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/CancelGroupBanTransaction.ts b/src/transactions/CancelGroupBanTransaction.ts index d1bbef7..fc3ca73 100644 --- a/src/transactions/CancelGroupBanTransaction.ts +++ b/src/transactions/CancelGroupBanTransaction.ts @@ -1,37 +1,35 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class CancelGroupBanTransaction extends TransactionBase { - constructor() { - super() - this.type = 27 - } + constructor() { + super(); + this.type = 27; + } - set rGroupId(rGroupId) { - this._rGroupId = rGroupId - this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId) - } + set rGroupId(rGroupId) { + this._rGroupId = rGroupId; + this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId); + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - this.theRecipient = recipient - } + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + this.theRecipient = recipient; + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - get params() { - const params = super.params - params.push( - this._rGroupIdBytes, - this._recipient, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push(this._rGroupIdBytes, this._recipient, this._feeBytes); + return params; + } } diff --git a/src/transactions/CancelGroupInviteTransaction.ts b/src/transactions/CancelGroupInviteTransaction.ts index b575f3e..6ece4fd 100644 --- a/src/transactions/CancelGroupInviteTransaction.ts +++ b/src/transactions/CancelGroupInviteTransaction.ts @@ -1,36 +1,33 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class CancelGroupInviteTransaction extends TransactionBase { - constructor() { - super() - this.type = 30 - } + constructor() { + super(); + this.type = 30; + } - set rGroupId(rGroupId) { - this._rGroupId = rGroupId - this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId) - } + set rGroupId(rGroupId) { + this._rGroupId = rGroupId; + this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId); + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - this.theRecipient = recipient - } + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + this.theRecipient = recipient; + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - get params() { - const params = super.params - params.push( - this._rGroupIdBytes, - this._recipient, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push(this._rGroupIdBytes, this._recipient, this._feeBytes); + return params; + } } diff --git a/src/transactions/CancelSellNameTransacion.ts b/src/transactions/CancelSellNameTransacion.ts index 04296c8..28420f7 100644 --- a/src/transactions/CancelSellNameTransacion.ts +++ b/src/transactions/CancelSellNameTransacion.ts @@ -1,33 +1,29 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class CancelSellNameTransacion extends TransactionBase { - constructor() { - super() - this.type = 6 - } + constructor() { + super(); + this.type = 6; + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set name(name) { - this.nameText = name - this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) - this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) - } + set name(name) { + this.nameText = name; + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name); + this._nameLength = this.constructor.utils.int32ToBytes( + this._nameBytes.length + ); + } - get params() { - const params = super.params - params.push( - this._nameLength, - this._nameBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push(this._nameLength, this._nameBytes, this._feeBytes); + return params; + } } diff --git a/src/transactions/ChatBase.ts b/src/transactions/ChatBase.ts index cdfacaf..c88d634 100644 --- a/src/transactions/ChatBase.ts +++ b/src/transactions/ChatBase.ts @@ -1,148 +1,161 @@ // @ts-nocheck -import { QORT_DECIMALS, TX_TYPES } from '../constants/constants' -import nacl from '../deps/nacl-fast' -import Base58 from '../deps/Base58' -import utils from '../utils/utils' - +import { QORT_DECIMALS, TX_TYPES } from '../constants/constants'; +import nacl from '../deps/nacl-fast'; +import Base58 from '../deps/Base58'; +import utils from '../utils/utils'; export default class ChatBase { - static get utils() { - return utils - } + static get utils() { + return utils; + } - static get nacl() { - return nacl - } + static get nacl() { + return nacl; + } - static get Base58() { - return Base58 - } + static get Base58() { + return Base58; + } - constructor() { - - this.fee = 0 - this.groupID = 0 - this.tests = [ - () => { - if (!(this._type >= 1 && this._type in TX_TYPES)) { - return 'Invalid type: ' + this.type - } - return true - }, - () => { - if (this._fee < 0) { - return 'Invalid fee: ' + this._fee / QORT_DECIMALS - } - return true - }, - () => { - if (this._groupID < 0 || !Number.isInteger(this._groupID)) { - return 'Invalid groupID: ' + this._groupID - } - return true - }, - () => { - if (!(new Date(this._timestamp)).getTime() > 0) { - return 'Invalid timestamp: ' + this._timestamp - } - return true - }, - () => { - - if (!(this._lastReference instanceof Uint8Array && this._lastReference.byteLength == 64)) { - return 'Invalid last reference: ' + this._lastReference - } - return true - }, - () => { - if (!(this._keyPair)) { - return 'keyPair must be specified' - } - if (!(this._keyPair.publicKey instanceof Uint8Array && this._keyPair.publicKey.byteLength === 32)) { - return 'Invalid publicKey' - } - if (!(this._keyPair.privateKey instanceof Uint8Array && this._keyPair.privateKey.byteLength === 64)) { - return 'Invalid privateKey' - } - return true - } - ] - } + constructor() { + this.fee = 0; + this.groupID = 0; + this.tests = [ + () => { + if (!(this._type >= 1 && this._type in TX_TYPES)) { + return 'Invalid type: ' + this.type; + } + return true; + }, + () => { + if (this._fee < 0) { + return 'Invalid fee: ' + this._fee / QORT_DECIMALS; + } + return true; + }, + () => { + if (this._groupID < 0 || !Number.isInteger(this._groupID)) { + return 'Invalid groupID: ' + this._groupID; + } + return true; + }, + () => { + if (!new Date(this._timestamp).getTime() > 0) { + return 'Invalid timestamp: ' + this._timestamp; + } + return true; + }, + () => { + if ( + !( + this._lastReference instanceof Uint8Array && + this._lastReference.byteLength == 64 + ) + ) { + return 'Invalid last reference: ' + this._lastReference; + } + return true; + }, + () => { + if (!this._keyPair) { + return 'keyPair must be specified'; + } + if ( + !( + this._keyPair.publicKey instanceof Uint8Array && + this._keyPair.publicKey.byteLength === 32 + ) + ) { + return 'Invalid publicKey'; + } + if ( + !( + this._keyPair.privateKey instanceof Uint8Array && + this._keyPair.privateKey.byteLength === 64 + ) + ) { + return 'Invalid privateKey'; + } + return true; + }, + ]; + } - set keyPair(keyPair) { - this._keyPair = keyPair - } + set keyPair(keyPair) { + this._keyPair = keyPair; + } - set type(type) { - this.typeText = TX_TYPES[type] - this._type = type - this._typeBytes = this.constructor.utils.int32ToBytes(this._type) - } + set type(type) { + this.typeText = TX_TYPES[type]; + this._type = type; + this._typeBytes = this.constructor.utils.int32ToBytes(this._type); + } - set groupID(groupID) { - this._groupID = groupID - this._groupIDBytes = this.constructor.utils.int32ToBytes(this._groupID) - } + set groupID(groupID) { + this._groupID = groupID; + this._groupIDBytes = this.constructor.utils.int32ToBytes(this._groupID); + } - set timestamp(timestamp) { - this._timestamp = timestamp - this._timestampBytes = this.constructor.utils.int64ToBytes(this._timestamp) - } + set timestamp(timestamp) { + this._timestamp = timestamp; + this._timestampBytes = this.constructor.utils.int64ToBytes(this._timestamp); + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set lastReference(lastReference) { + set lastReference(lastReference) { + this._lastReference = + lastReference instanceof Uint8Array + ? lastReference + : this.constructor.Base58.decode(lastReference); + } - this._lastReference = lastReference instanceof Uint8Array ? lastReference : this.constructor.Base58.decode(lastReference) - } + get params() { + return [ + this._typeBytes, + this._timestampBytes, + this._groupIDBytes, + this._lastReference, + this._keyPair.publicKey, + ]; + } - get params() { - return [ - this._typeBytes, - this._timestampBytes, - this._groupIDBytes, - this._lastReference, - this._keyPair.publicKey - ] - } + get chatBytes() { + const isValid = this.validParams(); + if (!isValid.valid) { + throw new Error(isValid.message); + } - get chatBytes() { - const isValid = this.validParams() - if (!isValid.valid) { - throw new Error(isValid.message) - } + let result = new Uint8Array(); - let result = new Uint8Array() + this.params.forEach((item) => { + result = this.constructor.utils.appendBuffer(result, item); + }); - this.params.forEach(item => { - result = this.constructor.utils.appendBuffer(result, item) - }) + this._chatBytes = result; - this._chatBytes = result + return this._chatBytes; + } - return this._chatBytes - } + validParams() { + let finalResult = { + valid: true, + }; - validParams() { - let finalResult = { - valid: true - } - - this.tests.some(test => { - const result = test() - if (result !== true) { - finalResult = { - valid: false, - message: result - } - return true - } - }) - - return finalResult - } + this.tests.some((test) => { + const result = test(); + if (result !== true) { + finalResult = { + valid: false, + message: result, + }; + return true; + } + }); + return finalResult; + } } diff --git a/src/transactions/ChatTransaction.ts b/src/transactions/ChatTransaction.ts index ec30c8c..fc67531 100644 --- a/src/transactions/ChatTransaction.ts +++ b/src/transactions/ChatTransaction.ts @@ -1,92 +1,126 @@ // @ts-nocheck -import ChatBase from './ChatBase' -import nacl from '../deps/nacl-fast' -import ed2curve from '../deps/ed2curve' -import { Sha256 } from 'asmcrypto.js' -import { CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP } from '../constants/constants' - +import ChatBase from './ChatBase'; +import nacl from '../deps/nacl-fast'; +import ed2curve from '../deps/ed2curve'; +import { Sha256 } from 'asmcrypto.js'; +import { CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP } from '../constants/constants'; export default class ChatTransaction extends ChatBase { - constructor() { - super() - this.type = 18 - this.fee = 0 - } + constructor() { + super(); + this.type = 18; + this.fee = 0; + } - set recipientPublicKey(recipientPublicKey) { - this._base58RecipientPublicKey = recipientPublicKey instanceof Uint8Array ? this.constructor.Base58.encode(recipientPublicKey) : recipientPublicKey - this._recipientPublicKey = this.constructor.Base58.decode(this._base58RecipientPublicKey) - } + set recipientPublicKey(recipientPublicKey) { + this._base58RecipientPublicKey = + recipientPublicKey instanceof Uint8Array + ? this.constructor.Base58.encode(recipientPublicKey) + : recipientPublicKey; + this._recipientPublicKey = this.constructor.Base58.decode( + this._base58RecipientPublicKey + ); + } - set proofOfWorkNonce(proofOfWorkNonce) { - this._proofOfWorkNonce = this.constructor.utils.int32ToBytes(proofOfWorkNonce) - } + set proofOfWorkNonce(proofOfWorkNonce) { + this._proofOfWorkNonce = + this.constructor.utils.int32ToBytes(proofOfWorkNonce); + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - this._hasReceipient = new Uint8Array(1) - this._hasReceipient[0] = 1 - } + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + this._hasReceipient = new Uint8Array(1); + this._hasReceipient[0] = 1; + } - set hasChatReference(hasChatReference) { - this._hasChatReference = new Uint8Array(1) - this._hasChatReference[0] = hasChatReference - } + set hasChatReference(hasChatReference) { + this._hasChatReference = new Uint8Array(1); + this._hasChatReference[0] = hasChatReference; + } - set chatReference(chatReference) { - this._chatReference = chatReference instanceof Uint8Array ? chatReference : this.constructor.Base58.decode(chatReference) - } + set chatReference(chatReference) { + this._chatReference = + chatReference instanceof Uint8Array + ? chatReference + : this.constructor.Base58.decode(chatReference); + } - set message(message) { - this.messageText = message; - this._message = this.constructor.utils.stringtoUTF8Array(message) - this._messageLength = this.constructor.utils.int32ToBytes(this._message.length) - } + set message(message) { + this.messageText = message; + this._message = this.constructor.utils.stringtoUTF8Array(message); + this._messageLength = this.constructor.utils.int32ToBytes( + this._message.length + ); + } - set isEncrypted(isEncrypted) { - this._isEncrypted = new Uint8Array(1) - this._isEncrypted[0] = isEncrypted + set isEncrypted(isEncrypted) { + this._isEncrypted = new Uint8Array(1); + this._isEncrypted[0] = isEncrypted; - if (isEncrypted === 1) { - const convertedPrivateKey = ed2curve.convertSecretKey(this._keyPair.privateKey) - const convertedPublicKey = ed2curve.convertPublicKey(this._recipientPublicKey) - const sharedSecret = new Uint8Array(32) - nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedPublicKey) + if (isEncrypted === 1) { + const convertedPrivateKey = ed2curve.convertSecretKey( + this._keyPair.privateKey + ); + const convertedPublicKey = ed2curve.convertPublicKey( + this._recipientPublicKey + ); + const sharedSecret = new Uint8Array(32); + nacl.lowlevel.crypto_scalarmult( + sharedSecret, + convertedPrivateKey, + convertedPublicKey + ); - this._chatEncryptionSeed = new Sha256().process(sharedSecret).finish().result - this._encryptedMessage = nacl.secretbox(this._message, this._lastReference.slice(0, 24), this._chatEncryptionSeed) - } + this._chatEncryptionSeed = new Sha256() + .process(sharedSecret) + .finish().result; + this._encryptedMessage = nacl.secretbox( + this._message, + this._lastReference.slice(0, 24), + this._chatEncryptionSeed + ); + } - this._myMessage = isEncrypted === 1 ? this._encryptedMessage : this._message - this._myMessageLenth = isEncrypted === 1 ? this.constructor.utils.int32ToBytes(this._myMessage.length) : this._messageLength - } + this._myMessage = + isEncrypted === 1 ? this._encryptedMessage : this._message; + this._myMessageLenth = + isEncrypted === 1 + ? this.constructor.utils.int32ToBytes(this._myMessage.length) + : this._messageLength; + } - set isText(isText) { - this._isText = new Uint8Array(1) - this._isText[0] = isText - } + set isText(isText) { + this._isText = new Uint8Array(1); + this._isText[0] = isText; + } - get params() { - const params = super.params - params.push( - this._proofOfWorkNonce, - this._hasReceipient, - this._recipient, - this._myMessageLenth, - this._myMessage, - this._isEncrypted, - this._isText, - this._feeBytes - ) + get params() { + const params = super.params; + params.push( + this._proofOfWorkNonce, + this._hasReceipient, + this._recipient, + this._myMessageLenth, + this._myMessage, + this._isEncrypted, + this._isText, + this._feeBytes + ); - // After the feature trigger timestamp we need to include chat reference - if (new Date(this._timestamp).getTime() >= CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP) { - params.push(this._hasChatReference) + // After the feature trigger timestamp we need to include chat reference + if ( + new Date(this._timestamp).getTime() >= + CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP + ) { + params.push(this._hasChatReference); - if (this._hasChatReference[0] == 1) { - params.push(this._chatReference) - } - } - return params - } + if (this._hasChatReference[0] == 1) { + params.push(this._chatReference); + } + } + return params; + } } diff --git a/src/transactions/CreateGroupTransaction.ts b/src/transactions/CreateGroupTransaction.ts index a2eb7ea..24aff1e 100644 --- a/src/transactions/CreateGroupTransaction.ts +++ b/src/transactions/CreateGroupTransaction.ts @@ -1,64 +1,73 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class CreateGroupTransaction extends TransactionBase { - constructor() { - super() - this.type = 22 - } + constructor() { + super(); + this.type = 22; + } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set rGroupName(rGroupName) { + this._rGroupName = rGroupName; + this._rGroupNameBytes = this.constructor.utils.stringtoUTF8Array( + this._rGroupName + ); + this._rGroupNameLength = this.constructor.utils.int32ToBytes( + this._rGroupNameBytes.length + ); + } - set rGroupName(rGroupName) { - this._rGroupName = rGroupName - this._rGroupNameBytes = this.constructor.utils.stringtoUTF8Array(this._rGroupName) - this._rGroupNameLength = this.constructor.utils.int32ToBytes(this._rGroupNameBytes.length) - } + set rGroupDesc(rGroupDesc) { + this._rGroupDesc = rGroupDesc; + this._rGroupDescBytes = this.constructor.utils.stringtoUTF8Array( + this._rGroupDesc + ); + this._rGroupDescLength = this.constructor.utils.int32ToBytes( + this._rGroupDescBytes.length + ); + } - set rGroupDesc(rGroupDesc) { - this._rGroupDesc = rGroupDesc - this._rGroupDescBytes = this.constructor.utils.stringtoUTF8Array(this._rGroupDesc) - this._rGroupDescLength = this.constructor.utils.int32ToBytes(this._rGroupDescBytes.length) - } + set rGroupType(rGroupType) { + this._rGroupType = new Uint8Array(1); + this._rGroupType[0] = rGroupType; + } - set rGroupType(rGroupType) { - this._rGroupType = new Uint8Array(1) - this._rGroupType[0] = rGroupType - } + set rGroupApprovalThreshold(rGroupApprovalThreshold) { + this._rGroupApprovalThreshold = new Uint8Array(1); + this._rGroupApprovalThreshold[0] = rGroupApprovalThreshold; + } - set rGroupApprovalThreshold(rGroupApprovalThreshold) { - this._rGroupApprovalThreshold = new Uint8Array(1) - this._rGroupApprovalThreshold[0] = rGroupApprovalThreshold - } + set rGroupMinimumBlockDelay(rGroupMinimumBlockDelay) { + this._rGroupMinimumBlockDelayBytes = this.constructor.utils.int32ToBytes( + rGroupMinimumBlockDelay + ); + } - set rGroupMinimumBlockDelay(rGroupMinimumBlockDelay) { - this._rGroupMinimumBlockDelayBytes = this.constructor.utils.int32ToBytes(rGroupMinimumBlockDelay) - } + set rGroupMaximumBlockDelay(rGroupMaximumBlockDelay) { + this._rGroupMaximumBlockDelayBytes = this.constructor.utils.int32ToBytes( + rGroupMaximumBlockDelay + ); + } - set rGroupMaximumBlockDelay(rGroupMaximumBlockDelay) { - this._rGroupMaximumBlockDelayBytes = this.constructor.utils.int32ToBytes(rGroupMaximumBlockDelay) - } - - get params() { - const params = super.params - params.push( - this._rGroupNameLength, - this._rGroupNameBytes, - this._rGroupDescLength, - this._rGroupDescBytes, - this._rGroupType, - this._rGroupApprovalThreshold, - this._rGroupMinimumBlockDelayBytes, - this._rGroupMaximumBlockDelayBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._rGroupNameLength, + this._rGroupNameBytes, + this._rGroupDescLength, + this._rGroupDescBytes, + this._rGroupType, + this._rGroupApprovalThreshold, + this._rGroupMinimumBlockDelayBytes, + this._rGroupMaximumBlockDelayBytes, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/CreatePollTransaction.ts b/src/transactions/CreatePollTransaction.ts index a2a9cc0..3717ba9 100644 --- a/src/transactions/CreatePollTransaction.ts +++ b/src/transactions/CreatePollTransaction.ts @@ -1,73 +1,84 @@ // @ts-nocheck -import { QORT_DECIMALS } from '../constants/constants' -import TransactionBase from './TransactionBase' - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class CreatePollTransaction extends TransactionBase { - constructor() { - super() - this.type = 8 - this._options = [] - } + constructor() { + super(); + this.type = 8; + this._options = []; + } - addOption(option) { - const optionBytes = this.constructor.utils.stringtoUTF8Array(option) - const optionLength = this.constructor.utils.int32ToBytes(optionBytes.length) - this._options.push({ length: optionLength, bytes: optionBytes }) - } + addOption(option) { + const optionBytes = this.constructor.utils.stringtoUTF8Array(option); + const optionLength = this.constructor.utils.int32ToBytes( + optionBytes.length + ); + this._options.push({ length: optionLength, bytes: optionBytes }); + } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set ownerAddress(ownerAddress) { + this._ownerAddress = + ownerAddress instanceof Uint8Array + ? ownerAddress + : this.constructor.Base58.decode(ownerAddress); + } - set ownerAddress(ownerAddress) { - this._ownerAddress = ownerAddress instanceof Uint8Array ? ownerAddress : this.constructor.Base58.decode(ownerAddress) - } + set rPollName(rPollName) { + this._rPollName = rPollName; + this._rPollNameBytes = this.constructor.utils.stringtoUTF8Array( + this._rPollName + ); + this._rPollNameLength = this.constructor.utils.int32ToBytes( + this._rPollNameBytes.length + ); + } - set rPollName(rPollName) { - this._rPollName = rPollName - this._rPollNameBytes = this.constructor.utils.stringtoUTF8Array(this._rPollName) - this._rPollNameLength = this.constructor.utils.int32ToBytes(this._rPollNameBytes.length) + set rPollDesc(rPollDesc) { + this._rPollDesc = rPollDesc; + this._rPollDescBytes = this.constructor.utils.stringtoUTF8Array( + this._rPollDesc + ); + this._rPollDescLength = this.constructor.utils.int32ToBytes( + this._rPollDescBytes.length + ); + } - } + set rOptions(rOptions) { + const optionsArray = rOptions[0].split(', ').map((opt) => opt.trim()); + this._pollOptions = optionsArray; - set rPollDesc(rPollDesc) { - this._rPollDesc = rPollDesc - this._rPollDescBytes = this.constructor.utils.stringtoUTF8Array(this._rPollDesc) - this._rPollDescLength = this.constructor.utils.int32ToBytes(this._rPollDescBytes.length) - } + for (let i = 0; i < optionsArray.length; i++) { + this.addOption(optionsArray[i]); + } - set rOptions(rOptions) { - const optionsArray = rOptions[0].split(', ').map(opt => opt.trim()) - this._pollOptions = optionsArray + this._rNumberOfOptionsBytes = this.constructor.utils.int32ToBytes( + optionsArray.length + ); + } - for (let i = 0; i < optionsArray.length; i++) { - this.addOption(optionsArray[i]) - } + get params() { + const params = super.params; + params.push( + this._ownerAddress, + this._rPollNameLength, + this._rPollNameBytes, + this._rPollDescLength, + this._rPollDescBytes, + this._rNumberOfOptionsBytes + ); - this._rNumberOfOptionsBytes = this.constructor.utils.int32ToBytes(optionsArray.length) - } + // Push the dynamic options + for (let i = 0; i < this._options.length; i++) { + params.push(this._options[i].length, this._options[i].bytes); + } + params.push(this._feeBytes); - get params() { - const params = super.params - params.push( - this._ownerAddress, - this._rPollNameLength, - this._rPollNameBytes, - this._rPollDescLength, - this._rPollDescBytes, - this._rNumberOfOptionsBytes - ) - - // Push the dynamic options - for (let i = 0; i < this._options.length; i++) { - params.push(this._options[i].length, this._options[i].bytes) - } - - params.push(this._feeBytes) - - return params - } + return params; + } } diff --git a/src/transactions/DeployAtTransaction.ts b/src/transactions/DeployAtTransaction.ts index 8a20553..698ef5a 100644 --- a/src/transactions/DeployAtTransaction.ts +++ b/src/transactions/DeployAtTransaction.ts @@ -1,78 +1,90 @@ // @ts-nocheck - -import TransactionBase from './TransactionBase' -import { QORT_DECIMALS } from '../constants/constants' - +import TransactionBase from './TransactionBase'; +import { QORT_DECIMALS } from '../constants/constants'; export default class DeployAtTransaction extends TransactionBase { - constructor() { - super() - this.type = 16 - } + constructor() { + super(); + this.type = 16; + } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } + set rAmount(rAmount) { + this._rAmount = Math.round(rAmount * QORT_DECIMALS); + this._rAmountBytes = this.constructor.utils.int64ToBytes(this._rAmount); + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set rName(rName) { + this._rName = rName; + this._rNameBytes = this.constructor.utils.stringtoUTF8Array( + this._rName.toLocaleLowerCase() + ); + this._rNameLength = this.constructor.utils.int32ToBytes( + this._rNameBytes.length + ); + } - set rAmount(rAmount) { - this._rAmount = Math.round(rAmount * QORT_DECIMALS) - this._rAmountBytes = this.constructor.utils.int64ToBytes(this._rAmount) - } + set rDescription(rDescription) { + this._rDescription = rDescription; + this._rDescriptionBytes = this.constructor.utils.stringtoUTF8Array( + this._rDescription.toLocaleLowerCase() + ); + this._rDescriptionLength = this.constructor.utils.int32ToBytes( + this._rDescriptionBytes.length + ); + } - set rName(rName) { - this._rName = rName - this._rNameBytes = this.constructor.utils.stringtoUTF8Array(this._rName.toLocaleLowerCase()) - this._rNameLength = this.constructor.utils.int32ToBytes(this._rNameBytes.length) - } + set atType(atType) { + this._atType = atType; + this._atTypeBytes = this.constructor.utils.stringtoUTF8Array(this._atType); + this._atTypeLength = this.constructor.utils.int32ToBytes( + this._atTypeBytes.length + ); + } - set rDescription(rDescription) { - this._rDescription = rDescription - this._rDescriptionBytes = this.constructor.utils.stringtoUTF8Array(this._rDescription.toLocaleLowerCase()) - this._rDescriptionLength = this.constructor.utils.int32ToBytes(this._rDescriptionBytes.length) - } + set rTags(rTags) { + this._rTags = rTags; + this._rTagsBytes = this.constructor.utils.stringtoUTF8Array( + this._rTags.toLocaleLowerCase() + ); + this._rTagsLength = this.constructor.utils.int32ToBytes( + this._rTagsBytes.length + ); + } - set atType(atType) { - this._atType = atType - this._atTypeBytes = this.constructor.utils.stringtoUTF8Array(this._atType) - this._atTypeLength = this.constructor.utils.int32ToBytes(this._atTypeBytes.length) - } + set rCreationBytes(rCreationBytes) { + const decode = this.constructor.Base58.decode(rCreationBytes); + this._rCreationBytes = this.constructor.utils.stringtoUTF8Array(decode); + this._rCreationBytesLength = this.constructor.utils.int32ToBytes( + this._rCreationBytes.length + ); + } - set rTags(rTags) { - this._rTags = rTags - this._rTagsBytes = this.constructor.utils.stringtoUTF8Array(this._rTags.toLocaleLowerCase()) - this._rTagsLength = this.constructor.utils.int32ToBytes(this._rTagsBytes.length) - } + set rAssetId(rAssetId) { + this._rAssetId = this.constructor.utils.int64ToBytes(rAssetId); + } - set rCreationBytes(rCreationBytes) { - const decode = this.constructor.Base58.decode(rCreationBytes) - this._rCreationBytes = this.constructor.utils.stringtoUTF8Array(decode) - this._rCreationBytesLength = this.constructor.utils.int32ToBytes(this._rCreationBytes.length) - } - - set rAssetId(rAssetId) { - this._rAssetId = this.constructor.utils.int64ToBytes(rAssetId) - } - - get params() { - const params = super.params - params.push( - this._rNameLength, - this._rNameBytes, - this._rDescriptionLength, - this._rDescriptionBytes, - this._atTypeLength, - this._atTypeBytes, - this._rTagsLength, - this._rTagsBytes, - this._rCreationBytesLength, - this._rCreationBytes, - this._rAmountBytes, - this._rAssetId, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._rNameLength, + this._rNameBytes, + this._rDescriptionLength, + this._rDescriptionBytes, + this._atTypeLength, + this._atTypeBytes, + this._rTagsLength, + this._rTagsBytes, + this._rCreationBytesLength, + this._rCreationBytes, + this._rAmountBytes, + this._rAssetId, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/GroupBanTransaction.ts b/src/transactions/GroupBanTransaction.ts index 3eb3210..de59e74 100644 --- a/src/transactions/GroupBanTransaction.ts +++ b/src/transactions/GroupBanTransaction.ts @@ -1,50 +1,56 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class GroupBanTransaction extends TransactionBase { - constructor() { - super() - this.type = 26 - } + constructor() { + super(); + this.type = 26; + } - set rGroupId(rGroupId) { - this._rGroupId = rGroupId - this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId) - } + set rGroupId(rGroupId) { + this._rGroupId = rGroupId; + this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId); + } - set rBanReason(rBanReason) { - this._rBanReason = rBanReason - this._rBanReasonBytes = this.constructor.utils.stringtoUTF8Array(this._rBanReason) - this._rBanReasonLength = this.constructor.utils.int32ToBytes(this._rBanReasonBytes.length) - } + set rBanReason(rBanReason) { + this._rBanReason = rBanReason; + this._rBanReasonBytes = this.constructor.utils.stringtoUTF8Array( + this._rBanReason + ); + this._rBanReasonLength = this.constructor.utils.int32ToBytes( + this._rBanReasonBytes.length + ); + } - set rBanTime(rBanTime) { - this._rBanTime = rBanTime - this._rBanTimeBytes = this.constructor.utils.int32ToBytes(this._rBanTime) - } + set rBanTime(rBanTime) { + this._rBanTime = rBanTime; + this._rBanTimeBytes = this.constructor.utils.int32ToBytes(this._rBanTime); + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - this.theRecipient = recipient - } + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + this.theRecipient = recipient; + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - get params() { - const params = super.params - params.push( - this._rGroupIdBytes, - this._recipient, - this._rBanReasonLength, - this._rBanReasonBytes, - this._rBanTimeBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._rGroupIdBytes, + this._recipient, + this._rBanReasonLength, + this._rBanReasonBytes, + this._rBanTimeBytes, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/GroupChatTransaction.ts b/src/transactions/GroupChatTransaction.ts index 1cd9d9b..1df7a4f 100644 --- a/src/transactions/GroupChatTransaction.ts +++ b/src/transactions/GroupChatTransaction.ts @@ -1,72 +1,79 @@ // @ts-nocheck - -import ChatBase from './ChatBase' -import { CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP } from '../constants/constants' - +import ChatBase from './ChatBase'; +import { CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP } from '../constants/constants'; export default class GroupChatTransaction extends ChatBase { - constructor() { - super(); - this.type = 18 - this.fee = 0 - } + constructor() { + super(); + this.type = 18; + this.fee = 0; + } - set proofOfWorkNonce(proofOfWorkNonce) { - this._proofOfWorkNonce = this.constructor.utils.int32ToBytes(proofOfWorkNonce) - } + set proofOfWorkNonce(proofOfWorkNonce) { + this._proofOfWorkNonce = + this.constructor.utils.int32ToBytes(proofOfWorkNonce); + } - set hasReceipient(hasReceipient) { - this._hasReceipient = new Uint8Array(1) - this._hasReceipient[0] = hasReceipient - } + set hasReceipient(hasReceipient) { + this._hasReceipient = new Uint8Array(1); + this._hasReceipient[0] = hasReceipient; + } - set message(message) { - this.messageText = message - - this._message = this.constructor.utils.stringtoUTF8Array(message) - this._messageLength = this.constructor.utils.int32ToBytes(this._message.length) - } + set message(message) { + this.messageText = message; - set hasChatReference(hasChatReference) { - this._hasChatReference = new Uint8Array(1) - this._hasChatReference[0] = hasChatReference - } + this._message = this.constructor.utils.stringtoUTF8Array(message); + this._messageLength = this.constructor.utils.int32ToBytes( + this._message.length + ); + } - set chatReference(chatReference) { - this._chatReference = chatReference instanceof Uint8Array ? chatReference : this.constructor.Base58.decode(chatReference) - } + set hasChatReference(hasChatReference) { + this._hasChatReference = new Uint8Array(1); + this._hasChatReference[0] = hasChatReference; + } - set isEncrypted(isEncrypted) { - this._isEncrypted = new Uint8Array(1) - this._isEncrypted[0] = isEncrypted - } + set chatReference(chatReference) { + this._chatReference = + chatReference instanceof Uint8Array + ? chatReference + : this.constructor.Base58.decode(chatReference); + } - set isText(isText) { - this._isText = new Uint8Array(1) - this._isText[0] = isText - } + set isEncrypted(isEncrypted) { + this._isEncrypted = new Uint8Array(1); + this._isEncrypted[0] = isEncrypted; + } - get params() { - const params = super.params - params.push( - this._proofOfWorkNonce, - this._hasReceipient, - this._messageLength, - this._message, - this._isEncrypted, - this._isText, - this._feeBytes - ) + set isText(isText) { + this._isText = new Uint8Array(1); + this._isText[0] = isText; + } - // After the feature trigger timestamp we need to include chat reference - if (new Date(this._timestamp).getTime() >= CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP) { - params.push(this._hasChatReference) + get params() { + const params = super.params; + params.push( + this._proofOfWorkNonce, + this._hasReceipient, + this._messageLength, + this._message, + this._isEncrypted, + this._isText, + this._feeBytes + ); - if (this._hasChatReference[0] == 1) { - params.push(this._chatReference) - } - } + // After the feature trigger timestamp we need to include chat reference + if ( + new Date(this._timestamp).getTime() >= + CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP + ) { + params.push(this._hasChatReference); - return params - } + if (this._hasChatReference[0] == 1) { + params.push(this._chatReference); + } + } + + return params; + } } diff --git a/src/transactions/GroupInviteTransaction.ts b/src/transactions/GroupInviteTransaction.ts index 3cbf978..448534b 100644 --- a/src/transactions/GroupInviteTransaction.ts +++ b/src/transactions/GroupInviteTransaction.ts @@ -1,42 +1,46 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class GroupInviteTransaction extends TransactionBase { - constructor() { - super() - this.type = 29 - } + constructor() { + super(); + this.type = 29; + } - set rGroupId(rGroupId) { - this._rGroupId = rGroupId - this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId) - } + set rGroupId(rGroupId) { + this._rGroupId = rGroupId; + this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId); + } - set rInviteTime(rInviteTime) { - this._rInviteTime = rInviteTime - this._rInviteTimeBytes = this.constructor.utils.int32ToBytes(this._rInviteTime) - } + set rInviteTime(rInviteTime) { + this._rInviteTime = rInviteTime; + this._rInviteTimeBytes = this.constructor.utils.int32ToBytes( + this._rInviteTime + ); + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - this.theRecipient = recipient - } + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + this.theRecipient = recipient; + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - get params() { - const params = super.params - params.push( - this._rGroupIdBytes, - this._recipient, - this._rInviteTimeBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._rGroupIdBytes, + this._recipient, + this._rInviteTimeBytes, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/GroupKickTransaction.ts b/src/transactions/GroupKickTransaction.ts index 969380f..298ebc9 100644 --- a/src/transactions/GroupKickTransaction.ts +++ b/src/transactions/GroupKickTransaction.ts @@ -1,46 +1,50 @@ // @ts-nocheck - -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class GroupKickTransaction extends TransactionBase { - constructor() { - super() - this.type = 28 - } + constructor() { + super(); + this.type = 28; + } - set rGroupId(rGroupId) { - this._rGroupId = rGroupId - this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId) - } + set rGroupId(rGroupId) { + this._rGroupId = rGroupId; + this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId); + } - set rBanReason(rBanReason) { - this._rBanReason = rBanReason - this._rBanReasonBytes = this.constructor.utils.stringtoUTF8Array(this._rBanReason) - this._rBanReasonLength = this.constructor.utils.int32ToBytes(this._rBanReasonBytes.length) - } + set rBanReason(rBanReason) { + this._rBanReason = rBanReason; + this._rBanReasonBytes = this.constructor.utils.stringtoUTF8Array( + this._rBanReason + ); + this._rBanReasonLength = this.constructor.utils.int32ToBytes( + this._rBanReasonBytes.length + ); + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - this.theRecipient = recipient - } + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + this.theRecipient = recipient; + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - get params() { - const params = super.params - params.push( - this._rGroupIdBytes, - this._recipient, - this._rBanReasonLength, - this._rBanReasonBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._rGroupIdBytes, + this._recipient, + this._rBanReasonLength, + this._rBanReasonBytes, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/JoinGroupTransaction.ts b/src/transactions/JoinGroupTransaction.ts index 0aae8f7..dc51048 100644 --- a/src/transactions/JoinGroupTransaction.ts +++ b/src/transactions/JoinGroupTransaction.ts @@ -1,38 +1,33 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class JoinGroupTransaction extends TransactionBase { - constructor() { - super() - this.type = 31 - } + constructor() { + super(); + this.type = 31; + } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } + set registrantAddress(registrantAddress) { + this._registrantAddress = + registrantAddress instanceof Uint8Array + ? registrantAddress + : this.constructor.Base58.decode(registrantAddress); + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set rGroupId(rGroupId) { + this._rGroupId = rGroupId; + this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId); + } - set registrantAddress(registrantAddress) { - this._registrantAddress = registrantAddress instanceof Uint8Array ? registrantAddress : this.constructor.Base58.decode(registrantAddress) - } - - set rGroupId(rGroupId) { - this._rGroupId = rGroupId - this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId) - } - - - get params() { - const params = super.params - params.push( - this._rGroupIdBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push(this._rGroupIdBytes, this._feeBytes); + return params; + } } diff --git a/src/transactions/LeaveGroupTransaction.ts b/src/transactions/LeaveGroupTransaction.ts index 222f250..85eeeec 100644 --- a/src/transactions/LeaveGroupTransaction.ts +++ b/src/transactions/LeaveGroupTransaction.ts @@ -1,35 +1,34 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class LeaveGroupTransaction extends TransactionBase { - constructor() { - super() - this.type = 32 - } - - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + constructor() { + super(); + this.type = 32; + } - set registrantAddress(registrantAddress) { - this._registrantAddress = registrantAddress instanceof Uint8Array ? registrantAddress : this.constructor.Base58.decode(registrantAddress) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set rGroupId(rGroupId) { - this._rGroupId = rGroupId - this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId) - } + set registrantAddress(registrantAddress) { + this._registrantAddress = + registrantAddress instanceof Uint8Array + ? registrantAddress + : this.constructor.Base58.decode(registrantAddress); + } - get params() { - const params = super.params - params.push( - this._rGroupIdBytes, - this._feeBytes - ) - return params - } + set rGroupId(rGroupId) { + this._rGroupId = rGroupId; + this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId); + } + + get params() { + const params = super.params; + params.push(this._rGroupIdBytes, this._feeBytes); + return params; + } } diff --git a/src/transactions/PaymentTransaction.ts b/src/transactions/PaymentTransaction.ts index 120bbe9..af770f8 100644 --- a/src/transactions/PaymentTransaction.ts +++ b/src/transactions/PaymentTransaction.ts @@ -1,38 +1,36 @@ // @ts-nocheck -import { QORT_DECIMALS } from '../constants/constants' -import TransactionBase from './TransactionBase' - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class PaymentTransaction extends TransactionBase { - constructor() { - super() - this.type = 2 - } + constructor() { + super(); + this.type = 2; + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - } + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + } - set dialogto(dialogto) { - this._dialogto = dialogto - } + set dialogto(dialogto) { + this._dialogto = dialogto; + } - set dialogamount(dialogamount) { - this._dialogamount = dialogamount - } + set dialogamount(dialogamount) { + this._dialogamount = dialogamount; + } - set amount(amount) { - this._amount = Math.round(amount * QORT_DECIMALS) - this._amountBytes = this.constructor.utils.int64ToBytes(this._amount) - } + set amount(amount) { + this._amount = Math.round(amount * QORT_DECIMALS); + this._amountBytes = this.constructor.utils.int64ToBytes(this._amount); + } - get params() { - const params = super.params - params.push( - this._recipient, - this._amountBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push(this._recipient, this._amountBytes, this._feeBytes); + return params; + } } diff --git a/src/transactions/RegisterNameTransaction.ts b/src/transactions/RegisterNameTransaction.ts index 53046ee..e523f28 100644 --- a/src/transactions/RegisterNameTransaction.ts +++ b/src/transactions/RegisterNameTransaction.ts @@ -1,42 +1,44 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class RegisterNameTransaction extends TransactionBase { - constructor() { - super() - this.type = 3 - } + constructor() { + super(); + this.type = 3; + } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set name(name) { + this.nameText = name; + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name); + this._nameLength = this.constructor.utils.int32ToBytes( + this._nameBytes.length + ); + } - set name(name) { - this.nameText = name - this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) - this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) - } + set value(value) { + this.valueText = + value.length === 0 ? 'Registered Name on the Qortal Chain' : value; + this._valueBytes = this.constructor.utils.stringtoUTF8Array(this.valueText); + this._valueLength = this.constructor.utils.int32ToBytes( + this._valueBytes.length + ); + } - set value(value) { - this.valueText = value.length === 0 ? "Registered Name on the Qortal Chain" : value - this._valueBytes = this.constructor.utils.stringtoUTF8Array(this.valueText) - this._valueLength = this.constructor.utils.int32ToBytes(this._valueBytes.length) - } - - get params() { - const params = super.params - params.push( - this._nameLength, - this._nameBytes, - this._valueLength, - this._valueBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._nameLength, + this._nameBytes, + this._valueLength, + this._valueBytes, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/RemoveGroupAdminTransaction.ts b/src/transactions/RemoveGroupAdminTransaction.ts index 3392f79..11896be 100644 --- a/src/transactions/RemoveGroupAdminTransaction.ts +++ b/src/transactions/RemoveGroupAdminTransaction.ts @@ -1,38 +1,34 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class RemoveGroupAdminTransaction extends TransactionBase { - constructor() { - super() - this.type = 25 - } + constructor() { + super(); + this.type = 25; + } + set rGroupId(rGroupId) { + this._rGroupId = rGroupId; + this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId); + } + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + this.theRecipient = recipient; + } - set rGroupId(rGroupId) { - this._rGroupId = rGroupId - this._rGroupIdBytes = this.constructor.utils.int32ToBytes(this._rGroupId) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - this.theRecipient = recipient - } - - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } - - get params() { - const params = super.params - params.push( - this._rGroupIdBytes, - this._recipient, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push(this._rGroupIdBytes, this._recipient, this._feeBytes); + return params; + } } diff --git a/src/transactions/RemoveRewardShareTransaction.ts b/src/transactions/RemoveRewardShareTransaction.ts index 375711a..c91d389 100644 --- a/src/transactions/RemoveRewardShareTransaction.ts +++ b/src/transactions/RemoveRewardShareTransaction.ts @@ -1,46 +1,50 @@ // @ts-nocheck -import { DYNAMIC_FEE_TIMESTAMP } from "../constants/constants" -import Base58 from "../deps/Base58" -import publicKeyToAddress from "../utils/generateWallet/publicKeyToAddress" -import TransactionBase from "./TransactionBase" - - +import { DYNAMIC_FEE_TIMESTAMP } from '../constants/constants'; +import Base58 from '../deps/Base58'; +import publicKeyToAddress from '../utils/generateWallet/publicKeyToAddress'; +import TransactionBase from './TransactionBase'; export default class RemoveRewardShareTransaction extends TransactionBase { - constructor() { - super() - this.type = 38 - } + constructor() { + super(); + this.type = 38; + } + set rewardShareKeyPairPublicKey(rewardShareKeyPairPublicKey) { + this._rewardShareKeyPairPublicKey = Base58.decode( + rewardShareKeyPairPublicKey + ); + } - set rewardShareKeyPairPublicKey(rewardShareKeyPairPublicKey) { - this._rewardShareKeyPairPublicKey = Base58.decode(rewardShareKeyPairPublicKey) - } + set recipient(recipient) { + const _address = publicKeyToAddress(this._keyPair.publicKey); + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); - set recipient(recipient) { - const _address = publicKeyToAddress(this._keyPair.publicKey) - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) + if (new Date(this._timestamp).getTime() >= DYNAMIC_FEE_TIMESTAMP) { + this.fee = _address === recipient ? 0 : 0.01; + } else { + this.fee = _address === recipient ? 0 : 0.001; + } + } - if (new Date(this._timestamp).getTime() >= DYNAMIC_FEE_TIMESTAMP) { - this.fee = _address === recipient ? 0 : 0.01 - } else { - this.fee = _address === recipient ? 0 : 0.001 - } - } + set percentageShare(share) { + this._percentageShare = share * 100; + this._percentageShareBytes = this.constructor.utils.int64ToBytes( + this._percentageShare + ); + } - set percentageShare(share) { - this._percentageShare = share * 100 - this._percentageShareBytes = this.constructor.utils.int64ToBytes(this._percentageShare) - } - - get params() { - const params = super.params - params.push( - this._recipient, - this._rewardShareKeyPairPublicKey, - this._percentageShareBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._recipient, + this._rewardShareKeyPairPublicKey, + this._percentageShareBytes, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/RewardShareTransaction.ts b/src/transactions/RewardShareTransaction.ts index 8419432..67131b3 100644 --- a/src/transactions/RewardShareTransaction.ts +++ b/src/transactions/RewardShareTransaction.ts @@ -1,60 +1,86 @@ // @ts-nocheck -import TransactionBase from './TransactionBase' - -import { Sha256 } from 'asmcrypto.js' -import nacl from '../deps/nacl-fast' -import ed2curve from '../deps/ed2curve' -import { DYNAMIC_FEE_TIMESTAMP } from '../constants/constants' -import publicKeyToAddress from '../utils/generateWallet/publicKeyToAddress' - +import TransactionBase from './TransactionBase'; +import { Sha256 } from 'asmcrypto.js'; +import nacl from '../deps/nacl-fast'; +import ed2curve from '../deps/ed2curve'; +import { DYNAMIC_FEE_TIMESTAMP } from '../constants/constants'; +import publicKeyToAddress from '../utils/generateWallet/publicKeyToAddress'; export default class RewardShareTransaction extends TransactionBase { - constructor() { - super() - this.type = 38 - } + constructor() { + super(); + this.type = 38; + } + set recipientPublicKey(recipientPublicKey) { + this._base58RecipientPublicKey = + recipientPublicKey instanceof Uint8Array + ? this.constructor.Base58.encode(recipientPublicKey) + : recipientPublicKey; + this._recipientPublicKey = this.constructor.Base58.decode( + this._base58RecipientPublicKey + ); + this.recipient = publicKeyToAddress(this._recipientPublicKey); + const convertedPrivateKey = ed2curve.convertSecretKey( + this._keyPair.privateKey + ); + const convertedPublicKey = ed2curve.convertPublicKey( + this._recipientPublicKey + ); + const sharedSecret = new Uint8Array(32); - set recipientPublicKey(recipientPublicKey) { - this._base58RecipientPublicKey = recipientPublicKey instanceof Uint8Array ? this.constructor.Base58.encode(recipientPublicKey) : recipientPublicKey - this._recipientPublicKey = this.constructor.Base58.decode(this._base58RecipientPublicKey) - this.recipient = publicKeyToAddress(this._recipientPublicKey) + nacl.lowlevel.crypto_scalarmult( + sharedSecret, + convertedPrivateKey, + convertedPublicKey + ); - const convertedPrivateKey = ed2curve.convertSecretKey(this._keyPair.privateKey) - const convertedPublicKey = ed2curve.convertPublicKey(this._recipientPublicKey) - const sharedSecret = new Uint8Array(32) + this._rewardShareSeed = new Sha256().process(sharedSecret).finish().result; + this._base58RewardShareSeed = this.constructor.Base58.encode( + this._rewardShareSeed + ); + this._rewardShareKeyPair = nacl.sign.keyPair.fromSeed( + this._rewardShareSeed + ); - nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedPublicKey) + if (new Date(this._timestamp).getTime() >= DYNAMIC_FEE_TIMESTAMP) { + this.fee = + recipientPublicKey === + this.constructor.Base58.encode(this._keyPair.publicKey) + ? 0 + : 0.01; + } else { + this.fee = + recipientPublicKey === + this.constructor.Base58.encode(this._keyPair.publicKey) + ? 0 + : 0.001; + } + } - this._rewardShareSeed = new Sha256().process(sharedSecret).finish().result - this._base58RewardShareSeed = this.constructor.Base58.encode(this._rewardShareSeed) - this._rewardShareKeyPair = nacl.sign.keyPair.fromSeed(this._rewardShareSeed) + set recipient(recipient) { + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); + } - if (new Date(this._timestamp).getTime() >= DYNAMIC_FEE_TIMESTAMP) { - this.fee = (recipientPublicKey === this.constructor.Base58.encode(this._keyPair.publicKey) ? 0 : 0.01) - } else { - this.fee = (recipientPublicKey === this.constructor.Base58.encode(this._keyPair.publicKey) ? 0 : 0.001) - } - } + set percentageShare(share) { + this._percentageShare = share * 100; + this._percentageShareBytes = this.constructor.utils.int64ToBytes( + this._percentageShare + ); + } - set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) - } - - set percentageShare(share) { - this._percentageShare = share * 100 - this._percentageShareBytes = this.constructor.utils.int64ToBytes(this._percentageShare) - } - - get params() { - const params = super.params - params.push( - this._recipient, - this._rewardShareKeyPair.publicKey, - this._percentageShareBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._recipient, + this._rewardShareKeyPair.publicKey, + this._percentageShareBytes, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/SellNameTransacion.ts b/src/transactions/SellNameTransacion.ts index 3926568..2e5f15e 100644 --- a/src/transactions/SellNameTransacion.ts +++ b/src/transactions/SellNameTransacion.ts @@ -1,40 +1,40 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class SellNameTransacion extends TransactionBase { - constructor() { - super() - this.type = 5 - } + constructor() { + super(); + this.type = 5; + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set name(name) { - this.nameText = name - this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) - this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) - } + set name(name) { + this.nameText = name; + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name); + this._nameLength = this.constructor.utils.int32ToBytes( + this._nameBytes.length + ); + } - set sellPrice(sellPrice) { - this.showSellPrice = sellPrice - this._sellPrice = sellPrice * QORT_DECIMALS - this._sellPriceBytes = this.constructor.utils.int64ToBytes(this._sellPrice) - } + set sellPrice(sellPrice) { + this.showSellPrice = sellPrice; + this._sellPrice = sellPrice * QORT_DECIMALS; + this._sellPriceBytes = this.constructor.utils.int64ToBytes(this._sellPrice); + } - get params() { - const params = super.params - params.push( - this._nameLength, - this._nameBytes, - this._sellPriceBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._nameLength, + this._nameBytes, + this._sellPriceBytes, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/TradeBotCreateRequest.ts b/src/transactions/TradeBotCreateRequest.ts index 800ddec..fbdcdb5 100644 --- a/src/transactions/TradeBotCreateRequest.ts +++ b/src/transactions/TradeBotCreateRequest.ts @@ -2,64 +2,64 @@ /** * CrossChain - TradeBot Create Request (Sell Action) - * + * * These are special types of transactions (JSON ENCODED) */ export default class TradeBotCreateRequest { - constructor() { - // ... - } + constructor() { + // ... + } - createTransaction(txnReq) { - this.creatorPublicKey(txnReq.creatorPublicKey) - this.qortAmount(txnReq.qortAmount) - this.fundingQortAmount(txnReq.fundingQortAmount) - this.foreignBlockchain(txnReq.foreignBlockchain) - this.foreignAmount(txnReq.foreignAmount) - this.tradeTimeout(txnReq.tradeTimeout) - this.receivingAddress(txnReq.receivingAddress) + createTransaction(txnReq) { + this.creatorPublicKey(txnReq.creatorPublicKey); + this.qortAmount(txnReq.qortAmount); + this.fundingQortAmount(txnReq.fundingQortAmount); + this.foreignBlockchain(txnReq.foreignBlockchain); + this.foreignAmount(txnReq.foreignAmount); + this.tradeTimeout(txnReq.tradeTimeout); + this.receivingAddress(txnReq.receivingAddress); - return this.txnRequest() - } + return this.txnRequest(); + } - creatorPublicKey(creatorPublicKey) { - this._creatorPublicKey = creatorPublicKey - } + creatorPublicKey(creatorPublicKey) { + this._creatorPublicKey = creatorPublicKey; + } - qortAmount(qortAmount) { - this._qortAmount = qortAmount - } + qortAmount(qortAmount) { + this._qortAmount = qortAmount; + } - fundingQortAmount(fundingQortAmount) { - this._fundingQortAmount = fundingQortAmount - } + fundingQortAmount(fundingQortAmount) { + this._fundingQortAmount = fundingQortAmount; + } - foreignBlockchain(foreignBlockchain) { - this._foreignBlockchain = foreignBlockchain - } + foreignBlockchain(foreignBlockchain) { + this._foreignBlockchain = foreignBlockchain; + } - foreignAmount(foreignAmount) { - this._foreignAmount = foreignAmount - } + foreignAmount(foreignAmount) { + this._foreignAmount = foreignAmount; + } - tradeTimeout(tradeTimeout) { - this._tradeTimeout = tradeTimeout - } + tradeTimeout(tradeTimeout) { + this._tradeTimeout = tradeTimeout; + } - receivingAddress(receivingAddress) { - this._receivingAddress = receivingAddress - } + receivingAddress(receivingAddress) { + this._receivingAddress = receivingAddress; + } - txnRequest() { - return { - creatorPublicKey: this._creatorPublicKey, - qortAmount: this._qortAmount, - fundingQortAmount: this._fundingQortAmount, - foreignBlockchain: this._foreignBlockchain, - foreignAmount: this._foreignAmount, - tradeTimeout: this._tradeTimeout, - receivingAddress: this._receivingAddress - } - } + txnRequest() { + return { + creatorPublicKey: this._creatorPublicKey, + qortAmount: this._qortAmount, + fundingQortAmount: this._fundingQortAmount, + foreignBlockchain: this._foreignBlockchain, + foreignAmount: this._foreignAmount, + tradeTimeout: this._tradeTimeout, + receivingAddress: this._receivingAddress, + }; + } } diff --git a/src/transactions/TradeBotDeleteRequest.ts b/src/transactions/TradeBotDeleteRequest.ts index cd3c1b4..1e4a69d 100644 --- a/src/transactions/TradeBotDeleteRequest.ts +++ b/src/transactions/TradeBotDeleteRequest.ts @@ -1,35 +1,35 @@ // @ts-nocheck /** - * CrossChain - DELETE TradeOffer - * + * CrossChain - DELETE TradeOffer + * * These are special types of transactions (JSON ENCODED) */ export default class DeleteTradeOffer { - constructor() { - // ... - } + constructor() { + // ... + } - createTransaction(txnReq) { - this.creatorPublicKey(txnReq.creatorPublicKey) - this.atAddress(txnReq.atAddress) + createTransaction(txnReq) { + this.creatorPublicKey(txnReq.creatorPublicKey); + this.atAddress(txnReq.atAddress); - return this.txnRequest() - } + return this.txnRequest(); + } - creatorPublicKey(creatorPublicKey) { - this._creatorPublicKey = creatorPublicKey - } + creatorPublicKey(creatorPublicKey) { + this._creatorPublicKey = creatorPublicKey; + } - atAddress(atAddress) { - this._atAddress = atAddress - } + atAddress(atAddress) { + this._atAddress = atAddress; + } - txnRequest() { - return { - creatorPublicKey: this._creatorPublicKey, - atAddress: this._atAddress - } - } + txnRequest() { + return { + creatorPublicKey: this._creatorPublicKey, + atAddress: this._atAddress, + }; + } } diff --git a/src/transactions/TradeBotRespondMultipleRequest.ts b/src/transactions/TradeBotRespondMultipleRequest.ts index a7eb4d8..da3b93e 100644 --- a/src/transactions/TradeBotRespondMultipleRequest.ts +++ b/src/transactions/TradeBotRespondMultipleRequest.ts @@ -7,36 +7,35 @@ */ export class TradeBotRespondMultipleRequest { - constructor() { - // ... - } + constructor() { + // ... + } - createTransaction(txnReq) { - this.addresses(txnReq.addresses) - this.foreignKey(txnReq.foreignKey) - this.receivingAddress(txnReq.receivingAddress) + createTransaction(txnReq) { + this.addresses(txnReq.addresses); + this.foreignKey(txnReq.foreignKey); + this.receivingAddress(txnReq.receivingAddress); - return this.txnRequest() - } + return this.txnRequest(); + } - addresses(addresses) { - this._addresses = addresses - } + addresses(addresses) { + this._addresses = addresses; + } - foreignKey(foreignKey) { - this._foreignKey = foreignKey - } + foreignKey(foreignKey) { + this._foreignKey = foreignKey; + } - receivingAddress(receivingAddress) { - this._receivingAddress = receivingAddress - } + receivingAddress(receivingAddress) { + this._receivingAddress = receivingAddress; + } - txnRequest() { - return { - addresses: this._addresses, - foreignKey: this._foreignKey, - receivingAddress: this._receivingAddress - } - } + txnRequest() { + return { + addresses: this._addresses, + foreignKey: this._foreignKey, + receivingAddress: this._receivingAddress, + }; + } } - diff --git a/src/transactions/TradeBotRespondRequest.ts b/src/transactions/TradeBotRespondRequest.ts index fcd1af5..76f548a 100644 --- a/src/transactions/TradeBotRespondRequest.ts +++ b/src/transactions/TradeBotRespondRequest.ts @@ -2,40 +2,40 @@ /** * CrossChain - TradeBot Respond Request (Buy Action) - * + * * These are special types of transactions (JSON ENCODED) */ export default class TradeBotRespondRequest { - constructor() { - // ... - } + constructor() { + // ... + } - createTransaction(txnReq) { - this.atAddress(txnReq.atAddress) - this.foreignKey(txnReq.foreignKey) - this.receivingAddress(txnReq.receivingAddress) + createTransaction(txnReq) { + this.atAddress(txnReq.atAddress); + this.foreignKey(txnReq.foreignKey); + this.receivingAddress(txnReq.receivingAddress); - return this.txnRequest() - } + return this.txnRequest(); + } - atAddress(atAddress) { - this._atAddress = atAddress - } + atAddress(atAddress) { + this._atAddress = atAddress; + } - foreignKey(foreignKey) { - this._foreignKey = foreignKey - } + foreignKey(foreignKey) { + this._foreignKey = foreignKey; + } - receivingAddress(receivingAddress) { - this._receivingAddress = receivingAddress - } + receivingAddress(receivingAddress) { + this._receivingAddress = receivingAddress; + } - txnRequest() { - return { - atAddress: this._atAddress, - foreignKey: this._foreignKey, - receivingAddress: this._receivingAddress - } - } + txnRequest() { + return { + atAddress: this._atAddress, + foreignKey: this._foreignKey, + receivingAddress: this._receivingAddress, + }; + } } diff --git a/src/transactions/TransactionBase.ts b/src/transactions/TransactionBase.ts index ca9e7ea..055a08b 100644 --- a/src/transactions/TransactionBase.ts +++ b/src/transactions/TransactionBase.ts @@ -1,165 +1,188 @@ // @ts-nocheck -import nacl from '../deps/nacl-fast' -import Base58 from '../deps/Base58' -import utils from '../utils/utils' -import { QORT_DECIMALS, TX_TYPES } from '../constants/constants.js' - +import nacl from '../deps/nacl-fast'; +import Base58 from '../deps/Base58'; +import utils from '../utils/utils'; +import { QORT_DECIMALS, TX_TYPES } from '../constants/constants.js'; export default class TransactionBase { - static get utils() { - return utils - } - static get nacl() { - return nacl - } - static get Base58() { - return Base58 - } + static get utils() { + return utils; + } + static get nacl() { + return nacl; + } + static get Base58() { + return Base58; + } - constructor() { - this.fee = 0 - this.groupID = 0 - this.timestamp = Date.now() - this.tests = [ - () => { - if (!(this._type >= 1 && this._type in TX_TYPES)) { - return 'Invalid type: ' + this.type - } - return true - }, - () => { - if (this._fee < 0) { - return 'Invalid fee: ' + this._fee / QORT_DECIMALS - } - return true - }, - () => { - if (this._groupID < 0 || !Number.isInteger(this._groupID)) { - return 'Invalid groupID: ' + this._groupID - } - return true - }, - () => { - if (!(new Date(this._timestamp)).getTime() > 0) { - return 'Invalid timestamp: ' + this._timestamp - } - return true - }, - () => { - if (!(this._lastReference instanceof Uint8Array && this._lastReference.byteLength == 64)) { - if (this._lastReference == 0) { - return 'Invalid last reference. Please ensure that you have at least 0.001 QORT for the transaction fee.' - } - return 'Invalid last reference: ' + this._lastReference - } - return true - }, - () => { - if (!(this._keyPair)) { - return 'keyPair must be specified' - } - if (!(this._keyPair.publicKey instanceof Uint8Array && this._keyPair.publicKey.byteLength === 32)) { - return 'Invalid publicKey' - } - if (!(this._keyPair.privateKey instanceof Uint8Array && this._keyPair.privateKey.byteLength === 64)) { - return 'Invalid privateKey' - } - return true - } - ] - } + constructor() { + this.fee = 0; + this.groupID = 0; + this.timestamp = Date.now(); + this.tests = [ + () => { + if (!(this._type >= 1 && this._type in TX_TYPES)) { + return 'Invalid type: ' + this.type; + } + return true; + }, + () => { + if (this._fee < 0) { + return 'Invalid fee: ' + this._fee / QORT_DECIMALS; + } + return true; + }, + () => { + if (this._groupID < 0 || !Number.isInteger(this._groupID)) { + return 'Invalid groupID: ' + this._groupID; + } + return true; + }, + () => { + if (!new Date(this._timestamp).getTime() > 0) { + return 'Invalid timestamp: ' + this._timestamp; + } + return true; + }, + () => { + if ( + !( + this._lastReference instanceof Uint8Array && + this._lastReference.byteLength == 64 + ) + ) { + if (this._lastReference == 0) { + return 'Invalid last reference. Please ensure that you have at least 0.001 QORT for the transaction fee.'; + } + return 'Invalid last reference: ' + this._lastReference; + } + return true; + }, + () => { + if (!this._keyPair) { + return 'keyPair must be specified'; + } + if ( + !( + this._keyPair.publicKey instanceof Uint8Array && + this._keyPair.publicKey.byteLength === 32 + ) + ) { + return 'Invalid publicKey'; + } + if ( + !( + this._keyPair.privateKey instanceof Uint8Array && + this._keyPair.privateKey.byteLength === 64 + ) + ) { + return 'Invalid privateKey'; + } + return true; + }, + ]; + } - set keyPair(keyPair) { - this._keyPair = keyPair - } + set keyPair(keyPair) { + this._keyPair = keyPair; + } - set type(type) { - this.typeText = TX_TYPES[type] - this._type = type - this._typeBytes = this.constructor.utils.int32ToBytes(this._type) - } + set type(type) { + this.typeText = TX_TYPES[type]; + this._type = type; + this._typeBytes = this.constructor.utils.int32ToBytes(this._type); + } - set groupID(groupID) { - this._groupID = groupID - this._groupIDBytes = this.constructor.utils.int32ToBytes(this._groupID) - } + set groupID(groupID) { + this._groupID = groupID; + this._groupIDBytes = this.constructor.utils.int32ToBytes(this._groupID); + } - set timestamp(timestamp) { - this._timestamp = timestamp - this._timestampBytes = this.constructor.utils.int64ToBytes(this._timestamp) - } + set timestamp(timestamp) { + this._timestamp = timestamp; + this._timestampBytes = this.constructor.utils.int64ToBytes(this._timestamp); + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set lastReference(lastReference) { - this._lastReference = lastReference instanceof Uint8Array ? lastReference : this.constructor.Base58.decode(lastReference) - } + set lastReference(lastReference) { + this._lastReference = + lastReference instanceof Uint8Array + ? lastReference + : this.constructor.Base58.decode(lastReference); + } - get params() { - return [ - this._typeBytes, - this._timestampBytes, - this._groupIDBytes, - this._lastReference, - this._keyPair.publicKey - ] - } + get params() { + return [ + this._typeBytes, + this._timestampBytes, + this._groupIDBytes, + this._lastReference, + this._keyPair.publicKey, + ]; + } - get signedBytes() { - if (!this._signedBytes) { - this.sign() - } - return this._signedBytes - } + get signedBytes() { + if (!this._signedBytes) { + this.sign(); + } + return this._signedBytes; + } - validParams() { - let finalResult = { - valid: true - } - this.tests.some(test => { - const result = test() - if (result !== true) { - finalResult = { - valid: false, - message: result - } - return true // exists the loop - } - }) - return finalResult - } + validParams() { + let finalResult = { + valid: true, + }; + this.tests.some((test) => { + const result = test(); + if (result !== true) { + finalResult = { + valid: false, + message: result, + }; + return true; // exists the loop + } + }); + return finalResult; + } - generateBase() { - const isValid = this.validParams() - if (!isValid.valid) { - throw new Error(isValid.message) - } - let result = new Uint8Array() + generateBase() { + const isValid = this.validParams(); + if (!isValid.valid) { + throw new Error(isValid.message); + } + let result = new Uint8Array(); - this.params.forEach(item => { - result = this.constructor.utils.appendBuffer(result, item) - }) + this.params.forEach((item) => { + result = this.constructor.utils.appendBuffer(result, item); + }); - this._base = result - return result - } + this._base = result; + return result; + } - sign() { - if (!this._keyPair) { - throw new Error('keyPair not defined') - } + sign() { + if (!this._keyPair) { + throw new Error('keyPair not defined'); + } - if (!this._base) { - this.generateBase() - } + if (!this._base) { + this.generateBase(); + } - this._signature = this.constructor.nacl.sign.detached(this._base, this._keyPair.privateKey) + this._signature = this.constructor.nacl.sign.detached( + this._base, + this._keyPair.privateKey + ); - this._signedBytes = this.constructor.utils.appendBuffer(this._base, this._signature) + this._signedBytes = this.constructor.utils.appendBuffer( + this._base, + this._signature + ); - return this._signature - } + return this._signature; + } } diff --git a/src/transactions/TransferAssetTransaction.ts b/src/transactions/TransferAssetTransaction.ts index 9d0bedb..31bc69a 100644 --- a/src/transactions/TransferAssetTransaction.ts +++ b/src/transactions/TransferAssetTransaction.ts @@ -1,35 +1,37 @@ // @ts-nocheck -import { QORT_DECIMALS } from '../constants/constants' -import TransactionBase from './TransactionBase' - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class TransferAssetTransaction extends TransactionBase { constructor() { - super() - this.type = 12 + super(); + this.type = 12; } set recipient(recipient) { - this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) + this._recipient = + recipient instanceof Uint8Array + ? recipient + : this.constructor.Base58.decode(recipient); } set amount(amount) { - this._amount = Math.round(amount * QORT_DECIMALS) - this._amountBytes = this.constructor.utils.int64ToBytes(this._amount) + this._amount = Math.round(amount * QORT_DECIMALS); + this._amountBytes = this.constructor.utils.int64ToBytes(this._amount); } set assetId(assetId) { - this._assetId = this.constructor.utils.int64ToBytes(assetId) + this._assetId = this.constructor.utils.int64ToBytes(assetId); } get params() { - const params = super.params + const params = super.params; params.push( this._recipient, this._assetId, this._amountBytes, this._feeBytes - ) - return params + ); + return params; } } diff --git a/src/transactions/UpdateGroupTransaction.ts b/src/transactions/UpdateGroupTransaction.ts index 9d9856b..cc0ce48 100644 --- a/src/transactions/UpdateGroupTransaction.ts +++ b/src/transactions/UpdateGroupTransaction.ts @@ -1,62 +1,64 @@ // @ts-nocheck - -import { QORT_DECIMALS } from "../constants/constants"; -import TransactionBase from "./TransactionBase"; - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class UpdateGroupTransaction extends TransactionBase { - constructor() { - super() - this.type = 23 - } + constructor() { + super(); + this.type = 23; + } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } + set newOwner(newOwner) { + this._newOwner = + newOwner instanceof Uint8Array + ? newOwner + : this.constructor.Base58.decode(newOwner); + } + set newIsOpen(newIsOpen) { + this._rGroupType = new Uint8Array(1); + this._rGroupType[0] = newIsOpen; + } + set newDescription(newDescription) { + this._rGroupDescBytes = this.constructor.utils.stringtoUTF8Array( + newDescription.toLocaleLowerCase() + ); + this._rGroupDescLength = this.constructor.utils.int32ToBytes( + this._rGroupDescBytes.length + ); + } + set newApprovalThreshold(newApprovalThreshold) { + this._rGroupApprovalThreshold = new Uint8Array(1); + this._rGroupApprovalThreshold[0] = newApprovalThreshold; + } + set newMinimumBlockDelay(newMinimumBlockDelay) { + this._rGroupMinimumBlockDelayBytes = + this.constructor.utils.int32ToBytes(newMinimumBlockDelay); + } + set newMaximumBlockDelay(newMaximumBlockDelay) { + this._rGroupMaximumBlockDelayBytes = + this.constructor.utils.int32ToBytes(newMaximumBlockDelay); + } - - - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } - set newOwner(newOwner) { - this._newOwner = newOwner instanceof Uint8Array ? newOwner : this.constructor.Base58.decode(newOwner) - } - set newIsOpen(newIsOpen) { - - this._rGroupType = new Uint8Array(1) - this._rGroupType[0] = newIsOpen - } - set newDescription(newDescription) { - this._rGroupDescBytes = this.constructor.utils.stringtoUTF8Array(newDescription.toLocaleLowerCase()) - this._rGroupDescLength = this.constructor.utils.int32ToBytes(this._rGroupDescBytes.length) - } - set newApprovalThreshold(newApprovalThreshold) { - this._rGroupApprovalThreshold = new Uint8Array(1) - this._rGroupApprovalThreshold[0] = newApprovalThreshold; - } - set newMinimumBlockDelay(newMinimumBlockDelay) { - this._rGroupMinimumBlockDelayBytes = this.constructor.utils.int32ToBytes(newMinimumBlockDelay) - } - set newMaximumBlockDelay(newMaximumBlockDelay) { - - this._rGroupMaximumBlockDelayBytes = this.constructor.utils.int32ToBytes(newMaximumBlockDelay) - } - - set _groupId(_groupId){ - this._groupBytes = this.constructor.utils.int32ToBytes(_groupId) - } - get params() { - const params = super.params - params.push( - this._groupBytes, - this._newOwner, - this._rGroupDescLength, - this._rGroupDescBytes, - this._rGroupType, - this._rGroupApprovalThreshold, - this._rGroupMinimumBlockDelayBytes, - this._rGroupMaximumBlockDelayBytes, - this._feeBytes - ) - return params - } -} \ No newline at end of file + set _groupId(_groupId) { + this._groupBytes = this.constructor.utils.int32ToBytes(_groupId); + } + get params() { + const params = super.params; + params.push( + this._groupBytes, + this._newOwner, + this._rGroupDescLength, + this._rGroupDescBytes, + this._rGroupType, + this._rGroupApprovalThreshold, + this._rGroupMinimumBlockDelayBytes, + this._rGroupMaximumBlockDelayBytes, + this._feeBytes + ); + return params; + } +} diff --git a/src/transactions/UpdateNameTransaction.ts b/src/transactions/UpdateNameTransaction.ts index e90b388..1aee16e 100644 --- a/src/transactions/UpdateNameTransaction.ts +++ b/src/transactions/UpdateNameTransaction.ts @@ -1,51 +1,56 @@ // @ts-nocheck -import { QORT_DECIMALS } from "../constants/constants" -import TransactionBase from "./TransactionBase" - - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class UpdateNameTransaction extends TransactionBase { - constructor() { - super() - this.type = 4 - } + constructor() { + super(); + this.type = 4; + } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } + set name(name) { + this.nameText = name; + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name); + this._nameLength = this.constructor.utils.int32ToBytes( + this._nameBytes.length + ); + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set newName(newName) { + this.newNameText = newName; + this._newNameBytes = this.constructor.utils.stringtoUTF8Array(newName); + this._newNameLength = this.constructor.utils.int32ToBytes( + this._newNameBytes.length + ); + } - set name(name) { - this.nameText = name - this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) - this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) - } + set newData(newData) { + this.newDataText = + newData.length === 0 ? 'Registered Name on the Qortal Chain' : newData; + this._newDataBytes = this.constructor.utils.stringtoUTF8Array( + this.newDataText + ); + this._newDataLength = this.constructor.utils.int32ToBytes( + this._newDataBytes.length + ); + } - set newName(newName) { - this.newNameText = newName - this._newNameBytes = this.constructor.utils.stringtoUTF8Array(newName) - this._newNameLength = this.constructor.utils.int32ToBytes(this._newNameBytes.length) - } - - set newData(newData) { - this.newDataText = newData.length === 0 ? "Registered Name on the Qortal Chain" : newData - this._newDataBytes = this.constructor.utils.stringtoUTF8Array(this.newDataText) - this._newDataLength = this.constructor.utils.int32ToBytes(this._newDataBytes.length) - } - - get params() { - const params = super.params - params.push( - this._nameLength, - this._nameBytes, - this._newNameLength, - this._newNameBytes, - this._newDataLength, - this._newDataBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._nameLength, + this._nameBytes, + this._newNameLength, + this._newNameBytes, + this._newDataLength, + this._newDataBytes, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/VoteOnPollTransaction.ts b/src/transactions/VoteOnPollTransaction.ts index 327295e..5b37163 100644 --- a/src/transactions/VoteOnPollTransaction.ts +++ b/src/transactions/VoteOnPollTransaction.ts @@ -1,38 +1,42 @@ // @ts-nocheck -import { QORT_DECIMALS } from '../constants/constants' -import TransactionBase from './TransactionBase' - +import { QORT_DECIMALS } from '../constants/constants'; +import TransactionBase from './TransactionBase'; export default class VoteOnPollTransaction extends TransactionBase { - constructor() { - super() - this.type = 9 - } + constructor() { + super(); + this.type = 9; + } + set fee(fee) { + this._fee = fee * QORT_DECIMALS; + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee); + } - set fee(fee) { - this._fee = fee * QORT_DECIMALS - this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) - } + set rPollName(rPollName) { + this._rPollName = rPollName; + this._rPollNameBytes = this.constructor.utils.stringtoUTF8Array( + this._rPollName + ); + this._rPollNameLength = this.constructor.utils.int32ToBytes( + this._rPollNameBytes.length + ); + } - set rPollName(rPollName) { - this._rPollName = rPollName - this._rPollNameBytes = this.constructor.utils.stringtoUTF8Array(this._rPollName) - this._rPollNameLength = this.constructor.utils.int32ToBytes(this._rPollNameBytes.length) - } + set rOptionIndex(rOptionIndex) { + this._rOptionIndex = rOptionIndex; + this._rOptionIndexBytes = this.constructor.utils.int32ToBytes( + this._rOptionIndex + ); + } - set rOptionIndex(rOptionIndex) { - this._rOptionIndex = rOptionIndex - this._rOptionIndexBytes = this.constructor.utils.int32ToBytes(this._rOptionIndex) - } - - get params() { - const params = super.params - params.push( - this._rPollNameLength, - this._rPollNameBytes, - this._rOptionIndexBytes, - this._feeBytes - ) - return params - } + get params() { + const params = super.params; + params.push( + this._rPollNameLength, + this._rPollNameBytes, + this._rOptionIndexBytes, + this._feeBytes + ); + return params; + } } diff --git a/src/transactions/signChat.ts b/src/transactions/signChat.ts index 8814c2a..5995128 100644 --- a/src/transactions/signChat.ts +++ b/src/transactions/signChat.ts @@ -1,39 +1,40 @@ // @ts-nocheck -import nacl from '../deps/nacl-fast' -import utils from '../utils/utils' +import nacl from '../deps/nacl-fast'; +import utils from '../utils/utils'; export const signChat = (chatBytes, nonce, keyPair) => { - if (!chatBytes) { - throw new Error('Chat Bytes not defined') - } + if (!chatBytes) { + throw new Error('Chat Bytes not defined'); + } - if (!nonce) { - throw new Error('Nonce not defined') - } + if (!nonce) { + throw new Error('Nonce not defined'); + } - if (!keyPair) { - throw new Error('keyPair not defined') - } + if (!keyPair) { + throw new Error('keyPair not defined'); + } - const _nonce = utils.int32ToBytes(nonce) + const _nonce = utils.int32ToBytes(nonce); - if (chatBytes.length === undefined) { - const _chatBytesBuffer = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; }) + if (chatBytes.length === undefined) { + const _chatBytesBuffer = Object.keys(chatBytes).map(function (key) { + return chatBytes[key]; + }); - const chatBytesBuffer = new Uint8Array(_chatBytesBuffer) - chatBytesBuffer.set(_nonce, 112) + const chatBytesBuffer = new Uint8Array(_chatBytesBuffer); + chatBytesBuffer.set(_nonce, 112); - const signature = nacl.sign.detached(chatBytesBuffer, keyPair.privateKey) + const signature = nacl.sign.detached(chatBytesBuffer, keyPair.privateKey); - return utils.appendBuffer(chatBytesBuffer, signature) - } else { - const chatBytesBuffer = new Uint8Array(chatBytes) - chatBytesBuffer.set(_nonce, 112) + return utils.appendBuffer(chatBytesBuffer, signature); + } else { + const chatBytesBuffer = new Uint8Array(chatBytes); + chatBytesBuffer.set(_nonce, 112); - const signature = nacl.sign.detached(chatBytesBuffer, keyPair.privateKey) - - return utils.appendBuffer(chatBytesBuffer, signature) - } -} + const signature = nacl.sign.detached(chatBytesBuffer, keyPair.privateKey); + return utils.appendBuffer(chatBytesBuffer, signature); + } +}; diff --git a/src/transactions/signTradeBotTransaction.ts b/src/transactions/signTradeBotTransaction.ts index 929898f..31b7430 100644 --- a/src/transactions/signTradeBotTransaction.ts +++ b/src/transactions/signTradeBotTransaction.ts @@ -1,30 +1,31 @@ // @ts-nocheck - -import nacl from '../deps/nacl-fast' -import Base58 from '../deps/Base58' -import utils from '../utils/utils' +import nacl from '../deps/nacl-fast'; +import Base58 from '../deps/Base58'; +import utils from '../utils/utils'; const signTradeBotTransaction = async (unsignedTxn, keyPair) => { - if (!unsignedTxn) { - throw new Error('Unsigned Transaction Bytes not defined') - } + if (!unsignedTxn) { + throw new Error('Unsigned Transaction Bytes not defined'); + } - if (!keyPair) { - throw new Error('keyPair not defined') - } + if (!keyPair) { + throw new Error('keyPair not defined'); + } - const txnBuffer = Base58.decode(unsignedTxn) + const txnBuffer = Base58.decode(unsignedTxn); - if (keyPair.privateKey.length === undefined) { - const _privateKey = Object.keys(keyPair.privateKey).map(function (key) { return keyPair.privateKey[key] }) - const privateKey = new Uint8Array(_privateKey) - const signature = nacl.sign.detached(txnBuffer, privateKey) - return utils.appendBuffer(txnBuffer, signature) - } else { - const signature = nacl.sign.detached(txnBuffer, keyPair.privateKey) - return utils.appendBuffer(txnBuffer, signature) - } -} + if (keyPair.privateKey.length === undefined) { + const _privateKey = Object.keys(keyPair.privateKey).map(function (key) { + return keyPair.privateKey[key]; + }); + const privateKey = new Uint8Array(_privateKey); + const signature = nacl.sign.detached(txnBuffer, privateKey); + return utils.appendBuffer(txnBuffer, signature); + } else { + const signature = nacl.sign.detached(txnBuffer, keyPair.privateKey); + return utils.appendBuffer(txnBuffer, signature); + } +}; -export default signTradeBotTransaction +export default signTradeBotTransaction; diff --git a/src/transactions/transactions.ts b/src/transactions/transactions.ts index a117560..816e2b6 100644 --- a/src/transactions/transactions.ts +++ b/src/transactions/transactions.ts @@ -1,68 +1,65 @@ // @ts-nocheck -import PaymentTransaction from './PaymentTransaction.js' -import ChatTransaction from './ChatTransaction.js' -import GroupChatTransaction from './GroupChatTransaction.js' -import GroupInviteTransaction from './GroupInviteTransaction.js' -import CancelGroupInviteTransaction from './CancelGroupInviteTransaction.js' -import GroupKickTransaction from './GroupKickTransaction.js' -import GroupBanTransaction from './GroupBanTransaction.js' -import CancelGroupBanTransaction from './CancelGroupBanTransaction.js' -import CreateGroupTransaction from './CreateGroupTransaction.js' -import LeaveGroupTransaction from './LeaveGroupTransaction.js' -import JoinGroupTransaction from './JoinGroupTransaction.js' -import AddGroupAdminTransaction from './AddGroupAdminTransaction.js' -import RemoveGroupAdminTransaction from './RemoveGroupAdminTransaction.js' -import RegisterNameTransaction from './RegisterNameTransaction.js' -import VoteOnPollTransaction from './VoteOnPollTransaction.js' -import CreatePollTransaction from './CreatePollTransaction.js' -import DeployAtTransaction from './DeployAtTransaction.js' -import RewardShareTransaction from './RewardShareTransaction.js' -import RemoveRewardShareTransaction from './RemoveRewardShareTransaction.js' -import UpdateNameTransaction from './UpdateNameTransaction.js' -import UpdateGroupTransaction from './UpdateGroupTransaction.js' -import SellNameTransacion from './SellNameTransacion.js' -import CancelSellNameTransacion from './CancelSellNameTransacion.js' -import BuyNameTransacion from './BuyNameTransacion.js' -import TransferAssetTransaction from './TransferAssetTransaction.js' - +import PaymentTransaction from './PaymentTransaction.js'; +import ChatTransaction from './ChatTransaction.js'; +import GroupChatTransaction from './GroupChatTransaction.js'; +import GroupInviteTransaction from './GroupInviteTransaction.js'; +import CancelGroupInviteTransaction from './CancelGroupInviteTransaction.js'; +import GroupKickTransaction from './GroupKickTransaction.js'; +import GroupBanTransaction from './GroupBanTransaction.js'; +import CancelGroupBanTransaction from './CancelGroupBanTransaction.js'; +import CreateGroupTransaction from './CreateGroupTransaction.js'; +import LeaveGroupTransaction from './LeaveGroupTransaction.js'; +import JoinGroupTransaction from './JoinGroupTransaction.js'; +import AddGroupAdminTransaction from './AddGroupAdminTransaction.js'; +import RemoveGroupAdminTransaction from './RemoveGroupAdminTransaction.js'; +import RegisterNameTransaction from './RegisterNameTransaction.js'; +import VoteOnPollTransaction from './VoteOnPollTransaction.js'; +import CreatePollTransaction from './CreatePollTransaction.js'; +import DeployAtTransaction from './DeployAtTransaction.js'; +import RewardShareTransaction from './RewardShareTransaction.js'; +import RemoveRewardShareTransaction from './RemoveRewardShareTransaction.js'; +import UpdateNameTransaction from './UpdateNameTransaction.js'; +import UpdateGroupTransaction from './UpdateGroupTransaction.js'; +import SellNameTransacion from './SellNameTransacion.js'; +import CancelSellNameTransacion from './CancelSellNameTransacion.js'; +import BuyNameTransacion from './BuyNameTransacion.js'; +import TransferAssetTransaction from './TransferAssetTransaction.js'; export const transactionTypes = { - 2: PaymentTransaction, - 3: RegisterNameTransaction, - 4: UpdateNameTransaction, - 5: SellNameTransacion, - 6: CancelSellNameTransacion, - 7: BuyNameTransacion, - 8: CreatePollTransaction, - 9: VoteOnPollTransaction, - 12: TransferAssetTransaction, - 16: DeployAtTransaction, - 18: ChatTransaction, - 181: GroupChatTransaction, - 22: CreateGroupTransaction, - 23: UpdateGroupTransaction, - 24: AddGroupAdminTransaction, - 25: RemoveGroupAdminTransaction, - 26: GroupBanTransaction, - 27: CancelGroupBanTransaction, - 28: GroupKickTransaction, - 29: GroupInviteTransaction, - 30: CancelGroupInviteTransaction, - 31: JoinGroupTransaction, - 32: LeaveGroupTransaction, - 38: RewardShareTransaction, - 381: RemoveRewardShareTransaction -} - + 2: PaymentTransaction, + 3: RegisterNameTransaction, + 4: UpdateNameTransaction, + 5: SellNameTransacion, + 6: CancelSellNameTransacion, + 7: BuyNameTransacion, + 8: CreatePollTransaction, + 9: VoteOnPollTransaction, + 12: TransferAssetTransaction, + 16: DeployAtTransaction, + 18: ChatTransaction, + 181: GroupChatTransaction, + 22: CreateGroupTransaction, + 23: UpdateGroupTransaction, + 24: AddGroupAdminTransaction, + 25: RemoveGroupAdminTransaction, + 26: GroupBanTransaction, + 27: CancelGroupBanTransaction, + 28: GroupKickTransaction, + 29: GroupInviteTransaction, + 30: CancelGroupInviteTransaction, + 31: JoinGroupTransaction, + 32: LeaveGroupTransaction, + 38: RewardShareTransaction, + 381: RemoveRewardShareTransaction, +}; export const createTransaction = (type, keyPair, params) => { - const tx = new transactionTypes[type]() - tx.keyPair = keyPair - Object.keys(params).forEach(param => { - - tx[param] = params[param] - }) + const tx = new transactionTypes[type](); + tx.keyPair = keyPair; + Object.keys(params).forEach((param) => { + tx[param] = params[param]; + }); - return tx -} \ No newline at end of file + return tx; +}; diff --git a/src/utils/decode.ts b/src/utils/decode.ts index ba8d87d..d457cee 100644 --- a/src/utils/decode.ts +++ b/src/utils/decode.ts @@ -17,7 +17,6 @@ export function decodeIfEncoded(input) { export const isValidBase64 = (str: string): boolean => { if (typeof str !== 'string' || str.length % 4 !== 0) return false; - const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/; return base64Regex.test(str); }; diff --git a/src/utils/generateWallet/generateWallet.ts b/src/utils/generateWallet/generateWallet.ts index f05c707..ee3a683 100644 --- a/src/utils/generateWallet/generateWallet.ts +++ b/src/utils/generateWallet/generateWallet.ts @@ -5,119 +5,123 @@ import { doInitWorkers, kdf } from '../../deps/kdf'; import PhraseWallet from './phrase-wallet'; import * as WORDLISTS from './wordlists'; import FileSaver from 'file-saver'; - import { Filesystem, Directory, Encoding } from '@capacitor/filesystem'; import { mimeToExtensionMap } from '../memeTypes'; -export function generateRandomSentence(template = 'adverb verb noun adjective noun adverb verb noun adjective noun adjective verbed adjective noun', maxWordLength = 0, capitalize = true) { - const partsOfSpeechMap = { - 'noun': 'nouns', - 'adverb': 'adverbs', - 'adv': 'adverbs', - 'verb': 'verbs', - 'interjection': 'interjections', - 'adjective': 'adjectives', - 'adj': 'adjectives', - 'verbed': 'verbed' - }; - let _wordlists = WORDLISTS; - - function _RNG(entropy) { - if (entropy > 1074) { - throw new Error('Javascript can not handle that much entropy!'); - } - let randNum = 0; - const crypto = window.crypto || window.msCrypto; - - if (crypto) { - const entropy256 = Math.ceil(entropy / 8); - let buffer = new Uint8Array(entropy256); - crypto.getRandomValues(buffer); - randNum = buffer.reduce((num, value) => num * 256 + value, 0) / Math.pow(256, entropy256); - } else { - console.warn('Secure RNG not found. Using Math.random'); - randNum = Math.random(); - } - return randNum; - } - - function _capitalize(str) { - return str.charAt(0).toUpperCase() + str.slice(1); - } - - function getWord(partOfSpeech) { - let words = _wordlists[partsOfSpeechMap[partOfSpeech]]; - if (maxWordLength) { - words = words.filter(word => word.length <= maxWordLength); - } - const requiredEntropy = Math.log(words.length) / Math.log(2); - const index = Math.floor(_RNG(requiredEntropy) * words.length); - return words[index]; - } - - function parse(template) { - return template.split(/\s+/).map(token => { - const match = token.match(/^(\w+)(.*)$/); - if (!match) return token; // No match, return original token - - const [ , partOfSpeech, rest ] = match; - if (partsOfSpeechMap[partOfSpeech]) { - let word = getWord(partOfSpeech); - if (capitalize && token === token[0].toUpperCase() + token.slice(1).toLowerCase()) { - word = _capitalize(word); - } - return word + rest; - } - - return token; - }).join(' '); - } - - return parse(template); -} - -export const createAccount = async(generatedSeedPhrase)=> { - if(!generatedSeedPhrase) throw new Error('No generated seed-phrase') - const threads = doInitWorkers(crypto.kdfThreads) - - const seed = await kdf(generatedSeedPhrase, void 0, threads) - const wallet = new PhraseWallet(seed, walletVersion) - return wallet - - } - - const hasExtension = (filename) => { - return filename.includes(".") && filename.split(".").pop().length > 0; +export function generateRandomSentence( + template = 'adverb verb noun adjective noun adverb verb noun adjective noun adjective verbed adjective noun', + maxWordLength = 0, + capitalize = true +) { + const partsOfSpeechMap = { + noun: 'nouns', + adverb: 'adverbs', + adv: 'adverbs', + verb: 'verbs', + interjection: 'interjections', + adjective: 'adjectives', + adj: 'adjectives', + verbed: 'verbed', }; - export const saveFileToDisk = async (data, qortAddress) => { - - const dataString = JSON.stringify(data); - const blob = new Blob([dataString], { type: 'application/json' }); - const fileName = "qortal_backup_" + qortAddress + ".json"; + let _wordlists = WORDLISTS; - await FileSaver.saveAs(blob, fileName); - + function _RNG(entropy) { + if (entropy > 1074) { + throw new Error('Javascript can not handle that much entropy!'); + } + let randNum = 0; + const crypto = window.crypto || window.msCrypto; + + if (crypto) { + const entropy256 = Math.ceil(entropy / 8); + let buffer = new Uint8Array(entropy256); + crypto.getRandomValues(buffer); + randNum = + buffer.reduce((num, value) => num * 256 + value, 0) / + Math.pow(256, entropy256); + } else { + console.warn('Secure RNG not found. Using Math.random'); + randNum = Math.random(); + } + return randNum; + } + + function _capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); + } + + function getWord(partOfSpeech) { + let words = _wordlists[partsOfSpeechMap[partOfSpeech]]; + if (maxWordLength) { + words = words.filter((word) => word.length <= maxWordLength); + } + const requiredEntropy = Math.log(words.length) / Math.log(2); + const index = Math.floor(_RNG(requiredEntropy) * words.length); + return words[index]; + } + + function parse(template) { + return template + .split(/\s+/) + .map((token) => { + const match = token.match(/^(\w+)(.*)$/); + if (!match) return token; // No match, return original token + + const [, partOfSpeech, rest] = match; + if (partsOfSpeechMap[partOfSpeech]) { + let word = getWord(partOfSpeech); + if ( + capitalize && + token === token[0].toUpperCase() + token.slice(1).toLowerCase() + ) { + word = _capitalize(word); + } + return word + rest; + } + + return token; + }) + .join(' '); + } + + return parse(template); } +export const createAccount = async (generatedSeedPhrase) => { + if (!generatedSeedPhrase) throw new Error('No generated seed-phrase'); + const threads = doInitWorkers(crypto.kdfThreads); + + const seed = await kdf(generatedSeedPhrase, void 0, threads); + const wallet = new PhraseWallet(seed, walletVersion); + return wallet; +}; + +const hasExtension = (filename) => { + return filename.includes('.') && filename.split('.').pop().length > 0; +}; + +export const saveFileToDisk = async (data, qortAddress) => { + const dataString = JSON.stringify(data); + const blob = new Blob([dataString], { type: 'application/json' }); + const fileName = 'qortal_backup_' + qortAddress + '.json'; + + await FileSaver.saveAs(blob, fileName); +}; + export const saveFileToDiskGeneric = async (blob, filename) => { - const timestamp = new Date() - .toISOString() - .replace(/:/g, "-"); // Safe timestamp for filenames - - const fileExtension = mimeToExtensionMap[blob.type] -let fileName = filename || "qortal_file_" + timestamp + "." + fileExtension; -fileName = hasExtension(fileName) ? fileName : fileName + "." + fileExtension; + const timestamp = new Date().toISOString().replace(/:/g, '-'); // Safe timestamp for filenames -await FileSaver.saveAs(blob, fileName); + const fileExtension = mimeToExtensionMap[blob.type]; + let fileName = filename || 'qortal_file_' + timestamp + '.' + fileExtension; + fileName = hasExtension(fileName) ? fileName : fileName + '.' + fileExtension; -} + await FileSaver.saveAs(blob, fileName); +}; export const saveSeedPhraseToDisk = async (data) => { - - const blob = new Blob([data], { type: 'text/plain;charset=utf-8' }) - const fileName = "qortal_seedphrase.txt" + const blob = new Blob([data], { type: 'text/plain;charset=utf-8' }); + const fileName = 'qortal_seedphrase.txt'; -await FileSaver.saveAs(blob, fileName); - -} \ No newline at end of file + await FileSaver.saveAs(blob, fileName); +}; diff --git a/src/utils/generateWallet/storeWallet.ts b/src/utils/generateWallet/storeWallet.ts index 83222ae..458543f 100644 --- a/src/utils/generateWallet/storeWallet.ts +++ b/src/utils/generateWallet/storeWallet.ts @@ -1,31 +1,37 @@ // @ts-nocheck -import {AES_CBC, HmacSha512} from 'asmcrypto.js' +import { AES_CBC, HmacSha512 } from 'asmcrypto.js'; +import Base58 from '../../deps/Base58'; +import { doInitWorkers, kdf } from '../../deps/kdf.js'; +import { crypto as cryptoVals } from '../../constants/decryptWallet.js'; -import Base58 from '../../deps/Base58' -import { doInitWorkers, kdf } from '../../deps/kdf.js' -import { crypto as cryptoVals } from '../../constants/decryptWallet.js' - -const getRandomValues = crypto ? crypto.getRandomValues.bind(crypto) : msCrypto.getRandomValues.bind(msCrypto) +const getRandomValues = crypto + ? crypto.getRandomValues.bind(crypto) + : msCrypto.getRandomValues.bind(msCrypto); export const generateSaveWalletData = async (wallet, password, kdfThreads) => { - const threads = doInitWorkers(cryptoVals.kdfThreads) + const threads = doInitWorkers(cryptoVals.kdfThreads); - let iv = new Uint8Array(16) - getRandomValues(iv) - let salt = new Uint8Array(32) - getRandomValues(salt) - const key = await kdf(password, salt, threads) - const encryptionKey = key.slice(0, 32) - const macKey = key.slice(32, 63) - const encryptedSeed = AES_CBC.encrypt(wallet._byteSeed, encryptionKey, false, iv) - const mac = new HmacSha512(macKey).process(encryptedSeed).finish().result - return { - address0: wallet._addresses[0].address, - encryptedSeed: Base58.encode(encryptedSeed), - salt: Base58.encode(salt), - iv: Base58.encode(iv), - version: wallet._walletVersion, - mac: Base58.encode(mac), - kdfThreads - } -} + let iv = new Uint8Array(16); + getRandomValues(iv); + let salt = new Uint8Array(32); + getRandomValues(salt); + const key = await kdf(password, salt, threads); + const encryptionKey = key.slice(0, 32); + const macKey = key.slice(32, 63); + const encryptedSeed = AES_CBC.encrypt( + wallet._byteSeed, + encryptionKey, + false, + iv + ); + const mac = new HmacSha512(macKey).process(encryptedSeed).finish().result; + return { + address0: wallet._addresses[0].address, + encryptedSeed: Base58.encode(encryptedSeed), + salt: Base58.encode(salt), + iv: Base58.encode(iv), + version: wallet._walletVersion, + mac: Base58.encode(mac), + kdfThreads, + }; +}; diff --git a/src/utils/generateWallet/verb-past-tense.ts b/src/utils/generateWallet/verb-past-tense.ts index 2e9da0c..b3b12b3 100644 --- a/src/utils/generateWallet/verb-past-tense.ts +++ b/src/utils/generateWallet/verb-past-tense.ts @@ -2,36 +2,36 @@ // @ts-nocheck export const EXCEPTIONS = { - 'are': 'were', - 'eat': 'ate', - 'go': 'went', - 'have': 'had', - 'inherit': 'inherited', - 'is': 'was', - 'run': 'ran', - 'sit': 'sat', - 'visit': 'visited' -} + are: 'were', + eat: 'ate', + go: 'went', + have: 'had', + inherit: 'inherited', + is: 'was', + run: 'ran', + sit: 'sat', + visit: 'visited', +}; export const getPastTense = (verb, exceptions = EXCEPTIONS) => { - if (exceptions[verb]) { - return exceptions[verb] - } - if ((/e$/i).test(verb)) { - return verb + 'd' - } - if ((/[aeiou]c$/i).test(verb)) { - return verb + 'ked' - } - // for american english only - if ((/el$/i).test(verb)) { - return verb + 'ed' - } - if ((/[aeio][aeiou][dlmnprst]$/).test(verb)) { - return verb + 'ed' - } - if ((/[aeiou][bdglmnprst]$/i).test(verb)) { - return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed') - } - return verb + 'ed' -} + if (exceptions[verb]) { + return exceptions[verb]; + } + if (/e$/i.test(verb)) { + return verb + 'd'; + } + if (/[aeiou]c$/i.test(verb)) { + return verb + 'ked'; + } + // for american english only + if (/el$/i.test(verb)) { + return verb + 'ed'; + } + if (/[aeio][aeiou][dlmnprst]$/.test(verb)) { + return verb + 'ed'; + } + if (/[aeiou][bdglmnprst]$/i.test(verb)) { + return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed'); + } + return verb + 'ed'; +}; diff --git a/src/utils/qortalLink/index.ts b/src/utils/qortalLink/index.ts index bc2f132..0b884d6 100644 --- a/src/utils/qortalLink/index.ts +++ b/src/utils/qortalLink/index.ts @@ -1,12 +1,12 @@ export function convertQortalLinks(inputHtml: string) { - // Regular expression to match 'qortal://...' URLs. - // This will stop at the first whitespace, comma, or HTML tag - var regex = /(qortal:\/\/[^\s,<]+)/g; + // Regular expression to match 'qortal://...' URLs. + // This will stop at the first whitespace, comma, or HTML tag + var regex = /(qortal:\/\/[^\s,<]+)/g; - // Replace matches in inputHtml with formatted anchor tag - var outputHtml = inputHtml.replace(regex, function (match) { - return `${match}`; - }); + // Replace matches in inputHtml with formatted anchor tag + var outputHtml = inputHtml.replace(regex, function (match) { + return `${match}`; + }); - return outputHtml; -} \ No newline at end of file + return outputHtml; +} diff --git a/src/utils/seedPhrase/RandomSentenceGenerator.ts b/src/utils/seedPhrase/RandomSentenceGenerator.ts index 8fdf71d..86ba436 100644 --- a/src/utils/seedPhrase/RandomSentenceGenerator.ts +++ b/src/utils/seedPhrase/RandomSentenceGenerator.ts @@ -1,173 +1,182 @@ // Author: irontiga -import { html, LitElement, css } from 'lit' -import * as WORDLISTS from './wordList' +import { html, LitElement, css } from 'lit'; +import * as WORDLISTS from './wordList'; class RandomSentenceGenerator extends LitElement { - static get properties() { - return { - template: { type: String, attribute: 'template' }, - parsedString: { type: String }, - fetchedWordlistCount: { type: Number, value: 0 }, - capitalize: { type: Boolean }, - partsOfSpeechMap: { type: Object }, - templateEntropy: { type: Number, reflect: true, attribute: 'template-entropy' }, - maxWordLength: { type: Number, attribute: 'max-word-length' } - } - } + static get properties() { + return { + template: { type: String, attribute: 'template' }, + parsedString: { type: String }, + fetchedWordlistCount: { type: Number, value: 0 }, + capitalize: { type: Boolean }, + partsOfSpeechMap: { type: Object }, + templateEntropy: { + type: Number, + reflect: true, + attribute: 'template-entropy', + }, + maxWordLength: { type: Number, attribute: 'max-word-length' }, + }; + } - constructor() { - super() - this.template = 'adjective noun verb adverb.' - this.maxWordLength = 0 - this.parsedString = '' - this.fetchedWordlistCount = 0 - this.capitalize = true - this.partsOfSpeechMap = { - 'noun': 'nouns', - 'adverb': 'adverbs', - 'adv': 'adverbs', - 'verb': 'verbs', - 'interjection': 'interjections', - 'adjective': 'adjectives', - 'adj': 'adjectives', - 'verbed': 'verbed' - } - this.partsOfSpeech = Object.keys(this.partsOfSpeechMap) - this._wordlists = WORDLISTS - } + constructor() { + super(); + this.template = 'adjective noun verb adverb.'; + this.maxWordLength = 0; + this.parsedString = ''; + this.fetchedWordlistCount = 0; + this.capitalize = true; + this.partsOfSpeechMap = { + noun: 'nouns', + adverb: 'adverbs', + adv: 'adverbs', + verb: 'verbs', + interjection: 'interjections', + adjective: 'adjectives', + adj: 'adjectives', + verbed: 'verbed', + }; + this.partsOfSpeech = Object.keys(this.partsOfSpeechMap); + this._wordlists = WORDLISTS; + } - static styles = css` + static styles = css` div { text-align: center; - width: 100%; - background-color: #1f2023; - border-radius: 5px; - padding: 10px; + width: 100%; + background-color: #1f2023; + border-radius: 5px; + padding: 10px; } `; - render() { - return html` -
${this.parsedString}
- ` - } + render() { + return html`
${this.parsedString}
`; + } - + firstUpdated() { + // ... + } - firstUpdated() { - // ... - } + updated(changedProperties) { + let regen = false; - updated(changedProperties) { - let regen = false + if (changedProperties.has('template')) { + regen = true; + } - if (changedProperties.has('template')) { - regen = true - } + if (changedProperties.has('maxWordLength')) { + console.dir(this.maxWordLength); - if (changedProperties.has('maxWordLength')) { - console.dir(this.maxWordLength) + if (this.maxWordLength) { + const wl = { ...this._wordlists }; - if (this.maxWordLength) { - const wl = { ...this._wordlists } + for (const partOfSpeech in this._wordlists) { + if (Array.isArray(this._wordlists[partOfSpeech])) { + wl[partOfSpeech] = this._wordlists[partOfSpeech].filter( + (word) => word.length <= this.maxWordLength + ); + } + } - for (const partOfSpeech in this._wordlists) { - if (Array.isArray(this._wordlists[partOfSpeech])) { - wl[partOfSpeech] = this._wordlists[partOfSpeech].filter(word => word.length <= this.maxWordLength) - } - } + this._wordlists = wl; + } - this._wordlists = wl - } + regen = true; + } - regen = true - } + if (regen) this.generate(); + } - if (regen) this.generate() - } + _RNG(entropy) { + if (entropy > 1074) { + throw new Error('Javascript can not handle that much entropy!'); + } - _RNG(entropy) { - if (entropy > 1074) { - throw new Error('Javascript can not handle that much entropy!') - } + let randNum = 0; - let randNum = 0 + const crypto = window.crypto || window.msCrypto; - const crypto = window.crypto || window.msCrypto + if (crypto) { + const entropy256 = Math.ceil(entropy / 8); - if (crypto) { - const entropy256 = Math.ceil(entropy / 8) + let buffer = new Uint8Array(entropy256); - let buffer = new Uint8Array(entropy256) + crypto.getRandomValues(buffer); - crypto.getRandomValues(buffer) + randNum = + buffer.reduce((num, value) => { + return num * value; + }, 1) / Math.pow(256, entropy256); + } else { + console.warn('Secure RNG not found. Using Math.random'); - randNum = buffer.reduce((num, value) => { - return num * value - }, 1) / Math.pow(256, entropy256) - } else { - console.warn('Secure RNG not found. Using Math.random') + randNum = Math.random(); + } - randNum = Math.random() - } + return randNum; + } - return randNum - } + setRNG(fn) { + this._RNG = fn; + } - setRNG(fn) { - this._RNG = fn - } + _captitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); + } - _captitalize(str) { - return str.charAt(0).toUpperCase() + str.slice(1) - } + getWord(partOfSpeech) { + const words = this._wordlists[this.partsOfSpeechMap[partOfSpeech]]; + const requiredEntropy = Math.log(words.length) / Math.log(2); + const index = this._RNG(requiredEntropy) * words.length; - getWord(partOfSpeech) { - const words = this._wordlists[this.partsOfSpeechMap[partOfSpeech]] - const requiredEntropy = Math.log(words.length) / Math.log(2) - const index = this._RNG(requiredEntropy) * words.length + return { + word: words[Math.round(index)], + entropy: words.length, + }; + } - return { - word: words[Math.round(index)], - entropy: words.length - } - } + generate() { + this.parsedString = this.parse(this.template); + } - generate() { - this.parsedString = this.parse(this.template) - } + parse(template) { + const split = template.split(/[\s]/g); - parse(template) { - const split = template.split(/[\s]/g) + let entropy = 1; - let entropy = 1 + const final = split.map((word) => { + const lower = word.toLowerCase(); - const final = split.map(word => { - const lower = word.toLowerCase() + this.partsOfSpeech.some((partOfSpeech) => { + const partOfSpeechIndex = lower.indexOf(partOfSpeech); // Check it exists + const nextChar = word.charAt(partOfSpeech.length); - this.partsOfSpeech.some(partOfSpeech => { - const partOfSpeechIndex = lower.indexOf(partOfSpeech) // Check it exists - const nextChar = word.charAt(partOfSpeech.length) + if ( + partOfSpeechIndex === 0 && + !(nextChar && nextChar.match(/[a-zA-Z]/g) != null) + ) { + const replacement = this.getWord(partOfSpeech); + word = replacement.word + word.slice(partOfSpeech.length); // Append the rest of the "word" (punctuation) + entropy = entropy * replacement.entropy; - if (partOfSpeechIndex === 0 && !(nextChar && (nextChar.match(/[a-zA-Z]/g) != null))) { - const replacement = this.getWord(partOfSpeech) - word = replacement.word + word.slice(partOfSpeech.length) // Append the rest of the "word" (punctuation) - entropy = entropy * replacement.entropy + return true; + } + }); - return true - } - }) + return word; + }); - return word - }) + this.templateEntropy = Math.floor(Math.log(entropy) / Math.log(8)); - this.templateEntropy = Math.floor(Math.log(entropy) / Math.log(8)) - - return final.join(' ') - } + return final.join(' '); + } } -window.customElements.define('random-sentence-generator', RandomSentenceGenerator) +window.customElements.define( + 'random-sentence-generator', + RandomSentenceGenerator +); -export default RandomSentenceGenerator \ No newline at end of file +export default RandomSentenceGenerator; diff --git a/src/utils/seedPhrase/verb-past-tense.ts b/src/utils/seedPhrase/verb-past-tense.ts index c5b279f..5b55086 100644 --- a/src/utils/seedPhrase/verb-past-tense.ts +++ b/src/utils/seedPhrase/verb-past-tense.ts @@ -1,40 +1,40 @@ export const EXCEPTIONS = { - 'are': 'were', - 'eat': 'ate', - 'go': 'went', - 'have': 'had', - 'inherit': 'inherited', - 'is': 'was', - 'run': 'ran', - 'sit': 'sat', - 'visit': 'visited' -} + are: 'were', + eat: 'ate', + go: 'went', + have: 'had', + inherit: 'inherited', + is: 'was', + run: 'ran', + sit: 'sat', + visit: 'visited', +}; export const getPastTense = (verb, exceptions = EXCEPTIONS) => { - if (exceptions[verb]) { - return exceptions[verb] - } + if (exceptions[verb]) { + return exceptions[verb]; + } - if ((/e$/i).test(verb)) { - return verb + 'd' - } + if (/e$/i.test(verb)) { + return verb + 'd'; + } - if ((/[aeiou]c$/i).test(verb)) { - return verb + 'ked' - } + if (/[aeiou]c$/i.test(verb)) { + return verb + 'ked'; + } - // for american english only - if ((/el$/i).test(verb)) { - return verb + 'ed' - } + // for american english only + if (/el$/i.test(verb)) { + return verb + 'ed'; + } - if ((/[aeio][aeiou][dlmnprst]$/).test(verb)) { - return verb + 'ed' - } + if (/[aeio][aeiou][dlmnprst]$/.test(verb)) { + return verb + 'ed'; + } - if ((/[aeiou][bdglmnprst]$/i).test(verb)) { - return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed') - } + if (/[aeiou][bdglmnprst]$/i.test(verb)) { + return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed'); + } - return verb + 'ed' -} \ No newline at end of file + return verb + 'ed'; +}; diff --git a/src/utils/validateAddress.ts b/src/utils/validateAddress.ts index bbab210..4238311 100644 --- a/src/utils/validateAddress.ts +++ b/src/utils/validateAddress.ts @@ -4,6 +4,7 @@ import Base58 from '../deps/Base58'; export const validateAddress = (address) => { let isAddress = false; + try { const decodePubKey = Base58.decode(address); From b6f8b190c031acdd2cab2be901cc50f4bd3e4ed9 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 00:25:13 +0200 Subject: [PATCH 31/47] Format code (prettier) --- src/components/Auth/DownloadWallet.tsx | 4 + src/components/Chat/GroupAnnouncements.tsx | 6 +- src/components/QortPayment.tsx | 5 +- src/qdn/encryption/group-encryption.ts | 1077 +++++++++++--------- src/styles/App-styles.ts | 2 +- 5 files changed, 612 insertions(+), 482 deletions(-) diff --git a/src/components/Auth/DownloadWallet.tsx b/src/components/Auth/DownloadWallet.tsx index eb85a20..9683b02 100644 --- a/src/components/Auth/DownloadWallet.tsx +++ b/src/components/Auth/DownloadWallet.tsx @@ -216,7 +216,9 @@ export const DownloadWallet = ({
} /> + + {!keepCurrentPassword && ( <> @@ -226,11 +228,13 @@ export const DownloadWallet = ({ + setNewPassword(e.target.value)} /> + )} diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 6d54751..3671a51 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -351,7 +351,7 @@ export const GroupAnnouncements = ({ } }; - const getAnnouncements = React.useCallback( + const getAnnouncements = useCallback( async (selectedGroup, isPrivate) => { try { const offset = 0; @@ -387,7 +387,7 @@ export const GroupAnnouncements = ({ [secretKey] ); - React.useEffect(() => { + useEffect(() => { if (!secretKey && isPrivate) return; if ( selectedGroup && @@ -432,7 +432,7 @@ export const GroupAnnouncements = ({ const theme = useTheme(); - const checkNewMessages = React.useCallback(async () => { + const checkNewMessages = useCallback(async () => { try { const identifier = `grp-${selectedGroup}-anc-`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true`; diff --git a/src/components/QortPayment.tsx b/src/components/QortPayment.tsx index 3b48590..dab6ec2 100644 --- a/src/components/QortPayment.tsx +++ b/src/components/QortPayment.tsx @@ -37,7 +37,8 @@ export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { setSendPaymentError('Please enter your wallet password'); return; } - const fee = await getFee('PAYMENT'); + + const fee = await getFee('PAYMENT'); // TODO translate await show({ message: `Would you like to transfer ${Number(paymentAmount)} QORT?`, @@ -148,7 +149,7 @@ export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { - Confirm Wallet Password + Confirm wallet password diff --git a/src/qdn/encryption/group-encryption.ts b/src/qdn/encryption/group-encryption.ts index 82734ee..e883db5 100644 --- a/src/qdn/encryption/group-encryption.ts +++ b/src/qdn/encryption/group-encryption.ts @@ -1,509 +1,634 @@ // @ts-nocheck -import Base58 from "../../deps/Base58" -import ed2curve from "../../deps/ed2curve" -import nacl from "../../deps/nacl-fast" - +import Base58 from '../../deps/Base58'; +import ed2curve from '../../deps/ed2curve'; +import nacl from '../../deps/nacl-fast'; export function base64ToUint8Array(base64: string) { - const binaryString = atob(base64) - const len = binaryString.length - const bytes = new Uint8Array(len) - for (let i = 0; i < len; i++) { - bytes[i] = binaryString.charCodeAt(i) - } - return bytes + const binaryString = atob(base64); + const len = binaryString.length; + const bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes; } export function uint8ArrayToBase64(uint8Array: any) { - const length = uint8Array.length - let binaryString = '' - const chunkSize = 1024 * 1024; // Process 1MB at a time - for (let i = 0; i < length; i += chunkSize) { - const chunkEnd = Math.min(i + chunkSize, length) - const chunk = uint8Array.subarray(i, chunkEnd) - binaryString += Array.from(chunk, byte => String.fromCharCode(byte)).join('') - } - return btoa(binaryString) + const length = uint8Array.length; + let binaryString = ''; + const chunkSize = 1024 * 1024; // Process 1MB at a time + for (let i = 0; i < length; i += chunkSize) { + const chunkEnd = Math.min(i + chunkSize, length); + const chunk = uint8Array.subarray(i, chunkEnd); + binaryString += Array.from(chunk, (byte) => String.fromCharCode(byte)).join( + '' + ); + } + return btoa(binaryString); } export function objectToBase64(obj: Object) { - // Step 1: Convert the object to a JSON string - const jsonString = JSON.stringify(obj) - // Step 2: Create a Blob from the JSON string - const blob = new Blob([jsonString], { type: 'application/json' }) - // Step 3: Create a FileReader to read the Blob as a base64-encoded string - return new Promise((resolve, reject) => { - const reader = new FileReader() - reader.onloadend = () => { - if (typeof reader.result === 'string') { - // Remove 'data:application/json;base64,' prefix - const base64 = reader.result.replace( - 'data:application/json;base64,', - '' - ) - resolve(base64) - } else { - reject(new Error('Failed to read the Blob as a base64-encoded string')) - } - } - reader.onerror = () => { - reject(reader.error) - } - reader.readAsDataURL(blob) - }) + // Step 1: Convert the object to a JSON string + const jsonString = JSON.stringify(obj); + // Step 2: Create a Blob from the JSON string + const blob = new Blob([jsonString], { type: 'application/json' }); + // Step 3: Create a FileReader to read the Blob as a base64-encoded string + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => { + if (typeof reader.result === 'string') { + // Remove 'data:application/json;base64,' prefix + const base64 = reader.result.replace( + 'data:application/json;base64,', + '' + ); + resolve(base64); + } else { + reject(new Error('Failed to read the Blob as a base64-encoded string')); + } + }; + reader.onerror = () => { + reject(reader.error); + }; + reader.readAsDataURL(blob); + }); } // Function to create a symmetric key and nonce export const createSymmetricKeyAndNonce = () => { - const messageKey = new Uint8Array(32); // 32 bytes for the symmetric key - crypto.getRandomValues(messageKey); + const messageKey = new Uint8Array(32); // 32 bytes for the symmetric key + crypto.getRandomValues(messageKey); - - - return { messageKey: uint8ArrayToBase64(messageKey)}; + return { messageKey: uint8ArrayToBase64(messageKey) }; }; +export const encryptDataGroup = ({ + data64, + publicKeys, + privateKey, + userPublicKey, + customSymmetricKey, +}: any) => { + let combinedPublicKeys = [...publicKeys, userPublicKey]; + const decodedPrivateKey = Base58.decode(privateKey); + const publicKeysDuplicateFree = [...new Set(combinedPublicKeys)]; -export const encryptDataGroup = ({ data64, publicKeys, privateKey, userPublicKey, customSymmetricKey }: any) => { + const Uint8ArrayData = base64ToUint8Array(data64); + if (!(Uint8ArrayData instanceof Uint8Array)) { + throw new Error("The Uint8ArrayData you've submitted is invalid"); + } + try { + // Generate a random symmetric key for the message. - let combinedPublicKeys = [...publicKeys, userPublicKey] - const decodedPrivateKey = Base58.decode(privateKey) - const publicKeysDuplicateFree = [...new Set(combinedPublicKeys)] + let messageKey; + if (customSymmetricKey) { + messageKey = base64ToUint8Array(customSymmetricKey); + } else { + messageKey = new Uint8Array(32); + crypto.getRandomValues(messageKey); + } - const Uint8ArrayData = base64ToUint8Array(data64) - if (!(Uint8ArrayData instanceof Uint8Array)) { - throw new Error("The Uint8ArrayData you've submitted is invalid") - } - try { - // Generate a random symmetric key for the message. - - let messageKey - if(customSymmetricKey){ - messageKey = base64ToUint8Array(customSymmetricKey) - } else { - messageKey = new Uint8Array(32) - crypto.getRandomValues(messageKey) - } + if (!messageKey) throw new Error('Cannot create symmetric key'); + const nonce = new Uint8Array(24); + crypto.getRandomValues(nonce); + // Encrypt the data with the symmetric key. + const encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey); + // Generate a keyNonce outside of the loop. + const keyNonce = new Uint8Array(24); + crypto.getRandomValues(keyNonce); + // Encrypt the symmetric key for each recipient. + let encryptedKeys = []; + publicKeysDuplicateFree.forEach((recipientPublicKey) => { + const publicKeyUnit8Array = Base58.decode(recipientPublicKey); + const convertedPrivateKey = ed2curve.convertSecretKey(decodedPrivateKey); + const convertedPublicKey = ed2curve.convertPublicKey(publicKeyUnit8Array); + const sharedSecret = new Uint8Array(32); - if(!messageKey) throw new Error('Cannot create symmetric key') - const nonce = new Uint8Array(24) - crypto.getRandomValues(nonce) - // Encrypt the data with the symmetric key. - const encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey) - // Generate a keyNonce outside of the loop. - const keyNonce = new Uint8Array(24) - crypto.getRandomValues(keyNonce) - // Encrypt the symmetric key for each recipient. - let encryptedKeys = [] - publicKeysDuplicateFree.forEach((recipientPublicKey) => { - const publicKeyUnit8Array = Base58.decode(recipientPublicKey) - const convertedPrivateKey = ed2curve.convertSecretKey(decodedPrivateKey) - const convertedPublicKey = ed2curve.convertPublicKey(publicKeyUnit8Array) - const sharedSecret = new Uint8Array(32) + // the length of the sharedSecret will be 32 + 16 + // When you're encrypting data using nacl.secretbox, it's adding an authentication tag to the result, which is 16 bytes long. This tag is used for verifying the integrity and authenticity of the data when it is decrypted + nacl.lowlevel.crypto_scalarmult( + sharedSecret, + convertedPrivateKey, + convertedPublicKey + ); - // the length of the sharedSecret will be 32 + 16 - // When you're encrypting data using nacl.secretbox, it's adding an authentication tag to the result, which is 16 bytes long. This tag is used for verifying the integrity and authenticity of the data when it is decrypted - nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedPublicKey) + // Encrypt the symmetric key with the shared secret. + const encryptedKey = nacl.secretbox(messageKey, keyNonce, sharedSecret); - // Encrypt the symmetric key with the shared secret. - const encryptedKey = nacl.secretbox(messageKey, keyNonce, sharedSecret) + encryptedKeys.push(encryptedKey); + }); + const str = 'qortalGroupEncryptedData'; + const strEncoder = new TextEncoder(); + const strUint8Array = strEncoder.encode(str); + // Convert sender's public key to Uint8Array and add to the message + const senderPublicKeyUint8Array = Base58.decode(userPublicKey); + // Combine all data into a single Uint8Array. + // Calculate size of combinedData + let combinedDataSize = + strUint8Array.length + + nonce.length + + keyNonce.length + + senderPublicKeyUint8Array.length + + encryptedData.length + + 4; + let encryptedKeysSize = 0; + encryptedKeys.forEach((key) => { + encryptedKeysSize += key.length; + }); + combinedDataSize += encryptedKeysSize; + let combinedData = new Uint8Array(combinedDataSize); + combinedData.set(strUint8Array); + combinedData.set(nonce, strUint8Array.length); + combinedData.set(keyNonce, strUint8Array.length + nonce.length); + combinedData.set( + senderPublicKeyUint8Array, + strUint8Array.length + nonce.length + keyNonce.length + ); + combinedData.set( + encryptedData, + strUint8Array.length + + nonce.length + + keyNonce.length + + senderPublicKeyUint8Array.length + ); + // Initialize offset for encryptedKeys + let encryptedKeysOffset = + strUint8Array.length + + nonce.length + + keyNonce.length + + senderPublicKeyUint8Array.length + + encryptedData.length; + encryptedKeys.forEach((key) => { + combinedData.set(key, encryptedKeysOffset); + encryptedKeysOffset += key.length; + }); + const countArray = new Uint8Array( + new Uint32Array([publicKeysDuplicateFree.length]).buffer + ); + combinedData.set(countArray, combinedData.length - 4); + return uint8ArrayToBase64(combinedData); + } catch (error) { + console.log('error', error); + throw new Error('Error in encrypting data'); + } +}; - encryptedKeys.push(encryptedKey) - }) - const str = "qortalGroupEncryptedData" - const strEncoder = new TextEncoder() - const strUint8Array = strEncoder.encode(str) - // Convert sender's public key to Uint8Array and add to the message - const senderPublicKeyUint8Array = Base58.decode(userPublicKey) - // Combine all data into a single Uint8Array. - // Calculate size of combinedData - let combinedDataSize = strUint8Array.length + nonce.length + keyNonce.length + senderPublicKeyUint8Array.length + encryptedData.length + 4 - let encryptedKeysSize = 0 - encryptedKeys.forEach((key) => { - encryptedKeysSize += key.length - }) - combinedDataSize += encryptedKeysSize - let combinedData = new Uint8Array(combinedDataSize) - combinedData.set(strUint8Array) - combinedData.set(nonce, strUint8Array.length) - combinedData.set(keyNonce, strUint8Array.length + nonce.length) - combinedData.set(senderPublicKeyUint8Array, strUint8Array.length + nonce.length + keyNonce.length) - combinedData.set(encryptedData, strUint8Array.length + nonce.length + keyNonce.length + senderPublicKeyUint8Array.length) - // Initialize offset for encryptedKeys - let encryptedKeysOffset = strUint8Array.length + nonce.length + keyNonce.length + senderPublicKeyUint8Array.length + encryptedData.length - encryptedKeys.forEach((key) => { - combinedData.set(key, encryptedKeysOffset) - encryptedKeysOffset += key.length - }) - const countArray = new Uint8Array(new Uint32Array([publicKeysDuplicateFree.length]).buffer) - combinedData.set(countArray, combinedData.length - 4) - return uint8ArrayToBase64(combinedData) - } catch (error) { - console.log('error', error) - throw new Error("Error in encrypting data") - } +export const encryptSingle = async ({ + data64, + secretKeyObject, + typeNumber = 2, +}: any) => { + // Find the highest key in the secretKeyObject + const highestKey = Math.max( + ...Object.keys(secretKeyObject) + .filter((item) => !isNaN(+item)) + .map(Number) + ); + const highestKeyObject = secretKeyObject[highestKey]; + + // Convert data and keys from base64 + const Uint8ArrayData = base64ToUint8Array(data64); + const messageKey = base64ToUint8Array(highestKeyObject.messageKey); + + if (!(Uint8ArrayData instanceof Uint8Array)) { + throw new Error("The Uint8ArrayData you've submitted is invalid"); + } + + let nonce, encryptedData, encryptedDataBase64, finalEncryptedData; + + // Convert type number to a fixed length of 3 digits + const typeNumberStr = typeNumber.toString().padStart(3, '0'); + + if (highestKeyObject.nonce) { + // Old format: Use the nonce from secretKeyObject + nonce = base64ToUint8Array(highestKeyObject.nonce); + + // Encrypt the data with the existing nonce and message key + encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey); + encryptedDataBase64 = uint8ArrayToBase64(encryptedData); + + // Concatenate the highest key, type number, and encrypted data (old format) + const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits + finalEncryptedData = btoa(highestKeyStr + encryptedDataBase64); + } else { + // New format: Generate a random nonce and embed it in the message + nonce = new Uint8Array(24); // 24 bytes for the nonce + crypto.getRandomValues(nonce); + + // Encrypt the data with the new nonce and message key + encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey); + encryptedDataBase64 = uint8ArrayToBase64(encryptedData); + + // Convert the nonce to base64 + const nonceBase64 = uint8ArrayToBase64(nonce); + + // Concatenate the highest key, type number, nonce, and encrypted data (new format) + const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits + + const highestKeyBytes = new TextEncoder().encode( + highestKeyStr.padStart(10, '0') + ); + const typeNumberBytes = new TextEncoder().encode( + typeNumberStr.padStart(3, '0') + ); + + // Step 3: Concatenate all binary + const combinedBinary = new Uint8Array( + highestKeyBytes.length + + typeNumberBytes.length + + nonce.length + + encryptedData.length + ); + // finalEncryptedData = btoa(highestKeyStr) + btoa(typeNumberStr) + nonceBase64 + encryptedDataBase64; + combinedBinary.set(highestKeyBytes, 0); + combinedBinary.set(typeNumberBytes, highestKeyBytes.length); + combinedBinary.set(nonce, highestKeyBytes.length + typeNumberBytes.length); + combinedBinary.set( + encryptedData, + highestKeyBytes.length + typeNumberBytes.length + nonce.length + ); + + // Step 4: Base64 encode once + finalEncryptedData = uint8ArrayToBase64(combinedBinary); + } + + return finalEncryptedData; +}; + +export const decodeBase64ForUIChatMessages = (messages) => { + let msgs = []; + for (const msg of messages) { + try { + const decoded = atob(msg?.data); + const parseDecoded = JSON.parse(decodeURIComponent(escape(decoded))); + + msgs.push({ + ...msg, + ...parseDecoded, + }); + } catch (error) {} + } + return msgs; +}; + +export const decryptSingle = async ({ + data64, + secretKeyObject, + skipDecodeBase64, +}: any) => { + // First, decode the base64-encoded input (if skipDecodeBase64 is not set) + const decodedData = skipDecodeBase64 ? data64 : atob(data64); + + // Then, decode it again for the specific format (if double encoding is used) + const decodeForNumber = atob(decodedData); + + // Extract the key (assuming it's always the first 10 characters) + const keyStr = decodeForNumber.slice(0, 10); + + // Convert the key string back to a number + const highestKey = parseInt(keyStr, 10); + + // Check if we have a valid secret key for the extracted highestKey + if (!secretKeyObject[highestKey]) { + throw new Error('Cannot find correct secretKey'); + } + + const secretKeyEntry = secretKeyObject[highestKey]; + + let typeNumberStr, nonceBase64, encryptedDataBase64; + + // Determine if typeNumber exists by checking if the next 3 characters after keyStr are digits + const possibleTypeNumberStr = decodeForNumber.slice(10, 13); + const hasTypeNumber = /^\d{3}$/.test(possibleTypeNumberStr); // Check if next 3 characters are digits + + if (secretKeyEntry.nonce) { + // Old format: nonce is present in the secretKeyObject, so no type number exists + nonceBase64 = secretKeyEntry.nonce; + encryptedDataBase64 = decodeForNumber.slice(10); // The remaining part is the encrypted data + } else { + if (hasTypeNumber) { + // const typeNumberStr = new TextDecoder().decode(typeNumberBytes); + if (decodeForNumber.slice(10, 13) !== '001') { + const decodedBinary = base64ToUint8Array(decodedData); + const highestKeyBytes = decodedBinary.slice(0, 10); // if ASCII digits only + const highestKeyStr = new TextDecoder().decode(highestKeyBytes); + + const nonce = decodedBinary.slice(13, 13 + 24); + const encryptedData = decodedBinary.slice(13 + 24); + const highestKey = parseInt(highestKeyStr, 10); + + const messageKey = base64ToUint8Array( + secretKeyObject[+highestKey].messageKey + ); + const decryptedBytes = nacl.secretbox.open( + encryptedData, + nonce, + messageKey + ); + + // Check if decryption was successful + if (!decryptedBytes) { + throw new Error('Decryption failed'); + } + + // Convert the decrypted Uint8Array back to a Base64 string + return uint8ArrayToBase64(decryptedBytes); + } + // New format: Extract type number and nonce + typeNumberStr = possibleTypeNumberStr; // Extract type number + nonceBase64 = decodeForNumber.slice(13, 45); // Extract nonce (next 32 characters after type number) + encryptedDataBase64 = decodeForNumber.slice(45); // The remaining part is the encrypted data + } else { + // Old format without type number (nonce is embedded in the message, first 32 characters after keyStr) + nonceBase64 = decodeForNumber.slice(10, 42); // First 32 characters for the nonce + encryptedDataBase64 = decodeForNumber.slice(42); // The remaining part is the encrypted data + } + } + + // Convert Base64 strings to Uint8Array + const Uint8ArrayData = base64ToUint8Array(encryptedDataBase64); + const nonce = base64ToUint8Array(nonceBase64); + const messageKey = base64ToUint8Array(secretKeyEntry.messageKey); + + if (!(Uint8ArrayData instanceof Uint8Array)) { + throw new Error("The Uint8ArrayData you've submitted is invalid"); + } + + // Decrypt the data using the nonce and messageKey + const decryptedData = nacl.secretbox.open(Uint8ArrayData, nonce, messageKey); + + // Check if decryption was successful + if (!decryptedData) { + throw new Error('Decryption failed'); + } + + // Convert the decrypted Uint8Array back to a Base64 string + return uint8ArrayToBase64(decryptedData); +}; + +export const decryptGroupEncryptionWithSharingKey = async ({ + data64EncryptedData, + key, +}: any) => { + const allCombined = base64ToUint8Array(data64EncryptedData); + const str = 'qortalGroupEncryptedData'; + const strEncoder = new TextEncoder(); + const strUint8Array = strEncoder.encode(str); + // Extract the nonce + const nonceStartPosition = strUint8Array.length; + const nonceEndPosition = nonceStartPosition + 24; // Nonce is 24 bytes + const nonce = allCombined.slice(nonceStartPosition, nonceEndPosition); + // Extract the shared keyNonce + const keyNonceStartPosition = nonceEndPosition; + const keyNonceEndPosition = keyNonceStartPosition + 24; // Nonce is 24 bytes + const keyNonce = allCombined.slice( + keyNonceStartPosition, + keyNonceEndPosition + ); + // Extract the sender's public key + const senderPublicKeyStartPosition = keyNonceEndPosition; + const senderPublicKeyEndPosition = senderPublicKeyStartPosition + 32; // Public keys are 32 bytes + + // Calculate count first + const countStartPosition = allCombined.length - 4; // 4 bytes before the end, since count is stored in Uint32 (4 bytes) + const countArray = allCombined.slice( + countStartPosition, + countStartPosition + 4 + ); + const count = new Uint32Array(countArray.buffer)[0]; + // Then use count to calculate encryptedData + const encryptedDataStartPosition = senderPublicKeyEndPosition; // start position of encryptedData + const encryptedDataEndPosition = allCombined.length - (count * (32 + 16) + 4); + const encryptedData = allCombined.slice( + encryptedDataStartPosition, + encryptedDataEndPosition + ); + const symmetricKey = base64ToUint8Array(key); + + // Decrypt the data using the nonce and messageKey + const decryptedData = nacl.secretbox.open(encryptedData, nonce, symmetricKey); + + // Check if decryption was successful + if (!decryptedData) { + throw new Error('Decryption failed'); + } + // Convert the decrypted Uint8Array back to a Base64 string + return uint8ArrayToBase64(decryptedData); +}; + +export function decryptGroupDataQortalRequest(data64EncryptedData, privateKey) { + const allCombined = base64ToUint8Array(data64EncryptedData); + const str = 'qortalGroupEncryptedData'; + const strEncoder = new TextEncoder(); + const strUint8Array = strEncoder.encode(str); + // Extract the nonce + const nonceStartPosition = strUint8Array.length; + const nonceEndPosition = nonceStartPosition + 24; // Nonce is 24 bytes + const nonce = allCombined.slice(nonceStartPosition, nonceEndPosition); + // Extract the shared keyNonce + const keyNonceStartPosition = nonceEndPosition; + const keyNonceEndPosition = keyNonceStartPosition + 24; // Nonce is 24 bytes + const keyNonce = allCombined.slice( + keyNonceStartPosition, + keyNonceEndPosition + ); + // Extract the sender's public key + const senderPublicKeyStartPosition = keyNonceEndPosition; + const senderPublicKeyEndPosition = senderPublicKeyStartPosition + 32; // Public keys are 32 bytes + const senderPublicKey = allCombined.slice( + senderPublicKeyStartPosition, + senderPublicKeyEndPosition + ); + // Calculate count first + const countStartPosition = allCombined.length - 4; // 4 bytes before the end, since count is stored in Uint32 (4 bytes) + const countArray = allCombined.slice( + countStartPosition, + countStartPosition + 4 + ); + const count = new Uint32Array(countArray.buffer)[0]; + // Then use count to calculate encryptedData + const encryptedDataStartPosition = senderPublicKeyEndPosition; // start position of encryptedData + const encryptedDataEndPosition = allCombined.length - (count * (32 + 16) + 4); + const encryptedData = allCombined.slice( + encryptedDataStartPosition, + encryptedDataEndPosition + ); + // Extract the encrypted keys + // 32+16 = 48 + const combinedKeys = allCombined.slice( + encryptedDataEndPosition, + encryptedDataEndPosition + count * 48 + ); + if (!privateKey) { + throw new Error('Unable to retrieve keys'); + } + const decodedPrivateKey = Base58.decode(privateKey); + const convertedPrivateKey = ed2curve.convertSecretKey(decodedPrivateKey); + const convertedSenderPublicKey = ed2curve.convertPublicKey(senderPublicKey); + const sharedSecret = new Uint8Array(32); + nacl.lowlevel.crypto_scalarmult( + sharedSecret, + convertedPrivateKey, + convertedSenderPublicKey + ); + for (let i = 0; i < count; i++) { + const encryptedKey = combinedKeys.slice(i * 48, (i + 1) * 48); + // Decrypt the symmetric key. + const decryptedKey = nacl.secretbox.open( + encryptedKey, + keyNonce, + sharedSecret + ); + // If decryption was successful, decryptedKey will not be null. + if (decryptedKey) { + // Decrypt the data using the symmetric key. + const decryptedData = nacl.secretbox.open( + encryptedData, + nonce, + decryptedKey + ); + // If decryption was successful, decryptedData will not be null. + if (decryptedData) { + return decryptedData; + } + } + } + throw new Error('Unable to decrypt data'); } -export const encryptSingle = async ({ data64, secretKeyObject, typeNumber = 2 }: any) => { - // Find the highest key in the secretKeyObject - const highestKey = Math.max(...Object.keys(secretKeyObject).filter(item => !isNaN(+item)).map(Number)); - const highestKeyObject = secretKeyObject[highestKey]; - - // Convert data and keys from base64 - const Uint8ArrayData = base64ToUint8Array(data64); - const messageKey = base64ToUint8Array(highestKeyObject.messageKey); - - if (!(Uint8ArrayData instanceof Uint8Array)) { - throw new Error("The Uint8ArrayData you've submitted is invalid"); - } - - let nonce, encryptedData, encryptedDataBase64, finalEncryptedData; - - // Convert type number to a fixed length of 3 digits - const typeNumberStr = typeNumber.toString().padStart(3, '0'); - - if (highestKeyObject.nonce) { - // Old format: Use the nonce from secretKeyObject - nonce = base64ToUint8Array(highestKeyObject.nonce); - - // Encrypt the data with the existing nonce and message key - encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey); - encryptedDataBase64 = uint8ArrayToBase64(encryptedData); - - // Concatenate the highest key, type number, and encrypted data (old format) - const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits - finalEncryptedData = btoa(highestKeyStr + encryptedDataBase64); - } else { - // New format: Generate a random nonce and embed it in the message - nonce = new Uint8Array(24); // 24 bytes for the nonce - crypto.getRandomValues(nonce); - - // Encrypt the data with the new nonce and message key - encryptedData = nacl.secretbox(Uint8ArrayData, nonce, messageKey); - encryptedDataBase64 = uint8ArrayToBase64(encryptedData); - - // Convert the nonce to base64 - const nonceBase64 = uint8ArrayToBase64(nonce); - - // Concatenate the highest key, type number, nonce, and encrypted data (new format) - const highestKeyStr = highestKey.toString().padStart(10, '0'); // Fixed length of 10 digits +export function decryptGroupData( + data64EncryptedData: string, + privateKey: string +) { + const allCombined = base64ToUint8Array(data64EncryptedData); + const str = 'qortalGroupEncryptedData'; + const strEncoder = new TextEncoder(); + const strUint8Array = strEncoder.encode(str); + // Extract the nonce + const nonceStartPosition = strUint8Array.length; + const nonceEndPosition = nonceStartPosition + 24; // Nonce is 24 bytes + const nonce = allCombined.slice(nonceStartPosition, nonceEndPosition); + // Extract the shared keyNonce + const keyNonceStartPosition = nonceEndPosition; + const keyNonceEndPosition = keyNonceStartPosition + 24; // Nonce is 24 bytes + const keyNonce = allCombined.slice( + keyNonceStartPosition, + keyNonceEndPosition + ); + // Extract the sender's public key + const senderPublicKeyStartPosition = keyNonceEndPosition; + const senderPublicKeyEndPosition = senderPublicKeyStartPosition + 32; // Public keys are 32 bytes + const senderPublicKey = allCombined.slice( + senderPublicKeyStartPosition, + senderPublicKeyEndPosition + ); + // Calculate count first + const countStartPosition = allCombined.length - 4; // 4 bytes before the end, since count is stored in Uint32 (4 bytes) + const countArray = allCombined.slice( + countStartPosition, + countStartPosition + 4 + ); + const count = new Uint32Array(countArray.buffer)[0]; + // Then use count to calculate encryptedData + const encryptedDataStartPosition = senderPublicKeyEndPosition; // start position of encryptedData + const encryptedDataEndPosition = allCombined.length - (count * (32 + 16) + 4); + const encryptedData = allCombined.slice( + encryptedDataStartPosition, + encryptedDataEndPosition + ); + // Extract the encrypted keys + // 32+16 = 48 + const combinedKeys = allCombined.slice( + encryptedDataEndPosition, + encryptedDataEndPosition + count * 48 + ); + if (!privateKey) { + throw new Error('Unable to retrieve keys'); + } + const decodedPrivateKey = Base58.decode(privateKey); + const convertedPrivateKey = ed2curve.convertSecretKey(decodedPrivateKey); + const convertedSenderPublicKey = ed2curve.convertPublicKey(senderPublicKey); + const sharedSecret = new Uint8Array(32); + nacl.lowlevel.crypto_scalarmult( + sharedSecret, + convertedPrivateKey, + convertedSenderPublicKey + ); + for (let i = 0; i < count; i++) { + const encryptedKey = combinedKeys.slice(i * 48, (i + 1) * 48); + // Decrypt the symmetric key. + const decryptedKey = nacl.secretbox.open( + encryptedKey, + keyNonce, + sharedSecret + ); - const highestKeyBytes = new TextEncoder().encode(highestKeyStr.padStart(10, '0')); -const typeNumberBytes = new TextEncoder().encode(typeNumberStr.padStart(3, '0')); - -// Step 3: Concatenate all binary -const combinedBinary = new Uint8Array( - highestKeyBytes.length + typeNumberBytes.length + nonce.length + encryptedData.length -); - // finalEncryptedData = btoa(highestKeyStr) + btoa(typeNumberStr) + nonceBase64 + encryptedDataBase64; - combinedBinary.set(highestKeyBytes, 0); -combinedBinary.set(typeNumberBytes, highestKeyBytes.length); -combinedBinary.set(nonce, highestKeyBytes.length + typeNumberBytes.length); -combinedBinary.set(encryptedData, highestKeyBytes.length + typeNumberBytes.length + nonce.length); - -// Step 4: Base64 encode once - finalEncryptedData = uint8ArrayToBase64(combinedBinary); - } - - return finalEncryptedData; - }; - - -export const decodeBase64ForUIChatMessages = (messages)=> { - - let msgs = [] - for(const msg of messages){ - try { - const decoded = atob(msg?.data); - const parseDecoded =JSON.parse(decodeURIComponent(escape(decoded))) - - msgs.push({ - ...msg, - ...parseDecoded - }) - - } catch (error) { - - } - } - return msgs -} - -export const decryptSingle = async ({ data64, secretKeyObject, skipDecodeBase64 }: any) => { - // First, decode the base64-encoded input (if skipDecodeBase64 is not set) - const decodedData = skipDecodeBase64 ? data64 : atob(data64); - - // Then, decode it again for the specific format (if double encoding is used) - const decodeForNumber = atob(decodedData); - - // Extract the key (assuming it's always the first 10 characters) - const keyStr = decodeForNumber.slice(0, 10); - - // Convert the key string back to a number - const highestKey = parseInt(keyStr, 10); - - // Check if we have a valid secret key for the extracted highestKey - if (!secretKeyObject[highestKey]) { - throw new Error('Cannot find correct secretKey'); - } - - const secretKeyEntry = secretKeyObject[highestKey]; - - let typeNumberStr, nonceBase64, encryptedDataBase64; - - // Determine if typeNumber exists by checking if the next 3 characters after keyStr are digits - const possibleTypeNumberStr = decodeForNumber.slice(10, 13); - const hasTypeNumber = /^\d{3}$/.test(possibleTypeNumberStr); // Check if next 3 characters are digits - - if (secretKeyEntry.nonce) { - // Old format: nonce is present in the secretKeyObject, so no type number exists - nonceBase64 = secretKeyEntry.nonce; - encryptedDataBase64 = decodeForNumber.slice(10); // The remaining part is the encrypted data - } else { - if (hasTypeNumber) { - // const typeNumberStr = new TextDecoder().decode(typeNumberBytes); - if(decodeForNumber.slice(10, 13) !== '001'){ - const decodedBinary = base64ToUint8Array(decodedData); - const highestKeyBytes = decodedBinary.slice(0, 10); // if ASCII digits only - const highestKeyStr = new TextDecoder().decode(highestKeyBytes); - -const nonce = decodedBinary.slice(13, 13 + 24); -const encryptedData = decodedBinary.slice(13 + 24); -const highestKey = parseInt(highestKeyStr, 10); - -const messageKey = base64ToUint8Array(secretKeyObject[+highestKey].messageKey); -const decryptedBytes = nacl.secretbox.open(encryptedData, nonce, messageKey); - - // Check if decryption was successful - if (!decryptedBytes) { - throw new Error("Decryption failed"); - } - - // Convert the decrypted Uint8Array back to a Base64 string - return uint8ArrayToBase64(decryptedBytes); - - } - // New format: Extract type number and nonce - typeNumberStr = possibleTypeNumberStr; // Extract type number - nonceBase64 = decodeForNumber.slice(13, 45); // Extract nonce (next 32 characters after type number) - encryptedDataBase64 = decodeForNumber.slice(45); // The remaining part is the encrypted data - } else { - // Old format without type number (nonce is embedded in the message, first 32 characters after keyStr) - nonceBase64 = decodeForNumber.slice(10, 42); // First 32 characters for the nonce - encryptedDataBase64 = decodeForNumber.slice(42); // The remaining part is the encrypted data - } - } - - // Convert Base64 strings to Uint8Array - const Uint8ArrayData = base64ToUint8Array(encryptedDataBase64); - const nonce = base64ToUint8Array(nonceBase64); - const messageKey = base64ToUint8Array(secretKeyEntry.messageKey); - - if (!(Uint8ArrayData instanceof Uint8Array)) { - throw new Error("The Uint8ArrayData you've submitted is invalid"); - } - - // Decrypt the data using the nonce and messageKey - const decryptedData = nacl.secretbox.open(Uint8ArrayData, nonce, messageKey); - - // Check if decryption was successful - if (!decryptedData) { - throw new Error("Decryption failed"); - } - - // Convert the decrypted Uint8Array back to a Base64 string - return uint8ArrayToBase64(decryptedData); - }; - - - - - export const decryptGroupEncryptionWithSharingKey = async ({ data64EncryptedData, key }: any) => { - - const allCombined = base64ToUint8Array(data64EncryptedData) - const str = "qortalGroupEncryptedData" - const strEncoder = new TextEncoder() - const strUint8Array = strEncoder.encode(str) - // Extract the nonce - const nonceStartPosition = strUint8Array.length - const nonceEndPosition = nonceStartPosition + 24 // Nonce is 24 bytes - const nonce = allCombined.slice(nonceStartPosition, nonceEndPosition) - // Extract the shared keyNonce - const keyNonceStartPosition = nonceEndPosition - const keyNonceEndPosition = keyNonceStartPosition + 24 // Nonce is 24 bytes - const keyNonce = allCombined.slice(keyNonceStartPosition, keyNonceEndPosition) - // Extract the sender's public key - const senderPublicKeyStartPosition = keyNonceEndPosition - const senderPublicKeyEndPosition = senderPublicKeyStartPosition + 32 // Public keys are 32 bytes - - // Calculate count first - const countStartPosition = allCombined.length - 4 // 4 bytes before the end, since count is stored in Uint32 (4 bytes) - const countArray = allCombined.slice(countStartPosition, countStartPosition + 4) - const count = new Uint32Array(countArray.buffer)[0] - // Then use count to calculate encryptedData - const encryptedDataStartPosition = senderPublicKeyEndPosition // start position of encryptedData - const encryptedDataEndPosition = allCombined.length - ((count * (32 + 16)) + 4) - const encryptedData = allCombined.slice(encryptedDataStartPosition, encryptedDataEndPosition) - const symmetricKey = base64ToUint8Array(key); - - // Decrypt the data using the nonce and messageKey - const decryptedData = nacl.secretbox.open(encryptedData, nonce, symmetricKey) - - - // Check if decryption was successful - if (!decryptedData) { - throw new Error("Decryption failed"); - } - // Convert the decrypted Uint8Array back to a Base64 string - return uint8ArrayToBase64(decryptedData); - }; - - - - export function decryptGroupDataQortalRequest(data64EncryptedData, privateKey) { - const allCombined = base64ToUint8Array(data64EncryptedData) - const str = "qortalGroupEncryptedData" - const strEncoder = new TextEncoder() - const strUint8Array = strEncoder.encode(str) - // Extract the nonce - const nonceStartPosition = strUint8Array.length - const nonceEndPosition = nonceStartPosition + 24 // Nonce is 24 bytes - const nonce = allCombined.slice(nonceStartPosition, nonceEndPosition) - // Extract the shared keyNonce - const keyNonceStartPosition = nonceEndPosition - const keyNonceEndPosition = keyNonceStartPosition + 24 // Nonce is 24 bytes - const keyNonce = allCombined.slice(keyNonceStartPosition, keyNonceEndPosition) - // Extract the sender's public key - const senderPublicKeyStartPosition = keyNonceEndPosition - const senderPublicKeyEndPosition = senderPublicKeyStartPosition + 32 // Public keys are 32 bytes - const senderPublicKey = allCombined.slice(senderPublicKeyStartPosition, senderPublicKeyEndPosition) - // Calculate count first - const countStartPosition = allCombined.length - 4 // 4 bytes before the end, since count is stored in Uint32 (4 bytes) - const countArray = allCombined.slice(countStartPosition, countStartPosition + 4) - const count = new Uint32Array(countArray.buffer)[0] - // Then use count to calculate encryptedData - const encryptedDataStartPosition = senderPublicKeyEndPosition // start position of encryptedData - const encryptedDataEndPosition = allCombined.length - ((count * (32 + 16)) + 4) - const encryptedData = allCombined.slice(encryptedDataStartPosition, encryptedDataEndPosition) - // Extract the encrypted keys - // 32+16 = 48 - const combinedKeys = allCombined.slice(encryptedDataEndPosition, encryptedDataEndPosition + (count * 48)) - if (!privateKey) { - throw new Error("Unable to retrieve keys") - } - const decodedPrivateKey = Base58.decode(privateKey) - const convertedPrivateKey = ed2curve.convertSecretKey(decodedPrivateKey) - const convertedSenderPublicKey = ed2curve.convertPublicKey(senderPublicKey) - const sharedSecret = new Uint8Array(32) - nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedSenderPublicKey) - for (let i = 0; i < count; i++) { - const encryptedKey = combinedKeys.slice(i * 48, (i + 1) * 48) - // Decrypt the symmetric key. - const decryptedKey = nacl.secretbox.open(encryptedKey, keyNonce, sharedSecret) - // If decryption was successful, decryptedKey will not be null. - if (decryptedKey) { - // Decrypt the data using the symmetric key. - const decryptedData = nacl.secretbox.open(encryptedData, nonce, decryptedKey) - // If decryption was successful, decryptedData will not be null. - if (decryptedData) { - return decryptedData - } - } - } - throw new Error("Unable to decrypt data") -} - - -export function decryptGroupData(data64EncryptedData: string, privateKey: string) { - const allCombined = base64ToUint8Array(data64EncryptedData) - const str = "qortalGroupEncryptedData" - const strEncoder = new TextEncoder() - const strUint8Array = strEncoder.encode(str) - // Extract the nonce - const nonceStartPosition = strUint8Array.length - const nonceEndPosition = nonceStartPosition + 24 // Nonce is 24 bytes - const nonce = allCombined.slice(nonceStartPosition, nonceEndPosition) - // Extract the shared keyNonce - const keyNonceStartPosition = nonceEndPosition - const keyNonceEndPosition = keyNonceStartPosition + 24 // Nonce is 24 bytes - const keyNonce = allCombined.slice(keyNonceStartPosition, keyNonceEndPosition) - // Extract the sender's public key - const senderPublicKeyStartPosition = keyNonceEndPosition - const senderPublicKeyEndPosition = senderPublicKeyStartPosition + 32 // Public keys are 32 bytes - const senderPublicKey = allCombined.slice(senderPublicKeyStartPosition, senderPublicKeyEndPosition) - // Calculate count first - const countStartPosition = allCombined.length - 4 // 4 bytes before the end, since count is stored in Uint32 (4 bytes) - const countArray = allCombined.slice(countStartPosition, countStartPosition + 4) - const count = new Uint32Array(countArray.buffer)[0] - // Then use count to calculate encryptedData - const encryptedDataStartPosition = senderPublicKeyEndPosition // start position of encryptedData - const encryptedDataEndPosition = allCombined.length - ((count * (32 + 16)) + 4) - const encryptedData = allCombined.slice(encryptedDataStartPosition, encryptedDataEndPosition) - // Extract the encrypted keys - // 32+16 = 48 - const combinedKeys = allCombined.slice(encryptedDataEndPosition, encryptedDataEndPosition + (count * 48)) - if (!privateKey) { - throw new Error("Unable to retrieve keys") - } - const decodedPrivateKey = Base58.decode(privateKey) - const convertedPrivateKey = ed2curve.convertSecretKey(decodedPrivateKey) - const convertedSenderPublicKey = ed2curve.convertPublicKey(senderPublicKey) - const sharedSecret = new Uint8Array(32) - nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedSenderPublicKey) - for (let i = 0; i < count; i++) { - const encryptedKey = combinedKeys.slice(i * 48, (i + 1) * 48) - // Decrypt the symmetric key. - const decryptedKey = nacl.secretbox.open(encryptedKey, keyNonce, sharedSecret) - - // If decryption was successful, decryptedKey will not be null. - if (decryptedKey) { - // Decrypt the data using the symmetric key. - const decryptedData = nacl.secretbox.open(encryptedData, nonce, decryptedKey) - // If decryption was successful, decryptedData will not be null. - if (decryptedData) { - return {decryptedData, count} - } - } - } - throw new Error("Unable to decrypt data") + // If decryption was successful, decryptedKey will not be null. + if (decryptedKey) { + // Decrypt the data using the symmetric key. + const decryptedData = nacl.secretbox.open( + encryptedData, + nonce, + decryptedKey + ); + // If decryption was successful, decryptedData will not be null. + if (decryptedData) { + return { decryptedData, count }; + } + } + } + throw new Error('Unable to decrypt data'); } export function uint8ArrayStartsWith(uint8Array, string) { - const stringEncoder = new TextEncoder() - const stringUint8Array = stringEncoder.encode(string) - if (uint8Array.length < stringUint8Array.length) { - return false - } - for (let i = 0; i < stringUint8Array.length; i++) { - if (uint8Array[i] !== stringUint8Array[i]) { - return false - } - } - return true + const stringEncoder = new TextEncoder(); + const stringUint8Array = stringEncoder.encode(string); + if (uint8Array.length < stringUint8Array.length) { + return false; + } + for (let i = 0; i < stringUint8Array.length; i++) { + if (uint8Array[i] !== stringUint8Array[i]) { + return false; + } + } + return true; } export function decryptDeprecatedSingle(uint8Array, publicKey, privateKey) { - const combinedData = uint8Array - const str = "qortalEncryptedData" - const strEncoder = new TextEncoder() - const strUint8Array = strEncoder.encode(str) - const strData = combinedData.slice(0, strUint8Array.length) - const nonce = combinedData.slice(strUint8Array.length, strUint8Array.length + 24) - const _encryptedData = combinedData.slice(strUint8Array.length + 24) - - const _publicKey = window.parent.Base58.decode(publicKey) - if (!privateKey || !_publicKey) { - throw new Error("Unable to retrieve keys") - } - const convertedPrivateKey = ed2curve.convertSecretKey(privateKey) - const convertedPublicKey = ed2curve.convertPublicKey(_publicKey) - const sharedSecret = new Uint8Array(32) - nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedPublicKey) - const _chatEncryptionSeed = new window.parent.Sha256().process(sharedSecret).finish().result - const _decryptedData = nacl.secretbox.open(_encryptedData, nonce, _chatEncryptionSeed) - if (!_decryptedData) { - throw new Error("Unable to decrypt") - } - return uint8ArrayToBase64(_decryptedData) -} \ No newline at end of file + const combinedData = uint8Array; + const str = 'qortalEncryptedData'; + const strEncoder = new TextEncoder(); + const strUint8Array = strEncoder.encode(str); + const strData = combinedData.slice(0, strUint8Array.length); + const nonce = combinedData.slice( + strUint8Array.length, + strUint8Array.length + 24 + ); + const _encryptedData = combinedData.slice(strUint8Array.length + 24); + + const _publicKey = window.parent.Base58.decode(publicKey); + if (!privateKey || !_publicKey) { + throw new Error('Unable to retrieve keys'); + } + const convertedPrivateKey = ed2curve.convertSecretKey(privateKey); + const convertedPublicKey = ed2curve.convertPublicKey(_publicKey); + const sharedSecret = new Uint8Array(32); + nacl.lowlevel.crypto_scalarmult( + sharedSecret, + convertedPrivateKey, + convertedPublicKey + ); + const _chatEncryptionSeed = new window.parent.Sha256() + .process(sharedSecret) + .finish().result; + const _decryptedData = nacl.secretbox.open( + _encryptedData, + nonce, + _chatEncryptionSeed + ); + if (!_decryptedData) { + throw new Error('Unable to decrypt'); + } + return uint8ArrayToBase64(_decryptedData); +} diff --git a/src/styles/App-styles.ts b/src/styles/App-styles.ts index af273e5..3cbcdb5 100644 --- a/src/styles/App-styles.ts +++ b/src/styles/App-styles.ts @@ -228,5 +228,5 @@ export const CustomLabel = styled(InputLabel)(({ theme }) => ({ fontFamily: 'Inter', fontSize: '15px', fontWeight: 400, - lineHeight: '12px', + lineHeight: '24px', })); From 0940f29e3d424524be9bf388134d2f2c6473e21d Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 00:29:35 +0200 Subject: [PATCH 32/47] Move save_options into message.generic (json) --- src/App.tsx | 96 +++++++++++++++++++++++++---------- src/components/Save/Save.tsx | 18 +++---- src/i18n/locales/en/auth.json | 1 + src/i18n/locales/en/core.json | 27 +++++----- 4 files changed, 95 insertions(+), 47 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index c5455a6..72dae3b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -44,8 +44,6 @@ import { saveFileToDisk, saveSeedPhraseToDisk, } from './utils/generateWallet/generateWallet'; -import { kdf } from './deps/kdf'; -import { generateSaveWalletData } from './utils/generateWallet/storeWallet'; import { crypto, walletVersion } from './constants/decryptWallet'; import PhraseWallet from './utils/generateWallet/phrase-wallet'; import { @@ -147,21 +145,21 @@ import { useAtom, useSetAtom } from 'jotai'; import { useResetAtom } from 'jotai/utils'; type extStates = - | 'not-authenticated' | 'authenticated' - | 'send-qort' - | 'web-app-request-connection' - | 'web-app-request-payment' - | 'web-app-request-authentication' - | 'download-wallet' + | 'buy-order-submitted' | 'create-wallet' + | 'download-wallet' + | 'group' + | 'not-authenticated' + | 'send-qort' | 'transfer-success-regular' | 'transfer-success-request' | 'wallet-dropped' - | 'web-app-request-buy-order' - | 'buy-order-submitted' | 'wallets' - | 'group'; + | 'web-app-request-authentication' + | 'web-app-request-buy-order' + | 'web-app-request-connection' + | 'web-app-request-payment'; interface MyContextInterface { isShow: boolean; @@ -277,6 +275,7 @@ export const getBaseApiReactSocket = (customApi?: string) => { }; export const isMainWindow = true; + function App() { const [extState, setExtstate] = useState('not-authenticated'); const [desktopViewMode, setDesktopViewMode] = useState('home'); @@ -308,18 +307,22 @@ function App() { const [isLoading, setIsLoading] = useState(false); const [isLoadingSendCoin, setIsLoadingSendCoin] = useState(false); - const { t } = useTranslation(['auth', 'core']); + const { t } = useTranslation(['auth', 'core', 'group']); const theme = useTheme(); const [ walletToBeDownloadedPasswordConfirm, setWalletToBeDownloadedPasswordConfirm, ] = useState(''); + const [walletToBeDownloadedError, setWalletToBeDownloadedError] = useState(''); + const [walletToBeDecryptedError, setWalletToBeDecryptedError] = useState(''); + const [isFocused, setIsFocused] = useState(true); + const [hasSettingsChanged, setHasSettingsChanged] = useAtom( hasSettingsChangedAtom ); @@ -328,6 +331,7 @@ function App() { const downloadResource = useFetchResources(); const holdRefExtState = useRef('not-authenticated'); const isFocusedRef = useRef(true); + const { showTutorial, openTutorialModal, @@ -335,7 +339,9 @@ function App() { setOpenTutorialModal, hasSeenGettingStarted, } = useHandleTutorials(); + const { isShow, onCancel, onOk, show, message } = useModal(); + const { isShow: isShowUnsavedChanges, onCancel: onCancelUnsavedChanges, @@ -343,6 +349,7 @@ function App() { show: showUnsavedChanges, message: messageUnsavedChanges, } = useModal(); + const { isShow: isShowInfo, onCancel: onCancelInfo, @@ -358,6 +365,7 @@ function App() { isShow: isShowQortalRequest, message: messageQortalRequest, } = useModal(); + const { onCancel: onCancelQortalRequestExtension, onOk: onOkQortalRequestExtension, @@ -365,6 +373,7 @@ function App() { isShow: isShowQortalRequestExtension, message: messageQortalRequestExtension, } = useModal(); + const setIsRunningPublicNode = useSetAtom(isRunningPublicNodeAtom); const [infoSnack, setInfoSnack] = useState(null); @@ -375,15 +384,18 @@ function App() { const [apiKey, setApiKey] = useState(''); const [isOpenSendQort, setIsOpenSendQort] = useState(false); const [isOpenSendQortSuccess, setIsOpenSendQortSuccess] = useState(false); + const { isUserBlocked, addToBlockList, removeBlockFromList, getAllBlockedUsers, } = useBlockedAddresses(); + const [currentNode, setCurrentNode] = useState({ url: 'http://127.0.0.1:12391', }); + const [useLocalNode, setUseLocalNode] = useState(false); const [confirmRequestRead, setConfirmRequestRead] = useState(false); @@ -662,10 +674,15 @@ function App() { 'kdfThreads', ]; for (const field of requiredFields) { - if (!(field in pf)) throw new Error(field + ' not found in JSON'); + if (!(field in pf)) + throw new Error( + t('auth:message.error.field_not_found_json', { + field: field, + postProcess: 'capitalizeFirst', + }) + ); } setRawWallet(pf); - // setExtstate("authenticated"); setExtstate('wallet-dropped'); setdecryptedWallet(null); } catch (e) { @@ -958,25 +975,39 @@ function App() { const createAccountFunc = async () => { try { if (!walletToBeDownloadedPassword) { - setWalletToBeDownloadedError('Please enter a password'); + setWalletToBeDownloadedError( + t('core:message.generic.password_enter', { + postProcess: 'capitalizeFirst', + }) + ); return; } if (!walletToBeDownloadedPasswordConfirm) { - setWalletToBeDownloadedError('Please confirm your password'); + setWalletToBeDownloadedError( + t('core:message.generic.password_confirm', { + postProcess: 'capitalizeFirst', + }) + ); return; } if ( walletToBeDownloadedPasswordConfirm !== walletToBeDownloadedPassword ) { - setWalletToBeDownloadedError('Password fields do not match!'); + setWalletToBeDownloadedError( + t('core:message.error.password_not_matching', { + postProcess: 'capitalizeFirst', + }) + ); return; } setIsLoading(true); + await new Promise((res) => { setTimeout(() => { res(); }, 250); }); + const res = await createAccount(generatorRef.current.parsedString); const wallet = await res.generateSaveWalletData( walletToBeDownloadedPassword, @@ -1029,9 +1060,10 @@ function App() { const logoutFunc = useCallback(async () => { try { if (extState === 'authenticated') { - // TODO translate await showUnsavedChanges({ - message: 'Are you sure you would like to logout?', + message: t('core:question.logout', { + postProcess: 'capitalizeFirst', + }), }); } window @@ -1091,7 +1123,6 @@ function App() { setWalletToBeDownloadedPassword(''); setShowSeed(false); setCreationStep(1); - setWalletToBeDownloadedPasswordConfirm(''); setWalletToBeDownloadedError(''); setSendqortState(null); @@ -1166,7 +1197,11 @@ function App() { console.error('Failed to decrypt wallet:', error); }); } catch (error) { - setWalletToBeDecryptedError('Unable to authenticate. Wrong password'); + setWalletToBeDecryptedError( + t('core:message.error.password_wrong', { + postProcess: 'capitalizeFirst', + }) + ); } }; @@ -1242,17 +1277,18 @@ function App() { return ( + {authenticatedMode === 'qort' && ( @@ -1455,6 +1491,7 @@ function App() { > {balance?.toFixed(2)} QORT + + + )} @@ -2182,6 +2221,7 @@ function App() { {messageQortalRequest?.fee} {' QORT'} + )} @@ -2627,7 +2667,7 @@ function App() { {t('auth:wallet.password', { postProcess: 'capitalizeFirst' })} - + - Wallet Password + {t('auth:wallet.password', { + postProcess: 'capitalizeFirst', + })} @@ -2919,7 +2961,9 @@ function App() { - Confirm Wallet Password + {t('auth:wallet.password_confirmation', { + postProcess: 'capitalizeFirst', + })} diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index 70faac8..f85baee 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -155,7 +155,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { const fee = await getFee('ARBITRARY'); await show({ - message: t('core:save_options.publish_qnd', { + message: t('core:message.generic.publish_qnd', { postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', @@ -306,7 +306,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - {t('core:save_options.settings', { + {t('core:message.generic.settings', { postProcess: 'capitalizeFirst', })} {' '} @@ -335,7 +335,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { }, }} > - {t('core:save_options.qdn', { + {t('core:message.generic.qdn', { postProcess: 'capitalizeFirst', })} @@ -366,7 +366,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - {t('core:save_options.register_name', { + {t('core:message.generic.register_name', { postProcess: 'capitalizeFirst', })} @@ -387,7 +387,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - {t('core:save_options.unsaved_changes', { + {t('core:message.generic.unsaved_changes', { postProcess: 'capitalizeFirst', })} @@ -446,7 +446,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { }, }} > - {t('core:save_options.revert_qdn', { + {t('core:message.generic.revert_qdn', { postProcess: 'capitalizeFirst', })} @@ -471,7 +471,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { onClick={revertChanges} variant="contained" > - {t('core:save_options.revert_default', { + {t('core:message.generic.revert_default', { postProcess: 'capitalizeFirst', })} @@ -517,7 +517,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { }, }} > - {t('core:save_options.overwrite_qdn', { + {t('core:message.generic.overwrite_qdn', { postProcess: 'capitalizeFirst', })} @@ -537,7 +537,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - {t('core:save_options.no_pinned_changes', { + {t('core:message.generic.no_pinned_changes', { postProcess: 'capitalizeFirst', })} diff --git a/src/i18n/locales/en/auth.json b/src/i18n/locales/en/auth.json index d43873f..5d05e66 100644 --- a/src/i18n/locales/en/auth.json +++ b/src/i18n/locales/en/auth.json @@ -31,6 +31,7 @@ "message": { "error": { "account_creation": "could not create account.", + "field_not_found_json": "{{ field }} not found in JSON", "incorrect_password": "incorrect password", "invalid_secret_key": "secretKey is not valid", "unable_reencrypt_secret_key": "unable to re-encrypt secret key" diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 71afe1f..e93cdf2 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -128,6 +128,8 @@ "missing_fields": "missing: {{ fields }}", "navigation_timeout": "navigation timeout", "network_generic": "network error", + "password_not_matching": "password fields do not match!", + "password_wrong": "unable to authenticate. Wrong password", "publish_app": "unable to publish app", "rating_option": "cannot find rating option", "save_qdn": "unable to save to QDN", @@ -147,18 +149,30 @@ "name_unavailable": "{{ name }} is unavailable", "no_description": "no description", "no_notifications": "no new notifications", + "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.", + "overwrite_qdn": "overwrite to QDN", + "password_confirm": "please confirm a password", + "password_enter": "please enter a password", "publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!", "publishing": "publishing... Please wait.", + "qdn": "use QDN saving", "rating": "rating for {{ service }} {{ name }}", + "register_name": "you need a registered Qortal name to save your pinned apps to QDN.", + "revert_default": "revert to default", + "revert_qdn": "revert to QDN", + "save_qdn": "save to QDN", "secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party.", "select_file": "please select a file", "select_image": "please select an image for a logo", "select_zip": "select .zip file containing static content:", - "space_for_admins": "sorry, this space is only for Admins." + "settings": "you are using the export/import way of saving settings.", + "space_for_admins": "sorry, this space is only for Admins.", + "unsaved_changes": "you have unsaved changes to your pinned apps. Save them to QDN." }, "question": { + "logout": "are you sure you would like to logout?", "new_user": "are you a new user?" }, "status": { @@ -205,17 +219,6 @@ "reset_pinned": "don't like your current local changes? Would you like to reset to the default pinned apps?", "reset_qdn": "don't like your current local changes? Would you like to reset to your saved QDN pinned apps?" }, - "save_options": { - "no_pinned_changes": "you currently do not have any changes to your pinned apps", - "overwrite_qdn": "overwrite to QDN", - "qdn": "use QDN saving", - "register_name": "you need a registered Qortal name to save your pinned apps to QDN.", - "revert_default": "revert to default", - "revert_qdn": "revert to QDN", - "save_qdn": "save to QDN", - "settings": "you are using the export/import way of saving settings.", - "unsaved_changes": "you have unsaved changes to your pinned apps. Save them to QDN." - }, "server": "server", "settings": "settings", "supply": "supply", From 0a2bf196e46759254cc204b2567a713ad52e2082 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 01:35:33 +0200 Subject: [PATCH 33/47] Translate app --- src/App.tsx | 379 +++++++++++++++++++++++++--------- src/i18n/locales/en/auth.json | 14 +- src/i18n/locales/en/core.json | 11 + 3 files changed, 300 insertions(+), 104 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 72dae3b..3ad029c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -136,7 +136,7 @@ import { QortPayment } from './components/QortPayment'; import { GeneralNotifications } from './components/GeneralNotifications'; import { PdfViewer } from './common/PdfViewer'; import ThemeSelector from './components/Theme/ThemeSelector.tsx'; -import { useTranslation } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; import LanguageSelector from './components/Language/LanguageSelector.tsx'; import { DownloadWallet } from './components/Auth/DownloadWallet.tsx'; import { CopyIcon } from './assets/Icons/CopyIcon.tsx'; @@ -1295,7 +1295,7 @@ function App() { - LITECOIN WALLET + {t('core:wallet.litecoin', { postProcess: 'capitalizeAll' })}
} placement="left" @@ -1322,8 +1322,8 @@ function App() { src={ltcLogo} style={{ cursor: 'pointer', - width: '20px', height: 'auto', + width: '20px', }} /> @@ -1332,7 +1332,7 @@ function App() { - QORTAL WALLET + {t('core:wallet.qortal', { postProcess: 'capitalizeAll' })} } placement="left" @@ -1401,9 +1401,9 @@ function App() { {!isNaN(+ltcBalance) && !ltcBalanceLoading && ( {userInfo?.name} @@ -1470,15 +1470,17 @@ function App() { + {qortBalanceLoading && ( )} + {!qortBalanceLoading && balance >= 0 && ( - REGISTER NAME + {t('core:action.register_name', { + postProcess: 'capitalizeAll', + })} )} + + { setIsOpenSendQort(true); - // setExtstate("send-qort"); setIsOpenDrawerProfile(false); }} > - Transfer QORT + {t('core:action.transfer_qort', { + postProcess: 'capitalizeFirst', + })} )} + - Get QORT at Q-Trade + {t('core:action.get_qort', { postProcess: 'capitalizeFirst' })} ); @@ -1574,9 +1582,9 @@ function App() { + {extState === 'not-authenticated' && ( )} - {/* {extState !== "not-authenticated" && ( - - )} */} {extState === 'authenticated' && isMainWindow && ( )} + {isOpenSendQort && isMainWindow && ( + )} + {isShowQortalRequest && !isMainWindow && ( <> @@ -2141,6 +2152,7 @@ function App() { )} + {messageQortalRequest?.text3 && ( <> - {'Fee: '} - {messageQortalRequest?.fee} - {' QORT'} + {t('core:message.generic.fee_qort', { + message: messageQortalRequest?.fee, + postProcess: 'capitalizeFirst', + })} )} + {messageQortalRequest?.checkbox1 && ( onOkQortalRequest('accepted')} > - accept + {t('core:action.accept', { postProcess: 'capitalizeFirst' })} onCancelQortalRequest()} > - decline + {t('core:action.decline', { postProcess: 'capitalizeFirst' })} + {sendPaymentError} )} + {extState === 'web-app-request-buy-order' && !isMainWindow && ( <> @@ -2302,22 +2318,34 @@ function App() { lineHeight: '15px', }} > - The Application

{' '} - {requestBuyOrder?.hostname}

- - is requesting {requestBuyOrder?.crosschainAtInfo?.length}{' '} - {`buy order${ - requestBuyOrder?.crosschainAtInfo.length === 1 ? '' : 's' - }`} - + , + italic: , + span: , + }} + values={{ + hostname: requestBuyOrder?.hostname, + count: requestBuyOrder?.crosschainAtInfo?.length || 0, + }} + tOptions={{ postProcess: ['capitalizeFirst'] }} + > + The Application
+ {{ hostname }}
+ is requesting {{ count }} buy order +
+ + {requestBuyOrder?.crosschainAtInfo?.reduce((latest, cur) => { @@ -2325,7 +2353,9 @@ function App() { }, 0)}{' '} QORT + + - FOR + {t('core:for', { postProcess: 'capitalizeAll' })} + + + confirmBuyOrder(false)} > - accept + {t('core:action.accept', { postProcess: 'capitalizeFirst' })} + confirmBuyOrder(true)} > - decline + {t('core:action.decline', { postProcess: 'capitalizeFirst' })} + {sendPaymentError} )} @@ -2390,11 +2425,28 @@ function App() { lineHeight: '15px', }} > - The Application

{' '} - {sendqortState?.hostname}

- is requesting a payment + , + italic: , + span: , + }} + values={{ + hostname: requestBuyOrder?.hostname, + count: requestBuyOrder?.crosschainAtInfo?.length || 0, + }} + tOptions={{ postProcess: ['capitalizeFirst'] }} + > + The Application
+ {{ hostname }}
+ is requesting {{ count }} a payment +
+ + {sendqortState?.description} + + + confirmPayment(false)} > - accept + {t('core:action.accept', { postProcess: 'capitalizeFirst' })} + confirmPayment(true)} > - decline + {t('core:action.decline', { postProcess: 'capitalizeFirst' })} + {sendPaymentError} )} + {extState === 'web-app-request-connection' && !isMainWindow && ( <> +
+ + {requestConnection?.hostname}

is requestion a connection
+ + - accept + {t('core:action.accept', { postProcess: 'capitalizeFirst' })} - decline + {t('core:action.decline', { postProcess: 'capitalizeFirst' })} )} + {extState === 'web-app-request-authentication' && !isMainWindow && ( <> +
+
@@ -2545,7 +2611,9 @@ function App() { - Authenticate + {t('auth:action.authenticate', { + postProcess: 'capitalizeFirst', + })} @@ -2561,6 +2629,7 @@ function App() { )} + {extState === 'wallets' && ( <> @@ -2596,6 +2665,7 @@ function App() { /> )} + {rawWallet && extState === 'wallet-dropped' && ( <> @@ -2622,7 +2692,9 @@ function App() { }} /> + +
+ + @@ -2683,6 +2757,7 @@ function App() { {useLocalNode ? ( <> + + )} + {extState === 'create-wallet' && ( <> {!walletToBeDownloaded && ( @@ -2786,7 +2863,9 @@ function App() { fontSize: '18px', }} > - Set up your Qortal account + {t('auth:action.setup_qortal_account', { + postProcess: 'capitalizeFirst', + })} @@ -2801,11 +2880,11 @@ function App() { > - A ‘{' '} - { - setShowSeed(true); - }} - style={{ - fontSize: '14px', - color: 'steelblue', - cursor: 'pointer', + setShowSeed(true)} + style={{ + fontSize: '14px', + color: 'steelblue', + cursor: 'pointer', + }} + /> + ), }} + tOptions={{ postProcess: ['capitalizeFirst'] }} > - SEEDPHRASE - {' '} - ’ has been randomly generated in the background. + A SEEDPHRASE has been randomly generated in + the background. + + - If you wish to VIEW THE SEEDPHRASE, click the word - 'SEEDPHRASE' in this text. Seedphrases are used to - generate the private key for your Qortal account. For - security by default, seedphrases are NOT displayed unless - specifically chosen. + {t('auth:tips.view_seedphrase', { + postProcess: 'capitalizeFirst', + })} + - Create your Qortal account by clicking{' '} - + ), }} + tOptions={{ postProcess: ['capitalizeFirst'] }} > - NEXT - {' '} - below. + Create your Qortal account by clicking NEXT{' '} + below. + + + { setCreationStep(2); }} > - Next + {t('core:page.next', { postProcess: 'capitalizeFirst' })} +
+ - Your seedphrase + {t('auth:seed_your', { + postProcess: 'capitalizeFirst', + })}
- Export Seedphrase + {t('auth:action.export_seedphrase', { + postProcess: 'capitalizeFirst', + })} + @@ -2978,7 +3082,9 @@ function App() { - There is no minimum length requirement + {t('auth:message.generic.no_minimum_length', { + postProcess: 'capitalizeFirst', + })} @@ -2989,6 +3095,7 @@ function App() { })}
+ {walletToBeDownloadedError} )} @@ -2996,17 +3103,24 @@ function App() { {walletToBeDownloaded && ( <> + + + - Congrats, you’re all set up! + {t('auth:message.generic.congrats_setup', { + postProcess: 'capitalizeFirst', + })} + + + - Save your account in a place where you will remember it! + {t('auth:tips.safe_place', { + postProcess: 'capitalizeFirst', + })} + + { await saveFileToDiskFunc(); returnToMain(); await showInfo({ - message: `Keep your wallet file secure.`, + message: t('auth:tips.wallet_secure', { + postProcess: 'capitalizeFirst', + }), }); }} > @@ -3038,6 +3159,7 @@ function App() { )} )} + {isOpenSendQortSuccess && ( + + + + + { @@ -3077,11 +3204,15 @@ function App() { )} + {extState === 'transfer-success-request' && ( <> + + + + + { window.close(); @@ -3102,11 +3235,15 @@ function App() { )} + {extState === 'buy-order-submitted' && ( <> + + + + + { window.close(); @@ -3131,12 +3270,11 @@ function App() { {countdown && ( - {/* */} )} + {isLoading && } {isShow && ( {message.paymentFee ? 'Payment' : 'Publish'} + {message.message} @@ -3186,6 +3326,7 @@ function App() { )} + )} + {isShowInfo && ( {'Important Info'} + {messageInfo.message} + )} + {isShowUnsavedChanges && ( - {'LOGOUT'} + + {t('core:action.logout', { postProcess: 'capitalizeAll' })} + + {messageUnsavedChanges.message} + + )} + {isShowQortalRequestExtension && isMainWindow && ( {({ remainingTime }) => {remainingTime}} + + {messageQortalRequestExtension?.text2 && ( <> + + )} + {messageQortalRequestExtension?.text3 && ( <> + )} @@ -3408,6 +3566,7 @@ function App() { /> )} + - {'App Fee: '} - {messageQortalRequestExtension?.appFee} - {' QORT'} + {t('core:message.generic.fee_qort', { + message: messageQortalRequestExtension?.appFee, + postProcess: 'capitalizeFirst', + })} + )} + {messageQortalRequestExtension?.foreignFee && ( <> @@ -3486,12 +3648,16 @@ function App() { maxWidth: '90%', }} > - {'Foreign Fee: '} - {messageQortalRequestExtension?.foreignFee} + {t('core:message.generic.foreign_fee', { + message: messageQortalRequestExtension?.foreignFee, + postProcess: 'capitalizeFirst', + })} + )} + {messageQortalRequestExtension?.checkbox1 && ( + + )} + {isSettingsOpen && ( )} + + {renderProfileLeft()} + + + {extState === 'create-wallet' && walletToBeDownloaded && ( { showTutorial('important-information', true); }} sx={{ - position: 'fixed', bottom: '25px', + position: 'fixed', right: '25px', }} > @@ -3675,6 +3849,7 @@ function App() { /> )} + {isOpenMinting && ( NEXT below.", "choose_password": "choose new password", "download_account": "download account", + "export_seedphrase": "export Seedphrase", "publish_admin_secret_key": "publish admin secret key", "publish_group_secret_key": "publish group secret key", - "return_to_list": "return to list" + "return_to_list": "return to list", + "setup_qortal_account": "set up your Qortal account" }, "advanced_users": "for advanced users", "apikey": { @@ -37,13 +40,16 @@ "unable_reencrypt_secret_key": "unable to re-encrypt secret key" }, "generic": { + "congrats_setup": "congrats, you’re all set up!", "no_account": "no accounts saved", + "no_minimum_length": "there is no minimum length requirement", "no_secret_key_published": "no secret key published yet", "fetching_admin_secret_key": "fetching Admins secret key", "fetching_group_secret_key": "fetching Group secret key publishes", "last_encryption_date": "last encryption date: {{ date }} by {{ name }}", "keep_secure": "keep your account file secure", "publishing_key": "reminder: After publishing the key, it will take a couple of minutes for it to appear. Please just wait.", + "seedphrase_notice": "a SEEDPHRASE has been randomly generated in the background.", "type_seed": "type or paste in your seed-phrase", "your_accounts": "your saved accounts" }, @@ -63,6 +69,7 @@ "password": "password", "password_confirmation": "confirm password", "seed": "seed phrase", + "seed_your": "your seedphrase", "tips": { "additional_wallet": "use this option to connect additional Qortal wallets you've already made, in order to login with them afterwards. You will need access to your backup JSON file in order to do so.", "digital_id": "your wallet is like your digital ID on Qortal, and is how you will login to the Qortal User Interface. It holds your public address and the Qortal name you will eventually choose. Every transaction you make is linked to your ID, and this is where you manage all your QORT and other tradeable cryptocurrencies on Qortal.", @@ -70,7 +77,10 @@ "key_encrypt_admin": "this key is to encrypt ADMIN related content. Only admins would see content encrypted with it.", "key_encrypt_group": "this key is to encrypt GROUP related content. This is the only one used in this UI as of now. All group members will be able to see content encrypted with this key.", "new_account": "creating an account means creating a new wallet and digital ID to start using Qortal. Once you have made your account, you can start doing things like obtaining some QORT, buying a name and avatar, publishing videos and blogs, and much more.", - "new_users": "new users start here!" + "new_users": "new users start here!", + "safe_place": "save your account in a place where you will remember it!", + "view_seedphrase": "if you wish to VIEW THE SEEDPHRASE, click the word 'SEEDPHRASE' in this text. Seedphrases are used to generate the private key for your Qortal account. For security by default, seedphrases are NOT displayed unless specifically chosen.", + "wallet_secure": "keep your wallet file secure." }, "wallet": { "password_confirmation": "confirm wallet password", diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index e93cdf2..92e2dd6 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -29,6 +29,7 @@ "edit": "edit", "enter_name": "enter a name", "export": "export", + "get_qort": "get QORT at Q-Trade", "import": "import", "invite": "invite", "join": "join", @@ -59,6 +60,7 @@ "select_category": "select Category", "select_name_app": "select Name/App", "start_minting": "start minting", + "transfer_qort": "Transfer QORT", "unpin": "unpin", "unpin_app": "unpin app", "unpin_from_dashboard": "unpin from dashboard", @@ -97,6 +99,7 @@ "payment": "payment fee", "publish": "publish fee" }, + "for": "for", "general_settings": "general settings", "identifier": "identifier", "last_height": "last height", @@ -138,7 +141,11 @@ "unable_rate": "unable to rate" }, "generic": { + "buy_order_request": "the Application
{{hostname}}
is requesting {{count}} buy order", + "buy_order_request_other": "the Application
{{hostname}}
is requesting {{count}} buy orders", "devmode_local_node": "please use your local node for dev mode! Logout and use Local node.", + "fee_qort": "fee: {{ message }} QORT", + "foreign_fee": "foreign fee: {{ message }}", "name_available": "{{ name }} is available", "name_benefits": "benefits of a name", "name_checking": "checking if name already exists", @@ -148,6 +155,7 @@ "name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee", "name_unavailable": "{{ name }} is unavailable", "no_description": "no description", + "no_minting_details": "cannot view minting details on the gateway", "no_notifications": "no new notifications", "no_pinned_changes": "you currently do not have any changes to your pinned apps", "no_results": "no results", @@ -155,6 +163,7 @@ "overwrite_qdn": "overwrite to QDN", "password_confirm": "please confirm a password", "password_enter": "please enter a password", + "payment_request": "the Application
{{hostname}}
is requesting a payment", "publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!", "publishing": "publishing... Please wait.", "qdn": "use QDN saving", @@ -240,6 +249,8 @@ "user_lookup": "user lookup", "zip": "zip", "wallet": { + "litecoin": "litecoin wallet", + "qortal": "qortal wallet", "wallet": "wallet", "wallet_other": "wallets" }, From 7f1f7f86da0f56168d57067378362e5de9484752 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 01:42:57 +0200 Subject: [PATCH 34/47] Add TODO --- src/qortalRequests/get.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index a14676a..4607e81 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -55,7 +55,6 @@ import { QORT_DECIMALS } from '../constants/constants'; import Base58 from '../deps/Base58'; import ed2curve from '../deps/ed2curve'; import nacl from '../deps/nacl-fast'; - import { base64ToUint8Array, createSymmetricKeyAndNonce, @@ -132,6 +131,7 @@ export async function retryTransaction( throwError, retries = MAX_RETRIES ) { + // TODO transalte let attempt = 0; while (attempt < retries) { try { From 7684201ba934110a0f0946632a3f1e2532cd0a17 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 09:09:19 +0200 Subject: [PATCH 35/47] Add info --- docs/i18n_languages.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/docs/i18n_languages.md b/docs/i18n_languages.md index 9fcf779..da5a64b 100644 --- a/docs/i18n_languages.md +++ b/docs/i18n_languages.md @@ -1,10 +1,25 @@ # I18N Guidelines -In JSON file: +[react-i18next](https://react.i18next.com/) is the framework used for internationalization. + +## Locales + +Locales are in folder `./src/i18n/locales`, one folder per language. + +A single JSON file represents a namespace (group of translation). +It's a key/value structure. + +Please: - Keep the file sorted -- Always write in lowercase +- First letter of each value is lowercase -In GUI: +Translation in GUI: -- If the first letter of the translation must be uppercase, use the postProcess, for example: `{t_auth('advanced_users', { postProcess: 'capitalizeFirst' })}` +- If the first letter of the translation must be uppercase, use the postProcess, for example: `t('core:advanced_users', { postProcess: 'capitalizeFirst' })` +- For all translation in uppercase `{ postProcess: 'capitalizeAll' }` +- See `.src/i18n/i18n.ts` for processor definition + +## Missing language? + +- Please open an issue on the project's github repository and specify the missing language From 8c4b5d27eda44c0e220aabe68a540d2067a6e0a2 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 09:45:36 +0200 Subject: [PATCH 36/47] Add translations --- src/components/Chat/ChatGroup.tsx | 106 ++++++++++++++++++++---------- src/components/Chat/ChatList.tsx | 49 +++++++++----- src/i18n/locales/en/core.json | 4 ++ src/i18n/locales/en/group.json | 2 + src/qortalRequests/get.ts | 2 +- 5 files changed, 108 insertions(+), 55 deletions(-) diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index e7df961..b88b4e7 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -827,23 +827,33 @@ export const ChatGroup = ({ if (replyMessage?.chatReference) { repliedTo = replyMessage?.chatReference; } - let chatReference = onEditMessage?.signature; + + const chatReference = onEditMessage?.signature; const publicData = isPrivate ? {} : { isEdited: chatReference ? true : false, }; - const imagesToPublish = []; + + interface ImageToPublish { + service: string; + identifier: string; + name: string; + base64: string; + } + + const imagesToPublish: ImageToPublish[] = []; const deleteImage = onEditMessage && isDeleteImage && messageHasImage(onEditMessage); if (deleteImage) { const fee = await getFee('ARBITRARY'); - // TODO translate await show({ publishFee: fee.fee + ' QORT', - message: 'Would you like to delete your previous chat image?', + message: t('core:question.delete_chat_image', { + postProcess: 'capitalizeFirst', + }), }); // TODO magic string @@ -853,6 +863,7 @@ export const ChatGroup = ({ service: onEditMessage?.images[0]?.service, }); } + if (chatImagesToSave?.length > 0) { const imageToSave = chatImagesToSave[0]; @@ -877,7 +888,12 @@ export const ChatGroup = ({ 240000, true ); - if (res !== true) throw new Error('Unable to publish images'); + if (res !== true) + throw new Error( + t('core:message.error.unable_publish_image', { + postProcess: 'capitalizeFirst', + }) + ); } const images = @@ -916,7 +932,6 @@ export const ChatGroup = ({ isPrivate === false ? JSON.stringify(objectMessage) : await encryptChatMessage(message64, secretKeyObject); - // const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle}) const sendMessageFunc = async () => { return await sendChatGroup({ @@ -1033,13 +1048,19 @@ export const ChatGroup = ({ try { if (isSending) return; if (+balance < 4) - throw new Error('You need at least 4 QORT to send a message'); - pauseAllQueues(); + // TODO magic number + throw new Error( + t('group:message.error.qortals_required', { + quantity: 4, + postProcess: 'capitalizeFirst', + }) + ); + pauseAllQueues(); setIsSending(true); + const message = ''; const secretKeyObject = await getSecretKey(false, true); - const otherData = { specialId: uid.rnd(), type: 'reaction', @@ -1060,8 +1081,6 @@ export const ChatGroup = ({ secretKeyObject, reactiontypeNumber ); - // const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle}) - const sendMessageFunc = async () => { return await sendChatGroup({ groupId: selectedGroup, @@ -1113,7 +1132,9 @@ export const ChatGroup = ({ ) { setInfoSnack({ type: 'error', - message: 'This message already has an image', + message: t('core:message.generic.message_with_image', { + postProcess: 'capitalizeFirst', + }), }); setOpenSnack(true); return; @@ -1122,6 +1143,7 @@ export const ChatGroup = ({ }, [chatImagesToSave, onEditMessage?.images, isDeleteImage] ); + return (
{(!!secretKey || isPrivate === false) && (
{!isDeleteImage && @@ -1201,19 +1223,20 @@ export const ChatGroup = ({
+ setIsDeleteImage(true)} @@ -1243,12 +1266,13 @@ export const ChatGroup = ({
))} + {chatImagesToSave.map((imgBase64, index) => (
@@ -1261,6 +1285,7 @@ export const ChatGroup = ({ borderRadius: '3px', }} /> + @@ -1295,6 +1320,7 @@ export const ChatGroup = ({
))} + {replyMessage && ( )} + {onEditMessage && ( - 4000 ? theme.palette.other.danger : 'unset', }} - >{`Your message size is of ${messageSize} bytes out of a maximum of 4000`} + > + {t('core:message.error.message_size', { + maximum: 4000, + size: messageSize, + postProcess: 'capitalizeFirst', + })} + )}
@@ -1421,6 +1454,7 @@ export const ChatGroup = ({
)} + {isOpenQManager !== null && ( @@ -1493,7 +1527,7 @@ export const ChatGroup = ({ diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index ebacf06..68ac145 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -5,6 +5,7 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { Box, Button, Typography, useTheme } from '@mui/material'; import { ChatOptions } from './ChatOptions'; import ErrorBoundary from '../../common/ErrorBoundary'; +import { useTranslation } from 'react-i18next'; export const ChatList = ({ initialMessages, @@ -180,6 +181,7 @@ export const ChatList = ({ }, []); const theme = useTheme(); + const { t } = useTranslation(['auth', 'core', 'group']); return ( - Error loading message. + + {t('core:message.error.message_loading', { + postProcess: 'capitalizeFirst', + })} +
); } @@ -363,26 +369,28 @@ export const ChatList = ({ - Error loading content: Invalid Data + {t('group.message.generic.invalid_data', { + postProcess: 'capitalizeFirst', + })} } > @@ -409,9 +417,12 @@ export const ChatList = ({ zIndex: 10, }} > - Scroll to Unread Messages + {t('group:action.scroll_unread_messages', { + postProcess: 'capitalizeFirst', + })} )} + {showScrollDownButton && !showScrollButton && ( )} diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 92e2dd6..669dd8c 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -125,6 +125,7 @@ "generic": "an error occurred", "invalid_signature": "invalid signature", "invalid_zip": "invalid zip", + "message_loading": "error loading message.", "message_size": "your message size is of {{ size }} bytes out of a maximum of {{ maximum }}", "minting_account_add": "unable to add minting account", "minting_account_remove": "unable to remove minting account", @@ -138,6 +139,7 @@ "save_qdn": "unable to save to QDN", "unable_encrypt_app": "unable to encrypt app. App not published'", "unable_publish_app": "unable to publish app", + "unable_publish_image": "unable to publish image", "unable_rate": "unable to rate" }, "generic": { @@ -146,6 +148,7 @@ "devmode_local_node": "please use your local node for dev mode! Logout and use Local node.", "fee_qort": "fee: {{ message }} QORT", "foreign_fee": "foreign fee: {{ message }}", + "message_with_image": "this message already has an image", "name_available": "{{ name }} is available", "name_benefits": "benefits of a name", "name_checking": "checking if name already exists", @@ -218,6 +221,7 @@ "q_sandbox": "q-Sandbox" }, "question": { + "delete_chat_image": "would you like to delete your previous chat image?", "perform_transaction": "would you like to perform a {{action}} transaction?", "provide_thread": "please provide a thread title", "publish_app": "would you like to publish this app?", diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index 78292c4..ceea5ac 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -22,6 +22,8 @@ "remove_admin": "remove as admin", "remove_minting_account": "remove minting account", "return_to_thread": "return to threads", + "scroll_bottom": "scroll to bottom", + "scroll_unread_messages": "scroll to Unread Messages", "select_group": "select a group", "visit_q_mintership": "visit Q-Mintership" }, diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 4607e81..3f71405 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -85,7 +85,7 @@ import { RequestQueueWithPromise } from '../utils/queue/queue'; import utils from '../utils/utils'; import ShortUniqueId from 'short-unique-id'; import { isValidBase64WithDecode } from '../utils/decode'; - +//TODO translate const uid = new ShortUniqueId({ length: 6 }); export const requestQueueGetAtAddresses = new RequestQueueWithPromise(10); From c849854f58fd25c555d00e71aaf6d678f0eba0d5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 10:03:09 +0200 Subject: [PATCH 37/47] Add translations --- src/components/Chat/ChatList.tsx | 1 + src/components/Chat/ChatOptions.tsx | 51 +++++++++++++++++----- src/components/Chat/CreateCommonSecret.tsx | 46 ++++++++++++++----- src/i18n/locales/en/core.json | 6 +++ 4 files changed, 84 insertions(+), 20 deletions(-) diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 68ac145..afb88d0 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -449,6 +449,7 @@ export const ChatList = ({ )} + {enableMentions && (hasSecretKey || isPrivate === false) && ( { return untransformedMessages?.map((item) => { if (item?.messageText) { @@ -80,7 +81,7 @@ export const ChatOptions = ({ messageText: transformedMessage, }; } catch (error) { - // error + console.log(error); } } else return item; }); @@ -102,7 +103,12 @@ export const ChatOptions = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -154,6 +160,7 @@ export const ChatOptions = ({ } return []; } + if (selectedMember) { return messages .filter( @@ -165,6 +172,7 @@ export const ChatOptions = ({ ) ?.sort((a, b) => b?.timestamp - a?.timestamp); } + return messages .filter((message) => extractTextFromHTML( @@ -187,6 +195,7 @@ export const ChatOptions = ({ ) ?.sort((a, b) => b?.timestamp - a?.timestamp); } + return messages .filter((message) => extractTextFromHTML(message?.decryptedData?.message)?.includes( @@ -251,6 +260,7 @@ export const ChatOptions = ({ }} />
+ - No results + {t('core:message.generic.no_results', { + postProcess: 'capitalizeFirst', + })} )} + + {searchValue && ( + - By member + + {t('core:sort.by_member', { postProcess: 'capitalizeFirst' })} + + {members?.map((member) => { return ( @@ -455,6 +473,7 @@ export const ChatOptions = ({ ); })} + {!!selectedMember && ( { @@ -476,9 +495,12 @@ export const ChatOptions = ({ color: theme.palette.text.secondary, }} > - No results + {t('core:message.generic.no_results', { + postProcess: 'capitalizeFirst', + })} )} + - Error loading content: Invalid Data + {t('group.message.generic.invalid_data', { + postProcess: 'capitalizeFirst', + })} } > @@ -567,6 +591,7 @@ export const ChatOptions = ({ ); } + return ( - SEARCH + {t('core:action.search', { postProcess: 'capitalizeAll' })} } placement="left" @@ -629,6 +654,7 @@ export const ChatOptions = ({
+ { setMode('default'); @@ -646,7 +672,7 @@ export const ChatOptions = ({ fontWeight: 700, }} > - Q-MANAGER + {t('core:q_apps.q_manager', { postProcess: 'capitalizeAll' })} } placement="left" @@ -669,6 +695,7 @@ export const ChatOptions = ({ + - MENTIONED + {t('core:message.generic.mentioned', { + postProcess: 'capitalizeAll', + })} } placement="left" @@ -768,6 +797,7 @@ const ShowMessage = ({ message, goToMessage, messages }) => { > {message?.senderName?.charAt(0)} + { > {formatTimestamp(message.timestamp)} + { - // const validApi = await findUsableApi(); const queryString = admins.map((name) => `name=${name}`).join('&'); const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=symmetric-qchat-group-${ groupId @@ -55,9 +56,11 @@ export const CreateCommonSecret = ({ const filterId = adminData.filter( (data: any) => data.identifier === `symmetric-qchat-group-${groupId}` ); + if (filterId?.length === 0) { return false; } + const sortedData = filterId.sort((a: any, b: any) => { // Get the most recent date for both a and b const dateA = a.updated ? new Date(a.updated) : new Date(a.created); @@ -78,8 +81,13 @@ export const CreateCommonSecret = ({ pauseAllQueues(); const { names } = await getGroupAdmins(groupId); + if (!names.length) { - throw new Error('Network error'); + throw new Error( + t('core:message.error.network_generic', { + postProcess: 'capitalizeFirst', + }) + ); } const publish = await getPublishesFromAdmins(names); @@ -92,15 +100,18 @@ export const CreateCommonSecret = ({ publish.identifier }?encoding=base64&rebuild=true` ); + const data = await res.text(); - const decryptedKey: any = await decryptResource(data); - const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); if (!validateSecretKey(decryptedKeyToObject)) - throw new Error('SecretKey is not valid'); + throw new Error( + t('auth:message.error.invalid_secret_key', { + postProcess: 'capitalizeFirst', + }) + ); if (decryptedKeyToObject) { return decryptedKeyToObject; @@ -113,17 +124,31 @@ export const CreateCommonSecret = ({ const createCommonSecret = async () => { try { const fee = await getFee('ARBITRARY'); + await show({ - message: 'Would you like to perform an ARBITRARY transaction?', + message: t('core:question.perform_transaction', { + action: 'ARBITRARY', + postProcess: 'capitalizeFirst', + }), publishFee: fee.fee + ' QORT', }); setIsLoading(true); const secretKey2 = await getSecretKey(); + if (!secretKey2 && secretKey2 !== false) - throw new Error('invalid secret key'); + throw new Error( + t('auth:message.error.invalid_secret_key', { + postProcess: 'capitalizeFirst', + }) + ); + if (secretKey2 && !validateSecretKey(secretKey2)) - throw new Error('invalid secret key'); + throw new Error( + t('auth:message.error.invalid_secret_key', { + postProcess: 'capitalizeFirst', + }) + ); const secretKeyToSend = !secretKey2 ? null : secretKey2; @@ -136,8 +161,9 @@ export const CreateCommonSecret = ({ if (!response?.error) { setInfoSnack({ type: 'success', - message: - 'Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.', + message: t('auth:message.success.reencrypted_secret_key', { + postProcess: 'capitalizeFirst', + }), }); setOpenSnack(true); setTxList((prev) => [ diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 669dd8c..948140a 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -55,6 +55,7 @@ "remove": "remove", "return_apps_dashboard": "return to Apps Dashboard", "save": "save", + "search": "search", "search_apps": "search for apps", "select_app_type": "select App Type", "select_category": "select Category", @@ -148,6 +149,7 @@ "devmode_local_node": "please use your local node for dev mode! Logout and use Local node.", "fee_qort": "fee: {{ message }} QORT", "foreign_fee": "foreign fee: {{ message }}", + "mentioned": "mentioned", "message_with_image": "this message already has an image", "name_available": "{{ name }} is available", "name_benefits": "benefits of a name", @@ -218,6 +220,7 @@ "q_apps": { "about": "about this Q-App", "q_mail": "q-mail", + "q_manager": "q-manager", "q_sandbox": "q-Sandbox" }, "question": { @@ -234,6 +237,9 @@ }, "server": "server", "settings": "settings", + "sort": { + "by_member": "by member" + }, "supply": "supply", "tags": "tags", "theme": { From 628786165a7149b28d0e47414a86d0f9b4aa32d4 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 10:22:37 +0200 Subject: [PATCH 38/47] Update translations --- src/components/Chat/CreateCommonSecret.tsx | 30 ++++++++++++++++------ src/i18n/locales/en/auth.json | 1 + src/i18n/locales/en/core.json | 1 + src/i18n/locales/en/group.json | 5 ++++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/components/Chat/CreateCommonSecret.tsx b/src/components/Chat/CreateCommonSecret.tsx index a9da82f..bfb7684 100644 --- a/src/components/Chat/CreateCommonSecret.tsx +++ b/src/components/Chat/CreateCommonSecret.tsx @@ -170,8 +170,17 @@ export const CreateCommonSecret = ({ { ...response, type: 'created-common-secret', - label: `Published secret key for group ${groupId}: awaiting confirmation`, - labelDone: `Published secret key for group ${groupId}: success!`, + label: t('group:message.success.published_secret_key', { + group_id: groupId, + postProcess: 'capitalizeFirst', + }), + labelDone: t( + 'group:message.success.published_secret_key_label', + { + group_id: groupId, + postProcess: 'capitalizeFirst', + } + ), done: false, groupId, }, @@ -213,13 +222,15 @@ export const CreateCommonSecret = ({ variant="contained" onClick={createCommonSecret} > - Re-encrypt key + {t('auth:action.reencrypt_key', { postProcess: 'capitalizeFirst' })} {noSecretKey ? ( - There is no group secret key. Be the first admin to publish one! + {t('group:message.generic.group_no_secret_key', { + postProcess: 'capitalizeFirst', + })} ) : isOwner && @@ -228,14 +239,17 @@ export const CreateCommonSecret = ({ userInfo.name !== secretKeyDetails?.name ? ( - The latest group secret key was published by a non-owner. As the - owner of the group please re-encrypt the key as a safeguard + {t('group:message.generic.group_secret_key_no_owner', { + postProcess: 'capitalizeFirst', + })} ) : isForceShowCreationKeyPopup ? null : ( - The group member list has changed. Please re-encrypt the secret key. + {t('group:message.generic.group_member_list_changed', { + postProcess: 'capitalizeFirst', + })} )} @@ -254,7 +268,7 @@ export const CreateCommonSecret = ({ }} size="small" > - Hide + {t('core:action.hide', { postProcess: 'capitalizeFirst' })} diff --git a/src/i18n/locales/en/auth.json b/src/i18n/locales/en/auth.json index 8b9ef33..daf0c8d 100644 --- a/src/i18n/locales/en/auth.json +++ b/src/i18n/locales/en/auth.json @@ -18,6 +18,7 @@ "export_seedphrase": "export Seedphrase", "publish_admin_secret_key": "publish admin secret key", "publish_group_secret_key": "publish group secret key", + "reencrypt_key": "re-encrypt key", "return_to_list": "return to list", "setup_qortal_account": "set up your Qortal account" }, diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 948140a..bf54cee 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -30,6 +30,7 @@ "enter_name": "enter a name", "export": "export", "get_qort": "get QORT at Q-Trade", + "hide": "hide", "import": "import", "invite": "invite", "join": "join", diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index ceea5ac..c161605 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -64,6 +64,9 @@ "encryption_key": "the group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes...", "group_invited_you": "{{group}} has invited you", "group_key_created": "first group key created.", + "group_member_list_changed": "the group member list has changed. Please re-encrypt the secret key.", + "group_no_secret_key": "there is no group secret key. Be the first admin to publish one!", + "group_secret_key_no_owner": "the latest group secret key was published by a non-owner. As the owner of the group please re-encrypt the key as a safeguard.", "invalid_content": "invalid content, sender, or timestamp in reaction data", "invalid_data": "error loading content: Invalid Data", "latest_promotion": "only the latest promotion from the week will be shown for your group.", @@ -126,6 +129,8 @@ "invitation_request": "accepted join request: awaiting confirmation", "loading_threads": "loading threads... please wait.", "post_creation": "successfully created post. It may take some time for the publish to propagate", + "published_secret_key": "published secret key for group {{ group_id }}: awaiting confirmation", + "published_secret_key_label": "published secret key for group {{ group_id }}: success!", "registered_name": "successfully registered. It may take a couple of minutes for the changes to propagate", "registered_name_label": "registered name: awaiting confirmation. This may take a couple minutes.", "registered_name_success": "registered name: success!", From bec943149f064ab1126ee734854be721d6760110 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 11:20:42 +0200 Subject: [PATCH 39/47] Translation for chat pages --- src/components/Chat/GroupAnnouncements.tsx | 35 ++++++++--- src/components/Chat/GroupAvatar.tsx | 48 +++++++++++---- src/components/Chat/MentionList.tsx | 9 ++- src/components/Chat/MessageItem.tsx | 68 +++++++++++++++++----- src/components/Chat/TipTap.tsx | 36 ++++++++---- src/i18n/locales/en/core.json | 23 +++++++- src/i18n/locales/en/group.json | 6 ++ 7 files changed, 178 insertions(+), 47 deletions(-) diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 3671a51..d2a997f 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -234,7 +234,12 @@ export const GroupAnnouncements = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); } catch (error) { @@ -257,7 +262,12 @@ export const GroupAnnouncements = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); }; @@ -337,7 +347,7 @@ export const GroupAnnouncements = ({ setTempData(selectedGroup); clearEditorContent(); } - // TODO send chat message + // send chat message } catch (error) { if (!error) return; setInfoSnack({ @@ -586,7 +596,9 @@ export const GroupAnnouncements = ({ fontSize: '30px', }} /> - Group Announcements + {t('group:message.generic.group_announcement', { + postProcess: 'capitalizeFirst', + })} @@ -605,10 +617,13 @@ export const GroupAnnouncements = ({ fontSize: '16px', }} > - No announcements + {t('group:message.generic.no_announcement', { + postProcess: 'capitalizeFirst', + })} )} + - {` Close`} + {t('core:action.close', { postProcess: 'capitalizeFirst' })} )} @@ -723,7 +738,9 @@ export const GroupAnnouncements = ({ }} /> )} - {` Publish Announcement`} + {t('group:action.publish_announcement', { + postProcess: 'capitalizeFirst', + })} @@ -739,7 +756,9 @@ export const GroupAnnouncements = ({ diff --git a/src/components/Chat/GroupAvatar.tsx b/src/components/Chat/GroupAvatar.tsx index dbc25cd..caf3c4b 100644 --- a/src/components/Chat/GroupAvatar.tsx +++ b/src/components/Chat/GroupAvatar.tsx @@ -20,6 +20,7 @@ import { getFee } from '../../background'; import { fileToBase64 } from '../../utils/fileReading'; import { LoadingButton } from '@mui/lab'; import ErrorIcon from '@mui/icons-material/Error'; +import { useTranslation } from 'react-i18next'; export const GroupAvatar = ({ myName, @@ -32,7 +33,7 @@ export const GroupAvatar = ({ const [avatarFile, setAvatarFile] = useState(null); const [tempAvatar, setTempAvatar] = useState(null); const { show } = useContext(MyContext); - + const { t } = useTranslation(['auth', 'core', 'group']); const [anchorEl, setAnchorEl] = useState(null); const [isLoading, setIsLoading] = useState(false); // Handle child element click to open Popover @@ -68,6 +69,7 @@ export const GroupAvatar = ({ console.log(error); } }, []); + useEffect(() => { if (!myName || !groupId) return; checkIfAvatarExists(myName, groupId); @@ -79,14 +81,22 @@ export const GroupAvatar = ({ const fee = await getFee('ARBITRARY'); if (+balance < +fee.fee) - throw new Error(`Publishing an Avatar requires ${fee.fee}`); + throw new Error( + t('core:message.generic.avatar_publish_fee', { + fee: fee.fee, + postProcess: 'capitalizeFirst', + }) + ); await show({ - message: 'Would you like to publish an avatar?', + message: t('core:question.publish_avatar', { + postProcess: 'capitalizeFirst', + }), publishFee: fee.fee + ' QORT', }); setIsLoading(true); const avatarBase64 = await fileToBase64(avatarFile); + await new Promise((res, rej) => { window .sendMessage('publishOnQDN', { @@ -102,7 +112,12 @@ export const GroupAvatar = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { + postProcess: 'capitalizeFirst', + }) + ); }); }); setAvatarFile(null); @@ -142,7 +157,7 @@ export const GroupAvatar = ({ opacity: 0.5, }} > - change avatar + {t('core:action.change_avatar', { postProcess: 'capitalizeFirst' })} @@ -182,7 +197,7 @@ export const GroupAvatar = ({ opacity: 0.5, }} > - change avatar + {t('core:action.change_avatar', { postProcess: 'capitalizeFirst' })} @@ -211,7 +226,7 @@ export const GroupAvatar = ({ opacity: 0.5, }} > - set avatar + {t('core:action.set_avatar', { postProcess: 'capitalizeFirst' })} @@ -242,6 +257,8 @@ const PopoverComp = ({ myName, }) => { const theme = useTheme(); + const { t } = useTranslation(['auth', 'core', 'group']); + return ( - (500 KB max. for GIFS){' '} + {t('core:message.generic.avatar_size', { + size: 500, // TODO magic number + postProcess: 'capitalizeFirst', + })} setAvatarFile(file)}> - + {avatarFile?.name} @@ -277,9 +299,9 @@ const PopoverComp = ({ {!myName && ( - A registered name is required to set an avatar + {t('core:message.generic.avatar_registered_name', { + postProcess: 'capitalizeFirst', + })} )} @@ -301,7 +325,7 @@ const PopoverComp = ({ onClick={publishAvatar} variant="contained" > - Publish avatar + {t('group:action.publish_avatar', { postProcess: 'capitalizeFirst' })} diff --git a/src/components/Chat/MentionList.tsx b/src/components/Chat/MentionList.tsx index 06cea5c..e9f0f86 100644 --- a/src/components/Chat/MentionList.tsx +++ b/src/components/Chat/MentionList.tsx @@ -1,6 +1,9 @@ import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; +import { useTranslation } from 'react-i18next'; export default forwardRef((props, ref) => { + const { t } = useTranslation(['auth', 'core', 'group']); + const [selectedIndex, setSelectedIndex] = useState(0); const selectItem = (index) => { @@ -61,7 +64,11 @@ export default forwardRef((props, ref) => { )) ) : ( -
No result
+
+ {t('core:message.generic.no_results', { + postProcess: 'capitalizeFirst', + })} +
)} ); diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index 7b72d79..cc19ee3 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -48,6 +48,7 @@ import level9Img from '../../assets/badges/level-9.png'; import level10Img from '../../assets/badges/level-10.png'; import { Embed } from '../Embeds/Embed'; import { buildImageEmbedLink, messageHasImage } from '../../utils/chat'; +import { useTranslation } from 'react-i18next'; const getBadgeImg = (level) => { switch (level?.toString()) { @@ -169,12 +170,15 @@ export const MessageItem = memo( }, [message?.id]); const theme = useTheme(); + const { t } = useTranslation(['auth', 'core', 'group']); return ( <> {message?.divide && (
- Unread messages below + {t('core:message.generic.unread_messages', { + postProcess: 'capitalizeFirst', + })}
)} @@ -262,6 +266,7 @@ export const MessageItem = memo( {message?.senderName || message?.sender} + )} + {!isShowingAsReply && ( { @@ -288,6 +294,7 @@ export const MessageItem = memo( )} + {!isShowingAsReply && handleReaction && ( { @@ -307,6 +314,7 @@ export const MessageItem = memo( )} + {reply && ( <> @@ -333,6 +341,7 @@ export const MessageItem = memo( flexShrink: 0, }} /> + - Replied to {reply?.senderName || reply?.senderAddress} + {t('core:message.generic.replied_to', { + person: reply?.senderName || reply?.senderAddress, + postProcess: 'capitalizeFirst', + })} + {reply?.messageText && ( )} + {reply?.decryptedData?.type === 'notification' ? ( )} + {htmlText && } {message?.decryptedData?.type === 'notification' ? ( @@ -454,14 +469,17 @@ export const MessageItem = memo( > - People who reacted with {selectedReaction} + {t('core:message.generic.people_reaction', { + reaction: selectedReaction, + postProcess: 'capitalizeFirst', + })} {reactions[selectedReaction]?.map((reactionItem) => ( @@ -474,6 +492,7 @@ export const MessageItem = memo( ))} + )} + {message?.status === 'failed-permanent' - ? 'Failed to update' - : 'Updating...'} + ? t('core:message.error.update_failed', { + postProcess: 'capitalizeFirst', + }) + : t('core:message.generic.updating', { + postProcess: 'capitalizeFirst', + })} ) : isTemp ? ( {message?.status === 'failed-permanent' - ? 'Failed to send' - : 'Sending...'} + ? t('core:message.error.send_failed', { + postProcess: 'capitalizeFirst', + }) + : t('core:message.generic.sending', { + postProcess: 'capitalizeFirst', + })} ) : ( <> @@ -552,7 +584,9 @@ export const MessageItem = memo( fontStyle: 'italic', }} > - Edited + {t('core:message.generic.edited', { + postProcess: 'capitalizeFirst', + })} )} { const theme = useTheme(); + const { t } = useTranslation(['auth', 'core', 'group']); return ( { fontWeight: 600, }} > - Editing Message + {t('core:message.generic.editing_message', { + postProcess: 'capitalizeFirst', + })} ) : ( { fontWeight: 600, }} > - Replied to {message?.senderName || message?.senderAddress} + {t('core:message.generic.replied_to', { + person: message?.senderName || message?.senderAddress, + postProcess: 'capitalizeFirst', + })} )} diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 94894ab..cc315fe 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -31,6 +31,8 @@ import { isDisabledEditorEnterAtom } from '../../atoms/global.js'; import { Box, Checkbox, Typography, useTheme } from '@mui/material'; import { useAtom } from 'jotai'; import { fileToBase64 } from '../../utils/fileReading/index.js'; +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; function textMatcher(doc, from) { const textBeforeCursor = doc.textBetween(0, from, ' ', ' '); @@ -52,6 +54,7 @@ const MenuBar = memo( const { editor } = useCurrentEditor(); const fileInputRef = useRef(null); const theme = useTheme(); + const { t } = useTranslation(['auth', 'core', 'group']); useEffect(() => { if (editor && setEditorRef) { @@ -143,6 +146,7 @@ const MenuBar = memo( > + editor.chain().focus().toggleItalic().run()} disabled={!editor.can().chain().focus().toggleItalic().run()} @@ -155,6 +159,7 @@ const MenuBar = memo( > + editor.chain().focus().toggleStrike().run()} disabled={!editor.can().chain().focus().toggleStrike().run()} @@ -167,6 +172,7 @@ const MenuBar = memo( > + editor.chain().focus().toggleCode().run()} disabled={!editor.can().chain().focus().toggleCode().run()} @@ -179,6 +185,7 @@ const MenuBar = memo( > + editor.chain().focus().unsetAllMarks().run()} sx={{ @@ -194,6 +201,7 @@ const MenuBar = memo( > + editor.chain().focus().toggleBulletList().run()} sx={{ @@ -205,6 +213,7 @@ const MenuBar = memo( > + editor.chain().focus().toggleOrderedList().run()} sx={{ @@ -216,6 +225,7 @@ const MenuBar = memo( > + editor.chain().focus().toggleCodeBlock().run()} sx={{ @@ -227,6 +237,7 @@ const MenuBar = memo( > + editor.chain().focus().toggleBlockquote().run()} sx={{ @@ -238,6 +249,7 @@ const MenuBar = memo( > + editor.chain().focus().setHorizontalRule().run()} disabled={!editor.can().chain().focus().setHorizontalRule().run()} @@ -245,6 +257,7 @@ const MenuBar = memo( > + editor.chain().focus().toggleHeading({ level: 1 }).run() @@ -258,6 +271,7 @@ const MenuBar = memo( > + editor.chain().focus().undo().run()} disabled={!editor.can().chain().focus().undo().run()} @@ -265,6 +279,7 @@ const MenuBar = memo( > + editor.chain().focus().redo().run()} disabled={!editor.can().chain().focus().redo().run()} @@ -272,13 +287,14 @@ const MenuBar = memo( > + {isChat && ( { setIsDisabledEditorEnter(!isDisabledEditorEnter); @@ -304,10 +320,13 @@ const MenuBar = memo( color: theme.palette.text.primary, }} > - disable enter + {t('core:action.disable_enter', { + postProcess: 'capitalizeFirst', + })} )} + {!isChat && ( <> + { return (membersWithNames || [])?.map((item) => { return { diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index bf54cee..39f8358 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -2,6 +2,7 @@ "action": { "add": "add", "add_custom_framework": "add custom framework", + "add_reaction": "add reaction", "accept": "accept", "access": "access", "backup_account": "backup account", @@ -9,10 +10,12 @@ "cancel": "cancel", "cancel_invitation": "cancel invitation", "change": "change", + "change_avatar": "change avatar", "change_file": "change file", "change_language": "change language", "choose": "choose", "choose_file": "choose file", + "choose_image": "choose image", "close": "close", "close_chat": "close Direct Chat", "continue": "continue", @@ -25,6 +28,7 @@ "choose_name": "choose a name", "decline": "decline", "decrypt": "decrypt", + "disable_enter": "disable enter", "download": "download", "edit": "edit", "enter_name": "enter a name", @@ -54,6 +58,7 @@ "publish_comment": "publish comment", "register_name": "register name", "remove": "remove", + "remove_reaction": "remove reaction", "return_apps_dashboard": "return to Apps Dashboard", "save": "save", "search": "search", @@ -61,7 +66,9 @@ "select_app_type": "select App Type", "select_category": "select Category", "select_name_app": "select Name/App", + "set_avatar": "set avatar", "start_minting": "start minting", + "start_typing": "start typing here...", "transfer_qort": "Transfer QORT", "unpin": "unpin", "unpin_app": "unpin app", @@ -113,6 +120,7 @@ "member": "member list" }, "loading": { + "announcements": "announcements", "generic": "loading...", "chat": "loading chat... please wait.", "comments": "loading comments... please wait.", @@ -139,15 +147,20 @@ "publish_app": "unable to publish app", "rating_option": "cannot find rating option", "save_qdn": "unable to save to QDN", + "send_failed": "failed to send", "unable_encrypt_app": "unable to encrypt app. App not published'", "unable_publish_app": "unable to publish app", "unable_publish_image": "unable to publish image", - "unable_rate": "unable to rate" + "unable_rate": "unable to rate", + "update_failed": "failed to update" }, "generic": { + "avatar_size": "{{ size }} KB max. for GIFS", "buy_order_request": "the Application
{{hostname}}
is requesting {{count}} buy order", "buy_order_request_other": "the Application
{{hostname}}
is requesting {{count}} buy orders", "devmode_local_node": "please use your local node for dev mode! Logout and use Local node.", + "edited": "edited", + "editing_message": "editing message", "fee_qort": "fee: {{ message }} QORT", "foreign_fee": "foreign fee: {{ message }}", "mentioned": "mentioned", @@ -170,11 +183,13 @@ "password_confirm": "please confirm a password", "password_enter": "please enter a password", "payment_request": "the Application
{{hostname}}
is requesting a payment", + "people_reaction": "people who reacted with {{ reaction }}", "publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!", "publishing": "publishing... Please wait.", "qdn": "use QDN saving", "rating": "rating for {{ service }} {{ name }}", "register_name": "you need a registered Qortal name to save your pinned apps to QDN.", + "replied_to": "replied to {{ person }}", "revert_default": "revert to default", "revert_qdn": "revert to QDN", "save_qdn": "save to QDN", @@ -182,9 +197,12 @@ "select_file": "please select a file", "select_image": "please select an image for a logo", "select_zip": "select .zip file containing static content:", + "sending": "sending...", "settings": "you are using the export/import way of saving settings.", "space_for_admins": "sorry, this space is only for Admins.", - "unsaved_changes": "you have unsaved changes to your pinned apps. Save them to QDN." + "unread_messages": "unread messages below", + "unsaved_changes": "you have unsaved changes to your pinned apps. Save them to QDN.", + "updating": "updating" }, "question": { "logout": "are you sure you would like to logout?", @@ -229,6 +247,7 @@ "perform_transaction": "would you like to perform a {{action}} transaction?", "provide_thread": "please provide a thread title", "publish_app": "would you like to publish this app?", + "publish_avatar": "would you like to publish an avatar?", "publish_qdn": "would you like to publish your settings to QDN (encrypted)?", "overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?", "rate_app": "would you like to rate this app a rating of {{ rate }}?. It will create a POLL tx.", diff --git a/src/i18n/locales/en/group.json b/src/i18n/locales/en/group.json index c161605..8ad2d6b 100644 --- a/src/i18n/locales/en/group.json +++ b/src/i18n/locales/en/group.json @@ -18,6 +18,8 @@ "make_admin": "make an admin", "manage_members": "manage members", "promote_group": "promote your group to non-members", + "publish_announcement": "publish announcement", + "publish_avatar": "publish avatar", "refetch_page": "refetch page", "remove_admin": "remove as admin", "remove_minting_account": "remove minting account", @@ -57,11 +59,14 @@ "latest_mails": "latest Q-Mails", "message": { "generic": { + "avatar_publish_fee": "publishing an Avatar requires {{ fee }}", + "avatar_registered_name": "a registered name is required to set an avatar", "admin_only": "only groups where you are an admin will be shown", "already_in_group": "you are already in this group!", "closed_group": "this is a closed/private group, so you will need to wait until an admin accepts your request", "descrypt_wallet": "decrypting wallet...", "encryption_key": "the group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes...", + "group_announcement": "group Announcements", "group_invited_you": "{{group}} has invited you", "group_key_created": "first group key created.", "group_member_list_changed": "the group member list has changed. Please re-encrypt the secret key.", @@ -82,6 +87,7 @@ "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", "no_display": "nothing to display", "no_selection": "no group selected", "not_part_group": "you are not part of the encrypted group of members. Wait until an admin re-encrypts the keys.", From 9f9c50ddb8e25ebc31ef0f08d2854f667e31a164 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 11:39:18 +0200 Subject: [PATCH 40/47] Fix typo --- src/components/Chat/ChatList.tsx | 2 +- src/components/Chat/ChatOptions.tsx | 3 ++- src/components/Group/ListOfGroupPromotions.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index afb88d0..0589832 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -369,7 +369,7 @@ export const ChatList = ({ - {t('group.message.generic.invalid_data', { + {t('group:message.generic.invalid_data', { postProcess: 'capitalizeFirst', })} diff --git a/src/components/Chat/ChatOptions.tsx b/src/components/Chat/ChatOptions.tsx index 303ff34..9afd134 100644 --- a/src/components/Chat/ChatOptions.tsx +++ b/src/components/Chat/ChatOptions.tsx @@ -34,6 +34,7 @@ import { ContextMenuMentions } from '../ContextMenuMentions'; import { convert } from 'html-to-text'; import { generateHTML } from '@tiptap/react'; import ErrorBoundary from '../../common/ErrorBoundary'; +import { useTranslation } from 'react-i18next'; const extractTextFromHTML = (htmlString = '') => { return convert(htmlString, { @@ -567,7 +568,7 @@ export const ChatOptions = ({ - {t('group.message.generic.invalid_data', { + {t('group:message.generic.invalid_data', { postProcess: 'capitalizeFirst', })} diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index 4e4be16..32f032e 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -575,7 +575,7 @@ export const ListOfGroupPromotions = () => { - {t('group.message.generic.invalid_data', { + {t('group:message.generic.invalid_data', { postProcess: 'capitalizeFirst', })} From a8f95b3176382f8c9492c747baeabe8ac4cb4ff7 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 11:49:33 +0200 Subject: [PATCH 41/47] Remove TextStyle (it causes error in console) --- src/components/Chat/TipTap.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index cc315fe..8b17c65 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -3,7 +3,6 @@ import { EditorProvider, useCurrentEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import { Color } from '@tiptap/extension-color'; import ListItem from '@tiptap/extension-list-item'; -import TextStyle from '@tiptap/extension-text-style'; import Placeholder from '@tiptap/extension-placeholder'; import IconButton from '@mui/material/IconButton'; import FormatBoldIcon from '@mui/icons-material/FormatBold'; @@ -32,7 +31,7 @@ import { Box, Checkbox, Typography, useTheme } from '@mui/material'; import { useAtom } from 'jotai'; import { fileToBase64 } from '../../utils/fileReading/index.js'; import { useTranslation } from 'react-i18next'; -import { t } from 'i18next'; +import i18next from 'i18next'; function textMatcher(doc, from) { const textBeforeCursor = doc.textBetween(0, from, ' ', ' '); @@ -355,8 +354,7 @@ const MenuBar = memo( ); const extensions = [ - Color.configure({ types: [TextStyle.name, ListItem.name] }), - TextStyle.configure({ types: [ListItem.name] }), + Color.configure({ types: [ListItem.name] }), StarterKit.configure({ bulletList: { keepMarks: true, @@ -368,9 +366,7 @@ const extensions = [ }, }), Placeholder.configure({ - placeholder: t('core:action.start_typing', { - postProcess: 'capitalizeFirst', - }), + placeholder: i18next.t('core:action.start_typing'), }), ImageResize, ]; From d3b8430b90bb8fe0dc6e337663884563c6142164 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 11:53:38 +0200 Subject: [PATCH 42/47] Remove TextStyle (error in console) --- src/components/Chat/MessageItem.tsx | 4 ---- src/components/Group/Group.tsx | 4 ---- src/i18n/locales/en/core.json | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index cc19ee3..d52b388 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -34,7 +34,6 @@ import { Spacer } from '../../common/Spacer'; import { ReactionPicker } from '../ReactionPicker'; import KeyOffIcon from '@mui/icons-material/KeyOff'; import EditIcon from '@mui/icons-material/Edit'; -import TextStyle from '@tiptap/extension-text-style'; import level0Img from '../../assets/badges/level-0.png'; import level1Img from '../../assets/badges/level-1.png'; import level2Img from '../../assets/badges/level-2.png'; @@ -140,7 +139,6 @@ export const MessageItem = memo( Underline, Highlight, Mention, - TextStyle, ]); } }, [message?.editTimestamp]); @@ -152,7 +150,6 @@ export const MessageItem = memo( Underline, Highlight, Mention, - TextStyle, ]); } }, [reply?.editTimestamp]); @@ -673,7 +670,6 @@ export const ReplyPreview = ({ message, isEdit = false }) => { Underline, Highlight, Mention, - TextStyle, ])} /> )} diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index b524697..10e39a4 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -373,7 +373,6 @@ export const Group = ({ desktopViewMode, }: GroupProps) => { const [desktopSideView, setDesktopSideView] = useState('groups'); - const [secretKey, setSecretKey] = useState(null); const [secretKeyPublishDate, setSecretKeyPublishDate] = useState(null); const lastFetchedSecretKey = useRef(null); @@ -396,14 +395,11 @@ export const Group = ({ const [openAddGroup, setOpenAddGroup] = useState(false); const [isInitialGroups, setIsInitialGroups] = useState(false); const [openManageMembers, setOpenManageMembers] = useState(false); - const setMemberGroups = useSetAtom(memberGroupsAtom); - const lastGroupNotification = useRef(null); const [timestampEnterData, setTimestampEnterData] = useAtom( timestampEnterDataAtom ); - const [chatMode, setChatMode] = useState('groups'); const [newChat, setNewChat] = useState(false); const [openSnack, setOpenSnack] = useState(false); diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 39f8358..05c6ce5 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -68,7 +68,7 @@ "select_name_app": "select Name/App", "set_avatar": "set avatar", "start_minting": "start minting", - "start_typing": "start typing here...", + "start_typing": "start typing here...aa", "transfer_qort": "Transfer QORT", "unpin": "unpin", "unpin_app": "unpin app", From 504a6c5ba8235f973605ef8aead994fea4c56a8a Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 12:08:55 +0200 Subject: [PATCH 43/47] Restore TextStyle --- src/components/Chat/MessageItem.tsx | 4 ++++ src/components/Chat/TipTap.tsx | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index d52b388..cc19ee3 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -34,6 +34,7 @@ import { Spacer } from '../../common/Spacer'; import { ReactionPicker } from '../ReactionPicker'; import KeyOffIcon from '@mui/icons-material/KeyOff'; import EditIcon from '@mui/icons-material/Edit'; +import TextStyle from '@tiptap/extension-text-style'; import level0Img from '../../assets/badges/level-0.png'; import level1Img from '../../assets/badges/level-1.png'; import level2Img from '../../assets/badges/level-2.png'; @@ -139,6 +140,7 @@ export const MessageItem = memo( Underline, Highlight, Mention, + TextStyle, ]); } }, [message?.editTimestamp]); @@ -150,6 +152,7 @@ export const MessageItem = memo( Underline, Highlight, Mention, + TextStyle, ]); } }, [reply?.editTimestamp]); @@ -670,6 +673,7 @@ export const ReplyPreview = ({ message, isEdit = false }) => { Underline, Highlight, Mention, + TextStyle, ])} /> )} diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 8b17c65..497a92e 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -3,6 +3,7 @@ import { EditorProvider, useCurrentEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import { Color } from '@tiptap/extension-color'; import ListItem from '@tiptap/extension-list-item'; +import TextStyle from '@tiptap/extension-text-style'; import Placeholder from '@tiptap/extension-placeholder'; import IconButton from '@mui/material/IconButton'; import FormatBoldIcon from '@mui/icons-material/FormatBold'; @@ -354,7 +355,8 @@ const MenuBar = memo( ); const extensions = [ - Color.configure({ types: [ListItem.name] }), + Color.configure({ types: [TextStyle.name, ListItem.name] }), + TextStyle.configure({ types: [ListItem.name] }), StarterKit.configure({ bulletList: { keepMarks: true, From 9487cd92e9434a466eb1c3fb6621f27eed8240f8 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 12:26:17 +0200 Subject: [PATCH 44/47] Fix typo --- src/App.tsx | 2 +- src/components/Chat/TipTap.tsx | 2 +- src/i18n/locales/en/core.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 3ad029c..3f7dcca 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1061,7 +1061,7 @@ function App() { try { if (extState === 'authenticated') { await showUnsavedChanges({ - message: t('core:question.logout', { + message: t('core:message.question.logout', { postProcess: 'capitalizeFirst', }), }); diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 497a92e..aad7f1b 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -368,7 +368,7 @@ const extensions = [ }, }), Placeholder.configure({ - placeholder: i18next.t('core:action.start_typing'), + placeholder: 'Start typing here...', // doesn't work i18next.t('core:action.start_typing'), }), ImageResize, ]; diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 05c6ce5..39f8358 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -68,7 +68,7 @@ "select_name_app": "select Name/App", "set_avatar": "set avatar", "start_minting": "start minting", - "start_typing": "start typing here...aa", + "start_typing": "start typing here...", "transfer_qort": "Transfer QORT", "unpin": "unpin", "unpin_app": "unpin app", From e125b8edb302d8cc159427f382ece88bb4a4d1f6 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 12:56:23 +0200 Subject: [PATCH 45/47] Translation for auth namespace --- src/i18n/locales/de/auth.json | 74 +++++++++++++++++++++++---- src/i18n/locales/es/auth.json | 84 +++++++++++++++++++++++++------ src/i18n/locales/fr/auth.json | 78 +++++++++++++++++++++++----- src/i18n/locales/it/auth.json | 95 ++++++++++++++++++++++++++--------- src/i18n/locales/ru/auth.json | 76 ++++++++++++++++++++++++---- 5 files changed, 336 insertions(+), 71 deletions(-) diff --git a/src/i18n/locales/de/auth.json b/src/i18n/locales/de/auth.json index 5b14183..3445eb6 100644 --- a/src/i18n/locales/de/auth.json +++ b/src/i18n/locales/de/auth.json @@ -1,8 +1,26 @@ { "account": { - "your": "ihr Konto", + "your": "dein Konto", "account_many": "Konten", - "account_one": "Konto" + "account_one": "Konto", + "selected": "ausgewähltes Konto" + }, + "action": { + "add": { + "account": "Konto hinzufügen", + "seed_phrase": "Seed-Phrase hinzufügen" + }, + "authenticate": "authentifizieren", + "create_account": "Konto erstellen", + "create_qortal_account": "erstelle dein Qortal-Konto, indem du unten auf WEITER klickst.", + "choose_password": "neues Passwort wählen", + "download_account": "Konto herunterladen", + "export_seedphrase": "Seedphrase exportieren", + "publish_admin_secret_key": "Admin-Geheimschlüssel veröffentlichen", + "publish_group_secret_key": "Gruppen-Geheimschlüssel veröffentlichen", + "reencrypt_key": "Schlüssel neu verschlüsseln", + "return_to_list": "zur Liste zurückkehren", + "setup_qortal_account": "Qortal-Konto einrichten" }, "advanced_users": "für fortgeschrittene Benutzer", "apikey": { @@ -13,11 +31,33 @@ "key": "API-Schlüssel", "select_valid": "gültigen API-Schlüssel auswählen" }, - "authenticate": "authentifizieren", "build_version": "Build-Version", - "create_account": "Konto erstellen", - "download_account": "Konto herunterladen", - "keep_secure": "Bewahren Sie Ihre Kontodatei sicher auf", + "message": { + "error": { + "account_creation": "Konto konnte nicht erstellt werden.", + "field_not_found_json": "{{ field }} nicht im JSON gefunden", + "incorrect_password": "falsches Passwort", + "invalid_secret_key": "Geheimschlüssel ist ungültig", + "unable_reencrypt_secret_key": "Geheimschlüssel konnte nicht neu verschlüsselt werden" + }, + "generic": { + "congrats_setup": "Glückwunsch, du bist startklar!", + "no_account": "keine gespeicherten Konten", + "no_minimum_length": "es gibt keine Mindestlängenanforderung", + "no_secret_key_published": "noch kein Geheimschlüssel veröffentlicht", + "fetching_admin_secret_key": "Admin-Geheimschlüssel wird abgerufen", + "fetching_group_secret_key": "Gruppen-Geheimschlüssel wird veröffentlicht", + "last_encryption_date": "letztes Verschlüsselungsdatum: {{ date }} von {{ name }}", + "keep_secure": "halte deine Kontodatei sicher", + "publishing_key": "Hinweis: Nach der Veröffentlichung des Schlüssels dauert es ein paar Minuten, bis er erscheint. Bitte etwas Geduld.", + "seedphrase_notice": "eine SEEDPHRASE wurde im Hintergrund zufällig generiert.", + "type_seed": "gib deine Seed-Phrase ein oder füge sie ein", + "your_accounts": "deine gespeicherten Konten" + }, + "success": { + "reencrypted_secret_key": "Geheimschlüssel erfolgreich neu verschlüsselt. Die Änderungen werden in ein paar Minuten wirksam. Bitte die Gruppe in 5 Minuten aktualisieren." + } + }, "node": { "choose": "benutzerdefinierten Node auswählen", "custom_many": "benutzerdefinierte Nodes", @@ -26,17 +66,31 @@ "using": "verwende Node", "using_public": "öffentlichen Node verwenden" }, + "note": "Hinweis", "password": "Passwort", "password_confirmation": "Passwort bestätigen", - "return_to_list": "zurück zur Liste", + "seed": "Seed-Phrase", + "seed_your": "deine Seedphrase", + "tips": { + "additional_wallet": "verwende diese Option, um weitere Qortal-Wallets zu verbinden, die du bereits erstellt hast, um dich später mit ihnen anzumelden. Du benötigst deine Sicherungs-JSON-Datei.", + "digital_id": "deine Wallet ist wie deine digitale ID auf Qortal und dient zur Anmeldung im Qortal-Interface. Sie enthält deine öffentliche Adresse und den Qortal-Namen, den du später auswählst. Jede Transaktion ist mit deiner ID verknüpft. Hier verwaltest du dein QORT und andere handelbare Kryptowährungen auf Qortal.", + "existing_account": "du hast bereits ein Qortal-Konto? Gib hier deine geheime Sicherungsphrase ein, um darauf zuzugreifen. Diese Phrase ist eine Möglichkeit zur Wiederherstellung deines Kontos.", + "key_encrypt_admin": "dieser Schlüssel dient der Verschlüsselung von ADMIN-Inhalten. Nur Admins können diese Inhalte sehen.", + "key_encrypt_group": "dieser Schlüssel dient der Verschlüsselung von GRUPPEN-Inhalten. Aktuell wird nur dieser Schlüssel in der Benutzeroberfläche verwendet. Alle Gruppenmitglieder können die Inhalte sehen.", + "new_account": "ein Konto zu erstellen bedeutet, eine neue Wallet und digitale ID für die Nutzung von Qortal zu generieren. Nach der Kontoerstellung kannst du QORT erhalten, einen Namen und Avatar kaufen, Videos und Blogs veröffentlichen und vieles mehr.", + "new_users": "neue Nutzer starten hier!", + "safe_place": "speichere dein Konto an einem Ort, den du nicht vergisst!", + "view_seedphrase": "wenn du die SEEDPHRASE ANZEIGEN möchtest, klicke auf das Wort 'SEEDPHRASE' in diesem Text. Seedphrases erzeugen den privaten Schlüssel für dein Qortal-Konto. Aus Sicherheitsgründen werden sie standardmäßig NICHT angezeigt.", + "wallet_secure": "halte deine Wallet-Datei sicher." + }, "wallet": { "password_confirmation": "Wallet-Passwort bestätigen", "password": "Wallet-Passwort", - "keep_password": "aktuelles Passwort beibehalten", + "keep_password": "aktuelles Passwort behalten", "new_password": "neues Passwort", "error": { - "missing_new_password": "bitte neues Passwort eingeben", - "missing_password": "bitte Passwort eingeben" + "missing_new_password": "bitte ein neues Passwort eingeben", + "missing_password": "bitte dein Passwort eingeben" } }, "welcome": "willkommen bei" diff --git a/src/i18n/locales/es/auth.json b/src/i18n/locales/es/auth.json index 5d4b8b6..d43014f 100644 --- a/src/i18n/locales/es/auth.json +++ b/src/i18n/locales/es/auth.json @@ -2,22 +2,62 @@ "account": { "your": "tu cuenta", "account_many": "cuentas", - "account_one": "cuenta" + "account_one": "cuenta", + "selected": "cuenta seleccionada" + }, + "action": { + "add": { + "account": "añadir cuenta", + "seed_phrase": "añadir frase semilla" + }, + "authenticate": "autenticar", + "create_account": "crear cuenta", + "create_qortal_account": "crea tu cuenta Qortal haciendo clic en SIGUIENTE abajo.", + "choose_password": "elige nueva contraseña", + "download_account": "descargar cuenta", + "export_seedphrase": "exportar frase semilla", + "publish_admin_secret_key": "publicar clave secreta de administrador", + "publish_group_secret_key": "publicar clave secreta de grupo", + "reencrypt_key": "reencriptar clave", + "return_to_list": "volver a la lista", + "setup_qortal_account": "configurar tu cuenta Qortal" }, "advanced_users": "para usuarios avanzados", "apikey": { - "alternative": "alternativa: Seleccionar archivo", - "change": "cambiar clave API", - "enter": "ingresar clave API", - "import": "importar clave API", + "alternative": "alternativa: seleccionar archivo", + "change": "cambiar API key", + "enter": "ingresar API key", + "import": "importar API key", "key": "clave API", - "select_valid": "selecciona una clave API válida" + "select_valid": "selecciona una API key válida" }, - "authenticate": "autenticar", "build_version": "versión de compilación", - "create_account": "crear cuenta", - "download_account": "descargar cuenta", - "keep_secure": "mantén tu archivo de cuenta seguro", + "message": { + "error": { + "account_creation": "no se pudo crear la cuenta.", + "field_not_found_json": "{{ field }} no encontrado en el JSON", + "incorrect_password": "contraseña incorrecta", + "invalid_secret_key": "clave secreta no válida", + "unable_reencrypt_secret_key": "no se pudo reencriptar la clave secreta" + }, + "generic": { + "congrats_setup": "¡felicidades, estás listo!", + "no_account": "no hay cuentas guardadas", + "no_minimum_length": "no hay un requisito de longitud mínima", + "no_secret_key_published": "aún no se ha publicado una clave secreta", + "fetching_admin_secret_key": "obteniendo clave secreta de administrador", + "fetching_group_secret_key": "publicando clave secreta de grupo", + "last_encryption_date": "última fecha de encriptación: {{ date }} por {{ name }}", + "keep_secure": "mantén tu archivo de cuenta seguro", + "publishing_key": "recordatorio: después de publicar la clave, tardará un par de minutos en aparecer. Por favor espera.", + "seedphrase_notice": "una FRASE SEMILLA se ha generado aleatoriamente en segundo plano.", + "type_seed": "escribe o pega tu frase semilla", + "your_accounts": "tus cuentas guardadas" + }, + "success": { + "reencrypted_secret_key": "clave secreta reencriptada con éxito. Puede tardar unos minutos en reflejarse. Actualiza el grupo en 5 minutos." + } + }, "node": { "choose": "elegir nodo personalizado", "custom_many": "nodos personalizados", @@ -26,17 +66,31 @@ "using": "usando nodo", "using_public": "usando nodo público" }, + "note": "nota", "password": "contraseña", "password_confirmation": "confirmar contraseña", - "return_to_list": "volver a la lista", + "seed": "frase semilla", + "seed_your": "tu frase semilla", + "tips": { + "additional_wallet": "usa esta opción para conectar billeteras Qortal adicionales que ya hayas creado, para poder iniciar sesión con ellas más tarde. Necesitarás acceso a tu archivo de respaldo JSON.", + "digital_id": "tu billetera es como tu identificación digital en Qortal, y es la forma en que iniciarás sesión en la interfaz de usuario de Qortal. Contiene tu dirección pública y el nombre de Qortal que eventualmente elegirás. Cada transacción está vinculada a tu ID, y aquí es donde gestionas todo tu QORT y otras criptomonedas intercambiables en Qortal.", + "existing_account": "¿ya tienes una cuenta Qortal? Ingresa aquí tu frase de respaldo secreta para acceder. Esta frase es una de las formas de recuperar tu cuenta.", + "key_encrypt_admin": "esta clave es para cifrar contenido relacionado con ADMIN. Solo los administradores podrán ver el contenido cifrado con ella.", + "key_encrypt_group": "esta clave es para cifrar contenido relacionado con GRUPO. Es la única utilizada en esta interfaz. Todos los miembros del grupo podrán ver el contenido cifrado con esta clave.", + "new_account": "crear una cuenta significa crear una nueva billetera e identificación digital para comenzar a usar Qortal. Una vez que hayas creado tu cuenta, puedes empezar a obtener QORT, comprar un nombre y avatar, publicar videos y blogs, y mucho más.", + "new_users": "¡los nuevos usuarios comienzan aquí!", + "safe_place": "guarda tu cuenta en un lugar que recuerdes.", + "view_seedphrase": "si deseas VER LA FRASE SEMILLA, haz clic en la palabra 'FRASE SEMILLA' en este texto. Las frases semilla se usan para generar la clave privada de tu cuenta Qortal. Por seguridad, no se muestran por defecto.", + "wallet_secure": "mantén segura tu billetera." + }, "wallet": { - "password_confirmation": "confirmar contraseña del monedero", - "password": "contraseña del monedero", + "password_confirmation": "confirmar contraseña de la billetera", + "password": "contraseña de la billetera", "keep_password": "mantener la contraseña actual", "new_password": "nueva contraseña", "error": { - "missing_new_password": "por favor ingresa una nueva contraseña", - "missing_password": "por favor ingresa tu contraseña" + "missing_new_password": "por favor, ingresa una nueva contraseña", + "missing_password": "por favor, ingresa tu contraseña" } }, "welcome": "bienvenido a" diff --git a/src/i18n/locales/fr/auth.json b/src/i18n/locales/fr/auth.json index f314d3e..3a761ad 100644 --- a/src/i18n/locales/fr/auth.json +++ b/src/i18n/locales/fr/auth.json @@ -1,38 +1,92 @@ { "account": { - "your": "ton compte", + "your": "votre compte", "account_many": "comptes", - "account_one": "compte" + "account_one": "compte", + "selected": "compte sélectionné" + }, + "action": { + "add": { + "account": "ajouter un compte", + "seed_phrase": "ajouter une phrase secrète" + }, + "authenticate": "authentifier", + "create_account": "créer un compte", + "create_qortal_account": "créez votre compte Qortal en cliquant sur SUIVANT ci-dessous.", + "choose_password": "choisissez un nouveau mot de passe", + "download_account": "télécharger le compte", + "export_seedphrase": "exporter la phrase secrète", + "publish_admin_secret_key": "publier la clé secrète admin", + "publish_group_secret_key": "publier la clé secrète du groupe", + "reencrypt_key": "re-chiffrer la clé", + "return_to_list": "retour à la liste", + "setup_qortal_account": "configurer votre compte Qortal" }, "advanced_users": "pour les utilisateurs avancés", "apikey": { - "alternative": "alternative : Sélectionner un fichier", + "alternative": "alternative : sélection de fichier", "change": "changer la clé API", "enter": "entrer la clé API", "import": "importer la clé API", "key": "clé API", - "select_valid": "sélectionnez une clé API valide" + "select_valid": "sélectionner une clé API valide" }, - "authenticate": "authentifier", "build_version": "version de build", - "create_account": "créer un compte", - "download_account": "télécharger le compte", - "keep_secure": "Gardez votre fichier de compte en sécurité", + "message": { + "error": { + "account_creation": "impossible de créer le compte.", + "field_not_found_json": "{{ field }} introuvable dans le JSON", + "incorrect_password": "mot de passe incorrect", + "invalid_secret_key": "clé secrète invalide", + "unable_reencrypt_secret_key": "impossible de re-chiffrer la clé secrète" + }, + "generic": { + "congrats_setup": "félicitations, tout est prêt !", + "no_account": "aucun compte enregistré", + "no_minimum_length": "aucune exigence de longueur minimale", + "no_secret_key_published": "aucune clé secrète publiée pour le moment", + "fetching_admin_secret_key": "récupération de la clé secrète admin", + "fetching_group_secret_key": "publication de la clé secrète du groupe", + "last_encryption_date": "dernière date de chiffrement : {{ date }} par {{ name }}", + "keep_secure": "gardez votre fichier de compte en sécurité", + "publishing_key": "rappel : après publication, la clé peut mettre quelques minutes à apparaître. Veuillez patienter.", + "seedphrase_notice": "une PHRASE SECRÈTE a été générée aléatoirement en arrière-plan.", + "type_seed": "saisissez ou collez votre phrase secrète", + "your_accounts": "vos comptes enregistrés" + }, + "success": { + "reencrypted_secret_key": "clé secrète re-chiffrée avec succès. Les modifications peuvent prendre quelques minutes pour se propager. Rafraîchissez le groupe dans 5 minutes." + } + }, "node": { "choose": "choisir un nœud personnalisé", "custom_many": "nœuds personnalisés", "use_custom": "utiliser un nœud personnalisé", "use_local": "utiliser un nœud local", - "using": "utilise le nœud", - "using_public": "utilise un nœud public" + "using": "utilisation du nœud", + "using_public": "utilisation d’un nœud public" }, + "note": "note", "password": "mot de passe", "password_confirmation": "confirmer le mot de passe", - "return_to_list": "retour à la liste", + "seed": "phrase secrète", + "seed_your": "votre phrase secrète", + "tips": { + "additional_wallet": "utilisez cette option pour connecter d'autres portefeuilles Qortal que vous avez déjà créés, afin de pouvoir vous y connecter ultérieurement. Vous aurez besoin de votre fichier de sauvegarde JSON.", + "digital_id": "votre portefeuille est comme votre identifiant numérique sur Qortal, et c’est ainsi que vous vous connecterez à l’interface utilisateur Qortal. Il contient votre adresse publique et le nom Qortal que vous choisirez. Chaque transaction est liée à votre ID, et c’est là que vous gérez votre QORT et d'autres cryptomonnaies échangeables sur Qortal.", + "existing_account": "vous avez déjà un compte Qortal ? Entrez ici votre phrase secrète de sauvegarde pour y accéder. C’est un des moyens de récupérer votre compte.", + "key_encrypt_admin": "cette clé est utilisée pour chiffrer le contenu ADMIN. Seuls les administrateurs peuvent voir ce contenu.", + "key_encrypt_group": "cette clé est utilisée pour chiffrer le contenu du GROUPE. C’est la seule utilisée dans cette interface pour le moment. Tous les membres du groupe pourront voir le contenu chiffré avec cette clé.", + "new_account": "créer un compte signifie créer un nouveau portefeuille et identifiant numérique pour commencer à utiliser Qortal. Une fois votre compte créé, vous pourrez obtenir du QORT, acheter un nom et un avatar, publier des vidéos et blogs, et bien plus.", + "new_users": "nouveaux utilisateurs, commencez ici !", + "safe_place": "sauvegardez votre compte dans un endroit sûr et mémorable !", + "view_seedphrase": "si vous souhaitez VOIR LA PHRASE SECRÈTE, cliquez sur le mot 'PHRASE SECRÈTE' dans ce texte. Les phrases secrètes servent à générer la clé privée de votre compte Qortal. Par sécurité, elles ne sont PAS affichées par défaut.", + "wallet_secure": "gardez votre fichier de portefeuille sécurisé." + }, "wallet": { "password_confirmation": "confirmer le mot de passe du portefeuille", "password": "mot de passe du portefeuille", - "keep_password": "garder le mot de passe actuel", + "keep_password": "conserver le mot de passe actuel", "new_password": "nouveau mot de passe", "error": { "missing_new_password": "veuillez entrer un nouveau mot de passe", diff --git a/src/i18n/locales/it/auth.json b/src/i18n/locales/it/auth.json index 513378f..8879c06 100644 --- a/src/i18n/locales/it/auth.json +++ b/src/i18n/locales/it/auth.json @@ -2,47 +2,96 @@ "account": { "your": "il tuo account", "account_many": "account", - "account_one": "account" + "account_one": "account", + "selected": "account selezionato" }, - "advanced_users": "per utenti avanzati", + "action": { + "add": { + "account": "aggiungi account", + "seed_phrase": "aggiungi frase segreta" + }, + "authenticate": "autenticare", + "create_account": "crea account", + "create_qortal_account": "crea il tuo account Qortal cliccando su AVANTI qui sotto.", + "choose_password": "scegli una nuova password", + "download_account": "scarica account", + "export_seedphrase": "esporta frase segreta", + "publish_admin_secret_key": "pubblica chiave segreta admin", + "publish_group_secret_key": "pubblica chiave segreta gruppo", + "reencrypt_key": "ricripta chiave", + "return_to_list": "torna alla lista", + "setup_qortal_account": "configura il tuo account Qortal" + }, + "advanced_users": "per utenti esperti", "apikey": { - "alternative": "alternativa: selezione file", - "change": "cambia APIkey", - "enter": "inserisci APIkey", - "import": "importa APIkey", + "alternative": "alternativa: seleziona file", + "change": "cambia chiave API", + "enter": "inserisci chiave API", + "import": "importa chiave API", "key": "chiave API", - "select_valid": "seleziona una APIkey valida" + "select_valid": "seleziona una chiave API valida" }, - "authenticate": "autentica", "build_version": "versione build", - "create_account": "crea account", - "download_account": "scarica account", - "keep_secure": "mantieni sicuro il file del tuo account", + "message": { + "error": { + "account_creation": "impossibile creare l’account.", + "field_not_found_json": "{{ field }} non trovato nel JSON", + "incorrect_password": "password errata", + "invalid_secret_key": "chiave segreta non valida", + "unable_reencrypt_secret_key": "impossibile ricriptare la chiave segreta" + }, + "generic": { + "congrats_setup": "congratulazioni, sei pronto!", + "no_account": "nessun account salvato", + "no_minimum_length": "nessun requisito di lunghezza minima", + "no_secret_key_published": "nessuna chiave segreta pubblicata", + "fetching_admin_secret_key": "recupero chiave segreta admin", + "fetching_group_secret_key": "pubblicazione chiave segreta gruppo", + "last_encryption_date": "ultima data di cifratura: {{ date }} da {{ name }}", + "keep_secure": "mantieni sicuro il tuo file account", + "publishing_key": "promemoria: dopo la pubblicazione della chiave, ci vorranno alcuni minuti per vederla. Attendere cortesemente.", + "seedphrase_notice": "una FRASE SEGRETA è stata generata automaticamente in background.", + "type_seed": "digita o incolla la tua frase segreta", + "your_accounts": "i tuoi account salvati" + }, + "success": { + "reencrypted_secret_key": "chiave segreta ricriptata con successo. Potrebbero volerci alcuni minuti per vedere le modifiche. Aggiorna il gruppo tra 5 minuti." + } + }, "node": { "choose": "scegli nodo personalizzato", "custom_many": "nodi personalizzati", "use_custom": "usa nodo personalizzato", "use_local": "usa nodo locale", - "using": "utilizzo nodo", - "using_public": "utilizzo nodo pubblico" + "using": "utilizzando nodo", + "using_public": "utilizzando nodo pubblico" }, + "note": "nota", "password": "password", "password_confirmation": "conferma password", - "return_to_list": "torna alla lista", + "seed": "frase segreta", + "seed_your": "la tua frase segreta", "tips": { - "digital_id": "il tuo wallet è come la tua identità digitale su Qortal ed è il modo in cui accederai all'interfaccia utente di Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che sceglierai. Ogni transazione che esegui è collegata alla tua identità ed è qui che gestisci tutti i tuoi QORT e altre criptovalute scambiabili su Qortal.", - "new_account": "creare un account significa creare un nuovo wallet e un'identità digitale per iniziare a usare Qortal. Una volta creato l'account, potrai iniziare a ottenere QORT, acquistare un nome e un avatar, pubblicare video e blog, e molto altro.", - "new_users": "i nuovi utenti iniziano qui!" + "additional_wallet": "usa questa opzione per connettere altri portafogli Qortal che hai già creato, per accedervi successivamente. Avrai bisogno del file JSON di backup.", + "digital_id": "il tuo portafoglio è come la tua ID digitale su Qortal, ed è il modo con cui accedi all’interfaccia utente. Contiene il tuo indirizzo pubblico e il nome Qortal che sceglierai. Ogni transazione è legata alla tua ID, e qui gestisci QORT e altre criptovalute scambiabili su Qortal.", + "existing_account": "hai già un account Qortal? Inserisci qui la tua frase di backup segreta per accedervi. Questa frase è uno dei modi per recuperare l'account.", + "key_encrypt_admin": "questa chiave serve a criptare contenuti legati agli ADMIN. Solo gli admin potranno vederli.", + "key_encrypt_group": "questa chiave serve a criptare contenuti di GRUPPO. È l’unica usata in questa interfaccia. Tutti i membri del gruppo potranno vederli.", + "new_account": "creare un account significa creare un nuovo portafoglio e una nuova ID digitale per iniziare a usare Qortal. Una volta creato l’account, potrai ottenere QORT, acquistare un nome e avatar, pubblicare video e blog, e molto altro.", + "new_users": "nuovi utenti, iniziate qui!", + "safe_place": "salva il tuo account in un posto sicuro che ricorderai!", + "view_seedphrase": "se vuoi VEDERE LA FRASE SEGRETA, clicca sulla parola 'FRASE SEGRETA' in questo testo. Le frasi segrete generano la chiave privata del tuo account. Per sicurezza, non vengono mostrate di default.", + "wallet_secure": "mantieni sicuro il tuo file portafoglio." }, "wallet": { - "password_confirmation": "conferma password del wallet", - "password": "password del wallet", - "keep_password": "mantieni password corrente", + "password_confirmation": "conferma password portafoglio", + "password": "password del portafoglio", + "keep_password": "mantieni password attuale", "new_password": "nuova password", "error": { - "missing_new_password": "per favore inserisci una nuova password", - "missing_password": "per favore inserisci la tua password" + "missing_new_password": "inserisci una nuova password", + "missing_password": "inserisci la tua password" } }, - "welcome": "benvenuto in" + "welcome": "benvenuto su" } diff --git a/src/i18n/locales/ru/auth.json b/src/i18n/locales/ru/auth.json index 797220b..9d5d6be 100644 --- a/src/i18n/locales/ru/auth.json +++ b/src/i18n/locales/ru/auth.json @@ -1,10 +1,28 @@ { "account": { - "your": "Ваш аккаунт", + "your": "ваш аккаунт", "account_many": "аккаунты", - "account_one": "аккаунт" + "account_one": "аккаунт", + "selected": "выбранный аккаунт" }, - "advanced_users": "для продвинутых пользователей", + "action": { + "add": { + "account": "добавить аккаунт", + "seed_phrase": "добавить seed-фразу" + }, + "authenticate": "аутентификация", + "create_account": "создать аккаунт", + "create_qortal_account": "создайте свой аккаунт Qortal, нажав ДАЛЕЕ ниже.", + "choose_password": "выберите новый пароль", + "download_account": "скачать аккаунт", + "export_seedphrase": "экспортировать seed-фразу", + "publish_admin_secret_key": "опубликовать секретный ключ администратора", + "publish_group_secret_key": "опубликовать секретный ключ группы", + "reencrypt_key": "перешифровать ключ", + "return_to_list": "вернуться к списку", + "setup_qortal_account": "настройте ваш аккаунт Qortal" + }, + "advanced_users": "для опытных пользователей", "apikey": { "alternative": "альтернатива: выбрать файл", "change": "изменить API-ключ", @@ -13,11 +31,33 @@ "key": "API-ключ", "select_valid": "выберите действительный API-ключ" }, - "authenticate": "аутентификация", "build_version": "версия сборки", - "create_account": "создать аккаунт", - "download_account": "скачать аккаунт", - "keep_secure": "Храните файл аккаунта в безопасности", + "message": { + "error": { + "account_creation": "не удалось создать аккаунт.", + "field_not_found_json": "{{ field }} не найден в JSON", + "incorrect_password": "неверный пароль", + "invalid_secret_key": "некорректный секретный ключ", + "unable_reencrypt_secret_key": "не удалось перешифровать секретный ключ" + }, + "generic": { + "congrats_setup": "поздравляем, всё готово!", + "no_account": "аккаунты не сохранены", + "no_minimum_length": "требование к минимальной длине отсутствует", + "no_secret_key_published": "секретный ключ еще не опубликован", + "fetching_admin_secret_key": "получение секретного ключа администратора", + "fetching_group_secret_key": "публикация секретного ключа группы", + "last_encryption_date": "дата последнего шифрования: {{ date }}, автор: {{ name }}", + "keep_secure": "храните файл аккаунта в безопасности", + "publishing_key": "напоминание: после публикации ключа может пройти несколько минут до его появления. Пожалуйста, подождите.", + "seedphrase_notice": "в фоновом режиме была случайным образом сгенерирована SEED-ФРАЗА.", + "type_seed": "введите или вставьте свою seed-фразу", + "your_accounts": "ваши сохранённые аккаунты" + }, + "success": { + "reencrypted_secret_key": "секретный ключ успешно перешифрован. Обновления могут занять несколько минут. Обновите группу через 5 минут." + } + }, "node": { "choose": "выбрать пользовательский узел", "custom_many": "пользовательские узлы", @@ -26,17 +66,31 @@ "using": "используется узел", "using_public": "используется публичный узел" }, + "note": "заметка", "password": "пароль", "password_confirmation": "подтвердите пароль", - "return_to_list": "вернуться к списку", + "seed": "seed-фраза", + "seed_your": "ваша seed-фраза", + "tips": { + "additional_wallet": "используйте эту опцию, чтобы подключить дополнительные кошельки Qortal, которые вы уже создали. Вам понадобится файл резервной копии JSON.", + "digital_id": "ваш кошелёк — это ваш цифровой идентификатор в Qortal. Через него вы входите в интерфейс пользователя Qortal. Он содержит ваш публичный адрес и выбранное вами имя Qortal. Все ваши транзакции связаны с этим ID. Здесь вы управляете QORT и другими криптовалютами в Qortal.", + "existing_account": "уже есть аккаунт Qortal? Введите здесь вашу секретную фразу восстановления для доступа. Это один из способов восстановить аккаунт.", + "key_encrypt_admin": "этот ключ используется для шифрования контента, связанного с АДМИНИСТРАТОРОМ. Только администраторы смогут его видеть.", + "key_encrypt_group": "этот ключ используется для шифрования контента ГРУППЫ. В данный момент он единственный, используемый в этом интерфейсе. Все участники группы смогут видеть зашифрованный контент.", + "new_account": "создание аккаунта создаёт новый кошелёк и цифровой ID для начала работы с Qortal. После этого вы сможете получать QORT, покупать имя и аватар, публиковать видео и блоги и многое другое.", + "new_users": "новые пользователи — начните отсюда!", + "safe_place": "сохраните свой аккаунт в надёжном месте, которое вы не забудете!", + "view_seedphrase": "если вы хотите ПОСМОТРЕТЬ SEED-ФРАЗУ, нажмите на слово 'SEED-ФРАЗА' в этом тексте. Seed-фразы используются для генерации приватного ключа аккаунта. В целях безопасности по умолчанию они НЕ отображаются.", + "wallet_secure": "держите файл кошелька в безопасности." + }, "wallet": { "password_confirmation": "подтвердите пароль кошелька", "password": "пароль кошелька", - "keep_password": "сохранить текущий пароль", + "keep_password": "оставить текущий пароль", "new_password": "новый пароль", "error": { - "missing_new_password": "пожалуйста, введите новый пароль", - "missing_password": "пожалуйста, введите ваш пароль" + "missing_new_password": "введите новый пароль", + "missing_password": "введите пароль" } }, "welcome": "добро пожаловать в" From 5ba51b672fe60eb199998abf23c7bafdb431293f Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 14:15:34 +0200 Subject: [PATCH 46/47] Move question into message.question --- src/components/Apps/AppPublish.tsx | 2 +- src/components/Apps/AppRating.tsx | 2 +- src/components/Apps/AppsPrivate.tsx | 2 +- src/components/Chat/AdminSpaceInner.tsx | 2 +- .../Chat/AnnouncementDiscussion.tsx | 2 +- src/components/Chat/ChatGroup.tsx | 2 +- src/components/Chat/CreateCommonSecret.tsx | 2 +- src/components/Chat/GroupAnnouncements.tsx | 2 +- src/components/Chat/GroupAvatar.tsx | 2 +- src/components/GlobalActions/JoinGroup.tsx | 2 +- src/components/Group/AddGroup.tsx | 2 +- src/components/Group/AddGroupList.tsx | 2 +- src/components/Group/Forum/NewThread.tsx | 4 +- src/components/Group/InviteMember.tsx | 2 +- src/components/Group/ListOfBans.tsx | 2 +- .../Group/ListOfGroupPromotions.tsx | 2 +- src/components/Group/ListOfInvites.tsx | 2 +- src/components/Group/ListOfJoinRequests.tsx | 2 +- src/components/Group/ListOfMembers.tsx | 8 +- src/components/Group/ManageMembers.tsx | 2 +- src/components/Group/UserListOfInvites.tsx | 2 +- src/components/Minting/Minting.tsx | 2 +- src/components/RegisterName.tsx | 2 +- src/components/Save/Save.tsx | 6 +- src/i18n/locales/en/core.json | 26 +- src/i18n/locales/it/core.json | 240 ++++++++++++++---- 26 files changed, 236 insertions(+), 90 deletions(-) diff --git a/src/components/Apps/AppPublish.tsx b/src/components/Apps/AppPublish.tsx index c63cee8..163f619 100644 --- a/src/components/Apps/AppPublish.tsx +++ b/src/components/Apps/AppPublish.tsx @@ -178,7 +178,7 @@ export const AppPublish = ({ names, categories }) => { const fee = await getFee('ARBITRARY'); await show({ - message: t('core:question.publish_app', { + message: t('core:message.question.publish_app', { postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index 8ae7e99..860dfc4 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -113,7 +113,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => { const fee = await getFee('CREATE_POLL'); await show({ - message: t('core:question.rate_app', { + message: t('core:message.question.rate_app', { rate: newValue, postProcess: 'capitalizeFirst', }), diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx index d3af274..de2c744 100644 --- a/src/components/Apps/AppsPrivate.tsx +++ b/src/components/Apps/AppsPrivate.tsx @@ -192,7 +192,7 @@ export const AppsPrivate = ({ myName }) => { const fee = await getFee('ARBITRARY'); await show({ - message: t('core:question.publish_app', { + message: t('core:message.question.publish_app', { postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', diff --git a/src/components/Chat/AdminSpaceInner.tsx b/src/components/Chat/AdminSpaceInner.tsx index 652d2da..f70e813 100644 --- a/src/components/Chat/AdminSpaceInner.tsx +++ b/src/components/Chat/AdminSpaceInner.tsx @@ -132,7 +132,7 @@ export const AdminSpaceInner = ({ const fee = await getFee('ARBITRARY'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'ARBITRARY', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index 3ce480c..4ab4771 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -137,7 +137,7 @@ export const AnnouncementDiscussion = ({ pauseAllQueues(); const fee = await getFee('ARBITRARY'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'ARBITRARY', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index b88b4e7..5b9c660 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -851,7 +851,7 @@ export const ChatGroup = ({ const fee = await getFee('ARBITRARY'); await show({ publishFee: fee.fee + ' QORT', - message: t('core:question.delete_chat_image', { + message: t('core:message.question.delete_chat_image', { postProcess: 'capitalizeFirst', }), }); diff --git a/src/components/Chat/CreateCommonSecret.tsx b/src/components/Chat/CreateCommonSecret.tsx index bfb7684..b123a51 100644 --- a/src/components/Chat/CreateCommonSecret.tsx +++ b/src/components/Chat/CreateCommonSecret.tsx @@ -126,7 +126,7 @@ export const CreateCommonSecret = ({ const fee = await getFee('ARBITRARY'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'ARBITRARY', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index d2a997f..584e9bd 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -302,7 +302,7 @@ export const GroupAnnouncements = ({ const fee = await getFee('ARBITRARY'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'ARBITRARY', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Chat/GroupAvatar.tsx b/src/components/Chat/GroupAvatar.tsx index caf3c4b..0ef86ed 100644 --- a/src/components/Chat/GroupAvatar.tsx +++ b/src/components/Chat/GroupAvatar.tsx @@ -89,7 +89,7 @@ export const GroupAvatar = ({ ); await show({ - message: t('core:question.publish_avatar', { + message: t('core:message.question.publish_avatar', { postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx index fc77d85..6b63257 100644 --- a/src/components/GlobalActions/JoinGroup.tsx +++ b/src/components/GlobalActions/JoinGroup.tsx @@ -70,7 +70,7 @@ export const JoinGroup = () => { const fee = await getFee('JOIN_GROUP'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'JOIN_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 24ac2da..dc12ed3 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -118,7 +118,7 @@ export const AddGroup = ({ address, open, setOpen }) => { const fee = await getFee('CREATE_GROUP'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'CREATE_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index 06113a4..daf509c 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -113,7 +113,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const fee = await getFee('JOIN_GROUP'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'JOIN_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 802f4a4..d37eedd 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -182,7 +182,7 @@ export const NewThread = ({ const missingFields: string[] = []; if (!isMessage && !threadTitle) { - errorMsg = t('core:question.provide_thread', { + errorMsg = t('core:message.question.provide_thread', { postProcess: 'capitalizeFirst', }); } @@ -229,7 +229,7 @@ export const NewThread = ({ feeToShow = +feeToShow * 2; } await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'ARBITRARY', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index 8d7bcf2..ad027e4 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -17,7 +17,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const fee = await getFee('GROUP_INVITE'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'GROUP_INVITE', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 650c418..dda3760 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -88,7 +88,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { try { const fee = await getFee('CANCEL_GROUP_BAN'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'CANCEL_GROUP_BAN', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index 32f032e..8ca13d4 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -276,7 +276,7 @@ export const ListOfGroupPromotions = () => { const groupId = group.groupId; const fee = await getFee('JOIN_GROUP'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'JOIN_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ListOfInvites.tsx b/src/components/Group/ListOfInvites.tsx index fc63464..574a9d1 100644 --- a/src/components/Group/ListOfInvites.tsx +++ b/src/components/Group/ListOfInvites.tsx @@ -94,7 +94,7 @@ export const ListOfInvites = ({ const fee = await getFee('CANCEL_GROUP_INVITE'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'CANCEL_GROUP_INVITE', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ListOfJoinRequests.tsx b/src/components/Group/ListOfJoinRequests.tsx index 6d9c38a..3b2f740 100644 --- a/src/components/Group/ListOfJoinRequests.tsx +++ b/src/components/Group/ListOfJoinRequests.tsx @@ -97,7 +97,7 @@ export const ListOfJoinRequests = ({ const fee = await getFee('GROUP_INVITE'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'GROUP_INVITE', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ListOfMembers.tsx b/src/components/Group/ListOfMembers.tsx index bfdc11f..783c18c 100644 --- a/src/components/Group/ListOfMembers.tsx +++ b/src/components/Group/ListOfMembers.tsx @@ -59,7 +59,7 @@ const ListOfMembers = ({ try { const fee = await getFee('GROUP_KICK'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'GROUP_KICK', postProcess: 'capitalizeFirst', }), @@ -117,7 +117,7 @@ const ListOfMembers = ({ const fee = await getFee('GROUP_BAN'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'GROUP_BAN', postProcess: 'capitalizeFirst', }), @@ -177,7 +177,7 @@ const ListOfMembers = ({ try { const fee = await getFee('ADD_GROUP_ADMIN'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'ADD_GROUP_ADMIN', postProcess: 'capitalizeFirst', }), @@ -234,7 +234,7 @@ const ListOfMembers = ({ try { const fee = await getFee('REMOVE_GROUP_ADMIN'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'REMOVE_GROUP_ADMIN', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index d996184..30a2ef2 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -84,7 +84,7 @@ export const ManageMembers = ({ setIsLoadingLeave(true); const fee = await getFee('LEAVE_GROUP'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'LEAVE_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Group/UserListOfInvites.tsx b/src/components/Group/UserListOfInvites.tsx index fd22d58..1e348d2 100644 --- a/src/components/Group/UserListOfInvites.tsx +++ b/src/components/Group/UserListOfInvites.tsx @@ -99,7 +99,7 @@ export const UserListOfInvites = ({ const fee = await getFee('JOIN_GROUP'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { action: 'JOIN_GROUP', postProcess: 'capitalizeFirst', }), diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 9bffeb7..b0e1763 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -301,7 +301,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => { const createRewardShare = useCallback(async (publicKey, recipient) => { const fee = await getFee('REWARD_SHARE'); await show({ - message: t('core:question.perform_transaction', { + message: t('core:message.question.perform_transaction', { // TODO move from group into core namespace action: 'REWARD_SHARE', postProcess: 'capitalizeFirst', diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index 85f4390..e9a4116 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -127,7 +127,7 @@ export const RegisterName = ({ const fee = await getFee('REGISTER_NAME'); await show({ - message: t('core:question.register_name', { + message: t('core:message.question.register_name', { postProcess: 'capitalizeFirst', }), publishFee: fee.fee + ' QORT', diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index f85baee..38e328b 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -424,7 +424,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - {t('core:question.reset_qdn', { + {t('core:message.question.reset_qdn', { postProcess: 'capitalizeFirst', })} @@ -461,7 +461,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { }} > {' '} - {t('core:question.reset_pinned', { + {t('core:message.question.reset_pinned', { postProcess: 'capitalizeFirst', })} @@ -495,7 +495,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { fontSize: '14px', }} > - {t('core:question.overwrite_changes', { + {t('core:message.question.overwrite_changes', { postProcess: 'capitalizeFirst', })} diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 39f8358..cc7d495 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -206,7 +206,18 @@ }, "question": { "logout": "are you sure you would like to logout?", - "new_user": "are you a new user?" + "new_user": "are you a new user?", + "delete_chat_image": "would you like to delete your previous chat image?", + "perform_transaction": "would you like to perform a {{action}} transaction?", + "provide_thread": "please provide a thread title", + "publish_app": "would you like to publish this app?", + "publish_avatar": "would you like to publish an avatar?", + "publish_qdn": "would you like to publish your settings to QDN (encrypted)?", + "overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?", + "rate_app": "would you like to rate this app a rating of {{ rate }}?. It will create a POLL tx.", + "register_name": "would you like to register this name?", + "reset_pinned": "don't like your current local changes? Would you like to reset to the default pinned apps?", + "reset_qdn": "don't like your current local changes? Would you like to reset to your saved QDN pinned apps?" }, "status": { "minting": "(minting)", @@ -242,19 +253,6 @@ "q_manager": "q-manager", "q_sandbox": "q-Sandbox" }, - "question": { - "delete_chat_image": "would you like to delete your previous chat image?", - "perform_transaction": "would you like to perform a {{action}} transaction?", - "provide_thread": "please provide a thread title", - "publish_app": "would you like to publish this app?", - "publish_avatar": "would you like to publish an avatar?", - "publish_qdn": "would you like to publish your settings to QDN (encrypted)?", - "overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?", - "rate_app": "would you like to rate this app a rating of {{ rate }}?. It will create a POLL tx.", - "register_name": "would you like to register this name?", - "reset_pinned": "don't like your current local changes? Would you like to reset to the default pinned apps?", - "reset_qdn": "don't like your current local changes? Would you like to reset to your saved QDN pinned apps?" - }, "server": "server", "settings": "settings", "sort": { diff --git a/src/i18n/locales/it/core.json b/src/i18n/locales/it/core.json index 730d95d..8bf930d 100644 --- a/src/i18n/locales/it/core.json +++ b/src/i18n/locales/it/core.json @@ -1,41 +1,98 @@ { "action": { "add": "aggiungi", + "add_custom_framework": "aggiungi custom framework", + "add_reaction": "adaggiungid reazione", "accept": "accetta", + "access": "accedi", "backup_account": "esegui backup account", - "backup_wallet": "esegui backup portafoglio", - "cancel": "annulla", - "cancel_invitation": "annulla invito", + "backup_wallet": "esegui backup wallet", + "cancel": "cancella", + "cancel_invitation": "cancella invito", "change": "cambia", + "change_avatar": "cambia avatar", + "change_file": "cambia file", "change_language": "cambia lingua", "choose": "scegli", + "choose_file": "scegli file", + "choose_image": "scegli immagine", "close": "chiudi", + "close_chat": "chiudi Chat Diretta", "continue": "continua", - "continue_logout": "continua con il logout", - "create_thread": "crea discussione", - "decline": "rifiuta", - "decrypt": "decifra", + "continue_logout": "continua il logout", + "copy_link": "copia link", + "create_apps": "crea apps", + "create_file": "crea file", + "create_thread": "crea thread", + "choose_logo": "scegli a logo", + "choose_name": "scegli a name", + "decline": "declina", + "decrypt": "decripta", + "disable_enter": "disabilita invio", + "download": "download", "edit": "modifica", + "enter_name": "inserisci un nome", "export": "esporta", + "get_qort": "ottieni QORT in Q-Trade", + "hide": "nascondi", "import": "importa", "invite": "invita", "join": "unisciti", - "logout": "esci", + "leave_comment": "lascia un commento", + "load_announcements": "load older announcements", + "login": "login", + "logout": "logout", "new": { "post": "nuovo post", "thread": "nuova discussione" }, - "notify": "notifica", - "post": "pubblica", - "post_message": "invia messaggio" + "notify": "notify", + "open": "open", + "pin": "pin", + "pin_app": "pin app", + "pin_from_dashboard": "pin from dashboard", + "post": "post", + "post_message": "post message", + "publish": "publish", + "publish_app": "publish your app", + "publish_comment": "publish comment", + "register_name": "register name", + "remove": "remove", + "remove_reaction": "remove reaction", + "return_apps_dashboard": "return to Apps Dashboard", + "save": "save", + "search": "search", + "search_apps": "search for apps", + "select_app_type": "select App Type", + "select_category": "select Category", + "select_name_app": "select Name/App", + "set_avatar": "set avatar", + "start_minting": "start minting", + "start_typing": "start typing here...", + "transfer_qort": "Transfer QORT", + "unpin": "unpin", + "unpin_app": "unpin app", + "unpin_from_dashboard": "unpin from dashboard", + "update": "update", + "update_app": "update your app" }, - "admin": "amministratore", + "admin": "admin", + "all": "all", + "api": "API", + "app": "app", + "app_name": "app name", + "app_service_type": "app service type", + "apps_dashboard": "apps Dashboard", + "apps_official": "official Apps", + "category": "category", + "category_other": "categories", "core": { "block_height": "altezza blocco", "information": "informazioni core", "peers": "peer connessi", "version": "versione core" }, + "domain": "domain", "ui": { "version": "versione UI" }, @@ -44,27 +101,123 @@ "one": "uno" }, "description": "descrizione", - "downloading_qdn": "scaricamento da QDN", + "devmode_apps": "dev Mode Apps", + "directory": "directory", + "downloading_qdn": "download da QDN", "fee": { "payment": "commissione di pagamento", "publish": "commissione di pubblicazione" }, + "for": "per", "general_settings": "impostazioni generali", - "last_height": "ultima altezza", + "identifier": "identificatore", + "last_height": "altezza ultima", + "level": "livello", + "library": "libreria", "list": { "invite": "lista inviti", "join_request": "lista richieste di adesione", "member": "lista membri" }, - "loading": "caricamento...", - "loading_posts": "caricamento post... attendere.", - "message_us": "contattaci su Telegram o Discord se hai bisogno di 4 QORT per iniziare a chattare senza limitazioni", + "loading": { + "announcements": "annunci", + "generic": "caricamento...", + "chat": "caricamento chat... attendere, per favore.", + "comments": "caricamento commenti... attendere, per favore.", + "posts": "caricamento post... attendere, per favore." + }, + "message_us": "please message us on Telegram or Discord if you need 4 QORT to start chatting without any limitations", "message": { "error": { - "generic": "si è verificato un errore", - "incorrect_password": "password errata", - "missing_field": "manca: {{ field }}", - "save_qdn": "impossibile salvare su QDN" + "address_not_found": "your address was not found", + "app_need_name": "your app needs a name", + "file_too_large": "file {{ filename }} is too large. Max size allowed is {{ size }} MB.", + "generic": "an error occurred", + "invalid_signature": "invalid signature", + "invalid_zip": "invalid zip", + "message_loading": "error loading message.", + "message_size": "your message size is of {{ size }} bytes out of a maximum of {{ maximum }}", + "minting_account_add": "unable to add minting account", + "minting_account_remove": "unable to remove minting account", + "missing_fields": "missing: {{ fields }}", + "navigation_timeout": "navigation timeout", + "network_generic": "network error", + "password_not_matching": "password fields do not match!", + "password_wrong": "unable to authenticate. Wrong password", + "publish_app": "unable to publish app", + "rating_option": "cannot find rating option", + "save_qdn": "unable to save to QDN", + "send_failed": "failed to send", + "unable_encrypt_app": "unable to encrypt app. App not published'", + "unable_publish_app": "unable to publish app", + "unable_publish_image": "unable to publish image", + "unable_rate": "unable to rate", + "update_failed": "failed to update" + }, + "generic": { + "avatar_size": "{{ size }} KB max. for GIFS", + "buy_order_request": "the Application
{{hostname}}
is requesting {{count}} buy order", + "buy_order_request_other": "the Application
{{hostname}}
is requesting {{count}} buy orders", + "devmode_local_node": "please use your local node for dev mode! Logout and use Local node.", + "edited": "edited", + "editing_message": "editing message", + "fee_qort": "fee: {{ message }} QORT", + "foreign_fee": "foreign fee: {{ message }}", + "mentioned": "mentioned", + "message_with_image": "this message already has an image", + "name_available": "{{ name }} is available", + "name_benefits": "benefits of a name", + "name_checking": "checking if name already exists", + "name_preview": "you need a name to use preview", + "name_publish": "you need a Qortal name to publish", + "name_rate": "you need a name to rate.", + "name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee", + "name_unavailable": "{{ name }} is unavailable", + "no_description": "no description", + "no_minting_details": "cannot view minting details on the gateway", + "no_notifications": "no new notifications", + "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.", + "overwrite_qdn": "overwrite to QDN", + "password_confirm": "please confirm a password", + "password_enter": "please enter a password", + "payment_request": "the Application
{{hostname}}
is requesting a payment", + "people_reaction": "people who reacted with {{ reaction }}", + "publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!", + "publishing": "publishing... Please wait.", + "qdn": "use QDN saving", + "rating": "rating for {{ service }} {{ name }}", + "register_name": "you need a registered Qortal name to save your pinned apps to QDN.", + "replied_to": "replied to {{ person }}", + "revert_default": "revert to default", + "revert_qdn": "revert to QDN", + "save_qdn": "save to QDN", + "secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party.", + "select_file": "please select a file", + "select_image": "please select an image for a logo", + "select_zip": "select .zip file containing static content:", + "sending": "sending...", + "settings": "you are using the export/import way of saving settings.", + "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" + }, + "question": { + "logout": "are you sure you would like to logout?", + "new_user": "are you a new user?", + "delete_chat_image": "would you like to delete your previous chat image?", + "perform_transaction": "would you like to perform a {{action}} transaction?", + "provide_thread": "please provide a thread title", + "publish_app": "would you like to publish this app?", + "publish_avatar": "would you like to publish an avatar?", + "publish_qdn": "would you like to publish your settings to QDN (encrypted)?", + "overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?", + "rate_app": "would you like to rate this app a rating of {{ rate }}?. It will create a POLL tx.", + "register_name": "would you like to register this name?", + "reset_pinned": "don't like your current local changes? Would you like to reset to the default pinned apps?", + "reset_qdn": "don't like your current local changes? Would you like to reset to your saved QDN pinned apps?" }, "status": { "minting": "(in conio)", @@ -73,13 +226,18 @@ "synchronizing": "sincronizzazione" }, "success": { - "order_submitted": "il tuo ordine di acquisto è stato inviato", - "publish_qdn": "pubblicato con successo su QDN", - "request_read": "ho letto questa richiesta", - "transfer": "il trasferimento è riuscito!" + "order_submitted": "your buy order was submitted", + "published": "successfully published. Please wait a couple minutes for the network to propogate the changes.", + "published_qdn": "successfully published to QDN", + "rated_app": "successfully rated. Please wait a couple minutes for the network to propogate the changes.", + "request_read": "I have read this request", + "transfer": "the transfer was succesful!" } }, - "minting_status": "stato conio", + "minting_status": "stato del conio", + "name": "nome", + "name_app": "nome/App", + "none": "nessuno", "page": { "last": "ultima", "first": "prima", @@ -89,27 +247,13 @@ "payment_notification": "notifica di pagamento", "price": "prezzo", "q_mail": "q-mail", - "question": { - "new_user": "sei un nuovo utente?" + "server": "server", + "settings": "settings", + "sort": { + "by_member": "by member" }, - "save_options": { - "no_pinned_changes": "attualmente non hai modifiche nelle app fissate", - "overwrite_changes": "l'app non è riuscita a scaricare le tue app fissate salvate su QDN. Vuoi sovrascrivere le modifiche?", - "overwrite_qdn": "sovrascrivi su QDN", - "publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografate)?", - "qdn": "usa salvataggio QDN", - "register_name": "hai bisogno di un nome Qortal registrato per salvare le app fissate su QDN.", - "reset_pinned": "non ti piacciono le modifiche locali? Vuoi ripristinare le app fissate predefinite?", - "reset_qdn": "non ti piacciono le modifiche locali? Vuoi ripristinare le app fissate salvate su QDN?", - "revert_default": "ripristina ai valori predefiniti", - "revert_qdn": "ripristina da QDN", - "save_qdn": "salva su QDN", - "save": "salva", - "settings": "stai usando il metodo di esportazione/importazione per salvare le impostazioni.", - "unsaved_changes": "hai modifiche non salvate nelle app fissate. Salvale su QDN." - }, - "settings": "impostazioni", - "supply": "offerta", + "supply": "supply", + "tags": "tags", "theme": { "dark": "modalità scura", "light": "modalità chiara" @@ -125,9 +269,13 @@ "title": "titolo", "tutorial": "tutorial", "user_lookup": "ricerca utente", + "zip": "zip", "wallet": { + "litecoin": "portafoglio litecoin", + "qortal": "portafoglio qortal", "wallet": "portafoglio", "wallet_other": "portafogli" }, + "website": "website", "welcome": "benvenuto" } From ecb4cb909f33ef902fbddd2427874f3d006ea624 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 17 May 2025 14:50:39 +0200 Subject: [PATCH 47/47] Refactor --- src/components/Group/GroupList.tsx | 18 +++++++-- src/i18n/locales/en/group.json | 3 ++ src/i18n/locales/it/auth.json | 6 +-- src/i18n/locales/it/group.json | 64 +++++++++++++++++++++++++++--- 4 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/components/Group/GroupList.tsx b/src/components/Group/GroupList.tsx index 1057281..2c2e844 100644 --- a/src/components/Group/GroupList.tsx +++ b/src/components/Group/GroupList.tsx @@ -32,6 +32,7 @@ import { import { timeDifferenceForNotificationChats } from './Group'; import { useAtom, useAtomValue } from 'jotai'; +import { useTranslation } from 'react-i18next'; export const GroupList = ({ selectGroupFunc, @@ -49,6 +50,7 @@ export const GroupList = ({ myAddress, }) => { const theme = useTheme(); + const { t } = useTranslation(['auth', 'core', 'group']); const [isRunningPublicNode] = useAtom(isRunningPublicNodeAtom); return ( @@ -86,7 +88,9 @@ export const GroupList = ({ ? theme.palette.text.primary : theme.palette.text.secondary } - label="Groups" + label={t('group:group.group_other', { + postProcess: 'capitalizeFirst', + })} selected={desktopSideView === 'groups'} customWidth="75px" > @@ -102,6 +106,7 @@ export const GroupList = ({ /> + { setDesktopSideView('directs'); @@ -116,7 +121,9 @@ export const GroupList = ({ ? theme.palette.text.primary : theme.palette.text.secondary } - label="Messaging" + label={t('group:group.messaging', { + postProcess: 'capitalizeFirst', + })} selected={desktopSideView === 'directs'} > +
- Group + {t('group:group.group', { postProcess: 'capitalizeFirst' })} {!isRunningPublicNode && ( @@ -273,6 +281,7 @@ const GroupItem = React.memo( )} + + {announcement && !announcement?.seentimestamp && ( )} + )} + {groupProperty?.isOpen === false && ( AVANTI qui sotto.", "choose_password": "scegli una nuova password", @@ -63,8 +63,8 @@ "custom_many": "nodi personalizzati", "use_custom": "usa nodo personalizzato", "use_local": "usa nodo locale", - "using": "utilizzando nodo", - "using_public": "utilizzando nodo pubblico" + "using": "utilizzo nodo", + "using_public": "utilizzo di nodo pubblico" }, "note": "nota", "password": "password", diff --git a/src/i18n/locales/it/group.json b/src/i18n/locales/it/group.json index e53b6c8..510565f 100644 --- a/src/i18n/locales/it/group.json +++ b/src/i18n/locales/it/group.json @@ -16,9 +16,17 @@ "load_members": "carica membri con nomi", "make_admin": "rendi amministratore", "manage_members": "gestisci membri", + "promote_group": "promuovi il gruppo ai non-membri", + "publish_announcement": "pubblica annuncio", + "publish_avatar": "pubblica avatar", "refetch_page": "ricarica pagina", "remove_admin": "rimuovi come amministratore", - "return_to_thread": "torna alle discussioni" + "remove_minting_account": "rimuovi account di conio", + "return_to_thread": "torna alle discussioni", + "scroll_bottom": "vai in fondo", + "scroll_unread_messages": "vai ai messaggi non letti", + "select_group": "seleziona un gruppo", + "visit_q_mintership": "visita Q-Mintership" }, "advanced_options": "opzioni avanzate", "approval_threshold": "soglia di approvazione del gruppo (numero / percentuale di amministratori che devono approvare una transazione)", @@ -28,14 +36,21 @@ "maximum": "ritardo massimo dei blocchi per le approvazioni delle transazioni di gruppo" }, "group": { + "avatar": "avatar del gruppo", "closed": "chiuso (privato) – gli utenti hanno bisogno del permesso per entrare", "description": "descrizione del gruppo", "id": "ID gruppo", "invites": "inviti del gruppo", + "group": "gruppo", + "group_other": "gruppi", "management": "gestione del gruppo", "member_number": "numero di membri", + "messaging": "messaggi", "name": "nome del gruppo", "open": "aperto (pubblico)", + "private": "gruppo privato", + "promotions": "promozione gruppi", + "public": "gruppo pubblico", "type": "tipo di gruppo" }, "invitation_expiry": "tempo di scadenza dell'invito", @@ -46,16 +61,40 @@ "latest_mails": "ultimi Q-Mails", "message": { "generic": { + "avatar_publish_fee": "publishing an Avatar requires {{ fee }}", + "avatar_registered_name": "a registered name is required to set an avatar", + "admin_only": "only groups where you are an admin will be shown", "already_in_group": "sei già in questo gruppo!", "closed_group": "questo è un gruppo chiuso/privato, devi aspettare che un admin accetti la tua richiesta", "descrypt_wallet": "decrittazione portafoglio in corso...", "encryption_key": "la prima chiave di cifratura comune del gruppo è in fase di creazione. Attendere qualche minuto affinché venga recuperata dalla rete. Controllo ogni 2 minuti...", + "group_announcement": "group Announcements", "group_invited_you": "{{group}} ti ha invitato", + "group_key_created": "first group key created.", + "group_member_list_changed": "the group member list has changed. Please re-encrypt the secret key.", + "group_no_secret_key": "there is no group secret key. Be the first admin to publish one!", + "group_secret_key_no_owner": "the latest group secret key was published by a non-owner. As the owner of the group please re-encrypt the key as a safeguard.", + "invalid_content": "invalid content, sender, or timestamp in reaction data", + "invalid_data": "error loading content: Invalid Data", + "latest_promotion": "only the latest promotion from the week will be shown for your group.", "loading_members": "caricamento elenco membri con nomi... attendere.", + "max_chars": "max 200 characters. Publish Fee", + "manage_minting": "manage your minting", + "minter_group": "you are currently not part of the MINTER group", + "mintership_app": "visit the Q-Mintership app to apply to be a minter", + "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", "no_display": "niente da mostrare", "no_selection": "nessun gruppo selezionato", "not_part_group": "non fai parte del gruppo cifrato. Attendi che un amministratore re-critti le chiavi.", "only_encrypted": "verranno mostrati solo i messaggi non cifrati.", + "only_private_groups": "only private groups will be shown", "private_key_copied": "chiave privata copiata", "provide_message": "inserisci un primo messaggio per la discussione", "secure_place": "conserva la tua chiave privata in un luogo sicuro. Non condividerla!", @@ -66,10 +105,15 @@ "descrypt_wallet": "errore nella decrittazione del portafoglio {{ :errorMessage }}", "description_required": "inserisci una descrizione", "group_info": "impossibile accedere alle informazioni del gruppo", + "group_promotion": "error publishing the promotion. Please try again", "group_secret_key": "impossibile ottenere la chiave segreta del gruppo", "name_required": "inserisci un nome", "notify_admins": "prova a contattare un amministratore dalla lista qui sotto:", - "thread_id": "impossibile trovare l'ID della discussione" + "qortals_required": "you need at least {{ quantity }} QORT to send a message", + "timeout_reward": "timeout waiting for reward share confirmation", + "thread_id": "impossibile trovare l'ID della discussione", + "unable_determine_group_private": "unable to determine if group is private", + "unable_minting": "unable to start minting" }, "success": { "group_ban": "membro bannato con successo dal gruppo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", @@ -87,19 +131,27 @@ "group_leave_name": "uscito dal gruppo {{group_name}}: in attesa di conferma", "group_leave_label": "uscito dal gruppo {{name}}: successo!", "group_member_admin": "membro nominato amministratore con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", + "group_promotion": "successfully published promotion. It may take a couple of minutes for the promotion to appear", "group_remove_member": "amministratore rimosso con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", "invitation_cancellation": "invito annullato con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", "invitation_request": "richiesta di adesione accettata: in attesa di conferma", "loading_threads": "caricamento discussioni... attendere.", "post_creation": "post creato con successo. Potrebbe volerci un po' per la pubblicazione", + "published_secret_key": "published secret key for group {{ group_id }}: awaiting confirmation", + "published_secret_key_label": "published secret key for group {{ group_id }}: success!", + "registered_name": "successfully registered. It may take a couple of minutes for the changes to propagate", + "registered_name_label": "registered name: awaiting confirmation. This may take a couple minutes.", + "registered_name_success": "registered name: success!", + "rewardshare_add": "add rewardshare: awaiting confirmation", + "rewardshare_add_label": "add rewardshare: success!", + "rewardshare_creation": "confirming creation of rewardshare on chain. Please be patient, this could take up to 90 seconds.", + "rewardshare_confirmed": "rewardshare confirmed. Please click Next.", + "rewardshare_remove": "remove rewardshare: awaiting confirmation", + "rewardshare_remove_label": "remove rewardshare: success!", "thread_creation": "discussione creata con successo. Potrebbe volerci un po' per la pubblicazione", "unbanned_user": "utente sbannato con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", "user_joined": "utente entrato con successo!" } }, - "question": { - "perform_transaction": "vuoi eseguire una transazione di tipo {{action}}?", - "provide_thread": "inserisci un titolo per la discussione" - }, "thread_posts": "nuovi messaggi nella discussione" }