diff --git a/i18n.js b/i18n.js index 78df3c1..5277e59 100644 --- a/i18n.js +++ b/i18n.js @@ -19,6 +19,15 @@ const capitalize = { }, }; +export const supportedLanguages = { + de: { name: 'Deutsch', flag: '🇩🇪' }, + en: { name: 'English', flag: '🇺🇸' }, + es: { name: 'Español', flag: '🇪🇸' }, + fr: { name: 'Français', flag: '🇫🇷' }, + it: { name: 'Italiano', flag: '🇮🇹' }, + ru: { name: 'Русский', flag: '🇷🇺' }, +}; + i18n .use(HttpApi) .use(LanguageDetector) @@ -42,8 +51,8 @@ i18n escapeValue: false, }, lng: navigator.language, - ns: ['auth', 'core', 'tutorial'], - supportedLngs: ['en', 'it', 'es', 'fr', 'de', 'ru'], + ns: ['auth', 'core', 'group', 'tutorial'], + supportedLngs: Object.keys(supportedLanguages), }); export default i18n; diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index 2ee037e..6591afe 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -29,6 +29,11 @@ "password": "password", "password_confirmation": "confirm password", "return_to_list": "return to list", + "tips": { + "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.", + "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!" + }, "wallet": { "password_confirmation": "confirm wallet password", "password": "wallet password", diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 8988c4a..5ba27e8 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -1,27 +1,57 @@ { - "add": "add", - "cancel": "cancel", - "choose": "choose", - "close": "close", - "continue": "continue", + "action": { + "add": "add", + "accept": "accept", + "backup_account": "backup account", + "backup_wallet": "backup wallet", + "cancel": "cancel", + "change": "change", + "change_language": "change language", + "choose": "choose", + "close": "close", + "continue": "continue", + "continue_logout": "continue to logout", + "decline": "decline", + "edit": "edit", + "export": "export", + "import": "import", + "invite": "invite", + "join": "join", + "logout": "logout", + "notify": "notify" + }, "core": { "block_height": "block height", "information": "core information", "peers": "connected peers", "version": "core version" }, + "count": { + "none": "none", + "one": "one" + }, "description": "description", - "edit": "edit", - "export": "export", - "import": "import", + "fee": { + "payment": "payment fee", + "publish": "publish fee" + }, + "page": { + "last": "last", + "first": "first", + "next": "next", + "previous": "previous" + }, + "downloading_qdn": "downloading from QDN", "last_height": "last height", "loading": "loading...", - "logout": "logout", + "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", "minting_status": "minting status", + "new_user": "are you a new user?", "payment_notification": "payment notification", "price": "price", "q_mail": "q-mail", - "result": { + "message": { "error": { "generic": "an error occurred", "incorrect_password": "incorrect password", @@ -34,7 +64,10 @@ "synchronizing": "synchronizing" }, "success": { - "publish_qdn": "successfully published to QDN" + "order_submitted": "your buy order was submitted", + "publish_qdn": "successfully published to QDN", + "request_read": "I have read this request", + "transfer": "the transfer was succesful!" } }, "save_options": { @@ -59,11 +92,18 @@ "dark": "dark mode", "light": "light mode" }, + "time": { + "day_one": "{{count}} day", + "day_other": "{{count}} days", + "hour_one": "{{count}} hour", + "hour_other": "{{count}} hours", + "minute_one": "{{count}} minute", + "minute_other": "{{count}} minutes" + }, "title": "title", "tutorial": "tutorial", "user_lookup": "user lookup", "wallet": { - "backup_wallet": "backup wallet", "wallet": "wallet", "wallet_other": "wallets" }, diff --git a/public/locales/en/group.json b/public/locales/en/group.json new file mode 100644 index 0000000..345c6d0 --- /dev/null +++ b/public/locales/en/group.json @@ -0,0 +1,65 @@ +{ + "action": { + "cancel_ban": "cancel ban", + "create_group": "create group", + "find_group": "find group", + "join_group": "join group", + "invite_member": "invite member", + "refetch_page": "refetch page", + "return_to_thread": "return to threads" + }, + "advanced_options": "advanced options", + "approval_threshold": "group Approval Threshold (number / percentage of Admins that must approve a transaction)", + "ban_list": "ban list", + "block_delay": { + "minimum": "minimum Block delay for Group Transaction Approvals", + "maximum": "maximum Block delay for Group Transaction Approvals" + }, + "group": { + "closed": "closed (private) - users need permission to join", + "description": "description of group", + "invites": "group invites", + "management": "group management", + "name": "name of group", + "open": "open (public)", + "type": "group type" + }, + "invitation_expiry": "invitation Expiry Time", + "join_requests": "join requests", + "question": { + "cancel_ban": "would you like to perform a CANCEL_GROUP_BAN transaction?", + "create_group": "would you like to perform an CREATE_GROUP transaction?", + "group_invite": "would you like to perform a GROUP_INVITE transaction?", + "join_group": "would you like to perform an JOIN_GROUP transaction?", + "provide_thread": "please provide a thread title" + }, + "message": { + "generic": { + "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", + "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.", + "only_encrypted": "only unencrypted messages will be displayed.", + "setting_group": "setting up group... please wait." + }, + "error": { + "access_name": "cannot send a message without a access to your name", + "description_required": "please provide a description", + "group_info": "cannot access group information", + "name_required": "please provide a name", + "notify_admins": "try notifying an admin from the list of admins below:" + }, + "success": { + "group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate", + "group_creation_name": "created group {{group_name}}: awaiting confirmation", + "group_creation_label": "created group {{name}}: success!", + "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", + "join_creation": "successfully requested to join group. It may take a couple of minutes for the changes to propagate", + "group_join_name": "joined group {{group_name}}: awaiting confirmation", + "group_join_label": "joined group {{name}}: success!", + "loading_threads": "loading threads... please wait.", + "unbanned_user": "successfully unbanned user. It may take a couple of minutes for the changes to propagate" + } + } +} diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index ad00833..513378f 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -6,38 +6,43 @@ }, "advanced_users": "per utenti avanzati", "apikey": { - "alternative": "alternativa: seleziona un file", - "change": "cambia la chiave API", - "enter": "inserisci la chiave API", - "import": "importa chiave API", + "alternative": "alternativa: selezione file", + "change": "cambia APIkey", + "enter": "inserisci APIkey", + "import": "importa APIkey", "key": "chiave API", - "select_valid": "selezione una chiave API valida" + "select_valid": "seleziona una APIkey valida" }, - "authenticate": "autenticazione", + "authenticate": "autentica", "build_version": "versione build", - "create_account": "crea un account", + "create_account": "crea account", "download_account": "scarica account", - "keep_secure": "metti al sicuro il file del tuo account", + "keep_secure": "mantieni sicuro il file del tuo account", "node": { - "choose": "scegli un nodo custom", - "custom_many": "nodi custom", - "use_custom": "use nodo custom", + "choose": "scegli nodo personalizzato", + "custom_many": "nodi personalizzati", + "use_custom": "usa nodo personalizzato", "use_local": "usa nodo locale", - "using": "nodo in uso", + "using": "utilizzo nodo", "using_public": "utilizzo nodo pubblico" }, - "password_confirmation": "confirma la password", "password": "password", + "password_confirmation": "conferma password", + "return_to_list": "torna alla lista", + "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!" + }, "wallet": { - "password_confirmation": "conferma la password del wallet", + "password_confirmation": "conferma password del wallet", "password": "password del wallet", - "keep_password": "mantieni la password attuale", + "keep_password": "mantieni password corrente", "new_password": "nuova password", "error": { "missing_new_password": "per favore inserisci una nuova password", "missing_password": "per favore inserisci la tua password" } }, - "return_to_list": "ritorna alla lista", "welcome": "benvenuto in" } diff --git a/public/locales/it/core.json b/public/locales/it/core.json index 8a6c4a2..8319b22 100644 --- a/public/locales/it/core.json +++ b/public/locales/it/core.json @@ -1,71 +1,111 @@ { - "add": "aggiungi", - "cancel": "annulla", - "choose": "scegli", - "close": "chiudi", - "continue": "continua", + "action": { + "add": "aggiungi", + "accept": "accetta", + "backup_account": "backup account", + "backup_wallet": "backup wallet", + "cancel": "annulla", + "change": "cambia", + "change_language": "cambia lingua", + "choose": "scegli", + "close": "chiudi", + "continue": "continua", + "continue_logout": "continua con il logout", + "decline": "rifiuta", + "edit": "modifica", + "export": "esporta", + "import": "importa", + "invite": "invita", + "join": "unisciti", + "logout": "esci", + "notify": "notifica" + }, "core": { - "block_height": "altezza del blocco", + "block_height": "altezza blocco", "information": "informazioni core", "peers": "peer connessi", "version": "versione core" }, + "count": { + "none": "nessuno", + "one": "uno" + }, "description": "descrizione", - "edit": "modifica", - "export": "esporta", - "import": "importa", + "fee": { + "payment": "commissione di pagamento", + "publish": "commissione di pubblicazione" + }, + "page": { + "last": "ultimo", + "first": "primo", + "next": "successivo", + "previous": "precedente" + }, + "downloading_qdn": "scaricamento da QDN", "last_height": "ultima altezza", "loading": "caricamento...", - "logout": "disconnetti", - "minting_status": "stato del conio", + "loading_posts": "caricamento post... attendere prego.", + "message_us": "per favore scrivici su Telegram o Discord se hai bisogno di 4 QORT per iniziare a chattare senza limitazioni", + "minting_status": "stato minting", + "new_user": "sei un nuovo utente?", "payment_notification": "notifica di pagamento", "price": "prezzo", "q_mail": "q-mail", - "result": { + "message": { "error": { "generic": "si è verificato un errore", "incorrect_password": "password errata", "save_qdn": "impossibile salvare su QDN" }, "status": { - "minting": "(conio in corso)", - "not_minting": "(conio non attivo)", + "minting": "(minting)", + "not_minting": "(non minting)", "synchronized": "sincronizzato", "synchronizing": "sincronizzazione in corso" }, "success": { - "publish_qdn": "pubblicato con successo su QDN" + "order_submitted": "il tuo ordine di acquisto è stato inviato", + "publish_qdn": "pubblicato su QDN con successo", + "request_read": "ho letto questa richiesta", + "transfer": "il trasferimento è stato effettuato con successo!" } }, "save_options": { "no_pinned_changes": "attualmente non hai modifiche alle tue app appuntate", - "overwrite_changes": "l'app non è riuscita a scaricare le tue app appuntate salvate su QDN. Vuoi sovrascrivere queste modifiche?", + "overwrite_changes": "l'app non è riuscita a scaricare le tue app appuntate salvate su QDN. Vuoi sovrascrivere le modifiche?", "overwrite_qdn": "sovrascrivi su QDN", - "publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografate)?", - "qdn": "usa il salvataggio QDN", - "register_name": "hai bisogno di un nome Qortal registrato per salvare le tue app appuntate su QDN.", + "publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografato)?", + "qdn": "usa il salvataggio su QDN", + "register_name": "devi avere un nome Qortal registrato per salvare le tue app appuntate su QDN.", "reset_pinned": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate predefinite?", - "reset_qdn": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le tue app appuntate salvate su QDN?", - "revert_default": "ripristina predefinite", + "reset_qdn": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate salvate su QDN?", + "revert_default": "ripristina predefinito", "revert_qdn": "ripristina da QDN", "save_qdn": "salva su QDN", "save": "salva", - "settings": "stai utilizzando il metodo esporta/importa per salvare le impostazioni.", + "settings": "stai usando il metodo di esportazione/importazione per salvare le impostazioni.", "unsaved_changes": "hai modifiche non salvate alle tue app appuntate. Salvale su QDN." }, "settings": "impostazioni", - "supply": "offerta", + "supply": "disponibilità", "theme": { "dark": "modalità scura", "light": "modalità chiara" }, + "time": { + "day_one": "{{count}} giorno", + "day_other": "{{count}} giorni", + "hour_one": "{{count}} ora", + "hour_other": "{{count}} ore", + "minute_one": "{{count}} minuto", + "minute_other": "{{count}} minuti" + }, "title": "titolo", "tutorial": "tutorial", "user_lookup": "ricerca utente", "wallet": { - "backup_wallet": "backup portafoglio", - "wallet": "portafoglio", - "wallet_other": "portafogli" + "wallet": "wallet", + "wallet_other": "wallet" }, "welcome": "benvenuto" } diff --git a/public/locales/it/group.json b/public/locales/it/group.json new file mode 100644 index 0000000..862496d --- /dev/null +++ b/public/locales/it/group.json @@ -0,0 +1,65 @@ +{ + "action": { + "cancel_ban": "annulla ban", + "create_group": "crea gruppo", + "find_group": "trova gruppo", + "join_group": "unisciti al gruppo", + "invite_member": "invita membro", + "refetch_page": "ricarica pagina", + "return_to_thread": "torna ai thread" + }, + "advanced_options": "opzioni avanzate", + "approval_threshold": "soglia di Approvazione del gruppo (numero/percentuale di Admin che devono approvare una transazione)", + "ban_list": "lista ban", + "block_delay": { + "minimum": "ritardo minimo dei blocchi per Approvazione di Transazioni di Gruppo", + "maximum": "ritardo massimo dei blocchi per Approvazione di Transazioni di Gruppo" + }, + "group": { + "closed": "chiuso (privato) - gli utenti necessitano di permesso per unirsi", + "description": "descrizione del gruppo", + "invites": "inviti del gruppo", + "management": "gestione del gruppo", + "name": "nome del gruppo", + "open": "aperto (pubblico)", + "type": "tipo di gruppo" + }, + "invitation_expiry": "tempo di scadenza dell'invito", + "join_requests": "richieste di adesione", + "question": { + "cancel_ban": "vuoi eseguire una transazione CANCEL_GROUP_BAN?", + "create_group": "vuoi eseguire una transazione CREATE_GROUP?", + "group_invite": "vuoi eseguire una transazione GROUP_INVITE?", + "join_group": "vuoi eseguire una transazione JOIN_GROUP?", + "provide_thread": "per favore fornisci un titolo per il thread" + }, + "message": { + "generic": { + "encryption_key": "la prima chiave di cifratura comune del gruppo è in fase di creazione. Attendere alcuni minuti affinché venga recuperata dalla rete. Controllo ogni 2 minuti...", + "group_invited_you": "{{group}} ti ha invitato", + "no_display": "niente da visualizzare", + "no_selection": "nessun gruppo selezionato", + "not_part_group": "non fai parte del gruppo cifrato dei membri. Attendi che un amministratore ri-codifichi le chiavi.", + "only_encrypted": "verranno visualizzati solo i messaggi non cifrati.", + "setting_group": "configurazione del gruppo... attendere prego." + }, + "error": { + "access_name": "impossibile inviare un messaggio senza accesso al tuo nome", + "description_required": "per favore fornisci una descrizione", + "group_info": "impossibile accedere alle informazioni del gruppo", + "name_required": "per favore fornisci un nome", + "notify_admins": "prova a notificare un admin dalla lista di amministratori qui sotto:" + }, + "success": { + "group_creation": "gruppo creato con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", + "group_creation_name": "gruppo {{group_name}} creato: in attesa di conferma", + "group_creation_label": "gruppo {{name}} creato: successo!", + "group_invite": "invito inviato con successo a {{value}}. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", + "join_creation": "richiesta di adesione al gruppo inviata con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", + "group_join_name": "entrato nel gruppo {{group_name}}: in attesa di conferma", + "group_join_label": "entrato nel gruppo {{name}}: successo!", + "loading_threads": "caricamento thread... attendere prego.", + "unbanned_user": "utente sbannato con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino" + } + } +} diff --git a/src/App.tsx b/src/App.tsx index a4f60f6..b3d11d0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -135,6 +135,7 @@ import { GeneralNotifications } from './components/GeneralNotifications'; import { PdfViewer } from './common/PdfViewer'; import ThemeSelector from './components/Theme/ThemeSelector.tsx'; import { useTranslation } from 'react-i18next'; +import LanguageSelector from './components/Language/LanguageSelector.tsx'; import { DownloadWallet } from './components/Auth/DownloadWallet.tsx'; type extStates = @@ -255,14 +256,7 @@ export const getBaseApiReact = (customApi?: string) => { return groupApi; } }; -// export const getArbitraryEndpointReact = () => { -// if (globalApiKey) { -// return `/arbitrary/resources/search`; -// } else { -// return `/arbitrary/resources/searchsimple`; -// } -// }; export const getArbitraryEndpointReact = () => { if (globalApiKey) { return `/arbitrary/resources/searchsimple`; @@ -571,26 +565,6 @@ function App() { isFocusedRef.current = isFocused; }, [isFocused]); - // const checkIfUserHasLocalNode = useCallback(async () => { - // try { - // const url = `http://127.0.0.1:12391/admin/status`; - // const response = await fetch(url, { - // method: "GET", - // headers: { - // "Content-Type": "application/json", - // }, - // }); - // const data = await response.json(); - // if (data?.isSynchronizing === false && data?.syncPercent === 100) { - // setHasLocalNode(true); - // } - // } catch (error) {} - // }, []); - - // useEffect(() => { - // checkIfUserHasLocalNode(); - // }, [extState]); - const address = useMemo(() => { if (!rawWallet?.address0) return ''; return rawWallet.address0; @@ -1007,7 +981,7 @@ function App() { await showUnsavedChanges({ message: 'Your settings have changed. If you logout you will lose your changes. Click on the save button in the header to keep your changed settings.', - }); + }); // TODO translate } else if (extState === 'authenticated') { await showUnsavedChanges({ message: 'Are you sure you would like to logout?', @@ -1311,19 +1285,24 @@ function App() { )} + {authenticatedMode === 'ltc' ? ( <> + + {rawWallet?.ltcAddress?.slice(0, 6)}... {rawWallet?.ltcAddress?.slice(-4)} + + {ltcBalanceLoading && ( )} @@ -1345,6 +1324,7 @@ function App() { > {ltcBalance} LTC + + + {userInfo?.name} + + {rawWallet?.address0?.slice(0, 6)}... @@ -1514,7 +1498,7 @@ function App() { textTransform: 'uppercase', }} > - {t('core:logout')} + {t('core:action.logout')} } placement="left" @@ -1869,7 +1853,7 @@ function App() { textTransform: 'uppercase', }} > - {t('core:backup_wallet')} + {t('core:action.backup_wallet')} } placement="left" @@ -1903,10 +1887,6 @@ function App() { @@ -2036,7 +2016,6 @@ function App() { /> )} - {isShowQortalRequest && !isMainWindow && ( <> @@ -2319,7 +2298,6 @@ function App() { {sendPaymentError} )} - {extState === 'web-app-request-payment' && !isMainWindow && ( <> @@ -2953,7 +2931,9 @@ function App() { }); }} > - Backup Account + {t('core:action.backup_account', { + postProcess: 'capitalize', + })} )} @@ -2981,7 +2961,9 @@ function App() { lineHeight: '15px', }} > - The transfer was succesful! + {t('core:message.success.transfer', { + postProcess: 'capitalize', + })} - Continue + {t('core:action.continue', { postProcess: 'capitalize' })} )} @@ -3004,7 +2986,9 @@ function App() { lineHeight: '15px', }} > - The transfer was succesful! + {t('core:message.success.transfer', { + postProcess: 'capitalize', + })} - Continue + {t('core:action.continue', { postProcess: 'capitalize' })} )} @@ -3027,7 +3011,9 @@ function App() { lineHeight: '15px', }} > - Your buy order was submitted + {t('core:message.success.order_submitted', { + postProcess: 'capitalize', + })} - Close + {t('core:action.close', { postProcess: 'capitalize' })} )} + {countdown && ( {message?.paymentFee && ( - payment fee: {message.paymentFee} + {t('core:fee.payment', { + postProcess: 'capitalize', + })} + : {message.paymentFee} )} {message?.publishFee && ( - publish fee: {message.publishFee} + {t('core:fee.publish', { + postProcess: 'capitalize', + })} + : {message.publishFee} )} @@ -3108,7 +3101,9 @@ function App() { onClick={onOk} autoFocus > - accept + {t('core:action.accept', { + postProcess: 'capitalize', + })} @@ -3146,7 +3143,9 @@ function App() { @@ -3165,14 +3164,18 @@ function App() { @@ -3443,7 +3446,9 @@ function App() { label={ - I have read this request + {t('core:message.success.request_read', { + postProcess: 'capitalize', + })} @@ -3454,8 +3459,8 @@ function App() { @@ -3491,7 +3496,9 @@ function App() { onOkQortalRequestExtension('accepted'); }} > - accept + {t('core:action.accept', { + postProcess: 'capitalize', + })} onCancelQortalRequestExtension()} > - decline + {t('core:action.decline', { + postProcess: 'capitalize', + })} {sendPaymentError} @@ -3566,6 +3575,7 @@ function App() { /> )} + ); diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 3c8afd9..1f58360 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -31,6 +31,7 @@ import { GlobalContext } from '../App'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; import ThemeSelector from '../components/Theme/ThemeSelector'; import { useTranslation } from 'react-i18next'; +import LanguageSelector from '../components/Language/LanguageSelector'; const manifestData = { version: '0.5.3', @@ -510,14 +511,8 @@ export const NotAuthenticated = ({ fontSize: '16px', }} > - 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. - {' '} - // TODO translate + {t('auth:tips.digital_id', { postProcess: 'capitalize' })} + } > @@ -547,9 +542,8 @@ export const NotAuthenticated = ({ fontSize: '18px', }} > - New users start here! - {' '} - // TODO translate + {t('auth:tips.new_users', { postProcess: 'capitalize' })} + - 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. - {' '} - // TODO translate + {t('auth:tips.new_account', { postProcess: 'capitalize' })} + } > @@ -816,7 +806,7 @@ export const NotAuthenticated = ({ }} variant="contained" > - {t('core:choose', { postProcess: 'capitalize' })} + {t('core:action.choose', { postProcess: 'capitalize' })} @@ -875,7 +865,9 @@ export const NotAuthenticated = ({ }} variant="contained" > - {t('core:choose', { postProcess: 'capitalize' })} + {t('core:action.choose', { + postProcess: 'capitalize', + })} )} @@ -953,7 +947,7 @@ export const NotAuthenticated = ({ }} autoFocus > - {t('core:close', { postProcess: 'capitalize' })} + {t('core:action.close', { postProcess: 'capitalize' })} )} @@ -1075,7 +1069,7 @@ export const NotAuthenticated = ({ setShowSelectApiKey(false); }} > - {t('core:close', { postProcess: 'capitalize' })} + {t('core:action.close', { postProcess: 'capitalize' })} @@ -1097,6 +1091,7 @@ export const NotAuthenticated = ({ /> + ); diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index d525c26..c77865c 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -15,6 +15,7 @@ import { extractComponents } from '../Chat/MessageDisplay'; import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward'; import { AppsPrivate } from './AppsPrivate'; import ThemeSelector from '../Theme/ThemeSelector'; +import LanguageSelector from '../Language/LanguageSelector'; export const AppsHomeDesktop = ({ setMode, @@ -157,6 +158,7 @@ export const AppsHomeDesktop = ({ /> + ); diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index f77580c..cf39375 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -79,21 +79,21 @@ export const CoreSyncStatus = () => { if (isMintingPossible && !isUsingGateway) { imagePath = syncedMintingImg; - message = `${t(`core:result.status.${isSynchronizing ? 'synchronizing' : 'synchronized'}`, { postProcess: 'capitalize' })} ${t('core:result.status.minting')}`; + message = `${t(`core:message.status.${isSynchronizing ? 'synchronizing' : 'synchronized'}`, { postProcess: 'capitalize' })} ${t('core:message.status.minting')}`; } else if (isSynchronizing === true && syncPercent === 99) { imagePath = syncingImg; } else if (isSynchronizing && !isMintingPossible && syncPercent === 100) { imagePath = syncingImg; - message = `${t('core:result.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.not_minting') : ''}`; + message = `${t('core:message.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.not_minting') : ''}`; } else if (!isSynchronizing && !isMintingPossible && syncPercent === 100) { imagePath = syncedImg; - message = `${t('core:result.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.not_minting') : ''}`; + message = `${t('core:message.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.not_minting') : ''}`; } else if (isSynchronizing && isMintingPossible && syncPercent === 100) { imagePath = syncingImg; - message = `${t('core:result.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.minting') : ''}`; + message = `${t('core:message.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.minting') : ''}`; } else if (!isSynchronizing && isMintingPossible && syncPercent === 100) { imagePath = syncedMintingImg; - message = `${t('core:result.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.minting') : ''}`; + message = `${t('core:message.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.minting') : ''}`; } return ( diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index b422f81..06e54e8 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -8,6 +8,7 @@ import { enabledDevModeAtom } from '../atoms/global'; import { AppsIcon } from '../assets/Icons/AppsIcon'; import ThemeSelector from './Theme/ThemeSelector'; import { CoreSyncStatus } from './CoreSyncStatus'; +import LanguageSelector from './Language/LanguageSelector'; export const DesktopSideBar = ({ goToHome, @@ -143,6 +144,7 @@ export const DesktopSideBar = ({ )} + ); diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 91552cb..66e3880 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -1,4 +1,13 @@ -import * as React from 'react'; +import { + forwardRef, + Fragment, + ReactElement, + Ref, + SyntheticEvent, + useContext, + useEffect, + useState, +} from 'react'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; import AppBar from '@mui/material/AppBar'; @@ -28,6 +37,7 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { getFee } from '../../background'; import { MyContext } from '../../App'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; +import { useTranslation } from 'react-i18next'; export const Label = styled('label')` display: block; @@ -37,30 +47,29 @@ export const Label = styled('label')` margin-bottom: 4px; `; -const Transition = React.forwardRef(function Transition( +const Transition = forwardRef(function Transition( props: TransitionProps & { - children: React.ReactElement; + children: ReactElement; }, - ref: React.Ref + ref: Ref ) { return ; }); export const AddGroup = ({ address, open, setOpen }) => { - const { show, setTxList } = React.useContext(MyContext); - const [tab, setTab] = React.useState('create'); - const [openAdvance, setOpenAdvance] = React.useState(false); - const [name, setName] = React.useState(''); - const [description, setDescription] = React.useState(''); - const [groupType, setGroupType] = React.useState('1'); - const [approvalThreshold, setApprovalThreshold] = React.useState('40'); - const [minBlock, setMinBlock] = React.useState('5'); - const [maxBlock, setMaxBlock] = React.useState('21600'); - const [value, setValue] = React.useState(0); - const [openSnack, setOpenSnack] = React.useState(false); - const [infoSnack, setInfoSnack] = React.useState(null); + const { show, setTxList } = useContext(MyContext); + const [openAdvance, setOpenAdvance] = useState(false); + const [name, setName] = useState(''); + const [description, setDescription] = useState(''); + const [groupType, setGroupType] = useState('1'); + const [approvalThreshold, setApprovalThreshold] = useState('40'); + const [minBlock, setMinBlock] = useState('5'); + const [maxBlock, setMaxBlock] = useState('21600'); + const [value, setValue] = useState(0); + const [openSnack, setOpenSnack] = useState(false); + const [infoSnack, setInfoSnack] = useState(null); - const handleChange = (event: React.SyntheticEvent, newValue: number) => { + const handleChange = (event: SyntheticEvent, newValue: number) => { setValue(newValue); }; @@ -84,16 +93,30 @@ export const AddGroup = ({ address, open, setOpen }) => { setMaxBlock(event.target.value as string); }; + const { t } = useTranslation(['core', 'group']); const theme = useTheme(); const handleCreateGroup = async () => { try { - if (!name) throw new Error('Please provide a name'); - if (!description) throw new Error('Please provide a description'); + if (!name) + throw new Error( + t('group:message.error.name_required', { + postProcess: 'capitalize', + }) + ); + if (!description) + throw new Error( + t('group:message.error.description_required', { + postProcess: 'capitalize', + }) + ); + + const fee = await getFee('CREATE_GROUP'); - const fee = await getFee('CREATE_GROUP'); // TODO translate await show({ - message: 'Would you like to perform an CREATE_GROUP transaction?', + message: t('group:question.create_group', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); @@ -111,16 +134,23 @@ export const AddGroup = ({ address, open, setOpen }) => { if (!response?.error) { setInfoSnack({ type: 'success', - message: - 'Successfully created group. It may take a couple of minutes for the changes to propagate', + message: t('group:message.success.group_creation', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); setTxList((prev) => [ { ...response, type: 'created-group', - label: `Created group ${name}: awaiting confirmation`, - labelDone: `Created group ${name}: success!`, + label: t('group:message.success.group_creation_name', { + group_name: name, + postProcess: 'capitalize', + }), + labelDone: t('group:message.success.group_creation_label', { + group_name: name, + postProcess: 'capitalize', + }), done: false, }, ...prev, @@ -131,7 +161,11 @@ export const AddGroup = ({ address, open, setOpen }) => { 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) { @@ -143,22 +177,6 @@ export const AddGroup = ({ address, open, setOpen }) => { } }; - function CustomTabPanel(props: TabPanelProps) { - const { children, value, index, ...other } = props; - - return ( - - ); - } - function a11yProps(index: number) { return { id: `simple-tab-${index}`, @@ -170,7 +188,7 @@ export const AddGroup = ({ address, open, setOpen }) => { setValue(2); }; - React.useEffect(() => { + useEffect(() => { subscribeToEvent('openGroupInvitesRequest', openGroupInvitesRequestFunc); return () => { @@ -182,7 +200,7 @@ export const AddGroup = ({ address, open, setOpen }) => { }, []); return ( - + { > - Group Management + {t('group:group.management', { postProcess: 'capitalize' })} { > - - {/* */} + { }} > { }} /> { }} /> { gap: '5px', }} > - + setName(e.target.value)} /> @@ -309,14 +336,21 @@ export const AddGroup = ({ address, open, setOpen }) => { gap: '5px', }} > - + setDescription(e.target.value)} /> + { gap: '5px', }} > - + + + { }} onClick={() => setOpenAdvance((prev) => !prev)} > - Advanced options + + {t('group:advanced_options', { + postProcess: 'capitalize', + })} + {openAdvance ? : } + { }} > { }} > { }} > @@ -454,7 +564,9 @@ export const AddGroup = ({ address, open, setOpen }) => { color="primary" onClick={handleCreateGroup} > - Create Group + {t('group.action.create', { + postProcess: 'capitalize', + })} @@ -503,6 +615,6 @@ export const AddGroup = ({ address, open, setOpen }) => { setInfo={setInfoSnack} /> - + ); }; diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index b80cc34..efc7dbf 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -1,6 +1,5 @@ import { Box, - Button, ListItem, ListItemButton, ListItemText, @@ -8,7 +7,7 @@ import { TextField, Typography, } from '@mui/material'; -import React, { +import { useCallback, useContext, useEffect, @@ -25,10 +24,12 @@ import { import _ from 'lodash'; import { MyContext, getBaseApiReact } from '../../App'; import { LoadingButton } from '@mui/lab'; -import { getBaseApi, getFee } from '../../background'; +import { getFee } from '../../background'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { Spacer } from '../../common/Spacer'; +import { useTranslation } from 'react-i18next'; + const cache = new CellMeasurerCache({ fixedWidth: true, defaultHeight: 50, @@ -36,7 +37,7 @@ const cache = new CellMeasurerCache({ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const { memberGroups, show, setTxList } = useContext(MyContext); - + const { t } = useTranslation(['core', 'group']); const [groups, setGroups] = useState([]); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open @@ -101,12 +102,17 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const handleJoinGroup = async (group, isOpen) => { try { const groupId = group.groupId; - const fee = await getFee('JOIN_GROUP'); // TODO translate + + const fee = await getFee('JOIN_GROUP'); + await show({ - message: 'Would you like to perform an JOIN_GROUP transaction?', + message: t('group:question.join_group', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); setIsLoading(true); + await new Promise((res, rej) => { window .sendMessage('joinGroup', { @@ -116,8 +122,9 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { 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.join_group', { + postProcess: 'capitalize', + }), }); if (isOpen) { @@ -125,8 +132,14 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { { ...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, }, @@ -215,7 +228,10 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { padding: '10px', }} > - Join {group?.groupName} + + {t('core:action.join', { postProcess: 'capitalize' })}{' '} + {group?.groupName} + {group?.isOpen === false && 'This is a closed/private group, so you will need to wait until an admin accepts your request'} @@ -226,7 +242,9 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { variant="contained" onClick={() => handleJoinGroup(group, group?.isOpen)} > - Join group + {t('group:action.join_group', { + postProcess: 'capitalize', + })} diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index 80d9c4c..b2d58e7 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -129,7 +129,7 @@ export const BlockedUsersModal = () => { executeEvent('updateChatMessagesWithBlocks', true); } } catch (error) { - setOpenSnackGlobal(true); // TODO translate + setOpenSnackGlobal(true); setInfoSnackCustom({ type: 'error', message: error?.message || 'Unable to block user', diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 3c8972f..7ad7814 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -1,5 +1,4 @@ import React, { - FC, useCallback, useEffect, useMemo, @@ -17,7 +16,6 @@ import { ComposeIcon, ComposeP, GroupContainer, - GroupNameP, InstanceFooter, InstanceListContainer, InstanceListContainerRow, @@ -58,10 +56,12 @@ import { executeEvent } from '../../../utils/events'; import RefreshIcon from '@mui/icons-material/Refresh'; import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App'; import { addDataPublishesFunc, getDataPublishesFunc } from '../Group'; +import { useTranslation } from 'react-i18next'; const filterOptions = ['Recently active', 'Newest', 'Oldest']; export const threadIdentifier = 'DOCUMENT'; + export const GroupMail = ({ selectedGroup, userInfo, @@ -82,6 +82,7 @@ export const GroupMail = ({ const anchorElInstanceFilter = useRef(null); const [tempPublishedList, setTempPublishedList] = useState([]); const dataPublishes = useRef({}); + const { t } = useTranslation(['core']); const [isLoading, setIsLoading] = useState(false); const groupIdRef = useRef(null); @@ -120,7 +121,9 @@ export const GroupMail = ({ }); setTempPublishedList(tempData); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const getEncryptedResource = async ( @@ -627,9 +630,9 @@ export const GroupMail = ({ @@ -682,6 +685,7 @@ export const GroupMail = ({ }} /> + {combinedListTempAndReal.map((thread) => { @@ -754,8 +758,8 @@ export const GroupMail = ({ {filterMode === 'Recently active' && (
@@ -776,16 +780,16 @@ export const GroupMail = ({ }, 300); }} sx={{ - position: 'absolute', - bottom: '2px', - right: '2px', - borderRadius: '5px', + alignItems: 'center', backgroundColor: '#27282c', + borderRadius: '5px', + bottom: '2px', + cursor: 'pointer', display: 'flex', gap: '10px', - alignItems: 'center', padding: '5px', - cursor: 'pointer', + position: 'absolute', + right: '2px', '&:hover': { background: 'rgba(255, 255, 255, 0.60)', }, @@ -795,9 +799,11 @@ export const GroupMail = ({ sx={{ color: 'white', fontSize: '12px', - }} // TODO translate + }} > - Last page + {t('core:page.last', { + postProcess: 'capitalize', + })} diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index d5c4384..ddcafbb 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -1,11 +1,9 @@ import React, { useEffect, useRef, useState } from 'react'; import { Box, CircularProgress, Input } from '@mui/material'; import ShortUniqueId from 'short-unique-id'; -import CloseIcon from '@mui/icons-material/Close'; import ModalCloseSVG from '../../../assets/svgs/ModalClose.svg'; import ComposeIconSVG from '../../../assets/svgs/ComposeIcon.svg'; import { - AttachmentContainer, CloseContainer, ComposeContainer, ComposeIcon, @@ -30,6 +28,7 @@ import TipTap from '../../Chat/TipTap'; import { MessageDisplay } from '../../Chat/MessageDisplay'; import { CustomizedSnackbars } from '../../Snackbar/Snackbar'; import { saveTempPublish } from '../../Chat/GroupAnnouncements'; +import { useTranslation } from 'react-i18next'; const uid = new ShortUniqueId({ length: 8 }); @@ -129,6 +128,7 @@ export const encryptSingleFunc = async (data: string, secretKeyObject: any) => { console.log(error); } }; + export const NewThread = ({ groupInfo, members, @@ -143,8 +143,8 @@ export const NewThread = ({ setPostReply, isPrivate, }: NewMessageProps) => { + const { t } = useTranslation(['core', 'group']); const { show } = React.useContext(MyContext); - const [isOpen, setIsOpen] = useState(false); const [value, setValue] = useState(''); const [isSending, setIsSending] = useState(false); @@ -183,21 +183,28 @@ export const NewThread = ({ const missingFields: string[] = []; if (!isMessage && !threadTitle) { - errorMsg = 'Please provide a thread title'; + errorMsg = t('group:question.provide_thread', { + postProcess: 'capitalize', + }); } if (!name) { - errorMsg = 'Cannot send a message without a access to your name'; + errorMsg = t('group:message.error.access_name', { + postProcess: 'capitalize', + }); } + if (!groupInfo) { - errorMsg = 'Cannot access group information'; - } // TODO translate + errorMsg = t('group:message.error.group_info', { + postProcess: 'capitalize', + }); + } // if (!description) missingFields.push('subject') if (missingFields.length > 0) { const missingFieldsString = missingFields.join(', '); const errMsg = `Missing: ${missingFieldsString}`; - errorMsg = errMsg; + errorMsg = errMsg; // TODO translate } if (errorMsg) { diff --git a/src/components/Group/Forum/ShowMessageWithoutModal.tsx b/src/components/Group/Forum/ShowMessageWithoutModal.tsx index 7a4e594..914b9c5 100644 --- a/src/components/Group/Forum/ShowMessageWithoutModal.tsx +++ b/src/components/Group/Forum/ShowMessageWithoutModal.tsx @@ -3,7 +3,6 @@ import { Avatar, Box, IconButton } from '@mui/material'; import DOMPurify from 'dompurify'; import FormatQuoteIcon from '@mui/icons-material/FormatQuote'; import MoreSVG from '../../../assets/svgs/More.svg'; - import { MoreImg, MoreP, @@ -38,16 +37,16 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { > @@ -67,6 +66,7 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { {message?.name?.charAt(0)} + { > {message?.name} + {formatTimestampForum(message?.created)} @@ -205,6 +206,7 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { > {message?.reply?.name?.charAt(0)} + { + diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 2bf2318..4c94e12 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -1,20 +1,11 @@ import React, { - FC, useCallback, useEffect, useMemo, useRef, useState, } from 'react'; -import { - Avatar, - Box, - Button, - ButtonBase, - IconButton, - Skeleton, - Typography, -} from '@mui/material'; +import { Avatar, Box, Button, ButtonBase, Typography } from '@mui/material'; import { ShowMessage } from './ShowMessageWithoutModal'; import { ComposeP, @@ -51,8 +42,12 @@ import { RequestQueueWithPromise } from '../../../utils/queue/queue'; import { CustomLoader } from '../../../common/CustomLoader'; import { WrapperUserAction } from '../../WrapperUserAction'; import { formatTimestampForum } from '../../../utils/time'; +import { useTranslation } from 'react-i18next'; + const requestQueueSaveToLocal = new RequestQueueWithPromise(1); + const requestQueueDownloadPost = new RequestQueueWithPromise(3); + interface ThreadProps { currentThread: any; groupInfo: any; @@ -120,6 +115,7 @@ export const Thread = ({ const [isLoading, setIsLoading] = useState(true); const [postReply, setPostReply] = useState(null); const [hasLastPage, setHasLastPage] = useState(false); + const { t } = useTranslation(['core']); // Update: Use a new ref for the scrollable container const threadContainerRef = useRef(null); @@ -251,6 +247,7 @@ export const Thread = ({ 'Content-Type': 'application/json', }, }); + const responseData = await response.json(); let fullArrayMsg = [...responseData]; @@ -431,6 +428,7 @@ export const Thread = ({ } const newArray = responseData.slice(0, findMessage).reverse(); let fullArrayMsg = [...messages]; + for (const message of newArray) { try { const responseDataMessage = await getEncryptedResource({ @@ -468,7 +466,6 @@ export const Thread = ({ setMessages(fullArrayMsg); } catch (error) { console.log(error); - } finally { } }, [messages] @@ -565,20 +562,20 @@ export const Thread = ({ return ( - Return to Threads + + {t('group:action.return_to_thread', { + postProcess: 'capitalize', + })} + {/* Conditionally render the scroll buttons */} {showScrollButton && @@ -658,6 +659,7 @@ export const Thread = ({ > {currentThread?.threadData?.title} + - First + {t('core:page.first', { postProcess: 'capitalize' })} + @@ -925,7 +928,7 @@ export const Thread = ({ color: 'white', }} > - Downloading from QDN + {t('core:downloading_qdn', { postProcess: 'capitalize' })} @@ -959,7 +962,9 @@ export const Thread = ({ color: 'white', }} > - Refetch page + {t('group:action.refetch_page', { + postProcess: 'capitalize', + })} @@ -997,7 +1002,7 @@ export const Thread = ({ disabled={!hasFirstPage} variant="contained" > - First + {t('core:page.first', { postProcess: 'capitalize' })} @@ -1063,7 +1068,7 @@ export const Thread = ({ diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index eaa89e6..17ee3e6 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -55,8 +55,6 @@ import { RequestQueueWithPromise } from '../../utils/queue/queue'; import { WebSocketActive } from './WebsocketActive'; import { useMessageQueue } from '../../MessageQueueContext'; import { ContextMenu } from '../ContextMenu'; -import { ReturnIcon } from '../../assets/Icons/ReturnIcon'; -import { ExitIcon } from '../../assets/Icons/ExitIcon'; import { HomeDesktop } from './HomeDesktop'; import { IconWrapper } from '../Desktop/DesktopFooter'; import { DesktopHeader } from '../Desktop/DesktopHeader'; @@ -80,6 +78,7 @@ import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { BlockedUsersModal } from './BlockedUsersModal'; import { WalletsAppWrapper } from './WalletsAppWrapper'; +import { useTranslation } from 'react-i18next'; export const getPublishesFromAdmins = async (admins: string[], groupId) => { const queryString = admins.map((name) => `name=${name}`).join('&'); @@ -450,6 +449,7 @@ export const Group = ({ const [isOpenSideViewGroups, setIsOpenSideViewGroups] = useState(false); const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = useState(false); + const { t } = useTranslation(['core', 'group']); const [groupsProperties, setGroupsProperties] = useRecoilState(groupsPropertiesAtom); @@ -2219,9 +2219,10 @@ export const Group = ({ color: theme.palette.text.primary, }} > - No group selected - {' '} - // TODO translate + {t('group:message.generic.no_selection', { + postProcess: 'capitalize', + })} + )} @@ -2317,9 +2318,9 @@ export const Group = ({ > {' '} - 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... + {t('group:message.generic.encryption_key', { + postProcess: 'capitalize', + })}
)} @@ -2343,18 +2344,23 @@ export const Group = ({ > {' '} - You are not part of the encrypted group of members. Wait - until an admin re-encrypts the keys. + {t('group:message.generic.not_part_group', { + postProcess: 'capitalize', + })} - Only unencrypted messages will be displayed. + {t('group:message.generic.only_encrypted', { + postProcess: 'capitalize', + })} - Try notifying an admin from the list of admins below: + {t('group:message.generic.notify_admins', { + postProcess: 'capitalize', + })} {adminsWithNames.map((admin) => { @@ -2374,7 +2380,9 @@ export const Group = ({ variant="contained" onClick={() => notifyAdmin(admin)} > - Notify + {t('core:action.notify', { + postProcess: 'capitalize', + })}
); @@ -2594,14 +2602,19 @@ export const Group = ({ open={isLoadingGroup} info={{ message: - isLoadingGroupMessage || 'Setting up group... please wait.', + isLoadingGroupMessage || + t('group:message.generic.setting_group', { + postProcess: 'capitalize', + }), }} /> diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index ecbf88a..9883911 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { useEffect, useState } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; @@ -12,14 +12,13 @@ import { CustomLoader } from '../../common/CustomLoader'; import { getBaseApiReact } from '../../App'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import { useTranslation } from 'react-i18next'; export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { - const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState( - [] - ); - const [isExpanded, setIsExpanded] = React.useState(false); + const [groupsWithJoinRequests, setGroupsWithJoinRequests] = useState([]); + const [isExpanded, setIsExpanded] = useState(false); - const [loading, setLoading] = React.useState(true); + const [loading, setLoading] = useState(true); const getJoinRequests = async () => { try { @@ -38,9 +37,10 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { } }; + const { t } = useTranslation(['core', 'group']); const theme = useTheme(); - React.useEffect(() => { + useEffect(() => { if (myAddress) { getJoinRequests(); } @@ -69,9 +69,9 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { - Group Invites{' '} + {t('group:group_invites', { postProcess: 'capitalize' })}{' '} {groupsWithJoinRequests?.length > 0 && ` (${groupsWithJoinRequests?.length})`} @@ -130,7 +130,9 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { fontWeight: 400, }} > - Nothing to display + {t('group:message.generic.no_display', { + postProcess: 'capitalize', + })}
)} @@ -177,7 +179,10 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { fontWeight: 400, }, }} - primary={`${group?.groupName} has invited you`} + primary={t('group:message.generic.group_invited_you', { + group: group?.groupName, + postProcess: 'capitalize', + })} /> diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 4bc6b21..c7bb32e 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -14,6 +14,7 @@ import { myGroupsWhereIAmAdminAtom } from '../../atoms/global'; import { useSetRecoilState } from 'recoil'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import { useTranslation } from 'react-i18next'; export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2); export const GroupJoinRequests = ({ @@ -27,7 +28,7 @@ export const GroupJoinRequests = ({ setDesktopViewMode, }) => { const [isExpanded, setIsExpanded] = React.useState(false); - + const { t } = useTranslation(['core', 'group']); const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState( [] ); @@ -139,9 +140,9 @@ export const GroupJoinRequests = ({ - Join Requests{' '} + {t('group:join_requests', { postProcess: 'capitalize' })}{' '} {filteredJoinRequests?.filter((group) => group?.data?.length > 0) ?.length > 0 && ` (${filteredJoinRequests?.filter((group) => group?.data?.length > 0)?.length})`} @@ -163,14 +164,13 @@ export const GroupJoinRequests = ({ {loading && filteredJoinRequests.length === 0 && ( @@ -204,18 +204,20 @@ export const GroupJoinRequests = ({ color: 'rgba(255, 255, 255, 0.2)', }} > - Nothing to display + {t('group:message.generic.no_display', { + postProcess: 'capitalize', + })} )} {filteredJoinRequests?.map((group) => { diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index de3dc20..db01b59 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -4,16 +4,21 @@ import { useState } from 'react'; import { Spacer } from '../../common/Spacer'; import { Label } from './AddGroup'; import { getFee } from '../../background'; +import { useTranslation } from 'react-i18next'; export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const [value, setValue] = useState(''); const [expiryTime, setExpiryTime] = useState('259200'); const [isLoadingInvite, setIsLoadingInvite] = useState(false); + const { t } = useTranslation(['core', 'group']); + const inviteMember = async () => { try { const fee = await getFee('GROUP_INVITE'); await show({ - message: 'Would you like to perform a GROUP_INVITE transaction?', + message: t('group:question.group_invite', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); setIsLoadingInvite(true); @@ -27,10 +32,12 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { }) .then((response) => { if (!response?.error) { - // TODO translate setInfoSnack({ type: 'success', - message: `Successfully invited ${value}. It may take a couple of minutes for the changes to propagate`, + message: t('group:message.success.group_invite', { + value: value, + postProcess: 'capitalize', + }), }); setOpenSnack(true); res(response); @@ -72,7 +79,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { flexDirection: 'column', }} > - Invite member + {t('group:action.invite_member', { postProcess: 'capitalize' })} { onChange={(e) => setValue(e.target.value)} /> - + { loading={isLoadingInvite} onClick={inviteMember} > - Invite + {t('core:action.invite', { postProcess: 'capitalize' })} ); diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 850ce13..355f1d8 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -18,6 +18,7 @@ import { getNameInfo } from './Group'; import { getFee } from '../../background'; import { LoadingButton } from '@mui/lab'; import { getBaseApiReact } from '../../App'; +import { useTranslation } from 'react-i18next'; export const getMemberInvites = async (groupNumber) => { const response = await fetch( @@ -55,6 +56,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(); const [isLoadingUnban, setIsLoadingUnban] = useState(false); + const { t } = useTranslation(['core', 'group']); const getInvites = async (groupId) => { try { @@ -84,10 +86,9 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const handleCancelBan = async (address) => { try { - // TODO translate const fee = await getFee('CANCEL_GROUP_BAN'); await show({ - message: 'Would you like to perform a CANCEL_GROUP_BAN transaction?', + message: t('group:question.cancel_ban', { postProcess: 'capitalize' }), publishFee: fee.fee + ' QORT', }); setIsLoadingUnban(true); @@ -103,8 +104,9 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { setIsLoadingUnban(false); setInfoSnack({ type: 'success', - message: - 'Successfully unbanned user. It may take a couple of minutes for the changes to propagate', + message: t('group:message.success.unbanned_user', { + postProcess: 'capitalize', + }), }); handlePopoverClose(); setOpenSnack(true); @@ -127,6 +129,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { }); }); } catch (error) { + console.log(error); } finally { setIsLoadingUnban(false); } @@ -177,10 +180,13 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { variant="contained" onClick={() => handleCancelBan(member?.offender)} > - Cancel Ban + {t('group:action.cancel_ban', { + postProcess: 'capitalize', + })} + handlePopoverOpen(event, index)} > @@ -205,7 +211,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { return (
-

Ban list

+

{t('group:ban_list', { postProcess: 'capitalize' })}

({ padding: 8, '& .MuiSwitch-track': { @@ -51,11 +44,11 @@ const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ }, })); -const Transition = React.forwardRef(function Transition( +const Transition = forwardRef(function Transition( props: TransitionProps & { - children: React.ReactElement; + children: ReactElement; }, - ref: React.Ref + ref: Ref ) { return ; }); @@ -118,12 +111,12 @@ export const Settings = ({ address, open, setOpen }) => { } }; - React.useEffect(() => { + useEffect(() => { getUserSettings(); }, []); return ( - + { )} - + ); }; diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index 6c315cd..84c806b 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -141,21 +141,6 @@ export const ThingsToDoInitial = ({ outline: '1px solid rgba(9, 182, 232, 1)', }} /> - {/* */} @@ -163,15 +148,6 @@ export const ThingsToDoInitial = ({ sx={{ marginBottom: '20px', }} - // secondaryAction={ - // - // - // - // } disablePadding > - {/* - - - - - - - - */} )} diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx index 111e216..c569425 100644 --- a/src/components/Group/WalletsAppWrapper.tsx +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -88,7 +88,7 @@ export const WalletsAppWrapper = () => { justifyContent: 'space-between', }} > - Q-Wallets // TODO translate + Q-Wallets { directs: sortedDirects, }) .catch((error) => { - // TODO translate console.error( 'Failed to handle active group data from socket:', error.message || 'An error occurred' diff --git a/src/components/Group/useBlockUsers.tsx b/src/components/Group/useBlockUsers.tsx index 178c358..4165260 100644 --- a/src/components/Group/useBlockUsers.tsx +++ b/src/components/Group/useBlockUsers.tsx @@ -17,7 +17,7 @@ export const useBlockedAddresses = () => { if (userBlockedRef.current[address]) return true; return false; } catch (error) { - //error + console.log(error); } }, []); @@ -42,10 +42,13 @@ export const useBlockedAddresses = () => { console.error('Failed qortalRequest', error); }); }); + const blockedUsers = {}; + response?.forEach((item) => { blockedUsers[item] = true; }); + userBlockedRef.current = blockedUsers; const response2 = await new Promise((res, rej) => { @@ -66,10 +69,13 @@ export const useBlockedAddresses = () => { console.error('Failed qortalRequest', error); }); }); + const blockedUsers2 = {}; + response2?.forEach((item) => { blockedUsers2[item] = true; }); + userNamesBlockedRef.current = blockedUsers2; } catch (error) { console.error(error); diff --git a/src/components/Group/useHandleUserInfo.tsx b/src/components/Group/useHandleUserInfo.tsx index 622e737..da7ee13 100644 --- a/src/components/Group/useHandleUserInfo.tsx +++ b/src/components/Group/useHandleUserInfo.tsx @@ -22,7 +22,7 @@ export const useHandleUserInfo = () => { }; return data?.level; } catch (error) { - //error + console.log(error); } }, []); diff --git a/src/components/Home/NewUsersCTA.tsx b/src/components/Home/NewUsersCTA.tsx index 30dea8a..44666d6 100644 --- a/src/components/Home/NewUsersCTA.tsx +++ b/src/components/Home/NewUsersCTA.tsx @@ -1,46 +1,52 @@ import { Box, ButtonBase, Typography } from '@mui/material'; import { Spacer } from '../../common/Spacer'; +import { useTranslation } from 'react-i18next'; export const NewUsersCTA = ({ balance }) => { + const { t } = useTranslation(['core']); + if (balance === undefined || +balance > 0) return null; + return ( - Are you a new user? - {' '} - // TODO translate - - - Please message us on Telegram or Discord if you need 4 QORT to start - chatting without any limitations + {t('core:new_user', { postProcess: 'capitalize' })} + + + + {t('core:message_us', { postProcess: 'capitalize' })} + + + + { > Telegram + { + const { i18n, t } = useTranslation(['core']); + const [showSelect, setShowSelect] = useState(false); + const theme = useTheme(); + const selectorRef = useRef(null); + + const handleChange = (e) => { + const newLang = e.target.value; + i18n.changeLanguage(newLang); + setShowSelect(false); + }; + + const currentLang = i18n.language; + const { name, flag } = + supportedLanguages[currentLang] || supportedLanguages['en']; + + // Detect clicks outside the component + useEffect(() => { + const handleClickOutside = (event) => { + if (selectorRef.current && !selectorRef.current.contains(event.target)) { + setShowSelect(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + return ( +
+ + {showSelect ? ( + + ) : ( + + )} + +
+ ); +}; + +export default LanguageSelector; diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 7187986..4c98fa1 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -265,7 +265,7 @@ export const Minting = ({ rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || 'An error occurred' }); //TODO translate + rej({ message: error.message || 'An error occurred' }); }); }); } catch (error) { @@ -280,7 +280,7 @@ export const Minting = ({ }, []); const createRewardShare = useCallback(async (publicKey, recipient) => { - const fee = await getFee('REWARD_SHARE'); + const fee = await getFee('REWARD_SHARE'); // TODO translate await show({ message: 'Would you like to perform an REWARD_SHARE transaction?', publishFee: fee.fee + ' QORT', diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index dd6f9be..b733261 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -62,6 +62,7 @@ export const QMailStatus = () => { color: theme.palette.text.primary, fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > {t('core:q_mail', { diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index eef881e..413b9a5 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -176,7 +176,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { .catch((error) => { rej( error.message || - t('core:result.error.generic', { postProcess: 'capitalize' }) + t('core:message.error.generic', { postProcess: 'capitalize' }) ); }); }); @@ -185,7 +185,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { setSettingsQdnLastUpdated(Date.now()); setInfoSnack({ type: 'success', - message: t('core:result.success.publish_qdn', { + message: t('core:message.success.publish_qdn', { postProcess: 'capitalize', }), }); @@ -198,7 +198,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { type: 'error', message: error?.message || - t('core:result.error.save_qdn', { + t('core:message.error.save_qdn', { postProcess: 'capitalize', }), }); @@ -591,7 +591,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { } }} > - {t('core:import', { + {t('core:action.import', { postProcess: 'capitalize', })}
@@ -616,7 +616,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { } }} > - {t('core:export', { + {t('core:action.export', { postProcess: 'capitalize', })} diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index 42b75c0..bab403c 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'; const ThemeSelector = () => { const { t } = useTranslation(['core']); + const { themeMode, toggleTheme } = useThemeContext(); return ( @@ -14,7 +15,7 @@ const ThemeSelector = () => { bottom: '1%', display: 'flex', gap: '12px', - left: '1.5vh', + left: '1.2vh', position: 'absolute', }} > diff --git a/src/components/Tutorials/Tutorials.tsx b/src/components/Tutorials/Tutorials.tsx index 06274e7..522324f 100644 --- a/src/components/Tutorials/Tutorials.tsx +++ b/src/components/Tutorials/Tutorials.tsx @@ -91,7 +91,7 @@ export const Tutorials = () => { @@ -138,7 +138,7 @@ export const Tutorials = () => { diff --git a/src/useQortalGetSaveSettings.tsx b/src/useQortalGetSaveSettings.tsx index 9e31853..c74f1a2 100644 --- a/src/useQortalGetSaveSettings.tsx +++ b/src/useQortalGetSaveSettings.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; import { canSaveSettingToQdnAtom, @@ -46,6 +46,7 @@ const getPublishRecord = async (myName) => { return { hasPublishRecord: false }; }; + const getPublish = async (myName) => { try { let data; @@ -57,7 +58,6 @@ const getPublish = async (myName) => { if (!data) throw new Error('Unable to fetch publish'); const decryptedKey: any = await decryptResource(data); - const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); return decryptedKeyToObject; @@ -112,6 +112,7 @@ export const useQortalGetSaveSettings = (myName, isAuthenticated) => { }, [] ); + useEffect(() => { if ( !myName || diff --git a/src/useRetrieveDataLocalStorage.tsx b/src/useRetrieveDataLocalStorage.tsx index fafcd5c..41c2b8e 100644 --- a/src/useRetrieveDataLocalStorage.tsx +++ b/src/useRetrieveDataLocalStorage.tsx @@ -1,55 +1,69 @@ -import React, { useCallback, useEffect } from 'react' +import { useCallback, useEffect } from 'react'; import { useSetRecoilState } from 'recoil'; -import { isUsingImportExportSettingsAtom, oldPinnedAppsAtom, settingsLocalLastUpdatedAtom, settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom } from './atoms/global'; +import { + isUsingImportExportSettingsAtom, + oldPinnedAppsAtom, + settingsLocalLastUpdatedAtom, + settingsQDNLastUpdatedAtom, + sortablePinnedAppsAtom, +} from './atoms/global'; function fetchFromLocalStorage(key) { - try { - const serializedValue = localStorage.getItem(key); - if (serializedValue === null) { - return null; - } - return JSON.parse(serializedValue); - } catch (error) { - console.error('Error fetching from localStorage:', error); - return null; + try { + const serializedValue = localStorage.getItem(key); + if (serializedValue === null) { + return null; } + return JSON.parse(serializedValue); + } catch (error) { + console.error('Error fetching from localStorage:', error); + return null; + } } export const useRetrieveDataLocalStorage = (address) => { - const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom); - const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom); - const setIsUsingImportExportSettings = useSetRecoilState(isUsingImportExportSettingsAtom) - const setSettingsQDNLastUpdated = useSetRecoilState(settingsQDNLastUpdatedAtom); - const setOldPinnedApps = useSetRecoilState(oldPinnedAppsAtom) + const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom); - const getSortablePinnedApps = useCallback(()=> { - const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings') - if(pinnedAppsLocal?.sortablePinnedApps){ - setSortablePinnedApps(pinnedAppsLocal?.sortablePinnedApps) - setSettingsLocalLastUpdated(pinnedAppsLocal?.timestamp || -1) - } else { - setSettingsLocalLastUpdated(-1) - } - - }, []) - const getSortablePinnedAppsImportExport = useCallback(()=> { - const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings_import_export') - if(pinnedAppsLocal?.sortablePinnedApps){ - setOldPinnedApps(pinnedAppsLocal?.sortablePinnedApps) - - - setIsUsingImportExportSettings(true) - setSettingsQDNLastUpdated(pinnedAppsLocal?.timestamp || 0) - - } else { - setIsUsingImportExportSettings(false) - } - - }, []) - useEffect(()=> { - - getSortablePinnedApps() - getSortablePinnedAppsImportExport() - }, [getSortablePinnedApps, address]) - -} + const setSettingsLocalLastUpdated = useSetRecoilState( + settingsLocalLastUpdatedAtom + ); + + const setIsUsingImportExportSettings = useSetRecoilState( + isUsingImportExportSettingsAtom + ); + + const setSettingsQDNLastUpdated = useSetRecoilState( + settingsQDNLastUpdatedAtom + ); + + const setOldPinnedApps = useSetRecoilState(oldPinnedAppsAtom); + + const getSortablePinnedApps = useCallback(() => { + const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings'); + + if (pinnedAppsLocal?.sortablePinnedApps) { + setSortablePinnedApps(pinnedAppsLocal?.sortablePinnedApps); + setSettingsLocalLastUpdated(pinnedAppsLocal?.timestamp || -1); + } else { + setSettingsLocalLastUpdated(-1); + } + }, []); + + const getSortablePinnedAppsImportExport = useCallback(() => { + const pinnedAppsLocal = fetchFromLocalStorage( + 'ext_saved_settings_import_export' + ); + if (pinnedAppsLocal?.sortablePinnedApps) { + setOldPinnedApps(pinnedAppsLocal?.sortablePinnedApps); + setIsUsingImportExportSettings(true); + setSettingsQDNLastUpdated(pinnedAppsLocal?.timestamp || 0); + } else { + setIsUsingImportExportSettings(false); + } + }, []); + + useEffect(() => { + getSortablePinnedApps(); + getSortablePinnedAppsImportExport(); + }, [getSortablePinnedApps, address]); +};