diff --git a/i18n.js b/i18n.js deleted file mode 100644 index 5277e59..0000000 --- a/i18n.js +++ /dev/null @@ -1,58 +0,0 @@ -import { initReactI18next } from 'react-i18next'; -import HttpBackend from 'i18next-http-backend'; -import LocalStorageBackend from 'i18next-localstorage-backend'; -import HttpApi from 'i18next-http-backend'; -import i18n from 'i18next'; -import LanguageDetector from 'i18next-browser-languagedetector'; - -// Detect environment -const isDev = process.env.NODE_ENV === 'development'; - -// Register custom postProcessor: it capitalizes the first letter of a translation- -// Usage: -// t('greeting', { postProcess: 'capitalize' }) -const capitalize = { - type: 'postProcessor', - name: 'capitalize', - process: (value) => { - return value.charAt(0).toUpperCase() + value.slice(1); - }, -}; - -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) - .use(initReactI18next) - .use(capitalize) - .init({ - backend: { - backends: [LocalStorageBackend, HttpBackend], - backendOptions: [ - { - expirationTime: 7 * 24 * 60 * 60 * 1000, // 7 days - }, - { - loadPath: '/locales/{{lng}}/{{ns}}.json', - }, - ], - }, - debug: isDev, - fallbackLng: 'en', - interpolation: { - escapeValue: false, - }, - lng: navigator.language, - ns: ['auth', 'core', 'group', 'tutorial'], - supportedLngs: Object.keys(supportedLanguages), - }); - -export default i18n; diff --git a/public/locales/de/core.json b/public/locales/de/core.json deleted file mode 100644 index c2b7a29..0000000 --- a/public/locales/de/core.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "add": "hinzufügen", - "cancel": "abbrechen", - "choose": "auswählen", - "close": "schließen", - "continue": "fortfahren", - "core": { - "block_height": "Blockhöhe", - "information": "Kerninformationen", - "peers": "verbundene Peers", - "version": "Kernversion" - }, - "description": "Beschreibung", - "edit": "bearbeiten", - "export": "exportieren", - "import": "importieren", - "last_height": "letzte Höhe", - "loading": "Lade...", - "logout": "abmelden", - "minting_status": "Präge-Status", - "payment_notification": "Zahlungsbenachrichtigung", - "price": "Preis", - "q_mail": "Q-Mail", - "result": { - "error": { - "generic": "Ein Fehler ist aufgetreten", - "incorrect_password": "Falsches Passwort", - "save_qdn": "Speichern in QDN nicht möglich" - }, - "status": { - "minting": "(Prägung)", - "not_minting": "(keine Prägung)", - "synchronized": "synchronisiert", - "synchronizing": "synchronisiere" - }, - "success": { - "publish_qdn": "Erfolgreich in QDN veröffentlicht" - } - }, - "save_options": { - "no_pinned_changes": "Derzeit keine Änderungen an Ihren angehefteten Apps", - "overwrite_changes": "Die App konnte Ihre vorhandenen in QDN gespeicherten angehefteten Apps nicht herunterladen. Möchten Sie diese Änderungen überschreiben?", - "overwrite_qdn": "In QDN überschreiben", - "publish_qdn": "Möchten Sie Ihre Einstellungen in QDN (verschlüsselt) veröffentlichen?", - "qdn": "QDN-Speicherung verwenden", - "register_name": "Sie benötigen einen registrierten Qortal-Namen, um Ihre angehefteten Apps in QDN zu speichern.", - "reset_pinned": "Gefällt Ihnen Ihre aktuellen lokalen Änderungen nicht? Möchten Sie zu den Standard-Anheftungen zurückkehren?", - "reset_qdn": "Gefällt Ihnen Ihre aktuellen lokalen Änderungen nicht? Möchten Sie zu Ihren in QDN gespeicherten Anheftungen zurückkehren?", - "revert_default": "Auf Standard zurücksetzen", - "revert_qdn": "Auf QDN zurücksetzen", - "save_qdn": "In QDN speichern", - "save": "speichern", - "settings": "Sie verwenden die Export/Import-Methode zum Speichern von Einstellungen.", - "unsaved_changes": "Sie haben nicht gespeicherte Änderungen an Ihren angehefteten Apps. Speichern Sie sie in QDN." - }, - "settings": "Einstellungen", - "supply": "Angebot", - "theme": { - "dark": "Dunkelmodus", - "light": "Hellmodus" - }, - "title": "Titel", - "tutorial": "Tutorial", - "user_lookup": "Benutzersuche", - "wallet": { - "backup_wallet": "Wallet sichern", - "wallet": "Wallet", - "wallet_other": "Wallets" - }, - "welcome": "Willkommen" -} diff --git a/public/locales/en/group.json b/public/locales/en/group.json deleted file mode 100644 index 345c6d0..0000000 --- a/public/locales/en/group.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "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/es/core.json b/public/locales/es/core.json deleted file mode 100644 index 78b06e9..0000000 --- a/public/locales/es/core.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "add": "agregar", - "cancel": "cancelar", - "choose": "elegir", - "close": "cerrar", - "continue": "continuar", - "core": { - "block_height": "altura de bloque", - "information": "información del núcleo", - "peers": "pares conectados", - "version": "versión del núcleo" - }, - "description": "descripción", - "edit": "editar", - "export": "exportar", - "import": "importar", - "last_height": "última altura", - "loading": "cargando...", - "logout": "cerrar sesión", - "minting_status": "estado de acuñación", - "payment_notification": "notificación de pago", - "price": "precio", - "q_mail": "q-mail", - "result": { - "error": { - "generic": "ocurrió un error", - "incorrect_password": "contraseña incorrecta", - "save_qdn": "no se pudo guardar en QDN" - }, - "status": { - "minting": "(acuñando)", - "not_minting": "(no acuñando)", - "synchronized": "sincronizado", - "synchronizing": "sincronizando" - }, - "success": { - "publish_qdn": "publicado exitosamente en QDN" - } - }, - "save_options": { - "no_pinned_changes": "actualmente no tienes cambios en tus aplicaciones fijadas", - "overwrite_changes": "la aplicación no pudo descargar tus aplicaciones fijadas existentes guardadas en QDN. ¿Deseas sobrescribir esos cambios?", - "overwrite_qdn": "sobrescribir en QDN", - "publish_qdn": "¿Deseas publicar tus configuraciones en QDN (cifrado)?", - "qdn": "usar guardado en QDN", - "register_name": "necesitas un nombre Qortal registrado para guardar tus aplicaciones fijadas en QDN.", - "reset_pinned": "¿No te gustan tus cambios locales actuales? ¿Deseas restablecer las aplicaciones fijadas predeterminadas?", - "reset_qdn": "¿No te gustan tus cambios locales actuales? ¿Deseas restablecer tus aplicaciones fijadas guardadas en QDN?", - "revert_default": "restablecer a predeterminado", - "revert_qdn": "restablecer a QDN", - "save_qdn": "guardar en QDN", - "save": "guardar", - "settings": "estás utilizando el método de exportación/importación para guardar configuraciones.", - "unsaved_changes": "tienes cambios no guardados en tus aplicaciones fijadas. Guárdalos en QDN." - }, - "settings": "configuraciones", - "supply": "suministro", - "theme": { - "dark": "modo oscuro", - "light": "modo claro" - }, - "title": "título", - "tutorial": "tutorial", - "user_lookup": "búsqueda de usuario", - "wallet": { - "backup_wallet": "respaldar billetera", - "wallet": "billetera", - "wallet_other": "billeteras" - }, - "welcome": "bienvenido" -} diff --git a/public/locales/fr/core.json b/public/locales/fr/core.json deleted file mode 100644 index 61d231d..0000000 --- a/public/locales/fr/core.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "add": "ajouter", - "cancel": "annuler", - "choose": "choisir", - "close": "fermer", - "continue": "continuer", - "core": { - "block_height": "hauteur de bloc", - "information": "informations du noyau", - "peers": "pairs connectés", - "version": "version du noyau" - }, - "description": "description", - "edit": "éditer", - "export": "exporter", - "import": "importer", - "last_height": "dernière hauteur", - "loading": "chargement...", - "logout": "se déconnecter", - "minting_status": "statut de frappe", - "payment_notification": "notification de paiement", - "price": "prix", - "q_mail": "q-mail", - "result": { - "error": { - "generic": "une erreur s'est produite", - "incorrect_password": "mot de passe incorrect", - "save_qdn": "impossible d'enregistrer dans QDN" - }, - "status": { - "minting": "(frappe en cours)", - "not_minting": "(pas de frappe)", - "synchronized": "synchronisé", - "synchronizing": "synchronisation en cours" - }, - "success": { - "publish_qdn": "publié avec succès dans QDN" - } - }, - "save_options": { - "no_pinned_changes": "vous n'avez actuellement aucune modification de vos applications épinglées", - "overwrite_changes": "l'application n'a pas pu télécharger vos applications épinglées existantes enregistrées dans QDN. Voulez-vous écraser ces modifications ?", - "overwrite_qdn": "écraser dans QDN", - "publish_qdn": "souhaitez-vous publier vos paramètres dans QDN (chiffré) ?", - "qdn": "utiliser l'enregistrement QDN", - "register_name": "vous devez avoir un nom Qortal enregistré pour enregistrer vos applications épinglées dans QDN.", - "reset_pinned": "vous n'aimez pas vos modifications locales actuelles ? Voulez-vous réinitialiser les applications épinglées par défaut ?", - "reset_qdn": "vous n'aimez pas vos modifications locales actuelles ? Voulez-vous réinitialiser vos applications épinglées enregistrées dans QDN ?", - "revert_default": "revenir aux paramètres par défaut", - "revert_qdn": "revenir à QDN", - "save_qdn": "enregistrer dans QDN", - "save": "enregistrer", - "settings": "vous utilisez la méthode d'exportation/importation pour enregistrer les paramètres.", - "unsaved_changes": "vous avez des modifications non enregistrées de vos applications épinglées. Enregistrez-les dans QDN." - }, - "settings": "paramètres", - "supply": "approvisionnement", - "theme": { - "dark": "mode sombre", - "light": "mode clair" - }, - "title": "titre", - "tutorial": "tutoriel", - "user_lookup": "recherche d'utilisateur", - "wallet": { - "backup_wallet": "sauvegarder le portefeuille", - "wallet": "portefeuille", - "wallet_other": "portefeuilles" - }, - "welcome": "bienvenue" -} diff --git a/public/locales/it/group.json b/public/locales/it/group.json deleted file mode 100644 index 862496d..0000000 --- a/public/locales/it/group.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "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/public/locales/ru/core.json b/public/locales/ru/core.json deleted file mode 100644 index 04bf898..0000000 --- a/public/locales/ru/core.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "add": "добавить", - "cancel": "отмена", - "choose": "выбрать", - "close": "закрыть", - "continue": "продолжить", - "core": { - "block_height": "высота блока", - "information": "информация ядра", - "peers": "подключенные узлы", - "version": "версия ядра" - }, - "description": "описание", - "edit": "редактировать", - "export": "экспорт", - "import": "импорт", - "last_height": "последняя высота", - "loading": "загрузка...", - "logout": "выйти", - "minting_status": "статус чеканки", - "payment_notification": "уведомление о платеже", - "price": "цена", - "q_mail": "q-mail", - "result": { - "error": { - "generic": "произошла ошибка", - "incorrect_password": "неверный пароль", - "save_qdn": "не удалось сохранить в QDN" - }, - "status": { - "minting": "(чеканка)", - "not_minting": "(не чеканится)", - "synchronized": "синхронизировано", - "synchronizing": "синхронизация" - }, - "success": { - "publish_qdn": "успешно опубликовано в QDN" - } - }, - "save_options": { - "no_pinned_changes": "у вас нет изменений в закреплённых приложениях", - "overwrite_changes": "приложению не удалось загрузить ваши закреплённые приложения, сохранённые в QDN. Хотите перезаписать эти изменения?", - "overwrite_qdn": "перезаписать в QDN", - "publish_qdn": "хотите опубликовать свои настройки в QDN (зашифровано)?", - "qdn": "использовать сохранение в QDN", - "register_name": "вам необходимо зарегистрированное имя Qortal для сохранения закреплённых приложений в QDN.", - "reset_pinned": "не устраивают текущие локальные изменения? Хотите сбросить до приложений по умолчанию?", - "reset_qdn": "не устраивают текущие локальные изменения? Хотите сбросить до сохранённых в QDN приложений?", - "revert_default": "сбросить до стандартных", - "revert_qdn": "сбросить до QDN", - "save_qdn": "сохранить в QDN", - "save": "сохранить", - "settings": "вы используете метод экспорта/импорта для сохранения настроек.", - "unsaved_changes": "у вас есть несохранённые изменения в закреплённых приложениях. Сохраните их в QDN." - }, - "settings": "настройки", - "supply": "предложение", - "theme": { - "dark": "тёмная тема", - "light": "светлая тема" - }, - "title": "заголовок", - "tutorial": "учебник", - "user_lookup": "поиск пользователя", - "wallet": { - "backup_wallet": "резервная копия кошелька", - "wallet": "кошелёк", - "wallet_other": "кошельки" - }, - "welcome": "добро пожаловать" -} diff --git a/src/App.tsx b/src/App.tsx index ad1f496..d24a4ae 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1028,12 +1028,7 @@ function App() { const logoutFunc = useCallback(async () => { try { - if (hasSettingsChanged) { - 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') { + if (extState === 'authenticated') { await showUnsavedChanges({ message: 'Are you sure you would like to logout?', }); @@ -2074,6 +2069,8 @@ function App() { lineHeight: 1.2, maxWidth: '90%', textAlign: 'center', + fontSize: '16px', + marginBottom: '10px', }} > {messageQortalRequest?.text1} @@ -3012,13 +3009,16 @@ function App() { })} - { returnToMain(); }} > - {t('core:action.continue', { postProcess: 'capitalize' })} - + + {t('core:action.continue', { postProcess: 'capitalize' })} + + )} {extState === 'transfer-success-request' && ( @@ -3268,6 +3268,8 @@ function App() { lineHeight: 1.2, maxWidth: '90%', textAlign: 'center', + fontSize: '16px', + marginBottom: '10px', }} > {messageQortalRequestExtension?.text1} @@ -3314,8 +3316,8 @@ function App() { > {messageQortalRequestExtension?.text3} - + )} @@ -3340,11 +3342,15 @@ function App() { )} {messageQortalRequestExtension?.html && ( -
+ <> + + +
+ )} @@ -3564,7 +3570,11 @@ function App() { )} {isSettingsOpen && ( - + )} { const theme = useTheme(); - const setColor = color ? color : theme.palette.text.primary; + return ( { xmlns="http://www.w3.org/2000/svg" > ); diff --git a/src/background.ts b/src/background.ts index 93a5e28..b34a01b 100644 --- a/src/background.ts +++ b/src/background.ts @@ -937,6 +937,29 @@ export async function getBalanceInfo() { return data; } +export async function getAssetBalanceInfo(assetId: number) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const validApi = await getBaseApi(); + const response = await fetch( + validApi + + `/assets/balances?address=${address}&assetid=${assetId}&ordering=ASSET_BALANCE_ACCOUNT&limit=1` + ); + + if (!response?.ok) throw new Error('Cannot fetch asset balance'); + const data = await response.json(); + return +data?.[0]?.balance; +} + +export async function getAssetInfo(assetId: number) { + const validApi = await getBaseApi(); + const response = await fetch(validApi + `/assets/info?assetId=${assetId}`); + + if (!response?.ok) throw new Error('Cannot fetch asset info'); + const data = await response.json(); + return data; +} + export async function getLTCBalance() { const wallet = await getSaveWallet(); let _url = `${buyTradeNodeBaseUrl}/crosschain/ltc/walletbalance`; @@ -2288,6 +2311,34 @@ export async function kickFromGroup({ return res; } +export async function transferAsset({ amount, recipient, assetId }) { + const lastReference = await getLastRef(); + 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, + }; + const feeres = await getFee('TRANSFER_ASSET'); + + const tx = await createTransaction(12, keyPair, { + fee: feeres.fee, + recipient: recipient, + amount: amount, + assetId: assetId, + lastReference: lastReference, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error(res?.message || 'Transaction was not able to be processed'); + return res; +} + export async function createGroup({ groupName, groupDescription, @@ -3638,7 +3689,7 @@ export const checkThreads = async (bringBack) => { dataToBringBack.push(thread); } } catch (error) { - conosle.log({ error }); + console.log({ error }); } } diff --git a/src/common/CustomLoader.tsx b/src/common/CustomLoader.tsx index d1ae3b2..84dbfac 100644 --- a/src/common/CustomLoader.tsx +++ b/src/common/CustomLoader.tsx @@ -1,6 +1,6 @@ -import React from 'react'; import './customloader.css'; import { Box, useTheme } from '@mui/material'; + export const CustomLoader = () => { const theme = useTheme(); return ( diff --git a/src/common/CustomSvg.tsx b/src/common/CustomSvg.tsx index c1d8f09..fc805a9 100644 --- a/src/common/CustomSvg.tsx +++ b/src/common/CustomSvg.tsx @@ -1,17 +1,14 @@ -import React from 'react'; - export const CustomSvg = ({ src, color = 'black', size = 24 }) => { - return ( - - {src} - - ); - }; - \ No newline at end of file + return ( + + {src} + + ); +}; diff --git a/src/common/customloader.css b/src/common/customloader.css index dd00558..c16d21d 100644 --- a/src/common/customloader.css +++ b/src/common/customloader.css @@ -36,6 +36,7 @@ left: 56px; animation: lds-ellipsis3 0.6s infinite; } + @keyframes lds-ellipsis1 { 0% { transform: scale(0); diff --git a/src/common/useFetchResources.tsx b/src/common/useFetchResources.tsx index 242f418..8e388a7 100644 --- a/src/common/useFetchResources.tsx +++ b/src/common/useFetchResources.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef } from 'react'; +import { useCallback } from 'react'; import { resourceDownloadControllerAtom } from '../atoms/global'; import { getBaseApiReact } from '../App'; import { useSetAtom } from 'jotai'; diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index 754ba85..39f12ee 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -56,7 +56,7 @@ export const AppsCategoryDesktop = ({ isShow, }) => { const [searchValue, setSearchValue] = useState(''); - const virtuosoRef = useRef(); + const virtuosoRef = useRef(null); const theme = useTheme(); const categoryList = useMemo(() => { diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index b5a5cba..80f57b9 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -414,8 +414,23 @@ export const AppsDesktop = ({ setDesktopViewMode('dev'); }} > - - + + )} diff --git a/src/components/Apps/AppsDevModeNavBar.tsx b/src/components/Apps/AppsDevModeNavBar.tsx index 1c0422a..1bfcf42 100644 --- a/src/components/Apps/AppsDevModeNavBar.tsx +++ b/src/components/Apps/AppsDevModeNavBar.tsx @@ -180,7 +180,7 @@ export const AppsDevModeNavBar = () => { > { const [searchValue, setSearchValue] = useState(''); - const virtuosoRef = useRef(); + const virtuosoRef = useRef(null); const theme = useTheme(); const officialApps = useMemo(() => { diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index 8770807..b1736a2 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -253,6 +253,9 @@ export const listOfAllQortalRequests = [ 'SELL_NAME', 'CANCEL_SELL_NAME', 'BUY_NAME', + 'SIGN_FOREIGN_FEES', + 'MULTI_ASSET_PAYMENT_WITH_PRIVATE_DATA', + 'TRANSFER_ASSET', ]; export const UIQortalRequests = [ @@ -313,6 +316,9 @@ export const UIQortalRequests = [ 'SELL_NAME', 'CANCEL_SELL_NAME', 'BUY_NAME', + 'SIGN_FOREIGN_FEES', + 'MULTI_ASSET_PAYMENT_WITH_PRIVATE_DATA', + 'TRANSFER_ASSET', ]; async function retrieveFileFromIndexedDB(fileId) { diff --git a/src/components/Auth/DownloadWallet.tsx b/src/components/Auth/DownloadWallet.tsx index 838ea2c..cbf5e49 100644 --- a/src/components/Auth/DownloadWallet.tsx +++ b/src/components/Auth/DownloadWallet.tsx @@ -63,6 +63,7 @@ export const DownloadWallet = ({ wallet, qortAddress: rawWallet.address0, }); + return { wallet, qortAddress: rawWallet.address0, diff --git a/src/components/Chat/AnnouncementList.tsx b/src/components/Chat/AnnouncementList.tsx index 9c32cc1..5f3c75b 100644 --- a/src/components/Chat/AnnouncementList.tsx +++ b/src/components/Chat/AnnouncementList.tsx @@ -18,7 +18,7 @@ export const AnnouncementList = ({ loadMore, myName, }) => { - const listRef = useRef(); + const listRef = useRef(null); const [messages, setMessages] = useState(initialMessages); useEffect(() => { diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 56acbc7..8362fad 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -23,7 +23,7 @@ export const ChatList = ({ hasSecretKey, isPrivate, }) => { - const parentRef = useRef(); + const parentRef = useRef(null); const [messages, setMessages] = useState(initialMessages); const [showScrollButton, setShowScrollButton] = useState(false); const [showScrollDownButton, setShowScrollDownButton] = useState(false); diff --git a/src/components/Chat/ChatOptions.tsx b/src/components/Chat/ChatOptions.tsx index 4f92f53..0ec1866 100644 --- a/src/components/Chat/ChatOptions.tsx +++ b/src/components/Chat/ChatOptions.tsx @@ -60,8 +60,8 @@ export const ChatOptions = ({ const [searchValue, setSearchValue] = useState(''); const [selectedMember, setSelectedMember] = useState(0); const theme = useTheme(); - const parentRef = useRef(); - const parentRefMentions = useRef(); + const parentRef = useRef(null); + const parentRefMentions = useRef(null); const [lastMentionTimestamp, setLastMentionTimestamp] = useState(null); const [debouncedValue, setDebouncedValue] = useState(''); // Debounced value const messages = useMemo(() => { diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 9ac3ded..c91bfaa 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -31,6 +31,7 @@ import { import { RequestQueueWithPromise } from '../../utils/queue/queue'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { addDataPublishesFunc, getDataPublishesFunc } from '../Group/Group'; +import { useTranslation } from 'react-i18next'; const uid = new ShortUniqueId({ length: 8 }); @@ -101,6 +102,7 @@ export const decryptPublishes = async (encryptedMessages: any[], secretKey) => { console.log(error); } }; + export const handleUnencryptedPublishes = (publishes) => { let publishesData = []; publishes.forEach((pub) => { @@ -149,6 +151,7 @@ export const GroupAnnouncements = ({ editorRef.current = editorInstance; }; const [, forceUpdate] = React.useReducer((x) => x + 1, 0); + const { t } = useTranslation(['core', 'group']); const triggerRerender = () => { forceUpdate(); // Trigger re-render by updating the state @@ -209,7 +212,6 @@ export const GroupAnnouncements = ({ ) return; setIsLoading(true); - // initWebsocketMessageGroup() hasInitializedWebsocket.current = true; }, [secretKey, isPrivate]); @@ -287,7 +289,10 @@ export const GroupAnnouncements = ({ const fee = await getFee('ARBITRARY'); await show({ - message: 'Would you like to perform a ARBITRARY transaction?', + message: t('group:question.perform_transaction', { + action: 'ARBITRARY', + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); @@ -329,7 +334,7 @@ export const GroupAnnouncements = ({ setTempData(selectedGroup); clearEditorContent(); } - // send chat message + // TODO send chat message } catch (error) { if (!error) return; setInfoSnack({ diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index cf39375..79e22d2 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -6,6 +6,7 @@ import { getBaseApiReact } from '../App'; import '../styles/CoreSyncStatus.css'; import { useTheme } from '@mui/material'; import { useTranslation } from 'react-i18next'; +import { manifestData } from '../ExtStates/NotAuthenticated'; export const CoreSyncStatus = () => { const [nodeInfos, setNodeInfos] = useState({}); @@ -75,7 +76,9 @@ export const CoreSyncStatus = () => { : ''; let imagePath = syncingImg; - let message = t('core:status.synchronizing', { postProcess: 'capitalize' }); + let message = t('core:message.status.synchronizing', { + postProcess: 'capitalize', + }); if (isMintingPossible && !isUsingGateway) { imagePath = syncedMintingImg; @@ -111,7 +114,7 @@ export const CoreSyncStatus = () => {
{ }} >

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

+

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

+

{message}

+

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

+

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

+

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

+ +

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

); diff --git a/src/components/Desktop/DesktopFooter.tsx b/src/components/Desktop/DesktopFooter.tsx index d85c8a0..f3912ba 100644 --- a/src/components/Desktop/DesktopFooter.tsx +++ b/src/components/Desktop/DesktopFooter.tsx @@ -3,10 +3,8 @@ import Box from '@mui/material/Box'; import { HubsIcon } from '../../assets/Icons/HubsIcon'; import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; import AppIcon from '../../assets/svgs/AppIcon.svg'; - import { HomeIcon } from '../../assets/Icons/HomeIcon'; import { Save } from '../Save/Save'; - import { enabledDevModeAtom } from '../../atoms/global'; import { useAtom } from 'jotai'; diff --git a/src/components/Desktop/DesktopHeader.tsx b/src/components/Desktop/DesktopHeader.tsx index 0b6cff8..af501a4 100644 --- a/src/components/Desktop/DesktopHeader.tsx +++ b/src/components/Desktop/DesktopHeader.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { useState } from 'react'; import { ButtonBase, Typography, useTheme } from '@mui/material'; import Box from '@mui/material/Box'; import { NotificationIcon2 } from '../../assets/Icons/NotificationIcon2'; @@ -81,18 +81,18 @@ export const DesktopHeader = ({ setGroupSection, isPrivate, }) => { - const [value, setValue] = React.useState(0); + const [value, setValue] = useState(0); const theme = useTheme(); return ( + @@ -219,6 +220,7 @@ export const DesktopHeader = ({ /> + { setOpenManageMembers(true); @@ -226,17 +228,18 @@ export const DesktopHeader = ({ > + { setGroupSection('adminSpace'); diff --git a/src/components/Drawer/Drawer.tsx b/src/components/Drawer/Drawer.tsx index 65f1250..a6491a7 100644 --- a/src/components/Drawer/Drawer.tsx +++ b/src/components/Drawer/Drawer.tsx @@ -1,5 +1,6 @@ import Box from '@mui/material/Box'; import Drawer from '@mui/material/Drawer'; + export const DrawerComponent = ({ open, setOpen, children }) => { const toggleDrawer = (newOpen: boolean) => () => { setOpen(newOpen); diff --git a/src/components/Explore/Explore.tsx b/src/components/Explore/Explore.tsx index 266fdd5..3da3cf5 100644 --- a/src/components/Explore/Explore.tsx +++ b/src/components/Explore/Explore.tsx @@ -40,6 +40,7 @@ export const Explore = ({ setDesktopViewMode }) => { }} src={qTradeLogo} /> + { color: theme.palette.text.primary, }} /> + { color: theme.palette.text.primary, }} /> + { {t('tutorial:initial.general_chat', { postProcess: 'capitalize' })} + { color: theme.palette.text.primary, }} /> + { diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx index 6503de4..a56d642 100644 --- a/src/components/GlobalActions/JoinGroup.tsx +++ b/src/components/GlobalActions/JoinGroup.tsx @@ -1,8 +1,7 @@ -import React, { useContext, useEffect, useMemo, useState } from 'react'; +import { useContext, useEffect, useMemo, useState } from 'react'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { Box, - Button, ButtonBase, CircularProgress, Dialog, @@ -11,13 +10,14 @@ import { Typography, useTheme, } from '@mui/material'; -import { CustomButton, CustomButtonAccept } from '../../styles/App-styles'; +import { CustomButtonAccept } from '../../styles/App-styles'; import { getBaseApiReact, MyContext } from '../../App'; import { getFee } from '../../background'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { FidgetSpinner } from 'react-loader-spinner'; import { useAtom, useSetAtom } from 'jotai'; import { memberGroupsAtom, txListAtom } from '../../atoms/global'; +import { useTranslation } from 'react-i18next'; export const JoinGroup = () => { const { show } = useContext(MyContext); @@ -29,7 +29,9 @@ export const JoinGroup = () => { const [isLoadingInfo, setIsLoadingInfo] = useState(false); const [isOpen, setIsOpen] = useState(false); const theme = useTheme(); + const { t } = useTranslation(['core', 'group']); const [isLoadingJoinGroup, setIsLoadingJoinGroup] = useState(false); + const handleJoinGroup = async (e) => { setGroupInfo(null); const groupId = e?.detail?.groupId; @@ -41,6 +43,7 @@ export const JoinGroup = () => { const groupData = await response.json(); setGroupInfo(groupData); } catch (error) { + console.log(error); } finally { setIsLoadingInfo(false); } @@ -60,15 +63,22 @@ export const JoinGroup = () => { (item) => +item?.groupId === +groupInfo?.groupId ); }, [memberGroups, groupInfo]); + const joinGroup = async (group, isOpen) => { try { const groupId = group.groupId; const fee = await getFee('JOIN_GROUP'); + await show({ - message: 'Would you like to perform an JOIN_GROUP transaction?', + message: t('group:question.perform_transaction', { + action: 'JOIN_GROUP', + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); + setIsLoadingJoinGroup(true); + await new Promise((res, rej) => { window .sendMessage('joinGroup', { @@ -78,8 +88,9 @@ export const JoinGroup = () => { 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) { @@ -87,8 +98,14 @@ export const JoinGroup = () => { { ...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, }, @@ -99,15 +116,20 @@ export const JoinGroup = () => { { ...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); res(response); return; @@ -123,7 +145,9 @@ export const JoinGroup = () => { .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); @@ -131,10 +155,12 @@ export const JoinGroup = () => { }); setIsLoadingJoinGroup(false); } catch (error) { + console.log(error); } finally { setIsLoadingJoinGroup(false); } }; + return ( <> { {!groupInfo && ( - {' '} {' '} + /> )} { fontWeight: 600, }} > - Group name: {` ${groupInfo?.groupName}`} + {t('group:group.name', { postProcess: 'capitalize' })}:{' '} + {` ${groupInfo?.groupName}`} + - Number of members: {` ${groupInfo?.memberCount}`} + {t('group:group.member_number', { postProcess: 'capitalize' })}:{' '} + {` ${groupInfo?.memberCount}`} + {groupInfo?.description && ( { fontWeight: 600, }} > - *You are already in this group! + {t('group:message.generic.already_in_group', { + postProcess: 'capitalize', + })} )} {!isInGroup && groupInfo?.isOpen === false && ( @@ -217,12 +248,14 @@ export const JoinGroup = () => { 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', + })} )} + { @@ -242,7 +275,9 @@ export const JoinGroup = () => { opacity: isInGroup ? 0.1 : 1, }} > - Join + {t('core:action.join', { + postProcess: 'capitalize', + })} @@ -255,7 +290,9 @@ export const JoinGroup = () => { }} onClick={() => setIsOpen(false)} > - Close + {t('core:action.close', { + postProcess: 'capitalize', + })} @@ -269,14 +306,14 @@ export const JoinGroup = () => { {isLoadingJoinGroup && ( { const fee = await getFee('CREATE_GROUP'); await show({ - message: t('group:question.create_group', { + message: t('group:question.perform_transaction', { + action: 'CREATE_GROUP', postProcess: 'capitalize', }), publishFee: fee.fee + ' QORT', @@ -159,6 +160,9 @@ export const AddGroup = ({ address, open, setOpen }) => { }, ...prev, ]); + setName(''); + setDescription(''); + setGroupType('1'); res(response); return; } @@ -327,6 +331,7 @@ export const AddGroup = ({ address, open, setOpen }) => { postProcess: 'capitalize', })} + { onChange={(e) => setName(e.target.value)} /> + { onChange={handleChangeApprovalThreshold} > - {t('core.count.none', { + {t('core:count.none', { postProcess: 'capitalize', })} - {t('core.count.one', { + {t('core:count.one', { postProcess: 'capitalize', })} @@ -446,6 +452,7 @@ export const AddGroup = ({ address, open, setOpen }) => { 100% + { }} > + + { }} > + + { color="primary" onClick={handleCreateGroup} > - {t('group.action.create', { + {t('group:action.create_group', { postProcess: 'capitalize', })} diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index 971f6e0..49dce3d 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -48,7 +48,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { 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 - const listRef = useRef(); + const listRef = useRef(null); const [inputValue, setInputValue] = useState(''); const [filteredItems, setFilteredItems] = useState(groups); const [isLoading, setIsLoading] = useState(false); @@ -113,7 +113,8 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const fee = await getFee('JOIN_GROUP'); await show({ - message: t('group:question.join_group', { + message: t('group:question.perform_transaction', { + action: 'JOIN_GROUP', postProcess: 'capitalize', }), publishFee: fee.fee + ' QORT', @@ -157,8 +158,14 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { { ...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, }, diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 60bfd12..41f75ef 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -1,26 +1,17 @@ -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Avatar, Box, Popover, Typography, useTheme } from '@mui/material'; -// import { MAIL_SERVICE_TYPE, THREAD_SERVICE_TYPE } from "../../constants/mail"; import { Thread } from './Thread'; import { AllThreadP, ArrowDownIcon, ComposeContainer, ComposeContainerBlank, - ComposeIcon, ComposeP, GroupContainer, InstanceFooter, InstanceListContainer, InstanceListContainerRow, InstanceListContainerRowCheck, - InstanceListContainerRowCheckIcon, InstanceListContainerRowMain, InstanceListContainerRowMainP, InstanceListHeader, @@ -48,7 +39,6 @@ import { getTempPublish, handleUnencryptedPublishes, } from '../../Chat/GroupAnnouncements'; -import CheckSVG from '../../../assets/svgs/Check.svg'; import ArrowDownSVG from '../../../assets/svgs/ArrowDown.svg'; import { LoadingSnackbar } from '../../Snackbar/LoadingSnackbar'; import { executeEvent } from '../../../utils/events'; @@ -73,9 +63,9 @@ export const GroupMail = ({ hide, isPrivate, }) => { - const [viewedThreads, setViewedThreads] = React.useState({}); + const [viewedThreads, setViewedThreads] = useState({}); const [filterMode, setFilterMode] = useState('Recently active'); - const [currentThread, setCurrentThread] = React.useState(null); + const [currentThread, setCurrentThread] = useState(null); const [recentThreads, setRecentThreads] = useState([]); const [allThreads, setAllThreads] = useState([]); const [members, setMembers] = useState(null); @@ -178,7 +168,10 @@ export const GroupMail = ({ rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }) + ); }); }); } catch (error) { @@ -186,7 +179,7 @@ export const GroupMail = ({ } }; - const getAllThreads = React.useCallback( + const getAllThreads = useCallback( async (groupId: string, mode: string, isInitial?: boolean) => { try { setIsLoading(true); @@ -206,7 +199,7 @@ export const GroupMail = ({ }); const responseData = await response.json(); - let fullArrayMsg = isInitial ? [] : [...allThreads]; + const fullArrayMsg = isInitial ? [] : [...allThreads]; const getMessageForThreads = responseData.map(async (message: any) => { let fullObject: any = null; if (message?.metadata?.description) { @@ -271,13 +264,12 @@ export const GroupMail = ({ } finally { if (isInitial) { setIsLoading(false); - // dispatch(setIsLoadingCustom(null)); } } }, [allThreads, isPrivate] ); - const getMailMessages = React.useCallback( + const getMailMessages = useCallback( async (groupId: string, members: any) => { try { setIsLoading(true); @@ -315,7 +307,7 @@ export const GroupMail = ({ .sort((a, b) => b.created - a.created) .slice(0, 10); - let fullThreadArray: any = []; + const fullThreadArray: any = []; const getMessageForThreads = newArray.map(async (message: any) => { try { const identifierQuery = message.threadId; @@ -327,6 +319,7 @@ export const GroupMail = ({ }, }); const responseData = await response.json(); + if (responseData.length > 0) { const thread = responseData[0]; if (thread?.metadata?.description) { @@ -342,7 +335,7 @@ export const GroupMail = ({ }; fullThreadArray.push(fullObject); } else { - let threadRes = await Promise.race([ + const threadRes = await Promise.race([ getEncryptedResource( { name: thread.name, @@ -377,13 +370,12 @@ export const GroupMail = ({ console.log(error); } finally { setIsLoading(false); - // dispatch(setIsLoadingCustom(null)); } }, [secretKey, isPrivate] ); - const getMessages = React.useCallback(async () => { + const getMessages = useCallback(async () => { // if ( !groupId || members?.length === 0) return; if (!groupId || isPrivate === null) return; @@ -400,7 +392,6 @@ export const GroupMail = ({ if (filterModeRef.current !== filterMode) { firstMount.current = false; } - // if (groupId && !firstMount.current && members.length > 0) { if (groupId && !firstMount.current && isPrivate !== null) { if (filterMode === 'Recently active') { getMessages(); @@ -427,11 +418,6 @@ export const GroupMail = ({ if (groupData && Array.isArray(groupData?.members)) { for (const member of groupData.members) { if (member.member) { - // const res = await getNameInfo(member.member); - // const resAddress = await qortalRequest({ - // action: "GET_ACCOUNT_DATA", - // address: member.member, - // }); const name = res; const publicKey = resAddress.publicKey; if (name) { @@ -465,16 +451,6 @@ export const GroupMail = ({ [filterMode] ); - // useEffect(()=> { - // if(user?.name){ - // const threads = JSON.parse( - // localStorage.getItem(`qmail_threads_viewedtimestamp_${user.name}`) || "{}" - // ); - // setViewedThreads(threads) - - // } - // }, [user?.name, currentThread]) - const handleCloseThreadFilterList = () => { setIsOpenFilterList(false); }; @@ -596,7 +572,7 @@ export const GroupMail = ({ padding: '0px', }} > - + {filterOptions?.map((filter) => { return ( @@ -621,6 +597,7 @@ export const GroupMail = ({ /> )} + {filter} @@ -630,9 +607,10 @@ export const GroupMail = ({ ); })} - + + + + - last message:{' '} + {t('group:last_message', { + postProcess: 'capitalize', + })} + :{' '} {formatDate(thread?.created)}
)} + { setTimeout(() => { @@ -834,6 +820,7 @@ export const GroupMail = ({ + { const { t } = useTranslation(['core', 'group']); - const { show } = React.useContext(MyContext); + const { show } = useContext(MyContext); const [isOpen, setIsOpen] = useState(false); const [value, setValue] = useState(''); const [isSending, setIsSending] = useState(false); const [threadTitle, setThreadTitle] = useState(''); - const [openSnack, setOpenSnack] = React.useState(false); - const [infoSnack, setInfoSnack] = React.useState(null); + const [openSnack, setOpenSnack] = useState(false); + const [infoSnack, setInfoSnack] = useState(null); const editorRef = useRef(null); const theme = useTheme(); const setEditorRef = (editorInstance) => { @@ -203,31 +202,37 @@ export const NewThread = ({ // if (!description) missingFields.push('subject') if (missingFields.length > 0) { const missingFieldsString = missingFields.join(', '); - const errMsg = `Missing: ${missingFieldsString}`; - errorMsg = errMsg; // TODO translate + const errMsg = t('group:message.error.missing_field', { + field: missingFieldsString, + postProcess: 'capitalize', + }); + errorMsg = errMsg; } if (errorMsg) { - // dispatch( - // setNotification({ - // msg: errorMsg, - // alertType: "error", - // }) - // ); throw new Error(errorMsg); } const htmlContent = editorRef.current.getHTML(); - if (!htmlContent?.trim() || htmlContent?.trim() === '

') - throw new Error('Please provide a first message to the thread'); + if (!htmlContent?.trim() || htmlContent?.trim() === '

') { + const errMsg = t('group:message.generic.provide_message', { + postProcess: 'capitalize', + }); + throw new Error(errMsg); + } + const fee = await getFee('ARBITRARY'); let feeToShow = fee.fee; + if (!isMessage) { feeToShow = +feeToShow * 2; } await show({ - message: 'Would you like to perform a ARBITRARY transaction?', + message: t('group:question.perform_transaction', { + action: 'ARBITRARY', + postProcess: 'capitalize', + }), publishFee: feeToShow + ' QORT', }); @@ -238,6 +243,7 @@ export const NewThread = ({ delete reply.reply; } } + const mailObject: any = { createdAt: Date.now(), version: 1, @@ -250,7 +256,10 @@ export const NewThread = ({ const secretKey = isPrivate === false ? null : await getSecretKey(false, true); if (!secretKey && isPrivate) { - throw new Error('Cannot get group secret key'); + const errMsg = t('group:message.error.group_secret_key', { + postProcess: 'capitalize', + }); + throw new Error(errMsg); } if (!isMessage) { @@ -273,17 +282,18 @@ export const NewThread = ({ isPrivate === false ? threadToBase64 : await encryptSingleFunc(threadToBase64, secretKey); - let identifierThread = `grp-${groupInfo.groupId}-thread-${idThread}`; + const identifierThread = `grp-${groupInfo.groupId}-thread-${idThread}`; await publishGroupEncryptedResource({ identifier: identifierThread, encryptedData: encryptSingleThread, }); - let identifierPost = `thmsg-${identifierThread}-${idMsg}`; + const identifierPost = `thmsg-${identifierThread}-${idMsg}`; await publishGroupEncryptedResource({ identifier: identifierPost, encryptedData: encryptSingleFirstPost, }); + const dataToSaveToStorage = { name: myName, identifier: identifierThread, @@ -292,6 +302,7 @@ export const NewThread = ({ created: Date.now(), groupId: groupInfo.groupId, }; + const dataToSaveToStoragePost = { name: myName, identifier: identifierPost, @@ -300,6 +311,7 @@ export const NewThread = ({ created: Date.now(), threadId: identifierThread, }; + await saveTempPublish({ data: dataToSaveToStorage, key: 'thread' }); await saveTempPublish({ data: dataToSaveToStoragePost, @@ -307,36 +319,32 @@ export const NewThread = ({ }); setInfoSnack({ type: 'success', - message: - 'Successfully created thread. It may take some time for the publish to propagate', + message: t('group:message.success.thread_creation', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); - // dispatch( - // setNotification({ - // msg: "Message sent", - // alertType: "success", - // }) - // ); if (publishCallback) { publishCallback(); } closeModal(); } else { - if (!currentThread) throw new Error('unable to locate thread Id'); + if (!currentThread) { + const errMsg = t('group:message.error.thread_id', { + postProcess: 'capitalize', + }); + throw new Error(errMsg); + } const idThread = currentThread.threadId; const messageToBase64 = await objectToBase64(mailObject); const encryptSinglePost = isPrivate === false ? messageToBase64 : await encryptSingleFunc(messageToBase64, secretKey); - const idMsg = uid.rnd(); - let identifier = `thmsg-${idThread}-${idMsg}`; - const res = await publishGroupEncryptedResource({ - identifier: identifier, - encryptedData: encryptSinglePost, - }); + const idMsg = uid.rnd(); + const identifier = `thmsg-${idThread}-${idMsg}`; const dataToSaveToStoragePost = { threadId: idThread, name: myName, @@ -349,32 +357,17 @@ export const NewThread = ({ data: dataToSaveToStoragePost, key: 'thread-post', }); - // await qortalRequest(multiplePublishMsg); - // dispatch( - // setNotification({ - // msg: "Message sent", - // alertType: "success", - // }) - // ); setInfoSnack({ type: 'success', - message: - 'Successfully created post. It may take some time for the publish to propagate', + message: t('group:message.success.post_creation', { + postProcess: 'capitalize', + }), }); setOpenSnack(true); if (publishCallback) { publishCallback(); } - // messageCallback({ - // identifier, - // id: identifier, - // name, - // service: MAIL_SERVICE_TYPE, - // created: Date.now(), - // ...mailObject, - // }); } - closeModal(); } catch (error: any) { if (error?.message) { @@ -393,6 +386,7 @@ export const NewThread = ({ const sendMail = () => { publishQDNResource(); }; + return ( setIsOpen(true)} > - {currentThread ? 'New Post' : 'New Thread'} + + {currentThread + ? t('core:action.new.post', { + postProcess: 'capitalize', + }) + : t('core:action.new.thread', { + postProcess: 'capitalize', + })} + - {isMessage ? 'Post Message' : 'New Thread'} + {isMessage + ? t('core:action.post_message', { + postProcess: 'capitalize', + }) + : t('core:action.new.thread', { + postProcess: 'capitalize', + })} + + + - {isSending && ( - {isMessage ? 'Post' : 'Create Thread'} + {isMessage + ? t('core:action.post', { + postProcess: 'capitalize', + }) + : t('core:action.create_thread', { + postProcess: 'capitalize', + })} {isMessage ? ( diff --git a/src/components/Group/Forum/ReadOnlySlate.tsx b/src/components/Group/Forum/ReadOnlySlate.tsx index 5724f92..15e1436 100644 --- a/src/components/Group/Forum/ReadOnlySlate.tsx +++ b/src/components/Group/Forum/ReadOnlySlate.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { createEditor } from 'slate'; import { withReact, @@ -96,7 +96,7 @@ interface ReadOnlySlateProps { content: any; mode?: string; } -const ReadOnlySlate: React.FC = ({ content, mode }) => { +const ReadOnlySlate: FC = ({ content, mode }) => { const [load, setLoad] = useState(false); const editor = useMemo(() => withReact(createEditor()), []); const value = useMemo(() => content, [content]); diff --git a/src/components/Group/Forum/ReusableModal.tsx b/src/components/Group/Forum/ReusableModal.tsx index 1bedccb..9bd25c7 100644 --- a/src/components/Group/Forum/ReusableModal.tsx +++ b/src/components/Group/Forum/ReusableModal.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import { FC } from 'react'; import { Box, Modal, useTheme } from '@mui/material'; interface MyModalProps { @@ -9,7 +9,7 @@ interface MyModalProps { customStyles?: any; } -export const ReusableModal: React.FC = ({ +export const ReusableModal: FC = ({ open, onClose, onSubmit, diff --git a/src/components/Group/Forum/ShowMessageWithoutModal.tsx b/src/components/Group/Forum/ShowMessageWithoutModal.tsx index 914b9c5..4b862d4 100644 --- a/src/components/Group/Forum/ShowMessageWithoutModal.tsx +++ b/src/components/Group/Forum/ShowMessageWithoutModal.tsx @@ -118,27 +118,6 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { width: 'auto', }} > - {/* - - - - {file?.originalFilename || file?.filename} - - */} {message?.attachments?.length > 1 && isFirst && ( { try { setTempPublishedList([]); @@ -328,7 +321,7 @@ export const Thread = ({ }, [messages, secretKey] ); - const getMessages = React.useCallback(async () => { + const getMessages = useCallback(async () => { if ( !currentThread || (!secretKey && isPrivate) || @@ -410,7 +403,7 @@ export const Thread = ({ const interval = useRef(null); - const checkNewMessages = React.useCallback( + const checkNewMessages = useCallback( async (groupInfo: any) => { try { let threadId = groupInfo.threadId; @@ -494,7 +487,7 @@ export const Thread = ({ firstMount.current = true; }; - React.useEffect(() => { + useEffect(() => { subscribeToEvent('threadFetchMode', threadFetchModeFunc); return () => { @@ -656,6 +649,7 @@ export const Thread = ({
+ {t('core:page.previous', { postProcess: 'capitalize' })} + + + + + @@ -347,6 +384,7 @@ export const ManageMembers = ({ /> )} + {value === 1 && ( - + ); }; diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx index 72a23d5..b20f519 100644 --- a/src/components/Group/QMailMessages.tsx +++ b/src/components/Group/QMailMessages.tsx @@ -11,12 +11,12 @@ import MailIcon from '@mui/icons-material/Mail'; import MailOutlineIcon from '@mui/icons-material/MailOutline'; import { executeEvent } from '../../utils/events'; import { CustomLoader } from '../../common/CustomLoader'; - import { mailsAtom, qMailLastEnteredTimestampAtom } from '../../atoms/global'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import MarkEmailUnreadIcon from '@mui/icons-material/MarkEmailUnread'; import { useAtom } from 'jotai'; +import { useTranslation } from 'react-i18next'; export const isLessThanOneWeekOld = (timestamp) => { // Current time in milliseconds @@ -54,6 +54,7 @@ export const QMailMessages = ({ userName, userAddress }) => { const [loading, setLoading] = useState(true); const theme = useTheme(); + const { t } = useTranslation(['core', 'group']); const getMails = useCallback(async () => { try { @@ -89,7 +90,10 @@ export const QMailMessages = ({ userName, userAddress }) => { rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); // TODO translate + rej( + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }) + ); }); }); } catch (error) { @@ -129,20 +133,20 @@ export const QMailMessages = ({ userName, userAddress }) => { return ( setIsExpanded((prev) => !prev)} > @@ -151,8 +155,9 @@ export const QMailMessages = ({ userName, userAddress }) => { fontSize: '1rem', }} > - Latest Q-Mails + {t('group:latest_mails', { postProcess: 'capitalize' })} + { {loading && mails.length === 0 && ( @@ -206,11 +211,11 @@ export const QMailMessages = ({ userName, userAddress }) => { {!loading && mails.length === 0 && ( { color: theme.palette.primary, }} > - Nothing to display + {t('group:message.generic.no_display', { + postProcess: 'capitalize', + })} )} diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index 0279d2b..70859d4 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -4,6 +4,7 @@ import { Fragment, ReactElement, Ref, + useContext, useEffect, useState, } from 'react'; @@ -15,11 +16,30 @@ import Typography from '@mui/material/Typography'; import CloseIcon from '@mui/icons-material/Close'; import Slide from '@mui/material/Slide'; import { TransitionProps } from '@mui/material/transitions'; -import { Box, FormControlLabel, Switch, styled, useTheme } from '@mui/material'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; +import { + Box, + Button, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + FormControlLabel, + Switch, + TextField, + styled, + useTheme, +} from '@mui/material'; import { enabledDevModeAtom } from '../../atoms/global'; - import ThemeManager from '../Theme/ThemeManager'; import { useAtom } from 'jotai'; +import { decryptStoredWallet } from '../../utils/decryptWallet'; +import { Spacer } from '../../common/Spacer'; +import PhraseWallet from '../../utils/generateWallet/phrase-wallet'; +import { walletVersion } from '../../background'; +import Base58 from '../../deps/Base58'; +import { MyContext } from '../../App'; +import { useTranslation } from 'react-i18next'; const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ padding: 8, @@ -63,11 +83,11 @@ const Transition = forwardRef(function Transition( return ; }); -export const Settings = ({ address, open, setOpen }) => { +export const Settings = ({ open, setOpen, rawWallet }) => { const [checked, setChecked] = useState(false); const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom); - const theme = useTheme(); + const { t } = useTranslation(['core', 'group']); const handleChange = (event: ChangeEvent) => { setChecked(event.target.checked); @@ -82,7 +102,7 @@ export const Settings = ({ address, open, setOpen }) => { if (response?.error) { console.error('Error adding user settings:', response.error); } else { - console.log('User settings added successfully'); // TODO translate + console.log('User settings added successfully'); } }) .catch((error) => { @@ -113,7 +133,10 @@ export const Settings = ({ address, open, setOpen }) => { rej(response.error); }) .catch((error) => { - rej(error.message || 'An error occurred'); + rej( + error.message || + t('core:message.error.generic', { postProcess: 'capitalize' }) + ); }); }); } catch (error) { @@ -136,7 +159,9 @@ export const Settings = ({ address, open, setOpen }) => { - General Settings + {t('core:general_settings', { + postProcess: 'capitalize', + })} { { control={ } - label="Disable all push notifications" + label={t('group:action.disable_push_notifications', { + postProcess: 'capitalize', + })} /> {window?.electronAPI && ( { }} /> } - label="Enable dev mode" + label={t('group:action.enable_dev_mode', { + postProcess: 'capitalize', + })} /> )} + {isEnabledDevMode && } ); }; + +const ExportPrivateKey = ({ rawWallet }) => { + const [password, setPassword] = useState(''); + const [privateKey, setPrivateKey] = useState(''); + const [isOpen, setIsOpen] = useState(false); + const { setOpenSnackGlobal, setInfoSnackCustom } = useContext(MyContext); + const { t } = useTranslation(['core', 'group']); + + const exportPrivateKeyFunc = async () => { + try { + setInfoSnackCustom({ + type: 'info', + message: t('group:message.generic.descrypt_wallet', { + postProcess: 'capitalize', + }), + }); + + setOpenSnackGlobal(true); + const wallet = structuredClone(rawWallet); + + const res = await decryptStoredWallet(password, wallet); + const wallet2 = new PhraseWallet(res, wallet?.version || walletVersion); + + const keyPair = Base58.encode(wallet2._addresses[0].keyPair.privateKey); + setPrivateKey(keyPair); + setInfoSnackCustom({ + type: '', + message: '', + }); + + setOpenSnackGlobal(false); + } catch (error) { + setInfoSnackCustom({ + type: 'error', + message: error?.message + ? t('group:message.error.decrypt_wallet', { + errorMessage: error?.message, + postProcess: 'capitalize', + }) + : t('group:message.error.descrypt_wallet', { + postProcess: 'capitalize', + }), + }); + + setOpenSnackGlobal(true); + } + }; + + return ( + <> + + + + + {t('group:action.export_password', { + postProcess: 'capitalize', + })} + + + + + {t('group:message.generic.secure_place', { + postProcess: 'capitalize', + })} + + + + + setPassword(e.target.value)} + /> + {privateKey && ( + + )} + + + + + + + + + + ); +}; diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index 84c806b..94d66ed 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { useEffect, useMemo, useState } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; @@ -17,27 +17,27 @@ export const ThingsToDoInitial = ({ balance, userInfo, }) => { - const [checked1, setChecked1] = React.useState(false); - const [checked2, setChecked2] = React.useState(false); + const [checked1, setChecked1] = useState(false); + const [checked2, setChecked2] = useState(false); const { t } = useTranslation(['core', 'tutorial']); const theme = useTheme(); - React.useEffect(() => { + useEffect(() => { if (balance && +balance >= 6) { setChecked1(true); } }, [balance]); - React.useEffect(() => { + useEffect(() => { if (name) setChecked2(true); }, [name]); - const isLoaded = React.useMemo(() => { + const isLoaded = useMemo(() => { if (userInfo !== null) return true; return false; }, [userInfo]); - const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(() => { + const hasDoneNameAndBalanceAndIsLoaded = useMemo(() => { if (isLoaded && checked1 && checked2) return true; return false; }, [checked1, isLoaded, checked2]); @@ -55,18 +55,18 @@ export const ThingsToDoInitial = ({ return ( + + ([]); const [isLoading, setIsLoading] = useState(false); const theme = useTheme(); + const { t } = useTranslation(['core', 'group']); 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 - const listRef = useRef(); + const listRef = useRef(null); const getRequests = async () => { try { @@ -94,9 +96,13 @@ export const UserListOfInvites = ({ const handleJoinGroup = async (groupId, groupName) => { try { - 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.perform_transaction', { + action: 'JOIN_GROUP', + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); @@ -123,8 +129,9 @@ export const UserListOfInvites = ({ res(response); 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', + }), }); setOpenSnack(true); handlePopoverClose(); @@ -140,13 +147,16 @@ export const UserListOfInvites = ({ .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); }); }); } catch (error) { + console.log(error); } finally { setIsLoading(false); } @@ -182,16 +192,22 @@ export const UserListOfInvites = ({ > - Join {invite?.groupName} + + {t('core:action.join', { + postProcess: 'capitalize', + })}{' '} + {invite?.groupName} + + - Join group + {t('group:action.join_group', { + postProcess: 'capitalize', + })} + handlePopoverOpen(event, index)} > @@ -221,7 +240,9 @@ export const UserListOfInvites = ({ }} /> )} + + -

Invite list

+

+ {t('core:list.invite', { + postProcess: 'capitalize', + })} +

+
diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx index 7e44eb6..0b332b8 100644 --- a/src/components/Group/WalletsAppWrapper.tsx +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -1,5 +1,5 @@ -import { Box, ButtonBase, Divider, Typography, useTheme } from '@mui/material'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Box, ButtonBase, Divider, Typography, useTheme } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import AppViewerContainer from '../Apps/AppViewerContainer'; import { @@ -19,7 +19,6 @@ export const WalletsAppWrapper = () => { const [navigationController, setNavigationController] = useAtom( navigationControllerAtom ); - const [selectedTab, setSelectedTab] = useState({ tabId: '5558589', name: 'Q-Wallets', @@ -60,17 +59,17 @@ export const WalletsAppWrapper = () => { {isOpen && ( { display: 'flex', alignItems: 'center', padding: '5px', - justifyContent: 'space-between', }} > Q-Wallets + { ref={iframeRef} skipAuth={true} /> + { > + { if (selectedTab?.refreshFunc) { diff --git a/src/components/Group/useBlockUsers.tsx b/src/components/Group/useBlockUsers.tsx index e82d0c6..4aa100c 100644 --- a/src/components/Group/useBlockUsers.tsx +++ b/src/components/Group/useBlockUsers.tsx @@ -164,6 +164,7 @@ export const useBlockedAddresses = () => { }); }); } + if (address) { await new Promise((res, rej) => { window diff --git a/src/components/GroupAvatar.tsx b/src/components/GroupAvatar.tsx index 3364b8f..3244d5c 100644 --- a/src/components/GroupAvatar.tsx +++ b/src/components/GroupAvatar.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useContext, useEffect, useState } from 'react'; +import { useCallback, useContext, useEffect, useState } from 'react'; import Logo2 from '../assets/svgs/Logo2.svg'; import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from '../App'; import { diff --git a/src/components/Home/NewUsersCTA.tsx b/src/components/Home/NewUsersCTA.tsx index 44666d6..68e8b06 100644 --- a/src/components/Home/NewUsersCTA.tsx +++ b/src/components/Home/NewUsersCTA.tsx @@ -36,7 +36,7 @@ export const NewUsersCTA = ({ balance }) => { textAlign: 'center', }} > - {t('core:new_user', { postProcess: 'capitalize' })} + {t('core:question.new_user', { postProcess: 'capitalize' })} diff --git a/src/components/Home/QortPrice.tsx b/src/components/Home/QortPrice.tsx index ec74b11..a9bd62a 100644 --- a/src/components/Home/QortPrice.tsx +++ b/src/components/Home/QortPrice.tsx @@ -168,6 +168,7 @@ export const QortPrice = () => { )} + { )} + diff --git a/src/components/Language/LanguageSelector.tsx b/src/components/Language/LanguageSelector.tsx index 751c0a9..ff78246 100644 --- a/src/components/Language/LanguageSelector.tsx +++ b/src/components/Language/LanguageSelector.tsx @@ -1,7 +1,13 @@ -import { useEffect, useRef, useState } from 'react'; +import { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { supportedLanguages } from '../../../i18n'; -import { Tooltip, useTheme } from '@mui/material'; +import { supportedLanguages } from '../../i18n/i18n'; +import { + FormControl, + MenuItem, + Select, + Tooltip, + useTheme, +} from '@mui/material'; const LanguageSelector = () => { const { i18n, t } = useTranslation(['core']); @@ -19,20 +25,6 @@ const LanguageSelector = () => { 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 (
{ position: 'absolute', }} > - - {showSelect ? ( - - ) : ( + {!showSelect && ( + - )} - + + )} + + {showSelect && ( + + + + )}
); }; diff --git a/src/components/Loader.tsx b/src/components/Loader.tsx index 8db908e..dadf8fd 100644 --- a/src/components/Loader.tsx +++ b/src/components/Loader.tsx @@ -1,9 +1,9 @@ -import React from 'react' -import { Box, CircularProgress } from "@mui/material"; +import { Box, CircularProgress } from '@mui/material'; export const Loader = () => { return ( - { height: '100%', position: 'fixed', top: '0px', - left:'0px', + left: '0px', right: '0px', bottom: '0px', zIndex: 10, - background: 'rgba(0, 0, 0, 0.4)' - }}> - + background: 'rgba(0, 0, 0, 0.4)', + }} + > + - ) -} + ); +}; diff --git a/src/components/MainAvatar.tsx b/src/components/MainAvatar.tsx index 04d7172..10370de 100644 --- a/src/components/MainAvatar.tsx +++ b/src/components/MainAvatar.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import Logo2 from '../assets/svgs/Logo2.svg'; import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from '../App'; import { diff --git a/src/components/QortPayment.tsx b/src/components/QortPayment.tsx index d9baa65..3b48590 100644 --- a/src/components/QortPayment.tsx +++ b/src/components/QortPayment.tsx @@ -158,6 +158,12 @@ export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { value={paymentPassword} onChange={(e) => setPaymentPassword(e.target.value)} autoComplete="off" + onKeyDown={(e) => { + if (e.key === 'Enter') { + if (isLoadingSendCoin) return; + sendCoinFunc(); + } + }} />
diff --git a/src/components/ReactionPicker.tsx b/src/components/ReactionPicker.tsx index c95adcf..911a396 100644 --- a/src/components/ReactionPicker.tsx +++ b/src/components/ReactionPicker.tsx @@ -27,15 +27,25 @@ export const ReactionPicker = ({ onReaction }) => { if (showPicker) { setShowPicker(false); } else { - // Get the button's position const buttonRect = buttonRef.current.getBoundingClientRect(); const pickerWidth = 350; + const pickerHeight = 400; // Match Picker height prop - // Calculate position to align the right edge of the picker with the button's right edge - setPickerPosition({ - top: buttonRect.bottom + window.scrollY, // Position below the button - left: buttonRect.right + window.scrollX - pickerWidth, // Align right edges - }); + // Initial position (below the button) + let top = buttonRect.bottom + window.scrollY; + let left = buttonRect.right + window.scrollX - pickerWidth; + + // If picker would overflow bottom, show it above the button + const overflowBottom = + top + pickerHeight > window.innerHeight + window.scrollY; + if (overflowBottom) { + top = buttonRect.top + window.scrollY - pickerHeight; + } + + // Optional: prevent overflow on the left too + if (left < 0) left = 0; + + setPickerPosition({ top, left }); setShowPicker(true); } }; @@ -92,12 +102,13 @@ export const ReactionPicker = ({ onReaction }) => { allowExpandReactions={true} autoFocusSearch={false} emojiStyle={EmojiStyle.NATIVE} - height="450" + height={400} onEmojiClick={handlePicker} onReactionClick={handleReaction} - reactionsDefaultOpen={true} + // reactionsDefaultOpen={true} + // open={true} theme={Theme.DARK} - width="350" + width={350} />
, document.body diff --git a/src/components/RegisterName.tsx b/src/components/RegisterName.tsx index 4b52a45..adb8198 100644 --- a/src/components/RegisterName.tsx +++ b/src/components/RegisterName.tsx @@ -1,33 +1,22 @@ -import React, { useCallback, useContext, useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { - Avatar, Box, Button, - ButtonBase, - Collapse, Dialog, DialogActions, DialogContent, - DialogContentText, DialogTitle, - Input, ListItem, - ListItemAvatar, - ListItemButton, ListItemIcon, ListItemText, List, - MenuItem, - Popover, - Select, TextField, Typography, useTheme, } from '@mui/material'; import { Label } from './Group/AddGroup'; import { Spacer } from '../common/Spacer'; -import { LoadingButton } from '@mui/lab'; -import { getBaseApiReact, MyContext } from '../App'; +import { getBaseApiReact } from '../App'; import { getFee } from '../background'; import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'; import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events'; @@ -43,6 +32,7 @@ enum Availability { AVAILABLE = 'available', NOT_AVAILABLE = 'not-available', } + export const RegisterName = ({ setOpenSnack, setInfoSnack, @@ -77,7 +67,6 @@ export const RegisterName = ({ } } catch (error) { console.error(error); - } finally { } }; // Debounce logic @@ -195,21 +184,22 @@ export const RegisterName = ({ aria-describedby="alert-dialog-description" > {'Register name'} + - + // TODO: translate + )} @@ -307,6 +298,7 @@ export const RegisterName = ({
+
+ +