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 (
);
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 (
-
- );
- };
-
\ No newline at end of file
+ return (
+
+ );
+};
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 (
<>
)}
+
{
@@ -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}
>
@@ -446,6 +452,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
+
{
}}
>
+
+
{
}}
>
+
+
{
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' })}
+
+
+
+
+
+
+
{
groupId
}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`;
const response = await fetch(url);
+
if (!response.ok) {
throw new Error('network error');
}
@@ -100,9 +88,11 @@ export const getPublishesFromAdmins = async (admins: string[], groupId) => {
const filterId = adminData.filter(
(data: any) => data.identifier === `symmetric-qchat-group-${groupId}`
);
+
if (filterId?.length === 0) {
return false;
}
+
const sortedData = filterId.sort((a: any, b: any) => {
// Get the most recent date for both a and b
const dateA = a.updated ? new Date(a.updated) : new Date(a.created);
@@ -114,24 +104,18 @@ export const getPublishesFromAdmins = async (admins: string[], groupId) => {
return sortedData[0];
};
-
interface GroupProps {
- myAddress: string;
- isFocused: boolean;
- userInfo: any;
balance: number;
+ isFocused: boolean;
+ myAddress: string;
+ userInfo: any;
}
export const timeDifferenceForNotificationChats = 900000;
-
export const requestQueueMemberNames = new RequestQueueWithPromise(5);
export const requestQueueAdminMemberNames = new RequestQueueWithPromise(5);
-// const audio = new Audio(chrome.runtime?.getURL("msg-not1.wav"));
-
export const getGroupAdminsAddress = async (groupNumber: number) => {
- // const validApi = await findUsableApi();
-
const response = await fetch(
`${getBaseApiReact()}/groups/members/${groupNumber}?limit=0&onlyAdmins=true`
);
@@ -422,27 +406,24 @@ export const Group = ({
const [chatMode, setChatMode] = useState('groups');
const [newChat, setNewChat] = useState(false);
- const [openSnack, setOpenSnack] = React.useState(false);
- const [infoSnack, setInfoSnack] = React.useState(null);
- const [isLoadingNotifyAdmin, setIsLoadingNotifyAdmin] = React.useState(false);
- const [isLoadingGroups, setIsLoadingGroups] = React.useState(true);
- const [isLoadingGroup, setIsLoadingGroup] = React.useState(false);
+ const [openSnack, setOpenSnack] = useState(false);
+ const [infoSnack, setInfoSnack] = useState(null);
+ const [isLoadingNotifyAdmin, setIsLoadingNotifyAdmin] = useState(false);
+ const [isLoadingGroups, setIsLoadingGroups] = useState(true);
+ const [isLoadingGroup, setIsLoadingGroup] = useState(false);
const [firstSecretKeyInCreation, setFirstSecretKeyInCreation] =
- React.useState(false);
- const [groupSection, setGroupSection] = React.useState('home');
+ useState(false);
+ const [groupSection, setGroupSection] = useState('home');
const [groupAnnouncements, setGroupAnnouncements] = useAtom(
groupAnnouncementsAtom
);
-
- const [defaultThread, setDefaultThread] = React.useState(null);
- const [isOpenDrawer, setIsOpenDrawer] = React.useState(false);
+ const [defaultThread, setDefaultThread] = useState(null);
+ const [isOpenDrawer, setIsOpenDrawer] = useState(false);
const setIsOpenBlockedUserModal = useSetAtom(isOpenBlockedModalAtom);
-
- const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false);
- const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState('');
- const [drawerMode, setDrawerMode] = React.useState('groups');
+ const [hideCommonKeyPopup, setHideCommonKeyPopup] = useState(false);
+ const [isLoadingGroupMessage, setIsLoadingGroupMessage] = useState('');
+ const [drawerMode, setDrawerMode] = useState('groups');
const setMutedGroups = useSetAtom(mutedGroupsAtom);
-
const [mobileViewMode, setMobileViewMode] = useState('home');
const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState('');
const isFocusedRef = useRef(true);
@@ -531,7 +512,10 @@ export const Group = ({
rej(response.error);
})
.catch((error) => {
- rej(error.message || 'An error occurred');
+ rej(
+ error.message ||
+ t('core:message.error.generic', { postProcess: 'capitalize' })
+ );
});
});
} catch (error) {
@@ -557,7 +541,10 @@ export const Group = ({
rej(response.error);
})
.catch((error) => {
- rej(error.message || 'An error occurred');
+ rej(
+ error.message ||
+ t('core:message.error.generic', { postProcess: 'capitalize' })
+ );
});
});
} catch (error) {
@@ -586,7 +573,10 @@ export const Group = ({
rej(response.error);
})
.catch((error) => {
- rej(error.message || 'An error occurred');
+ rej(
+ error.message ||
+ t('core:message.error.generic', { postProcess: 'capitalize' })
+ );
});
});
} catch (error) {
@@ -1106,7 +1096,10 @@ export const Group = ({
rej(response.error);
})
.catch((error) => {
- rej(error.message || 'An error occurred');
+ rej(
+ error.message ||
+ t('core:message.error.generic', { postProcess: 'capitalize' })
+ );
});
});
setInfoSnack({
diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx
index db01b59..1d36137 100644
--- a/src/components/Group/InviteMember.tsx
+++ b/src/components/Group/InviteMember.tsx
@@ -16,7 +16,8 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
try {
const fee = await getFee('GROUP_INVITE');
await show({
- message: t('group:question.group_invite', {
+ message: t('group:question.perform_transaction', {
+ action: 'GROUP_INVITE',
postProcess: 'capitalize',
}),
publishFee: fee.fee + ' QORT',
@@ -97,16 +98,16 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
label={t('group:invitation_expiry', { postProcess: 'capitalize' })}
onChange={handleChange}
>
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
{
const [bans, setBans] = 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 [isLoadingUnban, setIsLoadingUnban] = useState(false);
const { t } = useTranslation(['core', 'group']);
@@ -88,7 +88,10 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
try {
const fee = await getFee('CANCEL_GROUP_BAN');
await show({
- message: t('group:question.cancel_ban', { postProcess: 'capitalize' }),
+ message: t('group:question.perform_transaction', {
+ action: 'CANCEL_GROUP_BAN',
+ postProcess: 'capitalize',
+ }),
publishFee: fee.fee + ' QORT',
});
setIsLoadingUnban(true);
@@ -165,13 +168,13 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
>
{
{t('group:ban_list', { postProcess: 'capitalize' })}
diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx
index c7c9ecf..1074ccc 100644
--- a/src/components/Group/ListOfGroupPromotions.tsx
+++ b/src/components/Group/ListOfGroupPromotions.tsx
@@ -51,6 +51,11 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import { getFee } from '../../background';
import { useAtom, useSetAtom } from 'jotai';
+import { useTranslation } from 'react-i18next';
+
+const THIRTY_MINUTES = 30 * 60 * 1000; // 30 minutes in milliseconds
+const uid = new ShortUniqueId({ length: 8 });
+
export const requestQueuePromos = new RequestQueueWithPromise(3);
export function utf8ToBase64(inputString: string): string {
@@ -65,13 +70,11 @@ export function utf8ToBase64(inputString: string): string {
return base64String;
}
-const uid = new ShortUniqueId({ length: 8 });
-
export function getGroupId(str) {
const match = str.match(/group-(\d+)-/);
return match ? match[1] : null;
}
-const THIRTY_MINUTES = 30 * 60 * 1000; // 30 minutes in milliseconds
+
export const ListOfGroupPromotions = () => {
const [popoverAnchor, setPopoverAnchor] = useState(null);
const [openPopoverIndex, setOpenPopoverIndex] = useState(null);
@@ -98,7 +101,8 @@ export const ListOfGroupPromotions = () => {
const setTxList = useSetAtom(txListAtom);
const theme = useTheme();
- const listRef = useRef();
+ const { t } = useTranslation(['core', 'group']);
+ const listRef = useRef(null);
const rowVirtualizer = useVirtualizer({
count: promotions.length,
getItemKey: React.useCallback(
@@ -120,6 +124,7 @@ export const ListOfGroupPromotions = () => {
console.log(error);
}
}, []);
+
const getPromotions = useCallback(async () => {
try {
setPromotionTimeInterval(Date.now());
@@ -135,6 +140,7 @@ export const ListOfGroupPromotions = () => {
let data: any[] = [];
const uniqueGroupIds = new Set();
const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
+
const getPromos = responseData?.map(async (promo: any) => {
if (promo?.size < 200 && promo.created > oneWeekAgo) {
const name = await requestQueuePromos.enqueue(async () => {
@@ -213,6 +219,7 @@ export const ListOfGroupPromotions = () => {
setPopoverAnchor(null);
setOpenPopoverIndex(null);
};
+
const publishPromo = async () => {
try {
setIsLoadingPublish(true);
@@ -235,9 +242,12 @@ export const ListOfGroupPromotions = () => {
rej(response.error);
})
.catch((error) => {
- rej(error.message || 'An error occurred');
+ rej(
+ error.message ||
+ t('core:message.error.generic', { postProcess: 'capitalize' })
+ );
});
- }); // TODO translate
+ });
setInfoSnack({
type: 'success',
message:
@@ -264,7 +274,10 @@ export const ListOfGroupPromotions = () => {
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);
@@ -331,6 +344,7 @@ export const ListOfGroupPromotions = () => {
});
setIsLoadingJoinGroup(false);
} catch (error) {
+ console.log(error);
} finally {
setIsLoadingJoinGroup(false);
}
@@ -339,30 +353,30 @@ export const ListOfGroupPromotions = () => {
return (
setIsExpanded((prev) => !prev)}
>
@@ -374,6 +388,7 @@ export const ListOfGroupPromotions = () => {
Group promotions{' '}
{promotions.length > 0 && ` (${promotions.length})`}
+
{isExpanded ? (
{
<>
{
const response = await fetch(
@@ -59,8 +60,8 @@ export const ListOfInvites = ({
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 [isLoadingCancelInvite, setIsLoadingCancelInvite] = useState(false);
-
- const listRef = useRef();
+ const { t } = useTranslation(['core', 'group']);
+ const listRef = useRef(null);
const getInvites = async (groupId) => {
try {
@@ -90,13 +91,18 @@ export const ListOfInvites = ({
const handleCancelInvitation = async (address) => {
try {
- // TODO translate
const fee = await getFee('CANCEL_GROUP_INVITE');
+
await show({
- message: 'Would you like to perform a CANCEL_GROUP_INVITE transaction?',
+ message: t('group:question.perform_transaction', {
+ action: 'CANCEL_GROUP_INVITE',
+ postProcess: 'capitalize',
+ }),
publishFee: fee.fee + ' QORT',
});
+
setIsLoadingCancelInvite(true);
+
await new Promise((res, rej) => {
window
.sendMessage('cancelInvitationToGroup', {
@@ -107,8 +113,9 @@ export const ListOfInvites = ({
if (!response?.error) {
setInfoSnack({
type: 'success',
- message:
- 'Successfully canceled invitation. It may take a couple of minutes for the changes to propagate',
+ message: t('group:message.success.invitation_cancellation', {
+ postProcess: 'capitalize',
+ }),
});
setOpenSnack(true);
handlePopoverClose();
@@ -126,13 +133,18 @@ export const ListOfInvites = ({
.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 {
setIsLoadingCancelInvite(false);
}
@@ -168,13 +180,13 @@ export const ListOfInvites = ({
>
handleCancelInvitation(member?.invitee)}
>
- Cancel Invitation
+ {t('core:action.cancel_invitation', {
+ postProcess: 'capitalize',
+ })}
+
handlePopoverOpen(event, index)}
>
@@ -200,6 +215,7 @@ export const ListOfInvites = ({
}
/>
+
@@ -211,15 +227,19 @@ export const ListOfInvites = ({
return (
-
Invitees list
+
+ {t('group:invitees_list', {
+ postProcess: 'capitalize',
+ })}
+
diff --git a/src/components/Group/ListOfJoinRequests.tsx b/src/components/Group/ListOfJoinRequests.tsx
index 8c51ee2..bb3a928 100644
--- a/src/components/Group/ListOfJoinRequests.tsx
+++ b/src/components/Group/ListOfJoinRequests.tsx
@@ -15,11 +15,12 @@ import {
List,
} from 'react-virtualized';
import { getNameInfo } from './Group';
-import { getBaseApi, getFee } from '../../background';
+import { getFee } from '../../background';
import { LoadingButton } from '@mui/lab';
import { getBaseApiReact } from '../../App';
import { txListAtom } from '../../atoms/global';
import { useAtom } from 'jotai';
+import { useTranslation } from 'react-i18next';
export const getMemberInvites = async (groupNumber) => {
const response = await fetch(
@@ -59,11 +60,11 @@ export const ListOfJoinRequests = ({
}) => {
const [invites, setInvites] = useState([]);
const [txList, setTxList] = useAtom(txListAtom);
-
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 [isLoadingAccept, setIsLoadingAccept] = useState(false);
+ const { t } = useTranslation(['core', 'group']);
const getInvites = async (groupId) => {
try {
@@ -93,12 +94,18 @@ export const ListOfJoinRequests = ({
const handleAcceptJoinRequest = async (address) => {
try {
- const fee = await getFee('GROUP_INVITE'); // TODO translate
+ const fee = await getFee('GROUP_INVITE');
+
await show({
- message: 'Would you like to perform a GROUP_INVITE transaction?',
+ message: t('group:question.perform_transaction', {
+ action: 'GROUP_INVITE',
+ postProcess: 'capitalize',
+ }),
publishFee: fee.fee + ' QORT',
});
+
setIsLoadingAccept(true);
+
await new Promise((res, rej) => {
window
.sendMessage('inviteToGroup', {
@@ -111,19 +118,23 @@ export const ListOfJoinRequests = ({
setIsLoadingAccept(false);
setInfoSnack({
type: 'success',
- message:
- 'Successfully accepted join request. It may take a couple of minutes for the changes to propagate',
+ message: t('group:message.success,group_join', {
+ postProcess: 'capitalize',
+ }),
});
setOpenSnack(true);
handlePopoverClose();
res(response);
-
setTxList((prev) => [
{
...response,
type: 'join-request-accept',
- label: `Accepted join request: awaiting confirmation`,
- labelDone: `User successfully joined!`,
+ label: t('group:message.success,invitation_request', {
+ postProcess: 'capitalize',
+ }),
+ labelDone: t('group:message.success,user_joined', {
+ postProcess: 'capitalize',
+ }),
done: false,
groupId,
qortalAddress: address,
@@ -144,13 +155,16 @@ export const ListOfJoinRequests = ({
.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 {
setIsLoadingAccept(false);
}
@@ -158,13 +172,15 @@ export const ListOfJoinRequests = ({
const rowRenderer = ({ index, key, parent, style }) => {
const member = invites[index];
- const findJoinRequsetInTxList = txList?.find(
+ const findJoinRequestInTxList = txList?.find(
(tx) =>
tx?.groupId === groupId &&
tx?.qortalAddress === member?.joiner &&
tx?.type === 'join-request-accept'
);
- if (findJoinRequsetInTxList) return null;
+
+ if (findJoinRequestInTxList) return null;
+
return (
handleAcceptJoinRequest(member?.joiner)}
>
- Accept
+ {t('core:action.accept', { postProcess: 'capitalize' })}
+
handlePopoverOpen(event, index)}
>
@@ -235,7 +252,7 @@ export const ListOfJoinRequests = ({
return (
-
Join request list
+
{t('core:list.join_request', { postProcess: 'capitalize' })}
{
setPopoverAnchor(event.currentTarget);
@@ -57,7 +59,10 @@ const ListOfMembers = ({
try {
const fee = await getFee('GROUP_KICK');
await show({
- message: 'Would you like to perform a GROUP_KICK transaction?',
+ message: t('group:question.perform_transaction', {
+ action: 'GROUP_KICK',
+ postProcess: 'capitalize',
+ }),
publishFee: fee.fee + ' QORT',
});
@@ -72,8 +77,9 @@ const ListOfMembers = ({
if (!response?.error) {
setInfoSnack({
type: 'success',
- message:
- 'Successfully kicked member from group. It may take a couple of minutes for the changes to propagate',
+ message: t('group:message.success.group_kick', {
+ postProcess: 'capitalize',
+ }),
});
setOpenSnack(true);
handlePopoverClose();
@@ -90,7 +96,9 @@ const ListOfMembers = ({
.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);
@@ -104,12 +112,18 @@ const ListOfMembers = ({
};
const handleBan = async (address) => {
try {
- const fee = await getFee('GROUP_BAN'); // TODO translate
+ const fee = await getFee('GROUP_BAN');
+
await show({
- message: 'Would you like to perform a GROUP_BAN transaction?',
+ message: t('group:question.perform_transaction', {
+ action: 'GROUP_BAN',
+ postProcess: 'capitalize',
+ }),
publishFee: fee.fee + ' QORT',
});
+
setIsLoadingBan(true);
+
await new Promise((res, rej) => {
window
.sendMessage('banFromGroup', {
@@ -121,8 +135,9 @@ const ListOfMembers = ({
if (!response?.error) {
setInfoSnack({
type: 'success',
- message:
- 'Successfully banned member from group. It may take a couple of minutes for the changes to propagate',
+ message: t('group:message.success.group_ban', {
+ postProcess: 'capitalize',
+ }),
});
setOpenSnack(true);
handlePopoverClose();
@@ -139,13 +154,16 @@ const ListOfMembers = ({
.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 {
setIsLoadingBan(false);
}
@@ -155,7 +173,10 @@ const ListOfMembers = ({
try {
const fee = await getFee('ADD_GROUP_ADMIN');
await show({
- message: 'Would you like to perform a ADD_GROUP_ADMIN transaction?',
+ message: t('group:question.perform_transaction', {
+ action: 'ADD_GROUP_ADMIN',
+ postProcess: 'capitalize',
+ }),
publishFee: fee.fee + ' QORT',
});
setIsLoadingMakeAdmin(true);
@@ -169,8 +190,9 @@ const ListOfMembers = ({
if (!response?.error) {
setInfoSnack({
type: 'success',
- message:
- 'Successfully made member an admin. It may take a couple of minutes for the changes to propagate',
+ message: t('group:message.success.group_member_admin', {
+ postProcess: 'capitalize',
+ }),
});
setOpenSnack(true);
handlePopoverClose();
@@ -187,13 +209,16 @@ const ListOfMembers = ({
.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 {
setIsLoadingMakeAdmin(false);
}
@@ -203,7 +228,10 @@ const ListOfMembers = ({
try {
const fee = await getFee('REMOVE_GROUP_ADMIN');
await show({
- message: 'Would you like to perform a REMOVE_GROUP_ADMIN transaction?',
+ message: t('group:question.perform_transaction', {
+ action: 'REMOVE_GROUP_ADMIN',
+ postProcess: 'capitalize',
+ }),
publishFee: fee.fee + ' QORT',
});
setIsLoadingRemoveAdmin(true);
@@ -217,8 +245,9 @@ const ListOfMembers = ({
if (!response?.error) {
setInfoSnack({
type: 'success',
- message:
- 'Successfully removed member as an admin. It may take a couple of minutes for the changes to propagate',
+ message: t('group:message.success.group_remove_member', {
+ postProcess: 'capitalize',
+ }),
});
setOpenSnack(true);
handlePopoverClose();
@@ -235,13 +264,16 @@ const ListOfMembers = ({
.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 {
setIsLoadingRemoveAdmin(false);
}
@@ -276,13 +308,13 @@ const ListOfMembers = ({
>
{isOwner && (
@@ -293,48 +325,49 @@ const ListOfMembers = ({
variant="contained"
onClick={() => handleKick(member?.member)}
>
- Kick member from group
+ {t('group:action.kick_member', {
+ postProcess: 'capitalize',
+ })}
+
handleBan(member?.member)}
>
- Ban member from group
+ {t('group:action.ban', {
+ postProcess: 'capitalize',
+ })}
+
makeAdmin(member?.member)}
>
- Make an admin
+ {t('group:action.make_admin', {
+ postProcess: 'capitalize',
+ })}
+
removeAdmin(member?.member)}
>
- Remove as admin
+ {t('group:action.remove_admin', {
+ postProcess: 'capitalize',
+ })}
>
)}
-
- // }
- disablePadding
- >
+
+
handlePopoverOpen(event, index)}
>
@@ -348,6 +381,7 @@ const ListOfMembers = ({
}
/>
+
- Admin
+ {t('core:admin', {
+ postProcess: 'capitalize',
+ })}
)}
@@ -372,28 +408,31 @@ const ListOfMembers = ({
return (
-
Member list
+
+ {t('core:list.member', {
+ postProcess: 'capitalize',
+ })}
+
{({ height, width }) => (
)}
diff --git a/src/components/Group/ListOfThreadPostsWatched.tsx b/src/components/Group/ListOfThreadPostsWatched.tsx
index 1cf0d2a..b03588c 100644
--- a/src/components/Group/ListOfThreadPostsWatched.tsx
+++ b/src/components/Group/ListOfThreadPostsWatched.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';
@@ -9,10 +9,12 @@ import { Box, Typography } from '@mui/material';
import { Spacer } from '../../common/Spacer';
import { CustomLoader } from '../../common/CustomLoader';
import VisibilityIcon from '@mui/icons-material/Visibility';
+import { useTranslation } from 'react-i18next';
export const ListOfThreadPostsWatched = () => {
- const [posts, setPosts] = React.useState([]);
- const [loading, setLoading] = React.useState(true);
+ const [posts, setPosts] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const { t } = useTranslation(['core', 'group']);
const getPosts = async () => {
try {
@@ -42,34 +44,38 @@ export const ListOfThreadPostsWatched = () => {
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) {
+ console.log(error);
} finally {
setLoading(false);
}
};
- React.useEffect(() => {
+ useEffect(() => {
getPosts();
}, []);
return (
{
fontWeight: 600,
}}
>
- New Thread Posts:
+ {t('group:thread_posts', {
+ postProcess: 'capitalize',
+ })}
+ :
+
@@ -97,9 +107,9 @@ export const ListOfThreadPostsWatched = () => {
{loading && posts.length === 0 && (
@@ -108,11 +118,11 @@ export const ListOfThreadPostsWatched = () => {
{!loading && posts.length === 0 && (
{
color: 'rgba(255, 255, 255, 0.2)',
}}
>
- Nothing to display
+ {t('group:message.generic.no_display', {
+ postProcess: 'capitalize',
+ })}
)}
@@ -130,11 +142,11 @@ export const ListOfThreadPostsWatched = () => {
{posts?.map((post) => {
diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx
index 6956693..5768f97 100644
--- a/src/components/Group/ManageMembers.tsx
+++ b/src/components/Group/ManageMembers.tsx
@@ -1,4 +1,14 @@
-import * as React from 'react';
+import {
+ forwardRef,
+ Fragment,
+ ReactElement,
+ Ref,
+ SyntheticEvent,
+ useCallback,
+ useContext,
+ useEffect,
+ useState,
+} from 'react';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import AppBar from '@mui/material/AppBar';
@@ -25,6 +35,7 @@ import { Spacer } from '../../common/Spacer';
import InsertLinkIcon from '@mui/icons-material/InsertLink';
import { useSetAtom } from 'jotai';
import { txListAtom } from '../../atoms/global';
+import { useTranslation } from 'react-i18next';
function a11yProps(index: number) {
return {
@@ -33,37 +44,35 @@ function a11yProps(index: number) {
};
}
-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 ManageMembers = ({
- address,
open,
setOpen,
selectedGroup,
-
isAdmin,
isOwner,
}) => {
- const [membersWithNames, setMembersWithNames] = React.useState([]);
- const [tab, setTab] = React.useState('create');
- const [value, setValue] = React.useState(0);
- const [openSnack, setOpenSnack] = React.useState(false);
- const [infoSnack, setInfoSnack] = React.useState(null);
- const [isLoadingMembers, setIsLoadingMembers] = React.useState(false);
- const [isLoadingLeave, setIsLoadingLeave] = React.useState(false);
- const [groupInfo, setGroupInfo] = React.useState(null);
- const handleChange = (event: React.SyntheticEvent, newValue: number) => {
+ const [membersWithNames, setMembersWithNames] = useState([]);
+ const [value, setValue] = useState(0);
+ const [openSnack, setOpenSnack] = useState(false);
+ const [infoSnack, setInfoSnack] = useState(null);
+ const [isLoadingMembers, setIsLoadingMembers] = useState(false);
+ const [isLoadingLeave, setIsLoadingLeave] = useState(false);
+ const [groupInfo, setGroupInfo] = useState(null);
+ const handleChange = (event: SyntheticEvent, newValue: number) => {
setValue(newValue);
};
const theme = useTheme();
- const { show } = React.useContext(MyContext);
+ const { t } = useTranslation(['core', 'group']);
+ const { show } = useContext(MyContext);
const setTxList = useSetAtom(txListAtom);
const handleClose = () => {
@@ -75,7 +84,10 @@ export const ManageMembers = ({
setIsLoadingLeave(true);
const fee = await getFee('LEAVE_GROUP');
await show({
- message: 'Would you like to perform an LEAVE_GROUP transaction?',
+ message: t('group:question.perform_transaction', {
+ action: 'LEAVE_GROUP',
+ postProcess: 'capitalize',
+ }),
publishFee: fee.fee + ' QORT',
});
@@ -90,8 +102,14 @@ export const ManageMembers = ({
{
...response,
type: 'leave-group',
- label: `Left Group ${selectedGroup?.groupName}: awaiting confirmation`,
- labelDone: `Left Group ${selectedGroup?.groupName}: success!`,
+ label: t('group:message.success.group_leave_name', {
+ group_name: selectedGroup?.groupName,
+ postProcess: 'capitalize',
+ }),
+ labelDone: t('group:message.success.group_leave_label', {
+ group_name: selectedGroup?.groupName,
+ postProcess: 'capitalize',
+ }),
done: false,
groupId: selectedGroup?.groupId,
},
@@ -100,8 +118,9 @@ export const ManageMembers = ({
res(response);
setInfoSnack({
type: 'success',
- message:
- 'Successfully requested to leave group. It may take a couple of minutes for the changes to propagate',
+ message: t('group:message.success.group_leave', {
+ postProcess: 'capitalize',
+ }),
});
setOpenSnack(true);
return;
@@ -109,7 +128,10 @@ export const ManageMembers = ({
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) {
@@ -119,7 +141,7 @@ export const ManageMembers = ({
}
};
- const getMembersWithNames = React.useCallback(async (groupId) => {
+ const getMembersWithNames = useCallback(async (groupId) => {
try {
setIsLoadingMembers(true);
const res = await getGroupMembers(groupId);
@@ -139,6 +161,7 @@ export const ManageMembers = ({
console.log(error);
}
};
+
const getGroupInfo = async (groupId) => {
try {
const response = await fetch(`${getBaseApiReact()}/groups/${groupId}`);
@@ -149,7 +172,7 @@ export const ManageMembers = ({
}
};
- React.useEffect(() => {
+ useEffect(() => {
if (selectedGroup?.groupId) {
getMembers(selectedGroup?.groupId);
getGroupInfo(selectedGroup?.groupId);
@@ -160,7 +183,7 @@ export const ManageMembers = ({
setValue(4);
};
- React.useEffect(() => {
+ useEffect(() => {
subscribeToEvent('openGroupJoinRequest', openGroupJoinRequestFunc);
return () => {
@@ -169,7 +192,7 @@ export const ManageMembers = ({
}, []);
return (
-
+
-
+
);
};
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 (
+ <>
+
+
+
+ >
+ );
+};
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 = ({
+
+
+