mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-04-27 13:27:52 +00:00
Merge pull request #25 from nbenaglia/feature/i18n-groups
I18N: add group namespace
This commit is contained in:
commit
4fa9aa3c91
13
i18n.js
13
i18n.js
@ -19,6 +19,15 @@ const capitalize = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const supportedLanguages = {
|
||||||
|
de: { name: 'Deutsch', flag: '🇩🇪' },
|
||||||
|
en: { name: 'English', flag: '🇺🇸' },
|
||||||
|
es: { name: 'Español', flag: '🇪🇸' },
|
||||||
|
fr: { name: 'Français', flag: '🇫🇷' },
|
||||||
|
it: { name: 'Italiano', flag: '🇮🇹' },
|
||||||
|
ru: { name: 'Русский', flag: '🇷🇺' },
|
||||||
|
};
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(HttpApi)
|
.use(HttpApi)
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
@ -42,8 +51,8 @@ i18n
|
|||||||
escapeValue: false,
|
escapeValue: false,
|
||||||
},
|
},
|
||||||
lng: navigator.language,
|
lng: navigator.language,
|
||||||
ns: ['auth', 'core', 'tutorial'],
|
ns: ['auth', 'core', 'group', 'tutorial'],
|
||||||
supportedLngs: ['en', 'it', 'es', 'fr', 'de', 'ru'],
|
supportedLngs: Object.keys(supportedLanguages),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
@ -29,6 +29,11 @@
|
|||||||
"password": "password",
|
"password": "password",
|
||||||
"password_confirmation": "confirm password",
|
"password_confirmation": "confirm password",
|
||||||
"return_to_list": "return to list",
|
"return_to_list": "return to list",
|
||||||
|
"tips": {
|
||||||
|
"digital_id": "your wallet is like your digital ID on Qortal, and is how you will login to the Qortal User Interface. It holds your public address and the Qortal name you will eventually choose. Every transaction you make is linked to your ID, and this is where you manage all your QORT and other tradeable cryptocurrencies on Qortal.",
|
||||||
|
"new_account": "creating an account means creating a new wallet and digital ID to start using Qortal. Once you have made your account, you can start doing things like obtaining some QORT, buying a name and avatar, publishing videos and blogs, and much more.",
|
||||||
|
"new_users": "new users start here!"
|
||||||
|
},
|
||||||
"wallet": {
|
"wallet": {
|
||||||
"password_confirmation": "confirm wallet password",
|
"password_confirmation": "confirm wallet password",
|
||||||
"password": "wallet password",
|
"password": "wallet password",
|
||||||
|
@ -1,27 +1,57 @@
|
|||||||
{
|
{
|
||||||
"add": "add",
|
"action": {
|
||||||
"cancel": "cancel",
|
"add": "add",
|
||||||
"choose": "choose",
|
"accept": "accept",
|
||||||
"close": "close",
|
"backup_account": "backup account",
|
||||||
"continue": "continue",
|
"backup_wallet": "backup wallet",
|
||||||
|
"cancel": "cancel",
|
||||||
|
"change": "change",
|
||||||
|
"change_language": "change language",
|
||||||
|
"choose": "choose",
|
||||||
|
"close": "close",
|
||||||
|
"continue": "continue",
|
||||||
|
"continue_logout": "continue to logout",
|
||||||
|
"decline": "decline",
|
||||||
|
"edit": "edit",
|
||||||
|
"export": "export",
|
||||||
|
"import": "import",
|
||||||
|
"invite": "invite",
|
||||||
|
"join": "join",
|
||||||
|
"logout": "logout",
|
||||||
|
"notify": "notify"
|
||||||
|
},
|
||||||
"core": {
|
"core": {
|
||||||
"block_height": "block height",
|
"block_height": "block height",
|
||||||
"information": "core information",
|
"information": "core information",
|
||||||
"peers": "connected peers",
|
"peers": "connected peers",
|
||||||
"version": "core version"
|
"version": "core version"
|
||||||
},
|
},
|
||||||
|
"count": {
|
||||||
|
"none": "none",
|
||||||
|
"one": "one"
|
||||||
|
},
|
||||||
"description": "description",
|
"description": "description",
|
||||||
"edit": "edit",
|
"fee": {
|
||||||
"export": "export",
|
"payment": "payment fee",
|
||||||
"import": "import",
|
"publish": "publish fee"
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"last": "last",
|
||||||
|
"first": "first",
|
||||||
|
"next": "next",
|
||||||
|
"previous": "previous"
|
||||||
|
},
|
||||||
|
"downloading_qdn": "downloading from QDN",
|
||||||
"last_height": "last height",
|
"last_height": "last height",
|
||||||
"loading": "loading...",
|
"loading": "loading...",
|
||||||
"logout": "logout",
|
"loading_posts": "loading posts... please wait.",
|
||||||
|
"message_us": "please message us on Telegram or Discord if you need 4 QORT to start chatting without any limitations",
|
||||||
"minting_status": "minting status",
|
"minting_status": "minting status",
|
||||||
|
"new_user": "are you a new user?",
|
||||||
"payment_notification": "payment notification",
|
"payment_notification": "payment notification",
|
||||||
"price": "price",
|
"price": "price",
|
||||||
"q_mail": "q-mail",
|
"q_mail": "q-mail",
|
||||||
"result": {
|
"message": {
|
||||||
"error": {
|
"error": {
|
||||||
"generic": "an error occurred",
|
"generic": "an error occurred",
|
||||||
"incorrect_password": "incorrect password",
|
"incorrect_password": "incorrect password",
|
||||||
@ -34,7 +64,10 @@
|
|||||||
"synchronizing": "synchronizing"
|
"synchronizing": "synchronizing"
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"publish_qdn": "successfully published to QDN"
|
"order_submitted": "your buy order was submitted",
|
||||||
|
"publish_qdn": "successfully published to QDN",
|
||||||
|
"request_read": "I have read this request",
|
||||||
|
"transfer": "the transfer was succesful!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"save_options": {
|
"save_options": {
|
||||||
@ -59,11 +92,18 @@
|
|||||||
"dark": "dark mode",
|
"dark": "dark mode",
|
||||||
"light": "light mode"
|
"light": "light mode"
|
||||||
},
|
},
|
||||||
|
"time": {
|
||||||
|
"day_one": "{{count}} day",
|
||||||
|
"day_other": "{{count}} days",
|
||||||
|
"hour_one": "{{count}} hour",
|
||||||
|
"hour_other": "{{count}} hours",
|
||||||
|
"minute_one": "{{count}} minute",
|
||||||
|
"minute_other": "{{count}} minutes"
|
||||||
|
},
|
||||||
"title": "title",
|
"title": "title",
|
||||||
"tutorial": "tutorial",
|
"tutorial": "tutorial",
|
||||||
"user_lookup": "user lookup",
|
"user_lookup": "user lookup",
|
||||||
"wallet": {
|
"wallet": {
|
||||||
"backup_wallet": "backup wallet",
|
|
||||||
"wallet": "wallet",
|
"wallet": "wallet",
|
||||||
"wallet_other": "wallets"
|
"wallet_other": "wallets"
|
||||||
},
|
},
|
||||||
|
65
public/locales/en/group.json
Normal file
65
public/locales/en/group.json
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"cancel_ban": "cancel ban",
|
||||||
|
"create_group": "create group",
|
||||||
|
"find_group": "find group",
|
||||||
|
"join_group": "join group",
|
||||||
|
"invite_member": "invite member",
|
||||||
|
"refetch_page": "refetch page",
|
||||||
|
"return_to_thread": "return to threads"
|
||||||
|
},
|
||||||
|
"advanced_options": "advanced options",
|
||||||
|
"approval_threshold": "group Approval Threshold (number / percentage of Admins that must approve a transaction)",
|
||||||
|
"ban_list": "ban list",
|
||||||
|
"block_delay": {
|
||||||
|
"minimum": "minimum Block delay for Group Transaction Approvals",
|
||||||
|
"maximum": "maximum Block delay for Group Transaction Approvals"
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"closed": "closed (private) - users need permission to join",
|
||||||
|
"description": "description of group",
|
||||||
|
"invites": "group invites",
|
||||||
|
"management": "group management",
|
||||||
|
"name": "name of group",
|
||||||
|
"open": "open (public)",
|
||||||
|
"type": "group type"
|
||||||
|
},
|
||||||
|
"invitation_expiry": "invitation Expiry Time",
|
||||||
|
"join_requests": "join requests",
|
||||||
|
"question": {
|
||||||
|
"cancel_ban": "would you like to perform a CANCEL_GROUP_BAN transaction?",
|
||||||
|
"create_group": "would you like to perform an CREATE_GROUP transaction?",
|
||||||
|
"group_invite": "would you like to perform a GROUP_INVITE transaction?",
|
||||||
|
"join_group": "would you like to perform an JOIN_GROUP transaction?",
|
||||||
|
"provide_thread": "please provide a thread title"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"generic": {
|
||||||
|
"encryption_key": "the group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes...",
|
||||||
|
"group_invited_you": "{{group}} has invited you",
|
||||||
|
"no_display": "nothing to display",
|
||||||
|
"no_selection": "no group selected",
|
||||||
|
"not_part_group": "you are not part of the encrypted group of members. Wait until an admin re-encrypts the keys.",
|
||||||
|
"only_encrypted": "only unencrypted messages will be displayed.",
|
||||||
|
"setting_group": "setting up group... please wait."
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"access_name": "cannot send a message without a access to your name",
|
||||||
|
"description_required": "please provide a description",
|
||||||
|
"group_info": "cannot access group information",
|
||||||
|
"name_required": "please provide a name",
|
||||||
|
"notify_admins": "try notifying an admin from the list of admins below:"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate",
|
||||||
|
"group_creation_name": "created group {{group_name}}: awaiting confirmation",
|
||||||
|
"group_creation_label": "created group {{name}}: success!",
|
||||||
|
"group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate",
|
||||||
|
"join_creation": "successfully requested to join group. It may take a couple of minutes for the changes to propagate",
|
||||||
|
"group_join_name": "joined group {{group_name}}: awaiting confirmation",
|
||||||
|
"group_join_label": "joined group {{name}}: success!",
|
||||||
|
"loading_threads": "loading threads... please wait.",
|
||||||
|
"unbanned_user": "successfully unbanned user. It may take a couple of minutes for the changes to propagate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,38 +6,43 @@
|
|||||||
},
|
},
|
||||||
"advanced_users": "per utenti avanzati",
|
"advanced_users": "per utenti avanzati",
|
||||||
"apikey": {
|
"apikey": {
|
||||||
"alternative": "alternativa: seleziona un file",
|
"alternative": "alternativa: selezione file",
|
||||||
"change": "cambia la chiave API",
|
"change": "cambia APIkey",
|
||||||
"enter": "inserisci la chiave API",
|
"enter": "inserisci APIkey",
|
||||||
"import": "importa chiave API",
|
"import": "importa APIkey",
|
||||||
"key": "chiave API",
|
"key": "chiave API",
|
||||||
"select_valid": "selezione una chiave API valida"
|
"select_valid": "seleziona una APIkey valida"
|
||||||
},
|
},
|
||||||
"authenticate": "autenticazione",
|
"authenticate": "autentica",
|
||||||
"build_version": "versione build",
|
"build_version": "versione build",
|
||||||
"create_account": "crea un account",
|
"create_account": "crea account",
|
||||||
"download_account": "scarica account",
|
"download_account": "scarica account",
|
||||||
"keep_secure": "metti al sicuro il file del tuo account",
|
"keep_secure": "mantieni sicuro il file del tuo account",
|
||||||
"node": {
|
"node": {
|
||||||
"choose": "scegli un nodo custom",
|
"choose": "scegli nodo personalizzato",
|
||||||
"custom_many": "nodi custom",
|
"custom_many": "nodi personalizzati",
|
||||||
"use_custom": "use nodo custom",
|
"use_custom": "usa nodo personalizzato",
|
||||||
"use_local": "usa nodo locale",
|
"use_local": "usa nodo locale",
|
||||||
"using": "nodo in uso",
|
"using": "utilizzo nodo",
|
||||||
"using_public": "utilizzo nodo pubblico"
|
"using_public": "utilizzo nodo pubblico"
|
||||||
},
|
},
|
||||||
"password_confirmation": "confirma la password",
|
|
||||||
"password": "password",
|
"password": "password",
|
||||||
|
"password_confirmation": "conferma password",
|
||||||
|
"return_to_list": "torna alla lista",
|
||||||
|
"tips": {
|
||||||
|
"digital_id": "il tuo wallet è come la tua identità digitale su Qortal ed è il modo in cui accederai all'interfaccia utente di Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che sceglierai. Ogni transazione che esegui è collegata alla tua identità ed è qui che gestisci tutti i tuoi QORT e altre criptovalute scambiabili su Qortal.",
|
||||||
|
"new_account": "creare un account significa creare un nuovo wallet e un'identità digitale per iniziare a usare Qortal. Una volta creato l'account, potrai iniziare a ottenere QORT, acquistare un nome e un avatar, pubblicare video e blog, e molto altro.",
|
||||||
|
"new_users": "i nuovi utenti iniziano qui!"
|
||||||
|
},
|
||||||
"wallet": {
|
"wallet": {
|
||||||
"password_confirmation": "conferma la password del wallet",
|
"password_confirmation": "conferma password del wallet",
|
||||||
"password": "password del wallet",
|
"password": "password del wallet",
|
||||||
"keep_password": "mantieni la password attuale",
|
"keep_password": "mantieni password corrente",
|
||||||
"new_password": "nuova password",
|
"new_password": "nuova password",
|
||||||
"error": {
|
"error": {
|
||||||
"missing_new_password": "per favore inserisci una nuova password",
|
"missing_new_password": "per favore inserisci una nuova password",
|
||||||
"missing_password": "per favore inserisci la tua password"
|
"missing_password": "per favore inserisci la tua password"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"return_to_list": "ritorna alla lista",
|
|
||||||
"welcome": "benvenuto in"
|
"welcome": "benvenuto in"
|
||||||
}
|
}
|
||||||
|
@ -1,71 +1,111 @@
|
|||||||
{
|
{
|
||||||
"add": "aggiungi",
|
"action": {
|
||||||
"cancel": "annulla",
|
"add": "aggiungi",
|
||||||
"choose": "scegli",
|
"accept": "accetta",
|
||||||
"close": "chiudi",
|
"backup_account": "backup account",
|
||||||
"continue": "continua",
|
"backup_wallet": "backup wallet",
|
||||||
|
"cancel": "annulla",
|
||||||
|
"change": "cambia",
|
||||||
|
"change_language": "cambia lingua",
|
||||||
|
"choose": "scegli",
|
||||||
|
"close": "chiudi",
|
||||||
|
"continue": "continua",
|
||||||
|
"continue_logout": "continua con il logout",
|
||||||
|
"decline": "rifiuta",
|
||||||
|
"edit": "modifica",
|
||||||
|
"export": "esporta",
|
||||||
|
"import": "importa",
|
||||||
|
"invite": "invita",
|
||||||
|
"join": "unisciti",
|
||||||
|
"logout": "esci",
|
||||||
|
"notify": "notifica"
|
||||||
|
},
|
||||||
"core": {
|
"core": {
|
||||||
"block_height": "altezza del blocco",
|
"block_height": "altezza blocco",
|
||||||
"information": "informazioni core",
|
"information": "informazioni core",
|
||||||
"peers": "peer connessi",
|
"peers": "peer connessi",
|
||||||
"version": "versione core"
|
"version": "versione core"
|
||||||
},
|
},
|
||||||
|
"count": {
|
||||||
|
"none": "nessuno",
|
||||||
|
"one": "uno"
|
||||||
|
},
|
||||||
"description": "descrizione",
|
"description": "descrizione",
|
||||||
"edit": "modifica",
|
"fee": {
|
||||||
"export": "esporta",
|
"payment": "commissione di pagamento",
|
||||||
"import": "importa",
|
"publish": "commissione di pubblicazione"
|
||||||
|
},
|
||||||
|
"page": {
|
||||||
|
"last": "ultimo",
|
||||||
|
"first": "primo",
|
||||||
|
"next": "successivo",
|
||||||
|
"previous": "precedente"
|
||||||
|
},
|
||||||
|
"downloading_qdn": "scaricamento da QDN",
|
||||||
"last_height": "ultima altezza",
|
"last_height": "ultima altezza",
|
||||||
"loading": "caricamento...",
|
"loading": "caricamento...",
|
||||||
"logout": "disconnetti",
|
"loading_posts": "caricamento post... attendere prego.",
|
||||||
"minting_status": "stato del conio",
|
"message_us": "per favore scrivici su Telegram o Discord se hai bisogno di 4 QORT per iniziare a chattare senza limitazioni",
|
||||||
|
"minting_status": "stato minting",
|
||||||
|
"new_user": "sei un nuovo utente?",
|
||||||
"payment_notification": "notifica di pagamento",
|
"payment_notification": "notifica di pagamento",
|
||||||
"price": "prezzo",
|
"price": "prezzo",
|
||||||
"q_mail": "q-mail",
|
"q_mail": "q-mail",
|
||||||
"result": {
|
"message": {
|
||||||
"error": {
|
"error": {
|
||||||
"generic": "si è verificato un errore",
|
"generic": "si è verificato un errore",
|
||||||
"incorrect_password": "password errata",
|
"incorrect_password": "password errata",
|
||||||
"save_qdn": "impossibile salvare su QDN"
|
"save_qdn": "impossibile salvare su QDN"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"minting": "(conio in corso)",
|
"minting": "(minting)",
|
||||||
"not_minting": "(conio non attivo)",
|
"not_minting": "(non minting)",
|
||||||
"synchronized": "sincronizzato",
|
"synchronized": "sincronizzato",
|
||||||
"synchronizing": "sincronizzazione in corso"
|
"synchronizing": "sincronizzazione in corso"
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"publish_qdn": "pubblicato con successo su QDN"
|
"order_submitted": "il tuo ordine di acquisto è stato inviato",
|
||||||
|
"publish_qdn": "pubblicato su QDN con successo",
|
||||||
|
"request_read": "ho letto questa richiesta",
|
||||||
|
"transfer": "il trasferimento è stato effettuato con successo!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"save_options": {
|
"save_options": {
|
||||||
"no_pinned_changes": "attualmente non hai modifiche alle tue app appuntate",
|
"no_pinned_changes": "attualmente non hai modifiche alle tue app appuntate",
|
||||||
"overwrite_changes": "l'app non è riuscita a scaricare le tue app appuntate salvate su QDN. Vuoi sovrascrivere queste modifiche?",
|
"overwrite_changes": "l'app non è riuscita a scaricare le tue app appuntate salvate su QDN. Vuoi sovrascrivere le modifiche?",
|
||||||
"overwrite_qdn": "sovrascrivi su QDN",
|
"overwrite_qdn": "sovrascrivi su QDN",
|
||||||
"publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografate)?",
|
"publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografato)?",
|
||||||
"qdn": "usa il salvataggio QDN",
|
"qdn": "usa il salvataggio su QDN",
|
||||||
"register_name": "hai bisogno di un nome Qortal registrato per salvare le tue app appuntate su QDN.",
|
"register_name": "devi avere un nome Qortal registrato per salvare le tue app appuntate su QDN.",
|
||||||
"reset_pinned": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate predefinite?",
|
"reset_pinned": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate predefinite?",
|
||||||
"reset_qdn": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le tue app appuntate salvate su QDN?",
|
"reset_qdn": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate salvate su QDN?",
|
||||||
"revert_default": "ripristina predefinite",
|
"revert_default": "ripristina predefinito",
|
||||||
"revert_qdn": "ripristina da QDN",
|
"revert_qdn": "ripristina da QDN",
|
||||||
"save_qdn": "salva su QDN",
|
"save_qdn": "salva su QDN",
|
||||||
"save": "salva",
|
"save": "salva",
|
||||||
"settings": "stai utilizzando il metodo esporta/importa per salvare le impostazioni.",
|
"settings": "stai usando il metodo di esportazione/importazione per salvare le impostazioni.",
|
||||||
"unsaved_changes": "hai modifiche non salvate alle tue app appuntate. Salvale su QDN."
|
"unsaved_changes": "hai modifiche non salvate alle tue app appuntate. Salvale su QDN."
|
||||||
},
|
},
|
||||||
"settings": "impostazioni",
|
"settings": "impostazioni",
|
||||||
"supply": "offerta",
|
"supply": "disponibilità",
|
||||||
"theme": {
|
"theme": {
|
||||||
"dark": "modalità scura",
|
"dark": "modalità scura",
|
||||||
"light": "modalità chiara"
|
"light": "modalità chiara"
|
||||||
},
|
},
|
||||||
|
"time": {
|
||||||
|
"day_one": "{{count}} giorno",
|
||||||
|
"day_other": "{{count}} giorni",
|
||||||
|
"hour_one": "{{count}} ora",
|
||||||
|
"hour_other": "{{count}} ore",
|
||||||
|
"minute_one": "{{count}} minuto",
|
||||||
|
"minute_other": "{{count}} minuti"
|
||||||
|
},
|
||||||
"title": "titolo",
|
"title": "titolo",
|
||||||
"tutorial": "tutorial",
|
"tutorial": "tutorial",
|
||||||
"user_lookup": "ricerca utente",
|
"user_lookup": "ricerca utente",
|
||||||
"wallet": {
|
"wallet": {
|
||||||
"backup_wallet": "backup portafoglio",
|
"wallet": "wallet",
|
||||||
"wallet": "portafoglio",
|
"wallet_other": "wallet"
|
||||||
"wallet_other": "portafogli"
|
|
||||||
},
|
},
|
||||||
"welcome": "benvenuto"
|
"welcome": "benvenuto"
|
||||||
}
|
}
|
||||||
|
65
public/locales/it/group.json
Normal file
65
public/locales/it/group.json
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"cancel_ban": "annulla ban",
|
||||||
|
"create_group": "crea gruppo",
|
||||||
|
"find_group": "trova gruppo",
|
||||||
|
"join_group": "unisciti al gruppo",
|
||||||
|
"invite_member": "invita membro",
|
||||||
|
"refetch_page": "ricarica pagina",
|
||||||
|
"return_to_thread": "torna ai thread"
|
||||||
|
},
|
||||||
|
"advanced_options": "opzioni avanzate",
|
||||||
|
"approval_threshold": "soglia di Approvazione del gruppo (numero/percentuale di Admin che devono approvare una transazione)",
|
||||||
|
"ban_list": "lista ban",
|
||||||
|
"block_delay": {
|
||||||
|
"minimum": "ritardo minimo dei blocchi per Approvazione di Transazioni di Gruppo",
|
||||||
|
"maximum": "ritardo massimo dei blocchi per Approvazione di Transazioni di Gruppo"
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"closed": "chiuso (privato) - gli utenti necessitano di permesso per unirsi",
|
||||||
|
"description": "descrizione del gruppo",
|
||||||
|
"invites": "inviti del gruppo",
|
||||||
|
"management": "gestione del gruppo",
|
||||||
|
"name": "nome del gruppo",
|
||||||
|
"open": "aperto (pubblico)",
|
||||||
|
"type": "tipo di gruppo"
|
||||||
|
},
|
||||||
|
"invitation_expiry": "tempo di scadenza dell'invito",
|
||||||
|
"join_requests": "richieste di adesione",
|
||||||
|
"question": {
|
||||||
|
"cancel_ban": "vuoi eseguire una transazione CANCEL_GROUP_BAN?",
|
||||||
|
"create_group": "vuoi eseguire una transazione CREATE_GROUP?",
|
||||||
|
"group_invite": "vuoi eseguire una transazione GROUP_INVITE?",
|
||||||
|
"join_group": "vuoi eseguire una transazione JOIN_GROUP?",
|
||||||
|
"provide_thread": "per favore fornisci un titolo per il thread"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"generic": {
|
||||||
|
"encryption_key": "la prima chiave di cifratura comune del gruppo è in fase di creazione. Attendere alcuni minuti affinché venga recuperata dalla rete. Controllo ogni 2 minuti...",
|
||||||
|
"group_invited_you": "{{group}} ti ha invitato",
|
||||||
|
"no_display": "niente da visualizzare",
|
||||||
|
"no_selection": "nessun gruppo selezionato",
|
||||||
|
"not_part_group": "non fai parte del gruppo cifrato dei membri. Attendi che un amministratore ri-codifichi le chiavi.",
|
||||||
|
"only_encrypted": "verranno visualizzati solo i messaggi non cifrati.",
|
||||||
|
"setting_group": "configurazione del gruppo... attendere prego."
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"access_name": "impossibile inviare un messaggio senza accesso al tuo nome",
|
||||||
|
"description_required": "per favore fornisci una descrizione",
|
||||||
|
"group_info": "impossibile accedere alle informazioni del gruppo",
|
||||||
|
"name_required": "per favore fornisci un nome",
|
||||||
|
"notify_admins": "prova a notificare un admin dalla lista di amministratori qui sotto:"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"group_creation": "gruppo creato con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino",
|
||||||
|
"group_creation_name": "gruppo {{group_name}} creato: in attesa di conferma",
|
||||||
|
"group_creation_label": "gruppo {{name}} creato: successo!",
|
||||||
|
"group_invite": "invito inviato con successo a {{value}}. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino",
|
||||||
|
"join_creation": "richiesta di adesione al gruppo inviata con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino",
|
||||||
|
"group_join_name": "entrato nel gruppo {{group_name}}: in attesa di conferma",
|
||||||
|
"group_join_label": "entrato nel gruppo {{name}}: successo!",
|
||||||
|
"loading_threads": "caricamento thread... attendere prego.",
|
||||||
|
"unbanned_user": "utente sbannato con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
118
src/App.tsx
118
src/App.tsx
@ -135,6 +135,7 @@ import { GeneralNotifications } from './components/GeneralNotifications';
|
|||||||
import { PdfViewer } from './common/PdfViewer';
|
import { PdfViewer } from './common/PdfViewer';
|
||||||
import ThemeSelector from './components/Theme/ThemeSelector.tsx';
|
import ThemeSelector from './components/Theme/ThemeSelector.tsx';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import LanguageSelector from './components/Language/LanguageSelector.tsx';
|
||||||
import { DownloadWallet } from './components/Auth/DownloadWallet.tsx';
|
import { DownloadWallet } from './components/Auth/DownloadWallet.tsx';
|
||||||
|
|
||||||
type extStates =
|
type extStates =
|
||||||
@ -255,14 +256,7 @@ export const getBaseApiReact = (customApi?: string) => {
|
|||||||
return groupApi;
|
return groupApi;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// export const getArbitraryEndpointReact = () => {
|
|
||||||
|
|
||||||
// if (globalApiKey) {
|
|
||||||
// return `/arbitrary/resources/search`;
|
|
||||||
// } else {
|
|
||||||
// return `/arbitrary/resources/searchsimple`;
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
export const getArbitraryEndpointReact = () => {
|
export const getArbitraryEndpointReact = () => {
|
||||||
if (globalApiKey) {
|
if (globalApiKey) {
|
||||||
return `/arbitrary/resources/searchsimple`;
|
return `/arbitrary/resources/searchsimple`;
|
||||||
@ -571,26 +565,6 @@ function App() {
|
|||||||
isFocusedRef.current = isFocused;
|
isFocusedRef.current = isFocused;
|
||||||
}, [isFocused]);
|
}, [isFocused]);
|
||||||
|
|
||||||
// const checkIfUserHasLocalNode = useCallback(async () => {
|
|
||||||
// try {
|
|
||||||
// const url = `http://127.0.0.1:12391/admin/status`;
|
|
||||||
// const response = await fetch(url, {
|
|
||||||
// method: "GET",
|
|
||||||
// headers: {
|
|
||||||
// "Content-Type": "application/json",
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// const data = await response.json();
|
|
||||||
// if (data?.isSynchronizing === false && data?.syncPercent === 100) {
|
|
||||||
// setHasLocalNode(true);
|
|
||||||
// }
|
|
||||||
// } catch (error) {}
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// checkIfUserHasLocalNode();
|
|
||||||
// }, [extState]);
|
|
||||||
|
|
||||||
const address = useMemo(() => {
|
const address = useMemo(() => {
|
||||||
if (!rawWallet?.address0) return '';
|
if (!rawWallet?.address0) return '';
|
||||||
return rawWallet.address0;
|
return rawWallet.address0;
|
||||||
@ -1007,7 +981,7 @@ function App() {
|
|||||||
await showUnsavedChanges({
|
await showUnsavedChanges({
|
||||||
message:
|
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.',
|
'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') {
|
} else if (extState === 'authenticated') {
|
||||||
await showUnsavedChanges({
|
await showUnsavedChanges({
|
||||||
message: 'Are you sure you would like to logout?',
|
message: 'Are you sure you would like to logout?',
|
||||||
@ -1311,19 +1285,24 @@ function App() {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height="48px" />
|
<Spacer height="48px" />
|
||||||
|
|
||||||
{authenticatedMode === 'ltc' ? (
|
{authenticatedMode === 'ltc' ? (
|
||||||
<>
|
<>
|
||||||
<img src={ltcLogo} />
|
<img src={ltcLogo} />
|
||||||
|
|
||||||
<Spacer height="32px" />
|
<Spacer height="32px" />
|
||||||
|
|
||||||
<CopyToClipboard text={rawWallet?.ltcAddress}>
|
<CopyToClipboard text={rawWallet?.ltcAddress}>
|
||||||
<AddressBox>
|
<AddressBox>
|
||||||
{rawWallet?.ltcAddress?.slice(0, 6)}...
|
{rawWallet?.ltcAddress?.slice(0, 6)}...
|
||||||
{rawWallet?.ltcAddress?.slice(-4)} <img src={Copy} />
|
{rawWallet?.ltcAddress?.slice(-4)} <img src={Copy} />
|
||||||
</AddressBox>
|
</AddressBox>
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
|
|
||||||
{ltcBalanceLoading && (
|
{ltcBalanceLoading && (
|
||||||
<CircularProgress color="success" size={16} />
|
<CircularProgress color="success" size={16} />
|
||||||
)}
|
)}
|
||||||
@ -1345,6 +1324,7 @@ function App() {
|
|||||||
>
|
>
|
||||||
{ltcBalance} LTC
|
{ltcBalance} LTC
|
||||||
</TextP>
|
</TextP>
|
||||||
|
|
||||||
<RefreshIcon
|
<RefreshIcon
|
||||||
onClick={getLtcBalanceFunc}
|
onClick={getLtcBalanceFunc}
|
||||||
sx={{
|
sx={{
|
||||||
@ -1364,7 +1344,9 @@ function App() {
|
|||||||
myName={userInfo?.name}
|
myName={userInfo?.name}
|
||||||
balance={balance}
|
balance={balance}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacer height="32px" />
|
<Spacer height="32px" />
|
||||||
|
|
||||||
<TextP
|
<TextP
|
||||||
sx={{
|
sx={{
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
@ -1374,7 +1356,9 @@ function App() {
|
|||||||
>
|
>
|
||||||
{userInfo?.name}
|
{userInfo?.name}
|
||||||
</TextP>
|
</TextP>
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
|
|
||||||
<CopyToClipboard text={rawWallet?.address0}>
|
<CopyToClipboard text={rawWallet?.address0}>
|
||||||
<AddressBox>
|
<AddressBox>
|
||||||
{rawWallet?.address0?.slice(0, 6)}...
|
{rawWallet?.address0?.slice(0, 6)}...
|
||||||
@ -1514,7 +1498,7 @@ function App() {
|
|||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('core:logout')}
|
{t('core:action.logout')}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
placement="left"
|
placement="left"
|
||||||
@ -1869,7 +1853,7 @@ function App() {
|
|||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('core:backup_wallet')}
|
{t('core:action.backup_wallet')}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
placement="left"
|
placement="left"
|
||||||
@ -1903,10 +1887,6 @@ function App() {
|
|||||||
<AppContainer
|
<AppContainer
|
||||||
sx={{
|
sx={{
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
// backgroundImage: desktopViewMode === "apps" && 'url("appsBg.svg")',
|
|
||||||
// backgroundSize: desktopViewMode === "apps" && "cover",
|
|
||||||
// backgroundPosition: desktopViewMode === "apps" && "center",
|
|
||||||
// backgroundRepeat: desktopViewMode === "apps" && "no-repeat",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PdfViewer />
|
<PdfViewer />
|
||||||
@ -2036,7 +2016,6 @@ function App() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isShowQortalRequest && !isMainWindow && (
|
{isShowQortalRequest && !isMainWindow && (
|
||||||
<>
|
<>
|
||||||
<Spacer height="120px" />
|
<Spacer height="120px" />
|
||||||
@ -2319,7 +2298,6 @@ function App() {
|
|||||||
<ErrorText>{sendPaymentError}</ErrorText>
|
<ErrorText>{sendPaymentError}</ErrorText>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{extState === 'web-app-request-payment' && !isMainWindow && (
|
{extState === 'web-app-request-payment' && !isMainWindow && (
|
||||||
<>
|
<>
|
||||||
<Spacer height="100px" />
|
<Spacer height="100px" />
|
||||||
@ -2953,7 +2931,9 @@ function App() {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Backup Account
|
{t('core:action.backup_account', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -2981,7 +2961,9 @@ function App() {
|
|||||||
lineHeight: '15px',
|
lineHeight: '15px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
The transfer was succesful!
|
{t('core:message.success.transfer', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</TextP>
|
</TextP>
|
||||||
<Spacer height="100px" />
|
<Spacer height="100px" />
|
||||||
<CustomButton
|
<CustomButton
|
||||||
@ -2989,7 +2971,7 @@ function App() {
|
|||||||
returnToMain();
|
returnToMain();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Continue
|
{t('core:action.continue', { postProcess: 'capitalize' })}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@ -3004,7 +2986,9 @@ function App() {
|
|||||||
lineHeight: '15px',
|
lineHeight: '15px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
The transfer was succesful!
|
{t('core:message.success.transfer', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</TextP>
|
</TextP>
|
||||||
<Spacer height="100px" />
|
<Spacer height="100px" />
|
||||||
<CustomButton
|
<CustomButton
|
||||||
@ -3012,7 +2996,7 @@ function App() {
|
|||||||
window.close();
|
window.close();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Continue
|
{t('core:action.continue', { postProcess: 'capitalize' })}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -3027,7 +3011,9 @@ function App() {
|
|||||||
lineHeight: '15px',
|
lineHeight: '15px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Your buy order was submitted
|
{t('core:message.success.order_submitted', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</TextP>
|
</TextP>
|
||||||
<Spacer height="100px" />
|
<Spacer height="100px" />
|
||||||
<CustomButton
|
<CustomButton
|
||||||
@ -3035,10 +3021,11 @@ function App() {
|
|||||||
window.close();
|
window.close();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Close
|
{t('core:action.close', { postProcess: 'capitalize' })}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{countdown && (
|
{countdown && (
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
@ -3082,12 +3069,18 @@ function App() {
|
|||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
{message?.paymentFee && (
|
{message?.paymentFee && (
|
||||||
<DialogContentText id="alert-dialog-description2">
|
<DialogContentText id="alert-dialog-description2">
|
||||||
payment fee: {message.paymentFee}
|
{t('core:fee.payment', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
: {message.paymentFee}
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
)}
|
)}
|
||||||
{message?.publishFee && (
|
{message?.publishFee && (
|
||||||
<DialogContentText id="alert-dialog-description2">
|
<DialogContentText id="alert-dialog-description2">
|
||||||
publish fee: {message.publishFee}
|
{t('core:fee.publish', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
: {message.publishFee}
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
)}
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
@ -3108,7 +3101,9 @@ function App() {
|
|||||||
onClick={onOk}
|
onClick={onOk}
|
||||||
autoFocus
|
autoFocus
|
||||||
>
|
>
|
||||||
accept
|
{t('core:action.accept', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
@ -3125,7 +3120,9 @@ function App() {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
>
|
>
|
||||||
decline
|
{t('core:action.decline', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -3146,7 +3143,9 @@ function App() {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button variant="contained" onClick={onOkInfo} autoFocus>
|
<Button variant="contained" onClick={onOkInfo} autoFocus>
|
||||||
Close
|
{t('core:action.close', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -3165,14 +3164,18 @@ function App() {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button variant="contained" onClick={onCancelUnsavedChanges}>
|
<Button variant="contained" onClick={onCancelUnsavedChanges}>
|
||||||
Cancel
|
{t('core:action.cancel', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={onOkUnsavedChanges}
|
onClick={onOkUnsavedChanges}
|
||||||
autoFocus
|
autoFocus
|
||||||
>
|
>
|
||||||
Continue to Logout
|
{t('core:action.decline', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -3443,7 +3446,9 @@ function App() {
|
|||||||
label={
|
label={
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<Typography sx={{ fontSize: '14px' }}>
|
<Typography sx={{ fontSize: '14px' }}>
|
||||||
I have read this request
|
{t('core:message.success.request_read', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
<PriorityHighIcon color="warning" />
|
<PriorityHighIcon color="warning" />
|
||||||
</Box>
|
</Box>
|
||||||
@ -3454,8 +3459,8 @@ function App() {
|
|||||||
<Spacer height="29px" />
|
<Spacer height="29px" />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
gap: '14px',
|
gap: '14px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -3491,7 +3496,9 @@ function App() {
|
|||||||
onOkQortalRequestExtension('accepted');
|
onOkQortalRequestExtension('accepted');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
accept
|
{t('core:action.accept', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</CustomButtonAccept>
|
</CustomButtonAccept>
|
||||||
<CustomButtonAccept
|
<CustomButtonAccept
|
||||||
color="black"
|
color="black"
|
||||||
@ -3501,7 +3508,9 @@ function App() {
|
|||||||
}}
|
}}
|
||||||
onClick={() => onCancelQortalRequestExtension()}
|
onClick={() => onCancelQortalRequestExtension()}
|
||||||
>
|
>
|
||||||
decline
|
{t('core:action.decline', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</CustomButtonAccept>
|
</CustomButtonAccept>
|
||||||
</Box>
|
</Box>
|
||||||
<ErrorText>{sendPaymentError}</ErrorText>
|
<ErrorText>{sendPaymentError}</ErrorText>
|
||||||
@ -3566,6 +3575,7 @@ function App() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<LanguageSelector />
|
||||||
<ThemeSelector />
|
<ThemeSelector />
|
||||||
</AppContainer>
|
</AppContainer>
|
||||||
);
|
);
|
||||||
|
@ -31,6 +31,7 @@ import { GlobalContext } from '../App';
|
|||||||
import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip';
|
import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip';
|
||||||
import ThemeSelector from '../components/Theme/ThemeSelector';
|
import ThemeSelector from '../components/Theme/ThemeSelector';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import LanguageSelector from '../components/Language/LanguageSelector';
|
||||||
|
|
||||||
const manifestData = {
|
const manifestData = {
|
||||||
version: '0.5.3',
|
version: '0.5.3',
|
||||||
@ -510,14 +511,8 @@ export const NotAuthenticated = ({
|
|||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Your wallet is like your digital ID on Qortal, and is how you
|
{t('auth:tips.digital_id', { postProcess: 'capitalize' })}
|
||||||
will login to the Qortal User Interface. It holds your public
|
</Typography>
|
||||||
address and the Qortal name you will eventually choose. Every
|
|
||||||
transaction you make is linked to your ID, and this is where you
|
|
||||||
manage all your QORT and other tradeable cryptocurrencies on
|
|
||||||
Qortal.
|
|
||||||
</Typography>{' '}
|
|
||||||
// TODO translate
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -547,9 +542,8 @@ export const NotAuthenticated = ({
|
|||||||
fontSize: '18px',
|
fontSize: '18px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
New users start here!
|
{t('auth:tips.new_users', { postProcess: 'capitalize' })}
|
||||||
</Typography>{' '}
|
</Typography>
|
||||||
// TODO translate
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
<Typography
|
<Typography
|
||||||
color="inherit"
|
color="inherit"
|
||||||
@ -557,12 +551,8 @@ export const NotAuthenticated = ({
|
|||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Creating an account means creating a new wallet and digital ID
|
{t('auth:tips.new_account', { postProcess: 'capitalize' })}
|
||||||
to start using Qortal. Once you have made your account, you can
|
</Typography>
|
||||||
start doing things like obtaining some QORT, buying a name and
|
|
||||||
avatar, publishing videos and blogs, and much more.
|
|
||||||
</Typography>{' '}
|
|
||||||
// TODO translate
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -816,7 +806,7 @@ export const NotAuthenticated = ({
|
|||||||
}}
|
}}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
{t('core:choose', { postProcess: 'capitalize' })}
|
{t('core:action.choose', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@ -875,7 +865,9 @@ export const NotAuthenticated = ({
|
|||||||
}}
|
}}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
{t('core:choose', { postProcess: 'capitalize' })}
|
{t('core:action.choose', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -888,7 +880,9 @@ export const NotAuthenticated = ({
|
|||||||
}}
|
}}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
{t('core:edit', { postProcess: 'capitalize' })}
|
{t('core:action.edit', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -940,7 +934,7 @@ export const NotAuthenticated = ({
|
|||||||
<DialogActions>
|
<DialogActions>
|
||||||
{mode === 'list' && (
|
{mode === 'list' && (
|
||||||
<Button variant="contained" onClick={addCustomNode}>
|
<Button variant="contained" onClick={addCustomNode}>
|
||||||
{t('core:add', { postProcess: 'capitalize' })}
|
{t('core:action.add', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -953,7 +947,7 @@ export const NotAuthenticated = ({
|
|||||||
}}
|
}}
|
||||||
autoFocus
|
autoFocus
|
||||||
>
|
>
|
||||||
{t('core:close', { postProcess: 'capitalize' })}
|
{t('core:action.close', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -1075,7 +1069,7 @@ export const NotAuthenticated = ({
|
|||||||
setShowSelectApiKey(false);
|
setShowSelectApiKey(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('core:close', { postProcess: 'capitalize' })}
|
{t('core:action.close', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -1097,6 +1091,7 @@ export const NotAuthenticated = ({
|
|||||||
/>
|
/>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
|
<LanguageSelector />
|
||||||
<ThemeSelector />
|
<ThemeSelector />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -15,6 +15,7 @@ import { extractComponents } from '../Chat/MessageDisplay';
|
|||||||
import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward';
|
import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward';
|
||||||
import { AppsPrivate } from './AppsPrivate';
|
import { AppsPrivate } from './AppsPrivate';
|
||||||
import ThemeSelector from '../Theme/ThemeSelector';
|
import ThemeSelector from '../Theme/ThemeSelector';
|
||||||
|
import LanguageSelector from '../Language/LanguageSelector';
|
||||||
|
|
||||||
export const AppsHomeDesktop = ({
|
export const AppsHomeDesktop = ({
|
||||||
setMode,
|
setMode,
|
||||||
@ -157,6 +158,7 @@ export const AppsHomeDesktop = ({
|
|||||||
/>
|
/>
|
||||||
</AppsContainer>
|
</AppsContainer>
|
||||||
|
|
||||||
|
<LanguageSelector />
|
||||||
<ThemeSelector />
|
<ThemeSelector />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -79,21 +79,21 @@ export const CoreSyncStatus = () => {
|
|||||||
|
|
||||||
if (isMintingPossible && !isUsingGateway) {
|
if (isMintingPossible && !isUsingGateway) {
|
||||||
imagePath = syncedMintingImg;
|
imagePath = syncedMintingImg;
|
||||||
message = `${t(`core:result.status.${isSynchronizing ? 'synchronizing' : 'synchronized'}`, { postProcess: 'capitalize' })} ${t('core:result.status.minting')}`;
|
message = `${t(`core:message.status.${isSynchronizing ? 'synchronizing' : 'synchronized'}`, { postProcess: 'capitalize' })} ${t('core:message.status.minting')}`;
|
||||||
} else if (isSynchronizing === true && syncPercent === 99) {
|
} else if (isSynchronizing === true && syncPercent === 99) {
|
||||||
imagePath = syncingImg;
|
imagePath = syncingImg;
|
||||||
} else if (isSynchronizing && !isMintingPossible && syncPercent === 100) {
|
} else if (isSynchronizing && !isMintingPossible && syncPercent === 100) {
|
||||||
imagePath = syncingImg;
|
imagePath = syncingImg;
|
||||||
message = `${t('core:result.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.not_minting') : ''}`;
|
message = `${t('core:message.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.not_minting') : ''}`;
|
||||||
} else if (!isSynchronizing && !isMintingPossible && syncPercent === 100) {
|
} else if (!isSynchronizing && !isMintingPossible && syncPercent === 100) {
|
||||||
imagePath = syncedImg;
|
imagePath = syncedImg;
|
||||||
message = `${t('core:result.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.not_minting') : ''}`;
|
message = `${t('core:message.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.not_minting') : ''}`;
|
||||||
} else if (isSynchronizing && isMintingPossible && syncPercent === 100) {
|
} else if (isSynchronizing && isMintingPossible && syncPercent === 100) {
|
||||||
imagePath = syncingImg;
|
imagePath = syncingImg;
|
||||||
message = `${t('core:result.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.minting') : ''}`;
|
message = `${t('core:message.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.minting') : ''}`;
|
||||||
} else if (!isSynchronizing && isMintingPossible && syncPercent === 100) {
|
} else if (!isSynchronizing && isMintingPossible && syncPercent === 100) {
|
||||||
imagePath = syncedMintingImg;
|
imagePath = syncedMintingImg;
|
||||||
message = `${t('core:result.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.minting') : ''}`;
|
message = `${t('core:message.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:message.status.minting') : ''}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -8,6 +8,7 @@ import { enabledDevModeAtom } from '../atoms/global';
|
|||||||
import { AppsIcon } from '../assets/Icons/AppsIcon';
|
import { AppsIcon } from '../assets/Icons/AppsIcon';
|
||||||
import ThemeSelector from './Theme/ThemeSelector';
|
import ThemeSelector from './Theme/ThemeSelector';
|
||||||
import { CoreSyncStatus } from './CoreSyncStatus';
|
import { CoreSyncStatus } from './CoreSyncStatus';
|
||||||
|
import LanguageSelector from './Language/LanguageSelector';
|
||||||
|
|
||||||
export const DesktopSideBar = ({
|
export const DesktopSideBar = ({
|
||||||
goToHome,
|
goToHome,
|
||||||
@ -143,6 +144,7 @@ export const DesktopSideBar = ({
|
|||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<LanguageSelector />
|
||||||
<ThemeSelector />
|
<ThemeSelector />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
import * as React from 'react';
|
import {
|
||||||
|
forwardRef,
|
||||||
|
Fragment,
|
||||||
|
ReactElement,
|
||||||
|
Ref,
|
||||||
|
SyntheticEvent,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import Dialog from '@mui/material/Dialog';
|
import Dialog from '@mui/material/Dialog';
|
||||||
import AppBar from '@mui/material/AppBar';
|
import AppBar from '@mui/material/AppBar';
|
||||||
@ -28,6 +37,7 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
|||||||
import { getFee } from '../../background';
|
import { getFee } from '../../background';
|
||||||
import { MyContext } from '../../App';
|
import { MyContext } from '../../App';
|
||||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const Label = styled('label')`
|
export const Label = styled('label')`
|
||||||
display: block;
|
display: block;
|
||||||
@ -37,30 +47,29 @@ export const Label = styled('label')`
|
|||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Transition = React.forwardRef(function Transition(
|
const Transition = forwardRef(function Transition(
|
||||||
props: TransitionProps & {
|
props: TransitionProps & {
|
||||||
children: React.ReactElement;
|
children: ReactElement;
|
||||||
},
|
},
|
||||||
ref: React.Ref<unknown>
|
ref: Ref<unknown>
|
||||||
) {
|
) {
|
||||||
return <Slide direction="up" ref={ref} {...props} />;
|
return <Slide direction="up" ref={ref} {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AddGroup = ({ address, open, setOpen }) => {
|
export const AddGroup = ({ address, open, setOpen }) => {
|
||||||
const { show, setTxList } = React.useContext(MyContext);
|
const { show, setTxList } = useContext(MyContext);
|
||||||
const [tab, setTab] = React.useState('create');
|
const [openAdvance, setOpenAdvance] = useState(false);
|
||||||
const [openAdvance, setOpenAdvance] = React.useState(false);
|
const [name, setName] = useState('');
|
||||||
const [name, setName] = React.useState('');
|
const [description, setDescription] = useState('');
|
||||||
const [description, setDescription] = React.useState('');
|
const [groupType, setGroupType] = useState('1');
|
||||||
const [groupType, setGroupType] = React.useState('1');
|
const [approvalThreshold, setApprovalThreshold] = useState('40');
|
||||||
const [approvalThreshold, setApprovalThreshold] = React.useState('40');
|
const [minBlock, setMinBlock] = useState('5');
|
||||||
const [minBlock, setMinBlock] = React.useState('5');
|
const [maxBlock, setMaxBlock] = useState('21600');
|
||||||
const [maxBlock, setMaxBlock] = React.useState('21600');
|
const [value, setValue] = useState(0);
|
||||||
const [value, setValue] = React.useState(0);
|
const [openSnack, setOpenSnack] = useState(false);
|
||||||
const [openSnack, setOpenSnack] = React.useState(false);
|
const [infoSnack, setInfoSnack] = useState(null);
|
||||||
const [infoSnack, setInfoSnack] = React.useState(null);
|
|
||||||
|
|
||||||
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
|
const handleChange = (event: SyntheticEvent, newValue: number) => {
|
||||||
setValue(newValue);
|
setValue(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -84,16 +93,30 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
setMaxBlock(event.target.value as string);
|
setMaxBlock(event.target.value as string);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { t } = useTranslation(['core', 'group']);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const handleCreateGroup = async () => {
|
const handleCreateGroup = async () => {
|
||||||
try {
|
try {
|
||||||
if (!name) throw new Error('Please provide a name');
|
if (!name)
|
||||||
if (!description) throw new Error('Please provide a description');
|
throw new Error(
|
||||||
|
t('group:message.error.name_required', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (!description)
|
||||||
|
throw new Error(
|
||||||
|
t('group:message.error.description_required', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const fee = await getFee('CREATE_GROUP');
|
||||||
|
|
||||||
const fee = await getFee('CREATE_GROUP'); // TODO translate
|
|
||||||
await show({
|
await show({
|
||||||
message: 'Would you like to perform an CREATE_GROUP transaction?',
|
message: t('group:question.create_group', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -111,16 +134,23 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message:
|
message: t('group:message.success.group_creation', {
|
||||||
'Successfully created group. It may take a couple of minutes for the changes to propagate',
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
setTxList((prev) => [
|
setTxList((prev) => [
|
||||||
{
|
{
|
||||||
...response,
|
...response,
|
||||||
type: 'created-group',
|
type: 'created-group',
|
||||||
label: `Created group ${name}: awaiting confirmation`,
|
label: t('group:message.success.group_creation_name', {
|
||||||
labelDone: `Created group ${name}: success!`,
|
group_name: name,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
|
labelDone: t('group:message.success.group_creation_label', {
|
||||||
|
group_name: name,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
done: false,
|
done: false,
|
||||||
},
|
},
|
||||||
...prev,
|
...prev,
|
||||||
@ -131,7 +161,11 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
rej({ message: response.error });
|
rej({ message: response.error });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej({ message: error.message || 'An error occurred' });
|
rej({
|
||||||
|
message:
|
||||||
|
error.message ||
|
||||||
|
t('core:message.error.generic', { postProcess: 'capitalize' }),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -143,22 +177,6 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function CustomTabPanel(props: TabPanelProps) {
|
|
||||||
const { children, value, index, ...other } = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
role="tabpanel"
|
|
||||||
hidden={value !== index}
|
|
||||||
id={`simple-tabpanel-${index}`}
|
|
||||||
aria-labelledby={`simple-tab-${index}`}
|
|
||||||
{...other}
|
|
||||||
>
|
|
||||||
{value === index && <Box sx={{ p: 3 }}>{children}</Box>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function a11yProps(index: number) {
|
function a11yProps(index: number) {
|
||||||
return {
|
return {
|
||||||
id: `simple-tab-${index}`,
|
id: `simple-tab-${index}`,
|
||||||
@ -170,7 +188,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
setValue(2);
|
setValue(2);
|
||||||
};
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
subscribeToEvent('openGroupInvitesRequest', openGroupInvitesRequestFunc);
|
subscribeToEvent('openGroupInvitesRequest', openGroupInvitesRequestFunc);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -182,7 +200,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<Fragment>
|
||||||
<Dialog
|
<Dialog
|
||||||
fullScreen
|
fullScreen
|
||||||
open={open}
|
open={open}
|
||||||
@ -197,7 +215,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
>
|
>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Typography sx={{ ml: 2, flex: 1 }} variant="h4" component="div">
|
<Typography sx={{ ml: 2, flex: 1 }} variant="h4" component="div">
|
||||||
Group Management
|
{t('group:group.management', { postProcess: 'capitalize' })}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
@ -208,12 +226,9 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
>
|
>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
{/* <Button autoFocus color="inherit" onClick={handleClose}>
|
|
||||||
save
|
|
||||||
</Button> */}
|
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
bgcolor: theme.palette.background.default,
|
bgcolor: theme.palette.background.default,
|
||||||
@ -241,7 +256,9 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tab
|
<Tab
|
||||||
label="Create Group"
|
label={t('group:action.create_group', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
{...a11yProps(0)}
|
{...a11yProps(0)}
|
||||||
sx={{
|
sx={{
|
||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
@ -251,7 +268,9 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label="Find Group"
|
label={t('group:action.find_group', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
{...a11yProps(1)}
|
{...a11yProps(1)}
|
||||||
sx={{
|
sx={{
|
||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
@ -261,7 +280,9 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label="Group Invites"
|
label={t('group:group.invites', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
{...a11yProps(2)}
|
{...a11yProps(2)}
|
||||||
sx={{
|
sx={{
|
||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
@ -295,9 +316,15 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
gap: '5px',
|
gap: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>Name of group</Label>
|
<Label>
|
||||||
|
{t('group:group.name', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Name of group"
|
placeholder={t('group:group.name', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@ -309,14 +336,21 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
gap: '5px',
|
gap: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>Description of group</Label>
|
<Label>
|
||||||
|
{t('group:group.description', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
placeholder="Description of group"
|
placeholder={t('group:group.description', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
value={description}
|
value={description}
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -324,7 +358,13 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
gap: '5px',
|
gap: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>Group type</Label>
|
<Label>
|
||||||
|
{' '}
|
||||||
|
{t('group:group.type', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
labelId="demo-simple-select-label"
|
labelId="demo-simple-select-label"
|
||||||
id="demo-simple-select"
|
id="demo-simple-select"
|
||||||
@ -332,12 +372,19 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
label="Group Type"
|
label="Group Type"
|
||||||
onChange={handleChangeGroupType}
|
onChange={handleChangeGroupType}
|
||||||
>
|
>
|
||||||
<MenuItem value={1}>Open (public)</MenuItem>
|
<MenuItem value={1}>
|
||||||
|
{t('group:group.open', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</MenuItem>
|
||||||
<MenuItem value={0}>
|
<MenuItem value={0}>
|
||||||
Closed (private) - users need permission to join
|
{t('group:group.closed', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -347,10 +394,15 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
}}
|
}}
|
||||||
onClick={() => setOpenAdvance((prev) => !prev)}
|
onClick={() => setOpenAdvance((prev) => !prev)}
|
||||||
>
|
>
|
||||||
<Typography>Advanced options</Typography>
|
<Typography>
|
||||||
|
{t('group:advanced_options', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
{openAdvance ? <ExpandLess /> : <ExpandMore />}
|
{openAdvance ? <ExpandLess /> : <ExpandMore />}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Collapse in={openAdvance} timeout="auto" unmountOnExit>
|
<Collapse in={openAdvance} timeout="auto" unmountOnExit>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -360,8 +412,9 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>
|
<Label>
|
||||||
Group Approval Threshold (number / percentage of Admins
|
{t('group:approval_threshold', {
|
||||||
that must approve a transaction)
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
labelId="demo-simple-select-label"
|
labelId="demo-simple-select-label"
|
||||||
@ -370,14 +423,21 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
label="Group Approval Threshold"
|
label="Group Approval Threshold"
|
||||||
onChange={handleChangeApprovalThreshold}
|
onChange={handleChangeApprovalThreshold}
|
||||||
>
|
>
|
||||||
<MenuItem value={0}>NONE</MenuItem>
|
<MenuItem value={0}>
|
||||||
<MenuItem value={1}>ONE </MenuItem>
|
{t('core.count.none', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
<MenuItem value={20}>20% </MenuItem>
|
})}
|
||||||
<MenuItem value={40}>40% </MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value={60}>60% </MenuItem>
|
<MenuItem value={1}>
|
||||||
<MenuItem value={80}>80% </MenuItem>
|
{t('core.count.one', {
|
||||||
<MenuItem value={100}>100% </MenuItem>
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={20}>20%</MenuItem>
|
||||||
|
<MenuItem value={40}>40%</MenuItem>
|
||||||
|
<MenuItem value={60}>60%</MenuItem>
|
||||||
|
<MenuItem value={80}>80%</MenuItem>
|
||||||
|
<MenuItem value={100}>100%</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@ -388,7 +448,9 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>
|
<Label>
|
||||||
Minimum Block delay for Group Transaction Approvals
|
{t('group.block_delay.minimum', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
labelId="demo-simple-select-label"
|
labelId="demo-simple-select-label"
|
||||||
@ -397,18 +459,42 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
label="Minimum Block delay"
|
label="Minimum Block delay"
|
||||||
onChange={handleChangeMinBlock}
|
onChange={handleChangeMinBlock}
|
||||||
>
|
>
|
||||||
<MenuItem value={5}>5 minutes</MenuItem>
|
<MenuItem value={5}>
|
||||||
<MenuItem value={10}>10 minutes</MenuItem>
|
{t('core.time.minute', { count: 5 })}
|
||||||
<MenuItem value={30}>30 minutes</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value={60}>1 hour</MenuItem>
|
<MenuItem value={10}>
|
||||||
<MenuItem value={180}>3 hours</MenuItem>
|
{t('core.time.minute', { count: 10 })}
|
||||||
<MenuItem value={300}>5 hours</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value={420}>7 hours</MenuItem>
|
<MenuItem value={30}>
|
||||||
<MenuItem value={720}>12 hours</MenuItem>
|
{t('core.time.minute', { count: 30 })}
|
||||||
<MenuItem value={1440}>1 day</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value={4320}>3 days</MenuItem>
|
<MenuItem value={60}>
|
||||||
<MenuItem value={7200}>5 days</MenuItem>
|
{t('core.time.hour', { count: 1 })}
|
||||||
<MenuItem value={10080}>7 days</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem value={180}>
|
||||||
|
{t('core.time.hour', { count: 3 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={300}>
|
||||||
|
{t('core.time.hour', { count: 5 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={420}>
|
||||||
|
{t('core.time.hour', { count: 7 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={720}>
|
||||||
|
{t('core.time.hour', { count: 12 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={1440}>
|
||||||
|
{t('core.time.day', { count: 1 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={4320}>
|
||||||
|
{t('core.time.day', { count: 3 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={7200}>
|
||||||
|
{t('core.time.day', { count: 5 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={10080}>
|
||||||
|
{t('core.time.day', { count: 7 })}
|
||||||
|
</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@ -419,7 +505,9 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>
|
<Label>
|
||||||
Maximum Block delay for Group Transaction Approvals
|
{t('group.block_delay.maximum', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
labelId="demo-simple-select-label"
|
labelId="demo-simple-select-label"
|
||||||
@ -428,17 +516,39 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
label="Maximum Block delay"
|
label="Maximum Block delay"
|
||||||
onChange={handleChangeMaxBlock}
|
onChange={handleChangeMaxBlock}
|
||||||
>
|
>
|
||||||
<MenuItem value={60}>1 hour</MenuItem>
|
<MenuItem value={60}>
|
||||||
<MenuItem value={180}>3 hours</MenuItem>
|
{t('core.time.hour', { count: 1 })}
|
||||||
<MenuItem value={300}>5 hours</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value={420}>7 hours</MenuItem>
|
<MenuItem value={180}>
|
||||||
<MenuItem value={720}>12 hours</MenuItem>
|
3{t('core.time.hour', { count: 3 })}
|
||||||
<MenuItem value={1440}>1 day</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value={4320}>3 days</MenuItem>
|
<MenuItem value={300}>
|
||||||
<MenuItem value={7200}>5 days</MenuItem>
|
{t('core.time.hour', { count: 5 })}
|
||||||
<MenuItem value={10080}>7 days</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value={14400}>10 days</MenuItem>
|
<MenuItem value={420}>
|
||||||
<MenuItem value={21600}>15 days</MenuItem>
|
{t('core.time.hour', { count: 7 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={720}>
|
||||||
|
{t('core.time.hour', { count: 12 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={1440}>
|
||||||
|
{t('core.time.day', { count: 1 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={4320}>
|
||||||
|
{t('core.time.day', { count: 3 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={7200}>
|
||||||
|
{t('core.time.day', { count: 5 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={10080}>
|
||||||
|
{t('core.time.day', { count: 7 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={14400}>
|
||||||
|
{t('core.time.day', { count: 10 })}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={21600}>
|
||||||
|
{t('core.time.day', { count: 15 })}
|
||||||
|
</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
</Box>
|
</Box>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
@ -454,7 +564,9 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleCreateGroup}
|
onClick={handleCreateGroup}
|
||||||
>
|
>
|
||||||
Create Group
|
{t('group.action.create', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@ -503,6 +615,6 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
setInfo={setInfoSnack}
|
setInfo={setInfoSnack}
|
||||||
/>
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</React.Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemButton,
|
ListItemButton,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
@ -8,7 +7,7 @@ import {
|
|||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import React, {
|
import {
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
@ -25,10 +24,12 @@ import {
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { MyContext, getBaseApiReact } from '../../App';
|
import { MyContext, getBaseApiReact } from '../../App';
|
||||||
import { LoadingButton } from '@mui/lab';
|
import { LoadingButton } from '@mui/lab';
|
||||||
import { getBaseApi, getFee } from '../../background';
|
import { getFee } from '../../background';
|
||||||
import LockIcon from '@mui/icons-material/Lock';
|
import LockIcon from '@mui/icons-material/Lock';
|
||||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const cache = new CellMeasurerCache({
|
const cache = new CellMeasurerCache({
|
||||||
fixedWidth: true,
|
fixedWidth: true,
|
||||||
defaultHeight: 50,
|
defaultHeight: 50,
|
||||||
@ -36,7 +37,7 @@ const cache = new CellMeasurerCache({
|
|||||||
|
|
||||||
export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
||||||
const { memberGroups, show, setTxList } = useContext(MyContext);
|
const { memberGroups, show, setTxList } = useContext(MyContext);
|
||||||
|
const { t } = useTranslation(['core', 'group']);
|
||||||
const [groups, setGroups] = useState([]);
|
const [groups, setGroups] = useState([]);
|
||||||
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to
|
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 [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
|
||||||
@ -101,12 +102,17 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
const handleJoinGroup = async (group, isOpen) => {
|
const handleJoinGroup = async (group, isOpen) => {
|
||||||
try {
|
try {
|
||||||
const groupId = group.groupId;
|
const groupId = group.groupId;
|
||||||
const fee = await getFee('JOIN_GROUP'); // TODO translate
|
|
||||||
|
const fee = await getFee('JOIN_GROUP');
|
||||||
|
|
||||||
await show({
|
await show({
|
||||||
message: 'Would you like to perform an JOIN_GROUP transaction?',
|
message: t('group:question.join_group', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
window
|
window
|
||||||
.sendMessage('joinGroup', {
|
.sendMessage('joinGroup', {
|
||||||
@ -116,8 +122,9 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message:
|
message: t('group:message.success.join_group', {
|
||||||
'Successfully requested to join group. It may take a couple of minutes for the changes to propagate',
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
@ -125,8 +132,14 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
{
|
{
|
||||||
...response,
|
...response,
|
||||||
type: 'joined-group',
|
type: 'joined-group',
|
||||||
label: `Joined Group ${group?.groupName}: awaiting confirmation`,
|
label: t('group:message.success.group_join_label', {
|
||||||
labelDone: `Joined Group ${group?.groupName}: success!`,
|
group_name: group?.groupName,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
|
labelDone: t('group:message.success.group_join_label', {
|
||||||
|
group_name: group?.groupName,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
done: false,
|
done: false,
|
||||||
groupId,
|
groupId,
|
||||||
},
|
},
|
||||||
@ -215,7 +228,10 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
padding: '10px',
|
padding: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>Join {group?.groupName}</Typography>
|
<Typography>
|
||||||
|
{t('core:action.join', { postProcess: 'capitalize' })}{' '}
|
||||||
|
{group?.groupName}
|
||||||
|
</Typography>
|
||||||
<Typography>
|
<Typography>
|
||||||
{group?.isOpen === false &&
|
{group?.isOpen === false &&
|
||||||
'This is a closed/private group, so you will need to wait until an admin accepts your request'}
|
'This is a closed/private group, so you will need to wait until an admin accepts your request'}
|
||||||
@ -226,7 +242,9 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => handleJoinGroup(group, group?.isOpen)}
|
onClick={() => handleJoinGroup(group, group?.isOpen)}
|
||||||
>
|
>
|
||||||
Join group
|
{t('group:action.join_group', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
@ -129,7 +129,7 @@ export const BlockedUsersModal = () => {
|
|||||||
executeEvent('updateChatMessagesWithBlocks', true);
|
executeEvent('updateChatMessagesWithBlocks', true);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setOpenSnackGlobal(true); // TODO translate
|
setOpenSnackGlobal(true);
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: error?.message || 'Unable to block user',
|
message: error?.message || 'Unable to block user',
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, {
|
import React, {
|
||||||
FC,
|
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
@ -17,7 +16,6 @@ import {
|
|||||||
ComposeIcon,
|
ComposeIcon,
|
||||||
ComposeP,
|
ComposeP,
|
||||||
GroupContainer,
|
GroupContainer,
|
||||||
GroupNameP,
|
|
||||||
InstanceFooter,
|
InstanceFooter,
|
||||||
InstanceListContainer,
|
InstanceListContainer,
|
||||||
InstanceListContainerRow,
|
InstanceListContainerRow,
|
||||||
@ -58,10 +56,12 @@ import { executeEvent } from '../../../utils/events';
|
|||||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App';
|
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App';
|
||||||
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group';
|
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const filterOptions = ['Recently active', 'Newest', 'Oldest'];
|
const filterOptions = ['Recently active', 'Newest', 'Oldest'];
|
||||||
|
|
||||||
export const threadIdentifier = 'DOCUMENT';
|
export const threadIdentifier = 'DOCUMENT';
|
||||||
|
|
||||||
export const GroupMail = ({
|
export const GroupMail = ({
|
||||||
selectedGroup,
|
selectedGroup,
|
||||||
userInfo,
|
userInfo,
|
||||||
@ -82,6 +82,7 @@ export const GroupMail = ({
|
|||||||
const anchorElInstanceFilter = useRef<any>(null);
|
const anchorElInstanceFilter = useRef<any>(null);
|
||||||
const [tempPublishedList, setTempPublishedList] = useState([]);
|
const [tempPublishedList, setTempPublishedList] = useState([]);
|
||||||
const dataPublishes = useRef({});
|
const dataPublishes = useRef({});
|
||||||
|
const { t } = useTranslation(['core']);
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const groupIdRef = useRef<any>(null);
|
const groupIdRef = useRef<any>(null);
|
||||||
@ -120,7 +121,9 @@ export const GroupMail = ({
|
|||||||
});
|
});
|
||||||
setTempPublishedList(tempData);
|
setTempPublishedList(tempData);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getEncryptedResource = async (
|
const getEncryptedResource = async (
|
||||||
@ -627,9 +630,9 @@ export const GroupMail = ({
|
|||||||
<ThreadContainer>
|
<ThreadContainer>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NewThread
|
<NewThread
|
||||||
@ -667,8 +670,8 @@ export const GroupMail = ({
|
|||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -682,6 +685,7 @@ export const GroupMail = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
|
|
||||||
{combinedListTempAndReal.map((thread) => {
|
{combinedListTempAndReal.map((thread) => {
|
||||||
@ -754,8 +758,8 @@ export const GroupMail = ({
|
|||||||
{filterMode === 'Recently active' && (
|
{filterMode === 'Recently active' && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ThreadSingleLastMessageP>
|
<ThreadSingleLastMessageP>
|
||||||
@ -776,16 +780,16 @@ export const GroupMail = ({
|
|||||||
}, 300);
|
}, 300);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
alignItems: 'center',
|
||||||
bottom: '2px',
|
|
||||||
right: '2px',
|
|
||||||
borderRadius: '5px',
|
|
||||||
backgroundColor: '#27282c',
|
backgroundColor: '#27282c',
|
||||||
|
borderRadius: '5px',
|
||||||
|
bottom: '2px',
|
||||||
|
cursor: 'pointer',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
alignItems: 'center',
|
|
||||||
padding: '5px',
|
padding: '5px',
|
||||||
cursor: 'pointer',
|
position: 'absolute',
|
||||||
|
right: '2px',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
background: 'rgba(255, 255, 255, 0.60)',
|
background: 'rgba(255, 255, 255, 0.60)',
|
||||||
},
|
},
|
||||||
@ -795,9 +799,11 @@ export const GroupMail = ({
|
|||||||
sx={{
|
sx={{
|
||||||
color: 'white',
|
color: 'white',
|
||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
}} // TODO translate
|
}}
|
||||||
>
|
>
|
||||||
Last page
|
{t('core:page.last', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
<ArrowForwardIosIcon
|
<ArrowForwardIosIcon
|
||||||
sx={{
|
sx={{
|
||||||
@ -828,7 +834,9 @@ export const GroupMail = ({
|
|||||||
<LoadingSnackbar
|
<LoadingSnackbar
|
||||||
open={isLoading}
|
open={isLoading}
|
||||||
info={{
|
info={{
|
||||||
message: 'Loading threads... please wait.',
|
message: t('group:message.success.loading_threads', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</GroupContainer>
|
</GroupContainer>
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { Box, CircularProgress, Input } from '@mui/material';
|
import { Box, CircularProgress, Input } from '@mui/material';
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
|
||||||
import ModalCloseSVG from '../../../assets/svgs/ModalClose.svg';
|
import ModalCloseSVG from '../../../assets/svgs/ModalClose.svg';
|
||||||
import ComposeIconSVG from '../../../assets/svgs/ComposeIcon.svg';
|
import ComposeIconSVG from '../../../assets/svgs/ComposeIcon.svg';
|
||||||
import {
|
import {
|
||||||
AttachmentContainer,
|
|
||||||
CloseContainer,
|
CloseContainer,
|
||||||
ComposeContainer,
|
ComposeContainer,
|
||||||
ComposeIcon,
|
ComposeIcon,
|
||||||
@ -30,6 +28,7 @@ import TipTap from '../../Chat/TipTap';
|
|||||||
import { MessageDisplay } from '../../Chat/MessageDisplay';
|
import { MessageDisplay } from '../../Chat/MessageDisplay';
|
||||||
import { CustomizedSnackbars } from '../../Snackbar/Snackbar';
|
import { CustomizedSnackbars } from '../../Snackbar/Snackbar';
|
||||||
import { saveTempPublish } from '../../Chat/GroupAnnouncements';
|
import { saveTempPublish } from '../../Chat/GroupAnnouncements';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 8 });
|
const uid = new ShortUniqueId({ length: 8 });
|
||||||
|
|
||||||
@ -129,6 +128,7 @@ export const encryptSingleFunc = async (data: string, secretKeyObject: any) => {
|
|||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NewThread = ({
|
export const NewThread = ({
|
||||||
groupInfo,
|
groupInfo,
|
||||||
members,
|
members,
|
||||||
@ -143,8 +143,8 @@ export const NewThread = ({
|
|||||||
setPostReply,
|
setPostReply,
|
||||||
isPrivate,
|
isPrivate,
|
||||||
}: NewMessageProps) => {
|
}: NewMessageProps) => {
|
||||||
|
const { t } = useTranslation(['core', 'group']);
|
||||||
const { show } = React.useContext(MyContext);
|
const { show } = React.useContext(MyContext);
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
const [isSending, setIsSending] = useState(false);
|
const [isSending, setIsSending] = useState(false);
|
||||||
@ -183,21 +183,28 @@ export const NewThread = ({
|
|||||||
const missingFields: string[] = [];
|
const missingFields: string[] = [];
|
||||||
|
|
||||||
if (!isMessage && !threadTitle) {
|
if (!isMessage && !threadTitle) {
|
||||||
errorMsg = 'Please provide a thread title';
|
errorMsg = t('group:question.provide_thread', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
errorMsg = 'Cannot send a message without a access to your name';
|
errorMsg = t('group:message.error.access_name', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!groupInfo) {
|
if (!groupInfo) {
|
||||||
errorMsg = 'Cannot access group information';
|
errorMsg = t('group:message.error.group_info', {
|
||||||
} // TODO translate
|
postProcess: 'capitalize',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// if (!description) missingFields.push('subject')
|
// if (!description) missingFields.push('subject')
|
||||||
if (missingFields.length > 0) {
|
if (missingFields.length > 0) {
|
||||||
const missingFieldsString = missingFields.join(', ');
|
const missingFieldsString = missingFields.join(', ');
|
||||||
const errMsg = `Missing: ${missingFieldsString}`;
|
const errMsg = `Missing: ${missingFieldsString}`;
|
||||||
errorMsg = errMsg;
|
errorMsg = errMsg; // TODO translate
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorMsg) {
|
if (errorMsg) {
|
||||||
|
@ -3,7 +3,6 @@ import { Avatar, Box, IconButton } from '@mui/material';
|
|||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import FormatQuoteIcon from '@mui/icons-material/FormatQuote';
|
import FormatQuoteIcon from '@mui/icons-material/FormatQuote';
|
||||||
import MoreSVG from '../../../assets/svgs/More.svg';
|
import MoreSVG from '../../../assets/svgs/More.svg';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MoreImg,
|
MoreImg,
|
||||||
MoreP,
|
MoreP,
|
||||||
@ -38,16 +37,16 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
|||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
alignItems: 'flex-start',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'flex-start',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
|
display: 'flex',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -67,6 +66,7 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
|||||||
{message?.name?.charAt(0)}
|
{message?.name?.charAt(0)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</WrapperUserAction>
|
</WrapperUserAction>
|
||||||
|
|
||||||
<ThreadInfoColumn>
|
<ThreadInfoColumn>
|
||||||
<WrapperUserAction
|
<WrapperUserAction
|
||||||
disabled={myName === message?.name}
|
disabled={myName === message?.name}
|
||||||
@ -75,6 +75,7 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
|||||||
>
|
>
|
||||||
<ThreadInfoColumnNameP>{message?.name}</ThreadInfoColumnNameP>
|
<ThreadInfoColumnNameP>{message?.name}</ThreadInfoColumnNameP>
|
||||||
</WrapperUserAction>
|
</WrapperUserAction>
|
||||||
|
|
||||||
<ThreadInfoColumnTime>
|
<ThreadInfoColumnTime>
|
||||||
{formatTimestampForum(message?.created)}
|
{formatTimestampForum(message?.created)}
|
||||||
</ThreadInfoColumnTime>
|
</ThreadInfoColumnTime>
|
||||||
@ -205,6 +206,7 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
|||||||
>
|
>
|
||||||
{message?.reply?.name?.charAt(0)}
|
{message?.reply?.name?.charAt(0)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|
||||||
<ThreadInfoColumn>
|
<ThreadInfoColumn>
|
||||||
<ThreadInfoColumnNameP
|
<ThreadInfoColumnNameP
|
||||||
sx={{
|
sx={{
|
||||||
@ -215,6 +217,7 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
|||||||
</ThreadInfoColumnNameP>
|
</ThreadInfoColumnNameP>
|
||||||
</ThreadInfoColumn>
|
</ThreadInfoColumn>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<MessageDisplay htmlContent={message?.reply?.textContentV2} />
|
<MessageDisplay htmlContent={message?.reply?.textContentV2} />
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
@ -1,20 +1,11 @@
|
|||||||
import React, {
|
import React, {
|
||||||
FC,
|
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import {
|
import { Avatar, Box, Button, ButtonBase, Typography } from '@mui/material';
|
||||||
Avatar,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
ButtonBase,
|
|
||||||
IconButton,
|
|
||||||
Skeleton,
|
|
||||||
Typography,
|
|
||||||
} from '@mui/material';
|
|
||||||
import { ShowMessage } from './ShowMessageWithoutModal';
|
import { ShowMessage } from './ShowMessageWithoutModal';
|
||||||
import {
|
import {
|
||||||
ComposeP,
|
ComposeP,
|
||||||
@ -51,8 +42,12 @@ import { RequestQueueWithPromise } from '../../../utils/queue/queue';
|
|||||||
import { CustomLoader } from '../../../common/CustomLoader';
|
import { CustomLoader } from '../../../common/CustomLoader';
|
||||||
import { WrapperUserAction } from '../../WrapperUserAction';
|
import { WrapperUserAction } from '../../WrapperUserAction';
|
||||||
import { formatTimestampForum } from '../../../utils/time';
|
import { formatTimestampForum } from '../../../utils/time';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const requestQueueSaveToLocal = new RequestQueueWithPromise(1);
|
const requestQueueSaveToLocal = new RequestQueueWithPromise(1);
|
||||||
|
|
||||||
const requestQueueDownloadPost = new RequestQueueWithPromise(3);
|
const requestQueueDownloadPost = new RequestQueueWithPromise(3);
|
||||||
|
|
||||||
interface ThreadProps {
|
interface ThreadProps {
|
||||||
currentThread: any;
|
currentThread: any;
|
||||||
groupInfo: any;
|
groupInfo: any;
|
||||||
@ -120,6 +115,7 @@ export const Thread = ({
|
|||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [postReply, setPostReply] = useState(null);
|
const [postReply, setPostReply] = useState(null);
|
||||||
const [hasLastPage, setHasLastPage] = useState(false);
|
const [hasLastPage, setHasLastPage] = useState(false);
|
||||||
|
const { t } = useTranslation(['core']);
|
||||||
|
|
||||||
// Update: Use a new ref for the scrollable container
|
// Update: Use a new ref for the scrollable container
|
||||||
const threadContainerRef = useRef(null);
|
const threadContainerRef = useRef(null);
|
||||||
@ -251,6 +247,7 @@ export const Thread = ({
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
|
|
||||||
let fullArrayMsg = [...responseData];
|
let fullArrayMsg = [...responseData];
|
||||||
@ -431,6 +428,7 @@ export const Thread = ({
|
|||||||
}
|
}
|
||||||
const newArray = responseData.slice(0, findMessage).reverse();
|
const newArray = responseData.slice(0, findMessage).reverse();
|
||||||
let fullArrayMsg = [...messages];
|
let fullArrayMsg = [...messages];
|
||||||
|
|
||||||
for (const message of newArray) {
|
for (const message of newArray) {
|
||||||
try {
|
try {
|
||||||
const responseDataMessage = await getEncryptedResource({
|
const responseDataMessage = await getEncryptedResource({
|
||||||
@ -468,7 +466,6 @@ export const Thread = ({
|
|||||||
setMessages(fullArrayMsg);
|
setMessages(fullArrayMsg);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[messages]
|
[messages]
|
||||||
@ -565,20 +562,20 @@ export const Thread = ({
|
|||||||
return (
|
return (
|
||||||
<GroupContainer
|
<GroupContainer
|
||||||
sx={{
|
sx={{
|
||||||
position: 'relative',
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
position: 'relative',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
// Removed the ref from here since the scrollable area has changed
|
// Removed the ref from here since the scrollable area has changed
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
flexShrink: 0, // Corrected property name
|
flexShrink: 0, // Corrected property name
|
||||||
|
justifyContent: 'space-between',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NewThread
|
<NewThread
|
||||||
@ -598,9 +595,9 @@ export const Thread = ({
|
|||||||
/>
|
/>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '35px',
|
gap: '35px',
|
||||||
alignItems: 'center',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ShowMessageReturnButton
|
<ShowMessageReturnButton
|
||||||
@ -610,7 +607,11 @@ export const Thread = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MailIconImg src={ReturnSVG} />
|
<MailIconImg src={ReturnSVG} />
|
||||||
<ComposeP>Return to Threads</ComposeP>
|
<ComposeP>
|
||||||
|
{t('group:action.return_to_thread', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</ComposeP>
|
||||||
</ShowMessageReturnButton>
|
</ShowMessageReturnButton>
|
||||||
{/* Conditionally render the scroll buttons */}
|
{/* Conditionally render the scroll buttons */}
|
||||||
{showScrollButton &&
|
{showScrollButton &&
|
||||||
@ -658,6 +659,7 @@ export const Thread = ({
|
|||||||
>
|
>
|
||||||
<GroupNameP>{currentThread?.threadData?.title}</GroupNameP>
|
<GroupNameP>{currentThread?.threadData?.title}</GroupNameP>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height={'15px'} />
|
<Spacer height={'15px'} />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
@ -685,8 +687,9 @@ export const Thread = ({
|
|||||||
disabled={!hasFirstPage}
|
disabled={!hasFirstPage}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
First
|
{t('core:page.first', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
textTransformation: 'capitalize',
|
textTransformation: 'capitalize',
|
||||||
@ -701,9 +704,9 @@ export const Thread = ({
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
disabled={!hasPreviousPage}
|
disabled={!hasPreviousPage}
|
||||||
variant="contained" // TODO translate
|
variant="contained"
|
||||||
>
|
>
|
||||||
Previous
|
{t('core:page.previous', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
@ -721,7 +724,7 @@ export const Thread = ({
|
|||||||
disabled={!hasNextPage}
|
disabled={!hasNextPage}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Next
|
{t('core:page.next', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
@ -739,7 +742,7 @@ export const Thread = ({
|
|||||||
disabled={!hasLastPage}
|
disabled={!hasLastPage}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Last
|
{t('core:page.last', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -925,7 +928,7 @@ export const Thread = ({
|
|||||||
color: 'white',
|
color: 'white',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Downloading from QDN
|
{t('core:downloading_qdn', { postProcess: 'capitalize' })}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@ -959,7 +962,9 @@ export const Thread = ({
|
|||||||
color: 'white',
|
color: 'white',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Refetch page
|
{t('group:action.refetch_page', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
@ -997,7 +1002,7 @@ export const Thread = ({
|
|||||||
disabled={!hasFirstPage}
|
disabled={!hasFirstPage}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
First
|
{t('core:page.first', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
@ -1015,7 +1020,7 @@ export const Thread = ({
|
|||||||
disabled={!hasPreviousPage}
|
disabled={!hasPreviousPage}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Previous
|
{t('core:page.previous', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
@ -1033,7 +1038,7 @@ export const Thread = ({
|
|||||||
disabled={!hasNextPage}
|
disabled={!hasNextPage}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Next
|
{t('core:page.next', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
@ -1051,7 +1056,7 @@ export const Thread = ({
|
|||||||
disabled={!hasLastPage}
|
disabled={!hasLastPage}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Last
|
{t('core:page.last', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
@ -1063,7 +1068,7 @@ export const Thread = ({
|
|||||||
<LoadingSnackbar
|
<LoadingSnackbar
|
||||||
open={isLoading}
|
open={isLoading}
|
||||||
info={{
|
info={{
|
||||||
message: 'Loading posts... please wait.',
|
message: t('core:loading_posts', { postProcess: 'capitalize' }),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</GroupContainer>
|
</GroupContainer>
|
||||||
|
@ -55,8 +55,6 @@ import { RequestQueueWithPromise } from '../../utils/queue/queue';
|
|||||||
import { WebSocketActive } from './WebsocketActive';
|
import { WebSocketActive } from './WebsocketActive';
|
||||||
import { useMessageQueue } from '../../MessageQueueContext';
|
import { useMessageQueue } from '../../MessageQueueContext';
|
||||||
import { ContextMenu } from '../ContextMenu';
|
import { ContextMenu } from '../ContextMenu';
|
||||||
import { ReturnIcon } from '../../assets/Icons/ReturnIcon';
|
|
||||||
import { ExitIcon } from '../../assets/Icons/ExitIcon';
|
|
||||||
import { HomeDesktop } from './HomeDesktop';
|
import { HomeDesktop } from './HomeDesktop';
|
||||||
import { IconWrapper } from '../Desktop/DesktopFooter';
|
import { IconWrapper } from '../Desktop/DesktopFooter';
|
||||||
import { DesktopHeader } from '../Desktop/DesktopHeader';
|
import { DesktopHeader } from '../Desktop/DesktopHeader';
|
||||||
@ -80,6 +78,7 @@ import LockIcon from '@mui/icons-material/Lock';
|
|||||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||||
import { BlockedUsersModal } from './BlockedUsersModal';
|
import { BlockedUsersModal } from './BlockedUsersModal';
|
||||||
import { WalletsAppWrapper } from './WalletsAppWrapper';
|
import { WalletsAppWrapper } from './WalletsAppWrapper';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const getPublishesFromAdmins = async (admins: string[], groupId) => {
|
export const getPublishesFromAdmins = async (admins: string[], groupId) => {
|
||||||
const queryString = admins.map((name) => `name=${name}`).join('&');
|
const queryString = admins.map((name) => `name=${name}`).join('&');
|
||||||
@ -450,6 +449,7 @@ export const Group = ({
|
|||||||
const [isOpenSideViewGroups, setIsOpenSideViewGroups] = useState(false);
|
const [isOpenSideViewGroups, setIsOpenSideViewGroups] = useState(false);
|
||||||
const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] =
|
const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
const { t } = useTranslation(['core', 'group']);
|
||||||
|
|
||||||
const [groupsProperties, setGroupsProperties] =
|
const [groupsProperties, setGroupsProperties] =
|
||||||
useRecoilState(groupsPropertiesAtom);
|
useRecoilState(groupsPropertiesAtom);
|
||||||
@ -2219,9 +2219,10 @@ export const Group = ({
|
|||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
No group selected
|
{t('group:message.generic.no_selection', {
|
||||||
</Typography>{' '}
|
postProcess: 'capitalize',
|
||||||
// TODO translate
|
})}
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -2317,9 +2318,9 @@ export const Group = ({
|
|||||||
>
|
>
|
||||||
{' '}
|
{' '}
|
||||||
<Typography>
|
<Typography>
|
||||||
The group's first common encryption key is in the process
|
{t('group:message.generic.encryption_key', {
|
||||||
of creation. Please wait a few minutes for it to be
|
postProcess: 'capitalize',
|
||||||
retrieved by the network. Checking every 2 minutes...
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -2343,18 +2344,23 @@ export const Group = ({
|
|||||||
>
|
>
|
||||||
{' '}
|
{' '}
|
||||||
<Typography>
|
<Typography>
|
||||||
You are not part of the encrypted group of members. Wait
|
{t('group:message.generic.not_part_group', {
|
||||||
until an admin re-encrypts the keys.
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Spacer height="25px" />
|
<Spacer height="25px" />
|
||||||
<Typography>
|
<Typography>
|
||||||
<strong>
|
<strong>
|
||||||
Only unencrypted messages will be displayed.
|
{t('group:message.generic.only_encrypted', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</strong>
|
</strong>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Spacer height="25px" />
|
<Spacer height="25px" />
|
||||||
<Typography>
|
<Typography>
|
||||||
Try notifying an admin from the list of admins below:
|
{t('group:message.generic.notify_admins', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Spacer height="25px" />
|
<Spacer height="25px" />
|
||||||
{adminsWithNames.map((admin) => {
|
{adminsWithNames.map((admin) => {
|
||||||
@ -2374,7 +2380,9 @@ export const Group = ({
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => notifyAdmin(admin)}
|
onClick={() => notifyAdmin(admin)}
|
||||||
>
|
>
|
||||||
Notify
|
{t('core:action.notify', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@ -2594,14 +2602,19 @@ export const Group = ({
|
|||||||
open={isLoadingGroup}
|
open={isLoadingGroup}
|
||||||
info={{
|
info={{
|
||||||
message:
|
message:
|
||||||
isLoadingGroupMessage || 'Setting up group... please wait.',
|
isLoadingGroupMessage ||
|
||||||
|
t('group:message.generic.setting_group', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LoadingSnackbar
|
<LoadingSnackbar
|
||||||
open={isLoadingGroups}
|
open={isLoadingGroups}
|
||||||
info={{
|
info={{
|
||||||
message: 'Setting up groups... please wait.',
|
message: t('group:message.generic.setting_group', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<WalletsAppWrapper />
|
<WalletsAppWrapper />
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as React from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import List from '@mui/material/List';
|
import List from '@mui/material/List';
|
||||||
import ListItem from '@mui/material/ListItem';
|
import ListItem from '@mui/material/ListItem';
|
||||||
import ListItemButton from '@mui/material/ListItemButton';
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
@ -12,14 +12,13 @@ import { CustomLoader } from '../../common/CustomLoader';
|
|||||||
import { getBaseApiReact } from '../../App';
|
import { getBaseApiReact } from '../../App';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
||||||
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState(
|
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = useState([]);
|
||||||
[]
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
);
|
|
||||||
const [isExpanded, setIsExpanded] = React.useState(false);
|
|
||||||
|
|
||||||
const [loading, setLoading] = React.useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
const getJoinRequests = async () => {
|
const getJoinRequests = async () => {
|
||||||
try {
|
try {
|
||||||
@ -38,9 +37,10 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { t } = useTranslation(['core', 'group']);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
if (myAddress) {
|
if (myAddress) {
|
||||||
getJoinRequests();
|
getJoinRequests();
|
||||||
}
|
}
|
||||||
@ -69,9 +69,9 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
|||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: '1rem',
|
fontSize: '1rem',
|
||||||
}} // TODO translate
|
}}
|
||||||
>
|
>
|
||||||
Group Invites{' '}
|
{t('group:group_invites', { postProcess: 'capitalize' })}{' '}
|
||||||
{groupsWithJoinRequests?.length > 0 &&
|
{groupsWithJoinRequests?.length > 0 &&
|
||||||
` (${groupsWithJoinRequests?.length})`}
|
` (${groupsWithJoinRequests?.length})`}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -130,7 +130,9 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
|||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Nothing to display
|
{t('group:message.generic.no_display', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@ -177,7 +179,10 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
|||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
primary={`${group?.groupName} has invited you`}
|
primary={t('group:message.generic.group_invited_you', {
|
||||||
|
group: group?.groupName,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
@ -14,6 +14,7 @@ import { myGroupsWhereIAmAdminAtom } from '../../atoms/global';
|
|||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2);
|
export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2);
|
||||||
|
|
||||||
export const GroupJoinRequests = ({
|
export const GroupJoinRequests = ({
|
||||||
@ -27,7 +28,7 @@ export const GroupJoinRequests = ({
|
|||||||
setDesktopViewMode,
|
setDesktopViewMode,
|
||||||
}) => {
|
}) => {
|
||||||
const [isExpanded, setIsExpanded] = React.useState(false);
|
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||||
|
const { t } = useTranslation(['core', 'group']);
|
||||||
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState(
|
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState(
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@ -139,9 +140,9 @@ export const GroupJoinRequests = ({
|
|||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: '1rem',
|
fontSize: '1rem',
|
||||||
}} // TODO translate
|
}}
|
||||||
>
|
>
|
||||||
Join Requests{' '}
|
{t('group:join_requests', { postProcess: 'capitalize' })}{' '}
|
||||||
{filteredJoinRequests?.filter((group) => group?.data?.length > 0)
|
{filteredJoinRequests?.filter((group) => group?.data?.length > 0)
|
||||||
?.length > 0 &&
|
?.length > 0 &&
|
||||||
` (${filteredJoinRequests?.filter((group) => group?.data?.length > 0)?.length})`}
|
` (${filteredJoinRequests?.filter((group) => group?.data?.length > 0)?.length})`}
|
||||||
@ -163,14 +164,13 @@ export const GroupJoinRequests = ({
|
|||||||
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
|
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '322px',
|
bgcolor: 'background.paper',
|
||||||
height: '250px',
|
borderRadius: '19px',
|
||||||
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
bgcolor: 'background.paper',
|
height: '250px',
|
||||||
padding: '20px',
|
padding: '20px',
|
||||||
borderRadius: '19px',
|
width: '322px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading && filteredJoinRequests.length === 0 && (
|
{loading && filteredJoinRequests.length === 0 && (
|
||||||
@ -204,18 +204,20 @@ export const GroupJoinRequests = ({
|
|||||||
color: 'rgba(255, 255, 255, 0.2)',
|
color: 'rgba(255, 255, 255, 0.2)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Nothing to display
|
{t('group:message.generic.no_display', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<List
|
<List
|
||||||
className="scrollable-container"
|
className="scrollable-container"
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
maxWidth: 360,
|
|
||||||
bgcolor: 'background.paper',
|
bgcolor: 'background.paper',
|
||||||
maxHeight: '300px',
|
maxHeight: '300px',
|
||||||
|
maxWidth: 360,
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{filteredJoinRequests?.map((group) => {
|
{filteredJoinRequests?.map((group) => {
|
||||||
|
@ -4,16 +4,21 @@ import { useState } from 'react';
|
|||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
import { Label } from './AddGroup';
|
import { Label } from './AddGroup';
|
||||||
import { getFee } from '../../background';
|
import { getFee } from '../../background';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
const [expiryTime, setExpiryTime] = useState<string>('259200');
|
const [expiryTime, setExpiryTime] = useState<string>('259200');
|
||||||
const [isLoadingInvite, setIsLoadingInvite] = useState(false);
|
const [isLoadingInvite, setIsLoadingInvite] = useState(false);
|
||||||
|
const { t } = useTranslation(['core', 'group']);
|
||||||
|
|
||||||
const inviteMember = async () => {
|
const inviteMember = async () => {
|
||||||
try {
|
try {
|
||||||
const fee = await getFee('GROUP_INVITE');
|
const fee = await getFee('GROUP_INVITE');
|
||||||
await show({
|
await show({
|
||||||
message: 'Would you like to perform a GROUP_INVITE transaction?',
|
message: t('group:question.group_invite', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
setIsLoadingInvite(true);
|
setIsLoadingInvite(true);
|
||||||
@ -27,10 +32,12 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
// TODO translate
|
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: `Successfully invited ${value}. It may take a couple of minutes for the changes to propagate`,
|
message: t('group:message.success.group_invite', {
|
||||||
|
value: value,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
res(response);
|
res(response);
|
||||||
@ -72,7 +79,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Invite member
|
{t('group:action.invite_member', { postProcess: 'capitalize' })}
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Input
|
<Input
|
||||||
value={value}
|
value={value}
|
||||||
@ -80,24 +87,26 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
onChange={(e) => setValue(e.target.value)}
|
onChange={(e) => setValue(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Label>Invitation Expiry Time</Label>
|
<Label>
|
||||||
|
{t('group:invitation_expiry', { postProcess: 'capitalize' })}
|
||||||
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
labelId="demo-simple-select-label"
|
labelId="demo-simple-select-label"
|
||||||
id="demo-simple-select"
|
id="demo-simple-select"
|
||||||
value={expiryTime}
|
value={expiryTime}
|
||||||
label="Invitation Expiry Time"
|
label={t('group:invitation_expiry', { postProcess: 'capitalize' })}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
>
|
>
|
||||||
<MenuItem value={10800}>3 hours</MenuItem>
|
<MenuItem value={10800}>{t('core.time.hour', { count: 3 })}</MenuItem>
|
||||||
<MenuItem value={21600}>6 hours</MenuItem>
|
<MenuItem value={21600}>{t('core.time.hour', { count: 6 })}</MenuItem>
|
||||||
<MenuItem value={43200}>12 hours</MenuItem>
|
<MenuItem value={43200}>{t('core.time.hour', { count: 12 })}</MenuItem>
|
||||||
<MenuItem value={86400}>1 day</MenuItem>
|
<MenuItem value={86400}>{t('core.time.day', { count: 1 })}</MenuItem>
|
||||||
<MenuItem value={259200}>3 days</MenuItem>
|
<MenuItem value={259200}>{t('core.time.day', { count: 3 })}</MenuItem>
|
||||||
<MenuItem value={432000}>5 days</MenuItem>
|
<MenuItem value={432000}>{t('core.time.day', { count: 5 })}</MenuItem>
|
||||||
<MenuItem value={604800}>7 days</MenuItem>
|
<MenuItem value={604800}>{t('core.time.day', { count: 7 })}</MenuItem>
|
||||||
<MenuItem value={864000}>10 days</MenuItem>
|
<MenuItem value={864000}>{t('core.time.day', { count: 10 })}</MenuItem>
|
||||||
<MenuItem value={1296000}>15 days</MenuItem>
|
<MenuItem value={1296000}>{t('core.time.day', { count: 15 })}</MenuItem>
|
||||||
<MenuItem value={2592000}>30 days</MenuItem>
|
<MenuItem value={2592000}>{t('core.time.day', { count: 30 })}</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
@ -106,7 +115,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
loading={isLoadingInvite}
|
loading={isLoadingInvite}
|
||||||
onClick={inviteMember}
|
onClick={inviteMember}
|
||||||
>
|
>
|
||||||
Invite
|
{t('core:action.invite', { postProcess: 'capitalize' })}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -18,6 +18,7 @@ import { getNameInfo } from './Group';
|
|||||||
import { getFee } from '../../background';
|
import { getFee } from '../../background';
|
||||||
import { LoadingButton } from '@mui/lab';
|
import { LoadingButton } from '@mui/lab';
|
||||||
import { getBaseApiReact } from '../../App';
|
import { getBaseApiReact } from '../../App';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const getMemberInvites = async (groupNumber) => {
|
export const getMemberInvites = async (groupNumber) => {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@ -55,6 +56,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
|
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
|
||||||
const listRef = useRef();
|
const listRef = useRef();
|
||||||
const [isLoadingUnban, setIsLoadingUnban] = useState(false);
|
const [isLoadingUnban, setIsLoadingUnban] = useState(false);
|
||||||
|
const { t } = useTranslation(['core', 'group']);
|
||||||
|
|
||||||
const getInvites = async (groupId) => {
|
const getInvites = async (groupId) => {
|
||||||
try {
|
try {
|
||||||
@ -84,10 +86,9 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
|
|
||||||
const handleCancelBan = async (address) => {
|
const handleCancelBan = async (address) => {
|
||||||
try {
|
try {
|
||||||
// TODO translate
|
|
||||||
const fee = await getFee('CANCEL_GROUP_BAN');
|
const fee = await getFee('CANCEL_GROUP_BAN');
|
||||||
await show({
|
await show({
|
||||||
message: 'Would you like to perform a CANCEL_GROUP_BAN transaction?',
|
message: t('group:question.cancel_ban', { postProcess: 'capitalize' }),
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
setIsLoadingUnban(true);
|
setIsLoadingUnban(true);
|
||||||
@ -103,8 +104,9 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
setIsLoadingUnban(false);
|
setIsLoadingUnban(false);
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message:
|
message: t('group:message.success.unbanned_user', {
|
||||||
'Successfully unbanned user. It may take a couple of minutes for the changes to propagate',
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
handlePopoverClose();
|
handlePopoverClose();
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
@ -127,6 +129,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingUnban(false);
|
setIsLoadingUnban(false);
|
||||||
}
|
}
|
||||||
@ -177,10 +180,13 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => handleCancelBan(member?.offender)}
|
onClick={() => handleCancelBan(member?.offender)}
|
||||||
>
|
>
|
||||||
Cancel Ban
|
{t('group:action.cancel_ban', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
onClick={(event) => handlePopoverOpen(event, index)}
|
onClick={(event) => handlePopoverOpen(event, index)}
|
||||||
>
|
>
|
||||||
@ -205,7 +211,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>Ban list</p>
|
<p>{t('group:ban_list', { postProcess: 'capitalize' })}</p>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as React from 'react';
|
import { forwardRef, Fragment, ReactElement, Ref, useEffect } from 'react';
|
||||||
import Dialog from '@mui/material/Dialog';
|
import Dialog from '@mui/material/Dialog';
|
||||||
import AppBar from '@mui/material/AppBar';
|
import AppBar from '@mui/material/AppBar';
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
@ -11,13 +11,6 @@ import { Box, FormControlLabel, Switch, styled, useTheme } from '@mui/material';
|
|||||||
import { enabledDevModeAtom } from '../../atoms/global';
|
import { enabledDevModeAtom } from '../../atoms/global';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
function a11yProps(index: number) {
|
|
||||||
return {
|
|
||||||
id: `simple-tab-${index}`,
|
|
||||||
'aria-controls': `simple-tabpanel-${index}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const LocalNodeSwitch = styled(Switch)(({ theme }) => ({
|
const LocalNodeSwitch = styled(Switch)(({ theme }) => ({
|
||||||
padding: 8,
|
padding: 8,
|
||||||
'& .MuiSwitch-track': {
|
'& .MuiSwitch-track': {
|
||||||
@ -51,11 +44,11 @@ const LocalNodeSwitch = styled(Switch)(({ theme }) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const Transition = React.forwardRef(function Transition(
|
const Transition = forwardRef(function Transition(
|
||||||
props: TransitionProps & {
|
props: TransitionProps & {
|
||||||
children: React.ReactElement;
|
children: ReactElement;
|
||||||
},
|
},
|
||||||
ref: React.Ref<unknown>
|
ref: Ref<unknown>
|
||||||
) {
|
) {
|
||||||
return <Slide direction="up" ref={ref} {...props} />;
|
return <Slide direction="up" ref={ref} {...props} />;
|
||||||
});
|
});
|
||||||
@ -118,12 +111,12 @@ export const Settings = ({ address, open, setOpen }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
getUserSettings();
|
getUserSettings();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<Fragment>
|
||||||
<Dialog
|
<Dialog
|
||||||
fullScreen
|
fullScreen
|
||||||
open={open}
|
open={open}
|
||||||
@ -192,6 +185,6 @@ export const Settings = ({ address, open, setOpen }) => {
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</React.Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -141,21 +141,6 @@ export const ThingsToDoInitial = ({
|
|||||||
outline: '1px solid rgba(9, 182, 232, 1)',
|
outline: '1px solid rgba(9, 182, 232, 1)',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* <Checkbox
|
|
||||||
edge="start"
|
|
||||||
checked={checked1}
|
|
||||||
tabIndex={-1}
|
|
||||||
disableRipple
|
|
||||||
disabled={true}
|
|
||||||
sx={{
|
|
||||||
"&.Mui-checked": {
|
|
||||||
color: "white", // Customize the color when checked
|
|
||||||
},
|
|
||||||
"& .MuiSvgIcon-root": {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/> */}
|
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
@ -163,15 +148,6 @@ export const ThingsToDoInitial = ({
|
|||||||
sx={{
|
sx={{
|
||||||
marginBottom: '20px',
|
marginBottom: '20px',
|
||||||
}}
|
}}
|
||||||
// secondaryAction={
|
|
||||||
// <IconButton edge="end" aria-label="comments">
|
|
||||||
// <InfoIcon
|
|
||||||
// sx={{
|
|
||||||
// color: "white",
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
// </IconButton>
|
|
||||||
// }
|
|
||||||
disablePadding
|
disablePadding
|
||||||
>
|
>
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
@ -215,34 +191,6 @@ export const ThingsToDoInitial = ({
|
|||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{/* <ListItem
|
|
||||||
disablePadding
|
|
||||||
>
|
|
||||||
<ListItemButton sx={{
|
|
||||||
padding: "0px",
|
|
||||||
}} disableRipple role={undefined} dense>
|
|
||||||
|
|
||||||
<ListItemText sx={{
|
|
||||||
"& .MuiTypography-root": {
|
|
||||||
fontSize: "13px",
|
|
||||||
fontWeight: 400,
|
|
||||||
},
|
|
||||||
}} primary={`Join a group`} />
|
|
||||||
<ListItemIcon sx={{
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
}}>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
height: "18px",
|
|
||||||
width: "18px",
|
|
||||||
borderRadius: "50%",
|
|
||||||
backgroundColor: checked3 ? "rgba(9, 182, 232, 1)" : "transparent",
|
|
||||||
outline: "1px solid rgba(9, 182, 232, 1)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ListItemIcon>
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem> */}
|
|
||||||
</List>
|
</List>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -88,7 +88,7 @@ export const WalletsAppWrapper = () => {
|
|||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>Q-Wallets</Typography> // TODO translate
|
<Typography>Q-Wallets</Typography>
|
||||||
<ButtonBase onClick={handleClose}>
|
<ButtonBase onClick={handleClose}>
|
||||||
<CloseIcon
|
<CloseIcon
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -107,7 +107,6 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
|
|||||||
directs: sortedDirects,
|
directs: sortedDirects,
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
// TODO translate
|
|
||||||
console.error(
|
console.error(
|
||||||
'Failed to handle active group data from socket:',
|
'Failed to handle active group data from socket:',
|
||||||
error.message || 'An error occurred'
|
error.message || 'An error occurred'
|
||||||
|
@ -17,7 +17,7 @@ export const useBlockedAddresses = () => {
|
|||||||
if (userBlockedRef.current[address]) return true;
|
if (userBlockedRef.current[address]) return true;
|
||||||
return false;
|
return false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//error
|
console.log(error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -42,10 +42,13 @@ export const useBlockedAddresses = () => {
|
|||||||
console.error('Failed qortalRequest', error);
|
console.error('Failed qortalRequest', error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const blockedUsers = {};
|
const blockedUsers = {};
|
||||||
|
|
||||||
response?.forEach((item) => {
|
response?.forEach((item) => {
|
||||||
blockedUsers[item] = true;
|
blockedUsers[item] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
userBlockedRef.current = blockedUsers;
|
userBlockedRef.current = blockedUsers;
|
||||||
|
|
||||||
const response2 = await new Promise((res, rej) => {
|
const response2 = await new Promise((res, rej) => {
|
||||||
@ -66,10 +69,13 @@ export const useBlockedAddresses = () => {
|
|||||||
console.error('Failed qortalRequest', error);
|
console.error('Failed qortalRequest', error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const blockedUsers2 = {};
|
const blockedUsers2 = {};
|
||||||
|
|
||||||
response2?.forEach((item) => {
|
response2?.forEach((item) => {
|
||||||
blockedUsers2[item] = true;
|
blockedUsers2[item] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
userNamesBlockedRef.current = blockedUsers2;
|
userNamesBlockedRef.current = blockedUsers2;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -22,7 +22,7 @@ export const useHandleUserInfo = () => {
|
|||||||
};
|
};
|
||||||
return data?.level;
|
return data?.level;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//error
|
console.log(error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -1,46 +1,52 @@
|
|||||||
import { Box, ButtonBase, Typography } from '@mui/material';
|
import { Box, ButtonBase, Typography } from '@mui/material';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const NewUsersCTA = ({ balance }) => {
|
export const NewUsersCTA = ({ balance }) => {
|
||||||
|
const { t } = useTranslation(['core']);
|
||||||
|
|
||||||
if (balance === undefined || +balance > 0) return null;
|
if (balance === undefined || +balance > 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
alignItems: 'center',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Spacer height="40px" />
|
<Spacer height="40px" />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '320px',
|
|
||||||
justifyContent: 'center',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
padding: '15px',
|
|
||||||
outline: '1px solid gray',
|
|
||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
outline: '1px solid gray',
|
||||||
|
padding: '15px',
|
||||||
|
width: '320px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
textAlign: 'center',
|
|
||||||
fontSize: '1.2rem',
|
fontSize: '1.2rem',
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
|
textAlign: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Are you a new user?
|
{t('core:new_user', { postProcess: 'capitalize' })}
|
||||||
</Typography>{' '}
|
|
||||||
// TODO translate
|
|
||||||
<Spacer height="20px" />
|
|
||||||
<Typography>
|
|
||||||
Please message us on Telegram or Discord if you need 4 QORT to start
|
|
||||||
chatting without any limitations
|
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
|
<Typography>
|
||||||
|
{t('core:message_us', { postProcess: 'capitalize' })}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Spacer height="20px" />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -68,6 +74,7 @@ export const NewUsersCTA = ({ balance }) => {
|
|||||||
>
|
>
|
||||||
Telegram
|
Telegram
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
sx={{
|
sx={{
|
||||||
textDecoration: 'underline',
|
textDecoration: 'underline',
|
||||||
|
92
src/components/Language/LanguageSelector.tsx
Normal file
92
src/components/Language/LanguageSelector.tsx
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { supportedLanguages } from '../../../i18n';
|
||||||
|
import { Tooltip, useTheme } from '@mui/material';
|
||||||
|
|
||||||
|
const LanguageSelector = () => {
|
||||||
|
const { i18n, t } = useTranslation(['core']);
|
||||||
|
const [showSelect, setShowSelect] = useState(false);
|
||||||
|
const theme = useTheme();
|
||||||
|
const selectorRef = useRef(null);
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const newLang = e.target.value;
|
||||||
|
i18n.changeLanguage(newLang);
|
||||||
|
setShowSelect(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentLang = i18n.language;
|
||||||
|
const { name, flag } =
|
||||||
|
supportedLanguages[currentLang] || supportedLanguages['en'];
|
||||||
|
|
||||||
|
// Detect clicks outside the component
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event) => {
|
||||||
|
if (selectorRef.current && !selectorRef.current.contains(event.target)) {
|
||||||
|
setShowSelect(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={selectorRef}
|
||||||
|
style={{
|
||||||
|
bottom: '5%',
|
||||||
|
display: 'flex',
|
||||||
|
gap: '12px',
|
||||||
|
left: '1.5vh',
|
||||||
|
position: 'absolute',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
title={t('core:action.change_language', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{showSelect ? (
|
||||||
|
<select
|
||||||
|
style={{
|
||||||
|
fontSize: '1rem',
|
||||||
|
border: '2px',
|
||||||
|
background: theme.palette.background.default,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
cursor: 'pointer',
|
||||||
|
position: 'relative',
|
||||||
|
bottom: '7px',
|
||||||
|
}}
|
||||||
|
value={currentLang}
|
||||||
|
onChange={handleChange}
|
||||||
|
autoFocus
|
||||||
|
>
|
||||||
|
{Object.entries(supportedLanguages).map(([code, { name }]) => (
|
||||||
|
<option key={code} value={code}>
|
||||||
|
{code.toUpperCase()} - {name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => setShowSelect(true)}
|
||||||
|
style={{
|
||||||
|
fontSize: '1.5rem',
|
||||||
|
border: 'none',
|
||||||
|
background: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
aria-label={`Current language: ${name}`}
|
||||||
|
>
|
||||||
|
{showSelect ? undefined : flag}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LanguageSelector;
|
@ -265,7 +265,7 @@ export const Minting = ({
|
|||||||
rej({ message: response.error });
|
rej({ message: response.error });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej({ message: error.message || 'An error occurred' }); //TODO translate
|
rej({ message: error.message || 'An error occurred' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -280,7 +280,7 @@ export const Minting = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const createRewardShare = useCallback(async (publicKey, recipient) => {
|
const createRewardShare = useCallback(async (publicKey, recipient) => {
|
||||||
const fee = await getFee('REWARD_SHARE');
|
const fee = await getFee('REWARD_SHARE'); // TODO translate
|
||||||
await show({
|
await show({
|
||||||
message: 'Would you like to perform an REWARD_SHARE transaction?',
|
message: 'Would you like to perform an REWARD_SHARE transaction?',
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
|
@ -62,6 +62,7 @@ export const QMailStatus = () => {
|
|||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
|
textTransform: 'uppercase',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('core:q_mail', {
|
{t('core:q_mail', {
|
||||||
|
@ -176,7 +176,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej(
|
rej(
|
||||||
error.message ||
|
error.message ||
|
||||||
t('core:result.error.generic', { postProcess: 'capitalize' })
|
t('core:message.error.generic', { postProcess: 'capitalize' })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -185,7 +185,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
setSettingsQdnLastUpdated(Date.now());
|
setSettingsQdnLastUpdated(Date.now());
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: t('core:result.success.publish_qdn', {
|
message: t('core:message.success.publish_qdn', {
|
||||||
postProcess: 'capitalize',
|
postProcess: 'capitalize',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@ -198,7 +198,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
type: 'error',
|
type: 'error',
|
||||||
message:
|
message:
|
||||||
error?.message ||
|
error?.message ||
|
||||||
t('core:result.error.save_qdn', {
|
t('core:message.error.save_qdn', {
|
||||||
postProcess: 'capitalize',
|
postProcess: 'capitalize',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@ -591,7 +591,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('core:import', {
|
{t('core:action.import', {
|
||||||
postProcess: 'capitalize',
|
postProcess: 'capitalize',
|
||||||
})}
|
})}
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
@ -616,7 +616,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('core:export', {
|
{t('core:action.export', {
|
||||||
postProcess: 'capitalize',
|
postProcess: 'capitalize',
|
||||||
})}
|
})}
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
const ThemeSelector = () => {
|
const ThemeSelector = () => {
|
||||||
const { t } = useTranslation(['core']);
|
const { t } = useTranslation(['core']);
|
||||||
|
|
||||||
const { themeMode, toggleTheme } = useThemeContext();
|
const { themeMode, toggleTheme } = useThemeContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -14,7 +15,7 @@ const ThemeSelector = () => {
|
|||||||
bottom: '1%',
|
bottom: '1%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '12px',
|
gap: '12px',
|
||||||
left: '1.5vh',
|
left: '1.2vh',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -91,7 +91,7 @@ export const Tutorials = () => {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button variant="contained" onClick={handleClose}>
|
<Button variant="contained" onClick={handleClose}>
|
||||||
{t('core:close', { postProcess: 'capitalize' })}
|
{t('core:action.close', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -138,7 +138,7 @@ export const Tutorials = () => {
|
|||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button variant="contained" onClick={handleClose}>
|
<Button variant="contained" onClick={handleClose}>
|
||||||
{t('core:close', { postProcess: 'capitalize' })}
|
{t('core:action.close', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||||
import {
|
import {
|
||||||
canSaveSettingToQdnAtom,
|
canSaveSettingToQdnAtom,
|
||||||
@ -46,6 +46,7 @@ const getPublishRecord = async (myName) => {
|
|||||||
|
|
||||||
return { hasPublishRecord: false };
|
return { hasPublishRecord: false };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPublish = async (myName) => {
|
const getPublish = async (myName) => {
|
||||||
try {
|
try {
|
||||||
let data;
|
let data;
|
||||||
@ -57,7 +58,6 @@ const getPublish = async (myName) => {
|
|||||||
if (!data) throw new Error('Unable to fetch publish');
|
if (!data) throw new Error('Unable to fetch publish');
|
||||||
|
|
||||||
const decryptedKey: any = await decryptResource(data);
|
const decryptedKey: any = await decryptResource(data);
|
||||||
|
|
||||||
const dataint8Array = base64ToUint8Array(decryptedKey.data);
|
const dataint8Array = base64ToUint8Array(decryptedKey.data);
|
||||||
const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
|
const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
|
||||||
return decryptedKeyToObject;
|
return decryptedKeyToObject;
|
||||||
@ -112,6 +112,7 @@ export const useQortalGetSaveSettings = (myName, isAuthenticated) => {
|
|||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
!myName ||
|
!myName ||
|
||||||
|
@ -1,55 +1,69 @@
|
|||||||
import React, { useCallback, useEffect } from 'react'
|
import { useCallback, useEffect } from 'react';
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
import { isUsingImportExportSettingsAtom, oldPinnedAppsAtom, settingsLocalLastUpdatedAtom, settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom } from './atoms/global';
|
import {
|
||||||
|
isUsingImportExportSettingsAtom,
|
||||||
|
oldPinnedAppsAtom,
|
||||||
|
settingsLocalLastUpdatedAtom,
|
||||||
|
settingsQDNLastUpdatedAtom,
|
||||||
|
sortablePinnedAppsAtom,
|
||||||
|
} from './atoms/global';
|
||||||
|
|
||||||
function fetchFromLocalStorage(key) {
|
function fetchFromLocalStorage(key) {
|
||||||
try {
|
try {
|
||||||
const serializedValue = localStorage.getItem(key);
|
const serializedValue = localStorage.getItem(key);
|
||||||
if (serializedValue === null) {
|
if (serializedValue === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
return JSON.parse(serializedValue);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching from localStorage:', error);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return JSON.parse(serializedValue);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching from localStorage:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useRetrieveDataLocalStorage = (address) => {
|
export const useRetrieveDataLocalStorage = (address) => {
|
||||||
const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom);
|
const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom);
|
||||||
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
|
|
||||||
const setIsUsingImportExportSettings = useSetRecoilState(isUsingImportExportSettingsAtom)
|
|
||||||
const setSettingsQDNLastUpdated = useSetRecoilState(settingsQDNLastUpdatedAtom);
|
|
||||||
const setOldPinnedApps = useSetRecoilState(oldPinnedAppsAtom)
|
|
||||||
|
|
||||||
const getSortablePinnedApps = useCallback(()=> {
|
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||||
const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings')
|
settingsLocalLastUpdatedAtom
|
||||||
if(pinnedAppsLocal?.sortablePinnedApps){
|
);
|
||||||
setSortablePinnedApps(pinnedAppsLocal?.sortablePinnedApps)
|
|
||||||
setSettingsLocalLastUpdated(pinnedAppsLocal?.timestamp || -1)
|
const setIsUsingImportExportSettings = useSetRecoilState(
|
||||||
} else {
|
isUsingImportExportSettingsAtom
|
||||||
setSettingsLocalLastUpdated(-1)
|
);
|
||||||
}
|
|
||||||
|
const setSettingsQDNLastUpdated = useSetRecoilState(
|
||||||
}, [])
|
settingsQDNLastUpdatedAtom
|
||||||
const getSortablePinnedAppsImportExport = useCallback(()=> {
|
);
|
||||||
const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings_import_export')
|
|
||||||
if(pinnedAppsLocal?.sortablePinnedApps){
|
const setOldPinnedApps = useSetRecoilState(oldPinnedAppsAtom);
|
||||||
setOldPinnedApps(pinnedAppsLocal?.sortablePinnedApps)
|
|
||||||
|
const getSortablePinnedApps = useCallback(() => {
|
||||||
|
const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings');
|
||||||
setIsUsingImportExportSettings(true)
|
|
||||||
setSettingsQDNLastUpdated(pinnedAppsLocal?.timestamp || 0)
|
if (pinnedAppsLocal?.sortablePinnedApps) {
|
||||||
|
setSortablePinnedApps(pinnedAppsLocal?.sortablePinnedApps);
|
||||||
} else {
|
setSettingsLocalLastUpdated(pinnedAppsLocal?.timestamp || -1);
|
||||||
setIsUsingImportExportSettings(false)
|
} else {
|
||||||
}
|
setSettingsLocalLastUpdated(-1);
|
||||||
|
}
|
||||||
}, [])
|
}, []);
|
||||||
useEffect(()=> {
|
|
||||||
|
const getSortablePinnedAppsImportExport = useCallback(() => {
|
||||||
getSortablePinnedApps()
|
const pinnedAppsLocal = fetchFromLocalStorage(
|
||||||
getSortablePinnedAppsImportExport()
|
'ext_saved_settings_import_export'
|
||||||
}, [getSortablePinnedApps, address])
|
);
|
||||||
|
if (pinnedAppsLocal?.sortablePinnedApps) {
|
||||||
}
|
setOldPinnedApps(pinnedAppsLocal?.sortablePinnedApps);
|
||||||
|
setIsUsingImportExportSettings(true);
|
||||||
|
setSettingsQDNLastUpdated(pinnedAppsLocal?.timestamp || 0);
|
||||||
|
} else {
|
||||||
|
setIsUsingImportExportSettings(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getSortablePinnedApps();
|
||||||
|
getSortablePinnedAppsImportExport();
|
||||||
|
}, [getSortablePinnedApps, address]);
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user