mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-11 02:36:59 +00:00
Merge branch 'develop' into feature/add-theme
This commit is contained in:
commit
3d17303d84
@ -18,4 +18,10 @@ Leveraging a redundant set of publicly accessible nodes provided by crowetic, Qo
|
||||
|
||||
Many additional details and a fully featured wiki will be created over time. Reach out on the chat on https://qortal.dev or in any of the community locations for Qortal, if you have any issues. Thank you!
|
||||
|
||||
## Internationalization (i18n)
|
||||
|
||||
Qortal-Hub supports internationalization (i18n) using [i18next](https://www.i18next.com/), allowing seamless translation of UI text into multiple languages.
|
||||
The setup includes modularized translation files, language detection, context and runtime language switching.
|
||||
Files with translation are in `public/locales/<locale>` folder.
|
||||
|
||||
See [guidelines](./docs/i18n_languages.md).
|
||||
|
84
docs/contribution.md
Normal file
84
docs/contribution.md
Normal file
@ -0,0 +1,84 @@
|
||||
|
||||
# 🤝 Contributing Guide
|
||||
|
||||
Thank you for your interest in contributing! We follow a structured Git workflow to keep the project clean, stable, and production-ready at all times.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Branch Overview
|
||||
|
||||
| Branch | Purpose |
|
||||
|------------------|----------------------------------------------------------|
|
||||
| `master` | Stable, production-ready code. All releases are tagged from here. |
|
||||
| `develop` | Active development branch. All new features go here first. |
|
||||
| `release/x.y.z` | Pre-release branch for staging, QA, and final polish. |
|
||||
|
||||
---
|
||||
|
||||
## 🌿 Creating a Feature or Fix
|
||||
|
||||
1. **Start from `develop`:**
|
||||
|
||||
```bash
|
||||
git checkout develop
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
|
||||
2. **Make your changes and commit them.**
|
||||
|
||||
3. **Push your branch:**
|
||||
|
||||
```bash
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
|
||||
4. **Open a Pull Request into `develop`.**
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Releasing Code (Maintainers Only)
|
||||
|
||||
A new `release/x.y.z` branch must be created for **every release**.
|
||||
|
||||
1. **Create a `release/` branch from `master`:**
|
||||
|
||||
```bash
|
||||
git checkout master
|
||||
git checkout -b release/1.2.0
|
||||
```
|
||||
|
||||
2. **Merge in `develop` or selected branches if `develop` is not ready:**
|
||||
|
||||
```bash
|
||||
git merge develop
|
||||
# or
|
||||
git merge feature/finished-feature
|
||||
git merge feature/another-complete-feature
|
||||
```
|
||||
|
||||
3. **Polish, test, and fix issues as needed.**
|
||||
|
||||
4. **Merge back into `develop`:**
|
||||
|
||||
```bash
|
||||
git checkout develop
|
||||
git merge release/1.2.0
|
||||
git push origin develop
|
||||
```
|
||||
|
||||
5. **Finalize the release:**
|
||||
|
||||
```bash
|
||||
git checkout master
|
||||
git merge release/1.2.0
|
||||
git tag v1.2.0
|
||||
git push origin master --tags
|
||||
```
|
||||
|
||||
6. **Delete the release branch:**
|
||||
|
||||
```bash
|
||||
git branch -d release/1.2.0
|
||||
git push origin --delete release/1.2.0
|
||||
```
|
||||
|
10
docs/i18n_languages.md
Normal file
10
docs/i18n_languages.md
Normal file
@ -0,0 +1,10 @@
|
||||
# I18N Guidelines
|
||||
|
||||
In JSON file:
|
||||
|
||||
- Keep the file sorted
|
||||
- Always write in lowercase
|
||||
|
||||
In GUI:
|
||||
|
||||
- If the first letter of the translation must be uppercase, use the postProcess, for example: `{t_auth('advanced_users', { postProcess: 'capitalize' })}`
|
49
i18n.js
Normal file
49
i18n.js
Normal file
@ -0,0 +1,49 @@
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import HttpBackend from 'i18next-http-backend';
|
||||
import LocalStorageBackend from 'i18next-localstorage-backend';
|
||||
import HttpApi from 'i18next-http-backend';
|
||||
import i18n from 'i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
// Detect environment
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
// Register custom postProcessor: it capitalizes the first letter of a translation-
|
||||
// Usage:
|
||||
// t('greeting', { postProcess: 'capitalize' })
|
||||
const capitalize = {
|
||||
type: 'postProcessor',
|
||||
name: 'capitalize',
|
||||
process: (value) => {
|
||||
return value.charAt(0).toUpperCase() + value.slice(1);
|
||||
},
|
||||
};
|
||||
|
||||
i18n
|
||||
.use(HttpApi)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.use(capitalize)
|
||||
.init({
|
||||
backend: {
|
||||
backends: [LocalStorageBackend, HttpBackend],
|
||||
backendOptions: [
|
||||
{
|
||||
expirationTime: 7 * 24 * 60 * 60 * 1000, // 7 days
|
||||
},
|
||||
{
|
||||
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
debug: isDev,
|
||||
fallbackLng: 'en',
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
lng: navigator.language,
|
||||
ns: ['auth', 'core', 'tutorial'],
|
||||
supportedLngs: ['en', 'it', 'es', 'fr', 'de', 'ru'],
|
||||
});
|
||||
|
||||
export default i18n;
|
161
package-lock.json
generated
161
package-lock.json
generated
@ -56,6 +56,10 @@
|
||||
"emoji-picker-react": "^4.12.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"html-to-text": "^9.0.5",
|
||||
"i18next": "^25.0.1",
|
||||
"i18next-browser-languagedetector": "^8.0.5",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"i18next-localstorage-backend": "^4.2.0",
|
||||
"jssha": "3.3.1",
|
||||
"lit": "^3.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
@ -69,6 +73,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-frame-component": "^5.2.7",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-infinite-scroller": "^1.2.6",
|
||||
"react-intersection-observer": "^9.13.0",
|
||||
"react-json-view-lite": "^2.0.1",
|
||||
@ -1479,9 +1484,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.24.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
|
||||
"integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
|
||||
"version": "7.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
|
||||
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
@ -8044,6 +8050,15 @@
|
||||
"resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz",
|
||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q=="
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.12"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@ -10680,6 +10695,15 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/html-parse-stringify": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"void-elements": "3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/html-to-text": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
|
||||
@ -10774,6 +10798,64 @@
|
||||
"ms": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "25.0.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.0.1.tgz",
|
||||
"integrity": "sha512-8S8PyZbrymJZn3DaN70/34JYWNhsqrU6yA4MuzcygJBv+41dgNMocEA8h+kV1P7MCc1ll03lOTOIXE7mpNCicw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://locize.com"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://locize.com/i18next.html"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-browser-languagedetector": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.5.tgz",
|
||||
"integrity": "sha512-OstebRKqKiQw8xEvQF5aRyUujsCatanj7Q9eo5iiH2gJpoXGZ7483ol3sVBwfqbobTQPNH1J+NAyJ1aCQoEC+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.2"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-http-backend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz",
|
||||
"integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cross-fetch": "4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-localstorage-backend": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/i18next-localstorage-backend/-/i18next-localstorage-backend-4.2.0.tgz",
|
||||
"integrity": "sha512-vglEQF0AnLriX7dLA2drHnqAYzHxnLwWQzBDw8YxcIDjOvYZz5rvpal59Dq4In+IHNmGNM32YgF0TDjBT0fHmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.22.15"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-corefoundation": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
|
||||
@ -12891,6 +12973,48 @@
|
||||
"semver": "^7.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz",
|
||||
@ -16533,6 +16657,28 @@
|
||||
"react-dom": ">= 16.3"
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.4.1.tgz",
|
||||
"integrity": "sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.0",
|
||||
"html-parse-stringify": "^3.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"i18next": ">= 23.2.3",
|
||||
"react": ">= 16.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-infinite-scroller": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/react-infinite-scroller/-/react-infinite-scroller-1.2.6.tgz",
|
||||
@ -19462,6 +19608,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/void-elements": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
|
@ -61,6 +61,10 @@
|
||||
"emoji-picker-react": "^4.12.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"html-to-text": "^9.0.5",
|
||||
"i18next": "^25.0.1",
|
||||
"i18next-browser-languagedetector": "^8.0.5",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"i18next-localstorage-backend": "^4.2.0",
|
||||
"jssha": "3.3.1",
|
||||
"lit": "^3.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
@ -74,6 +78,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-frame-component": "^5.2.7",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-infinite-scroller": "^1.2.6",
|
||||
"react-intersection-observer": "^9.13.0",
|
||||
"react-json-view-lite": "^2.0.1",
|
||||
|
37
public/locales/de/auth.json
Normal file
37
public/locales/de/auth.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"account": {
|
||||
"your": "ihr Konto",
|
||||
"account_many": "Konten",
|
||||
"account_one": "Konto"
|
||||
},
|
||||
"advanced_users": "für fortgeschrittene Benutzer",
|
||||
"apikey": {
|
||||
"alternative": "Alternative: Datei auswählen",
|
||||
"change": "API-Schlüssel ändern",
|
||||
"enter": "API-Schlüssel eingeben",
|
||||
"import": "API-Schlüssel importieren",
|
||||
"key": "API-Schlüssel",
|
||||
"select_valid": "gültigen API-Schlüssel auswählen"
|
||||
},
|
||||
"authenticate": "authentifizieren",
|
||||
"build_version": "Build-Version",
|
||||
"create_account": "Konto erstellen",
|
||||
"download_account": "Konto herunterladen",
|
||||
"keep_secure": "Bewahren Sie Ihre Kontodatei sicher auf",
|
||||
"node": {
|
||||
"choose": "benutzerdefinierten Node auswählen",
|
||||
"custom_many": "benutzerdefinierte Nodes",
|
||||
"use_custom": "benutzerdefinierten Node verwenden",
|
||||
"use_local": "lokalen Node verwenden",
|
||||
"using": "verwende Node",
|
||||
"using_public": "öffentlichen Node verwenden"
|
||||
},
|
||||
"password": "Passwort",
|
||||
"password_confirmation": "Passwort bestätigen",
|
||||
"return_to_list": "zurück zur Liste",
|
||||
"wallet": {
|
||||
"password_confirmation": "Wallet-Passwort bestätigen",
|
||||
"password": "Wallet-Passwort"
|
||||
},
|
||||
"welcome": "willkommen bei"
|
||||
}
|
71
public/locales/de/core.json
Normal file
71
public/locales/de/core.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"add": "hinzufügen",
|
||||
"cancel": "abbrechen",
|
||||
"choose": "auswählen",
|
||||
"close": "schließen",
|
||||
"continue": "fortfahren",
|
||||
"core": {
|
||||
"block_height": "Blockhöhe",
|
||||
"information": "Kerninformationen",
|
||||
"peers": "verbundene Peers",
|
||||
"version": "Kernversion"
|
||||
},
|
||||
"description": "Beschreibung",
|
||||
"edit": "bearbeiten",
|
||||
"export": "exportieren",
|
||||
"import": "importieren",
|
||||
"last_height": "letzte Höhe",
|
||||
"loading": "Lade...",
|
||||
"logout": "abmelden",
|
||||
"minting_status": "Präge-Status",
|
||||
"payment_notification": "Zahlungsbenachrichtigung",
|
||||
"price": "Preis",
|
||||
"q_mail": "Q-Mail",
|
||||
"result": {
|
||||
"error": {
|
||||
"generic": "Ein Fehler ist aufgetreten",
|
||||
"incorrect_password": "Falsches Passwort",
|
||||
"save_qdn": "Speichern in QDN nicht möglich"
|
||||
},
|
||||
"status": {
|
||||
"minting": "(Prägung)",
|
||||
"not_minting": "(keine Prägung)",
|
||||
"synchronized": "synchronisiert",
|
||||
"synchronizing": "synchronisiere"
|
||||
},
|
||||
"success": {
|
||||
"publish_qdn": "Erfolgreich in QDN veröffentlicht"
|
||||
}
|
||||
},
|
||||
"save_options": {
|
||||
"no_pinned_changes": "Derzeit keine Änderungen an Ihren angehefteten Apps",
|
||||
"overwrite_changes": "Die App konnte Ihre vorhandenen in QDN gespeicherten angehefteten Apps nicht herunterladen. Möchten Sie diese Änderungen überschreiben?",
|
||||
"overwrite_qdn": "In QDN überschreiben",
|
||||
"publish_qdn": "Möchten Sie Ihre Einstellungen in QDN (verschlüsselt) veröffentlichen?",
|
||||
"qdn": "QDN-Speicherung verwenden",
|
||||
"register_name": "Sie benötigen einen registrierten Qortal-Namen, um Ihre angehefteten Apps in QDN zu speichern.",
|
||||
"reset_pinned": "Gefällt Ihnen Ihre aktuellen lokalen Änderungen nicht? Möchten Sie zu den Standard-Anheftungen zurückkehren?",
|
||||
"reset_qdn": "Gefällt Ihnen Ihre aktuellen lokalen Änderungen nicht? Möchten Sie zu Ihren in QDN gespeicherten Anheftungen zurückkehren?",
|
||||
"revert_default": "Auf Standard zurücksetzen",
|
||||
"revert_qdn": "Auf QDN zurücksetzen",
|
||||
"save_qdn": "In QDN speichern",
|
||||
"save": "speichern",
|
||||
"settings": "Sie verwenden die Export/Import-Methode zum Speichern von Einstellungen.",
|
||||
"unsaved_changes": "Sie haben nicht gespeicherte Änderungen an Ihren angehefteten Apps. Speichern Sie sie in QDN."
|
||||
},
|
||||
"settings": "Einstellungen",
|
||||
"supply": "Angebot",
|
||||
"theme": {
|
||||
"dark": "Dunkelmodus",
|
||||
"light": "Hellmodus"
|
||||
},
|
||||
"title": "Titel",
|
||||
"tutorial": "Tutorial",
|
||||
"user_lookup": "Benutzersuche",
|
||||
"wallet": {
|
||||
"backup_wallet": "Wallet sichern",
|
||||
"wallet": "Wallet",
|
||||
"wallet_other": "Wallets"
|
||||
},
|
||||
"welcome": "Willkommen"
|
||||
}
|
21
public/locales/de/tutorial.json
Normal file
21
public/locales/de/tutorial.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"1_getting_started": "1. Erste Schritte",
|
||||
"2_overview": "2. Überblick",
|
||||
"3_groups": "3. Qortal-Gruppen",
|
||||
"4_obtain_qort": "4. QORT erhalten",
|
||||
"account_creation": "Kontoerstellung",
|
||||
"important_info": "wichtige Informationen!",
|
||||
"apps": {
|
||||
"dashboard": "1. App-Dashboard",
|
||||
"navigation": "2. App-Navigation"
|
||||
},
|
||||
"initial": {
|
||||
"6_qort": "mindestens 6 QORT im Wallet haben",
|
||||
"explore": "erkunden",
|
||||
"general_chat": "allgemeiner Chat",
|
||||
"getting_started": "erste Schritte",
|
||||
"register_name": "einen Namen registrieren",
|
||||
"see_apps": "apps ansehen",
|
||||
"trade_qort": "QORT handeln"
|
||||
}
|
||||
}
|
37
public/locales/en/auth.json
Normal file
37
public/locales/en/auth.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"account": {
|
||||
"your": "your account",
|
||||
"account_many": "accounts",
|
||||
"account_one": "account"
|
||||
},
|
||||
"advanced_users": "for advanced users",
|
||||
"apikey": {
|
||||
"alternative": "alternative: File select",
|
||||
"change": "change APIkey",
|
||||
"enter": "enter APIkey",
|
||||
"import": "import APIkey",
|
||||
"key": "API key",
|
||||
"select_valid": "select a valid apikey"
|
||||
},
|
||||
"authenticate": "authenticate",
|
||||
"build_version": "build version",
|
||||
"create_account": "create account",
|
||||
"download_account": "download account",
|
||||
"keep_secure": "keep your account file secure",
|
||||
"node": {
|
||||
"choose": "choose custom node",
|
||||
"custom_many": "custom nodes",
|
||||
"use_custom": "use custom node",
|
||||
"use_local": "use local node",
|
||||
"using": "using node",
|
||||
"using_public": "using public node"
|
||||
},
|
||||
"password": "password",
|
||||
"password_confirmation": "confirm password",
|
||||
"return_to_list": "return to list",
|
||||
"wallet": {
|
||||
"password_confirmation": "confirm wallet password",
|
||||
"password": "wallet password"
|
||||
},
|
||||
"welcome": "welcome to"
|
||||
}
|
71
public/locales/en/core.json
Normal file
71
public/locales/en/core.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"add": "add",
|
||||
"cancel": "cancel",
|
||||
"choose": "choose",
|
||||
"close": "close",
|
||||
"continue": "continue",
|
||||
"core": {
|
||||
"block_height": "block height",
|
||||
"information": "core information",
|
||||
"peers": "connected peers",
|
||||
"version": "core version"
|
||||
},
|
||||
"description": "description",
|
||||
"edit": "edit",
|
||||
"export": "export",
|
||||
"import": "import",
|
||||
"last_height": "last height",
|
||||
"loading": "loading...",
|
||||
"logout": "logout",
|
||||
"minting_status": "minting status",
|
||||
"payment_notification": "payment notification",
|
||||
"price": "price",
|
||||
"q_mail": "q-mail",
|
||||
"result": {
|
||||
"error": {
|
||||
"generic": "an error occurred",
|
||||
"incorrect_password": "incorrect password",
|
||||
"save_qdn": "unable to save to QDN"
|
||||
},
|
||||
"status": {
|
||||
"minting": "(minting)",
|
||||
"not_minting": "(not minting)",
|
||||
"synchronized": "synchronized",
|
||||
"synchronizing": "synchronizing"
|
||||
},
|
||||
"success": {
|
||||
"publish_qdn": "successfully published to QDN"
|
||||
}
|
||||
},
|
||||
"save_options": {
|
||||
"no_pinned_changes": "you currently do not have any changes to your pinned apps",
|
||||
"overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?",
|
||||
"overwrite_qdn": "overwrite to QDN",
|
||||
"publish_qdn": "would you like to publish your settings to QDN (encrypted)?",
|
||||
"qdn": "use QDN saving",
|
||||
"register_name": "you need a registered Qortal name to save your pinned apps to QDN.",
|
||||
"reset_pinned": "don't like your current local changes? Would you like to reset to the default pinned apps?",
|
||||
"reset_qdn": "don't like your current local changes? Would you like to reset to your saved QDN pinned apps?",
|
||||
"revert_default": "revert to default",
|
||||
"revert_qdn": "revert to QDN",
|
||||
"save_qdn": "save to QDN",
|
||||
"save": "save",
|
||||
"settings": "you are using the export/import way of saving settings.",
|
||||
"unsaved_changes": " you have unsaved changes to your pinned apps. Save them to QDN."
|
||||
},
|
||||
"settings": "settings",
|
||||
"supply": "supply",
|
||||
"theme": {
|
||||
"dark": "dark mode",
|
||||
"light": "light mode"
|
||||
},
|
||||
"title": "title",
|
||||
"tutorial": "tutorial",
|
||||
"user_lookup": "user lookup",
|
||||
"wallet": {
|
||||
"backup_wallet": "backup wallet",
|
||||
"wallet": "wallet",
|
||||
"wallet_other": "wallets"
|
||||
},
|
||||
"welcome": "welcome"
|
||||
}
|
21
public/locales/en/tutorial.json
Normal file
21
public/locales/en/tutorial.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"1_getting_started": "1. Getting Started",
|
||||
"2_overview": "2. Overview",
|
||||
"3_groups": "3. Qortal Groups",
|
||||
"4_obtain_qort": "4. Obtaining Qort",
|
||||
"account_creation": "account creation",
|
||||
"important_info": "important information!",
|
||||
"apps": {
|
||||
"dashboard": "1. Apps Dashboard",
|
||||
"navigation": "2. Apps Navigation"
|
||||
},
|
||||
"initial": {
|
||||
"6_qort": "have at least 6 QORT in your wallet",
|
||||
"explore": "explore",
|
||||
"general_chat": "general chat",
|
||||
"getting_started": "getting started",
|
||||
"register_name": "register a name",
|
||||
"see_apps": "see apps",
|
||||
"trade_qort": "trade QORT"
|
||||
}
|
||||
}
|
37
public/locales/es/auth.json
Normal file
37
public/locales/es/auth.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"account": {
|
||||
"your": "tu cuenta",
|
||||
"account_many": "cuentas",
|
||||
"account_one": "cuenta"
|
||||
},
|
||||
"advanced_users": "para usuarios avanzados",
|
||||
"apikey": {
|
||||
"alternative": "alternativa: Seleccionar archivo",
|
||||
"change": "cambiar clave API",
|
||||
"enter": "ingresar clave API",
|
||||
"import": "importar clave API",
|
||||
"key": "clave API",
|
||||
"select_valid": "selecciona una clave API válida"
|
||||
},
|
||||
"authenticate": "autenticar",
|
||||
"build_version": "versión de compilación",
|
||||
"create_account": "crear cuenta",
|
||||
"download_account": "descargar cuenta",
|
||||
"keep_secure": "mantén tu archivo de cuenta seguro",
|
||||
"node": {
|
||||
"choose": "elegir nodo personalizado",
|
||||
"custom_many": "nodos personalizados",
|
||||
"use_custom": "usar nodo personalizado",
|
||||
"use_local": "usar nodo local",
|
||||
"using": "usando nodo",
|
||||
"using_public": "usando nodo público"
|
||||
},
|
||||
"password": "contraseña",
|
||||
"password_confirmation": "confirmar contraseña",
|
||||
"return_to_list": "volver a la lista",
|
||||
"wallet": {
|
||||
"password_confirmation": "confirmar contraseña del monedero",
|
||||
"password": "contraseña del monedero"
|
||||
},
|
||||
"welcome": "bienvenido a"
|
||||
}
|
71
public/locales/es/core.json
Normal file
71
public/locales/es/core.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"add": "agregar",
|
||||
"cancel": "cancelar",
|
||||
"choose": "elegir",
|
||||
"close": "cerrar",
|
||||
"continue": "continuar",
|
||||
"core": {
|
||||
"block_height": "altura de bloque",
|
||||
"information": "información del núcleo",
|
||||
"peers": "pares conectados",
|
||||
"version": "versión del núcleo"
|
||||
},
|
||||
"description": "descripción",
|
||||
"edit": "editar",
|
||||
"export": "exportar",
|
||||
"import": "importar",
|
||||
"last_height": "última altura",
|
||||
"loading": "cargando...",
|
||||
"logout": "cerrar sesión",
|
||||
"minting_status": "estado de acuñación",
|
||||
"payment_notification": "notificación de pago",
|
||||
"price": "precio",
|
||||
"q_mail": "q-mail",
|
||||
"result": {
|
||||
"error": {
|
||||
"generic": "ocurrió un error",
|
||||
"incorrect_password": "contraseña incorrecta",
|
||||
"save_qdn": "no se pudo guardar en QDN"
|
||||
},
|
||||
"status": {
|
||||
"minting": "(acuñando)",
|
||||
"not_minting": "(no acuñando)",
|
||||
"synchronized": "sincronizado",
|
||||
"synchronizing": "sincronizando"
|
||||
},
|
||||
"success": {
|
||||
"publish_qdn": "publicado exitosamente en QDN"
|
||||
}
|
||||
},
|
||||
"save_options": {
|
||||
"no_pinned_changes": "actualmente no tienes cambios en tus aplicaciones fijadas",
|
||||
"overwrite_changes": "la aplicación no pudo descargar tus aplicaciones fijadas existentes guardadas en QDN. ¿Deseas sobrescribir esos cambios?",
|
||||
"overwrite_qdn": "sobrescribir en QDN",
|
||||
"publish_qdn": "¿Deseas publicar tus configuraciones en QDN (cifrado)?",
|
||||
"qdn": "usar guardado en QDN",
|
||||
"register_name": "necesitas un nombre Qortal registrado para guardar tus aplicaciones fijadas en QDN.",
|
||||
"reset_pinned": "¿No te gustan tus cambios locales actuales? ¿Deseas restablecer las aplicaciones fijadas predeterminadas?",
|
||||
"reset_qdn": "¿No te gustan tus cambios locales actuales? ¿Deseas restablecer tus aplicaciones fijadas guardadas en QDN?",
|
||||
"revert_default": "restablecer a predeterminado",
|
||||
"revert_qdn": "restablecer a QDN",
|
||||
"save_qdn": "guardar en QDN",
|
||||
"save": "guardar",
|
||||
"settings": "estás utilizando el método de exportación/importación para guardar configuraciones.",
|
||||
"unsaved_changes": "tienes cambios no guardados en tus aplicaciones fijadas. Guárdalos en QDN."
|
||||
},
|
||||
"settings": "configuraciones",
|
||||
"supply": "suministro",
|
||||
"theme": {
|
||||
"dark": "modo oscuro",
|
||||
"light": "modo claro"
|
||||
},
|
||||
"title": "título",
|
||||
"tutorial": "tutorial",
|
||||
"user_lookup": "búsqueda de usuario",
|
||||
"wallet": {
|
||||
"backup_wallet": "respaldar billetera",
|
||||
"wallet": "billetera",
|
||||
"wallet_other": "billeteras"
|
||||
},
|
||||
"welcome": "bienvenido"
|
||||
}
|
21
public/locales/es/tutorial.json
Normal file
21
public/locales/es/tutorial.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"1_getting_started": "1. Comenzando",
|
||||
"2_overview": "2. Visión general",
|
||||
"3_groups": "3. Grupos de Qortal",
|
||||
"4_obtain_qort": "4. Obtener QORT",
|
||||
"account_creation": "creación de cuenta",
|
||||
"important_info": "¡información importante!",
|
||||
"apps": {
|
||||
"dashboard": "1. Panel de aplicaciones",
|
||||
"navigation": "2. Navegación de aplicaciones"
|
||||
},
|
||||
"initial": {
|
||||
"6_qort": "tener al menos 6 QORT en tu monedero",
|
||||
"explore": "explorar",
|
||||
"general_chat": "chat general",
|
||||
"getting_started": "comenzando",
|
||||
"register_name": "registrar un nombre",
|
||||
"see_apps": "ver aplicaciones",
|
||||
"trade_qort": "intercambiar QORT"
|
||||
}
|
||||
}
|
37
public/locales/fr/auth.json
Normal file
37
public/locales/fr/auth.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"account": {
|
||||
"your": "ton compte",
|
||||
"account_many": "comptes",
|
||||
"account_one": "compte"
|
||||
},
|
||||
"advanced_users": "pour les utilisateurs avancés",
|
||||
"apikey": {
|
||||
"alternative": "alternative : Sélectionner un fichier",
|
||||
"change": "changer la clé API",
|
||||
"enter": "entrer la clé API",
|
||||
"import": "importer la clé API",
|
||||
"key": "clé API",
|
||||
"select_valid": "sélectionnez une clé API valide"
|
||||
},
|
||||
"authenticate": "authentifier",
|
||||
"build_version": "version de build",
|
||||
"create_account": "créer un compte",
|
||||
"download_account": "télécharger le compte",
|
||||
"keep_secure": "Gardez votre fichier de compte en sécurité",
|
||||
"node": {
|
||||
"choose": "choisir un nœud personnalisé",
|
||||
"custom_many": "nœuds personnalisés",
|
||||
"use_custom": "utiliser un nœud personnalisé",
|
||||
"use_local": "utiliser un nœud local",
|
||||
"using": "utilise le nœud",
|
||||
"using_public": "utilise un nœud public"
|
||||
},
|
||||
"password": "mot de passe",
|
||||
"password_confirmation": "confirmer le mot de passe",
|
||||
"return_to_list": "retour à la liste",
|
||||
"wallet": {
|
||||
"password_confirmation": "confirmer le mot de passe du portefeuille",
|
||||
"password": "mot de passe du portefeuille"
|
||||
},
|
||||
"welcome": "bienvenue sur"
|
||||
}
|
71
public/locales/fr/core.json
Normal file
71
public/locales/fr/core.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"add": "ajouter",
|
||||
"cancel": "annuler",
|
||||
"choose": "choisir",
|
||||
"close": "fermer",
|
||||
"continue": "continuer",
|
||||
"core": {
|
||||
"block_height": "hauteur de bloc",
|
||||
"information": "informations du noyau",
|
||||
"peers": "pairs connectés",
|
||||
"version": "version du noyau"
|
||||
},
|
||||
"description": "description",
|
||||
"edit": "éditer",
|
||||
"export": "exporter",
|
||||
"import": "importer",
|
||||
"last_height": "dernière hauteur",
|
||||
"loading": "chargement...",
|
||||
"logout": "se déconnecter",
|
||||
"minting_status": "statut de frappe",
|
||||
"payment_notification": "notification de paiement",
|
||||
"price": "prix",
|
||||
"q_mail": "q-mail",
|
||||
"result": {
|
||||
"error": {
|
||||
"generic": "une erreur s'est produite",
|
||||
"incorrect_password": "mot de passe incorrect",
|
||||
"save_qdn": "impossible d'enregistrer dans QDN"
|
||||
},
|
||||
"status": {
|
||||
"minting": "(frappe en cours)",
|
||||
"not_minting": "(pas de frappe)",
|
||||
"synchronized": "synchronisé",
|
||||
"synchronizing": "synchronisation en cours"
|
||||
},
|
||||
"success": {
|
||||
"publish_qdn": "publié avec succès dans QDN"
|
||||
}
|
||||
},
|
||||
"save_options": {
|
||||
"no_pinned_changes": "vous n'avez actuellement aucune modification de vos applications épinglées",
|
||||
"overwrite_changes": "l'application n'a pas pu télécharger vos applications épinglées existantes enregistrées dans QDN. Voulez-vous écraser ces modifications ?",
|
||||
"overwrite_qdn": "écraser dans QDN",
|
||||
"publish_qdn": "souhaitez-vous publier vos paramètres dans QDN (chiffré) ?",
|
||||
"qdn": "utiliser l'enregistrement QDN",
|
||||
"register_name": "vous devez avoir un nom Qortal enregistré pour enregistrer vos applications épinglées dans QDN.",
|
||||
"reset_pinned": "vous n'aimez pas vos modifications locales actuelles ? Voulez-vous réinitialiser les applications épinglées par défaut ?",
|
||||
"reset_qdn": "vous n'aimez pas vos modifications locales actuelles ? Voulez-vous réinitialiser vos applications épinglées enregistrées dans QDN ?",
|
||||
"revert_default": "revenir aux paramètres par défaut",
|
||||
"revert_qdn": "revenir à QDN",
|
||||
"save_qdn": "enregistrer dans QDN",
|
||||
"save": "enregistrer",
|
||||
"settings": "vous utilisez la méthode d'exportation/importation pour enregistrer les paramètres.",
|
||||
"unsaved_changes": "vous avez des modifications non enregistrées de vos applications épinglées. Enregistrez-les dans QDN."
|
||||
},
|
||||
"settings": "paramètres",
|
||||
"supply": "approvisionnement",
|
||||
"theme": {
|
||||
"dark": "mode sombre",
|
||||
"light": "mode clair"
|
||||
},
|
||||
"title": "titre",
|
||||
"tutorial": "tutoriel",
|
||||
"user_lookup": "recherche d'utilisateur",
|
||||
"wallet": {
|
||||
"backup_wallet": "sauvegarder le portefeuille",
|
||||
"wallet": "portefeuille",
|
||||
"wallet_other": "portefeuilles"
|
||||
},
|
||||
"welcome": "bienvenue"
|
||||
}
|
21
public/locales/fr/tutorial.json
Normal file
21
public/locales/fr/tutorial.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"1_getting_started": "1. Démarrer",
|
||||
"2_overview": "2. Aperçu",
|
||||
"3_groups": "3. Groupes Qortal",
|
||||
"4_obtain_qort": "4. Obtenir des QORT",
|
||||
"account_creation": "création de compte",
|
||||
"important_info": "informations importantes !",
|
||||
"apps": {
|
||||
"dashboard": "1. Tableau de bord des applications",
|
||||
"navigation": "2. Navigation des applications"
|
||||
},
|
||||
"initial": {
|
||||
"6_qort": "avoir au moins 6 QORT dans votre portefeuille",
|
||||
"explore": "explorer",
|
||||
"general_chat": "chat général",
|
||||
"getting_started": "démarrer",
|
||||
"register_name": "enregistrer un nom",
|
||||
"see_apps": "voir les applications",
|
||||
"trade_qort": "échanger des QORT"
|
||||
}
|
||||
}
|
37
public/locales/it/auth.json
Normal file
37
public/locales/it/auth.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"account": {
|
||||
"your": "il tuo account",
|
||||
"account_many": "account",
|
||||
"account_one": "account"
|
||||
},
|
||||
"advanced_users": "per utenti avanzati",
|
||||
"apikey": {
|
||||
"alternative": "alternativa: seleziona un file",
|
||||
"change": "cambia la chiave API",
|
||||
"enter": "inserisci la chiave API",
|
||||
"import": "importa chiave API",
|
||||
"key": "chiave API",
|
||||
"select_valid": "selezione una chiave API valida"
|
||||
},
|
||||
"authenticate": "autenticazione",
|
||||
"build_version": "versione build",
|
||||
"create_account": "crea un account",
|
||||
"download_account": "scarica account",
|
||||
"keep_secure": "metti al sicuro il file del tuo account",
|
||||
"node": {
|
||||
"choose": "scegli un nodo custom",
|
||||
"custom_many": "nodi custom",
|
||||
"use_custom": "use nodo custom",
|
||||
"use_local": "usa nodo locale",
|
||||
"using": "nodo in uso",
|
||||
"using_public": "utilizzo nodo pubblico"
|
||||
},
|
||||
"password_confirmation": "confirma la password",
|
||||
"password": "password",
|
||||
"wallet": {
|
||||
"password_confirmation": "conferma la password del wallet",
|
||||
"password": "password del wallet"
|
||||
},
|
||||
"return_to_list": "ritorna alla lista",
|
||||
"welcome": "benvenuto in"
|
||||
}
|
71
public/locales/it/core.json
Normal file
71
public/locales/it/core.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"add": "aggiungi",
|
||||
"cancel": "annulla",
|
||||
"choose": "scegli",
|
||||
"close": "chiudi",
|
||||
"continue": "continua",
|
||||
"core": {
|
||||
"block_height": "altezza del blocco",
|
||||
"information": "informazioni core",
|
||||
"peers": "peer connessi",
|
||||
"version": "versione core"
|
||||
},
|
||||
"description": "descrizione",
|
||||
"edit": "modifica",
|
||||
"export": "esporta",
|
||||
"import": "importa",
|
||||
"last_height": "ultima altezza",
|
||||
"loading": "caricamento...",
|
||||
"logout": "disconnetti",
|
||||
"minting_status": "stato del conio",
|
||||
"payment_notification": "notifica di pagamento",
|
||||
"price": "prezzo",
|
||||
"q_mail": "q-mail",
|
||||
"result": {
|
||||
"error": {
|
||||
"generic": "si è verificato un errore",
|
||||
"incorrect_password": "password errata",
|
||||
"save_qdn": "impossibile salvare su QDN"
|
||||
},
|
||||
"status": {
|
||||
"minting": "(conio in corso)",
|
||||
"not_minting": "(conio non attivo)",
|
||||
"synchronized": "sincronizzato",
|
||||
"synchronizing": "sincronizzazione in corso"
|
||||
},
|
||||
"success": {
|
||||
"publish_qdn": "pubblicato con successo su QDN"
|
||||
}
|
||||
},
|
||||
"save_options": {
|
||||
"no_pinned_changes": "attualmente non hai modifiche alle tue app appuntate",
|
||||
"overwrite_changes": "l'app non è riuscita a scaricare le tue app appuntate salvate su QDN. Vuoi sovrascrivere queste modifiche?",
|
||||
"overwrite_qdn": "sovrascrivi su QDN",
|
||||
"publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografate)?",
|
||||
"qdn": "usa il salvataggio QDN",
|
||||
"register_name": "hai bisogno di un nome Qortal registrato per salvare le tue app appuntate su QDN.",
|
||||
"reset_pinned": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate predefinite?",
|
||||
"reset_qdn": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le tue app appuntate salvate su QDN?",
|
||||
"revert_default": "ripristina predefinite",
|
||||
"revert_qdn": "ripristina da QDN",
|
||||
"save_qdn": "salva su QDN",
|
||||
"save": "salva",
|
||||
"settings": "stai utilizzando il metodo esporta/importa per salvare le impostazioni.",
|
||||
"unsaved_changes": "hai modifiche non salvate alle tue app appuntate. Salvale su QDN."
|
||||
},
|
||||
"settings": "impostazioni",
|
||||
"supply": "offerta",
|
||||
"theme": {
|
||||
"dark": "modalità scura",
|
||||
"light": "modalità chiara"
|
||||
},
|
||||
"title": "titolo",
|
||||
"tutorial": "tutorial",
|
||||
"user_lookup": "ricerca utente",
|
||||
"wallet": {
|
||||
"backup_wallet": "backup portafoglio",
|
||||
"wallet": "portafoglio",
|
||||
"wallet_other": "portafogli"
|
||||
},
|
||||
"welcome": "benvenuto"
|
||||
}
|
21
public/locales/it/tutorial.json
Normal file
21
public/locales/it/tutorial.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"1_getting_started": "1. Come iniziare",
|
||||
"2_overview": "2. Overview",
|
||||
"3_groups": "3. I gruppi in Qortal",
|
||||
"4_obtain_qort": "4. Ottenere Qort",
|
||||
"account_creation": "creazione account",
|
||||
"important_info": "important information!",
|
||||
"apps": {
|
||||
"dashboard": "1. Dashboard delle app",
|
||||
"navigation": "2. Navigation tra le app"
|
||||
},
|
||||
"initial": {
|
||||
"6_qort": "avere almeno 6 QORT nel proprio wallet",
|
||||
"explore": "esplora",
|
||||
"general_chat": "chat generale",
|
||||
"getting_started": "come iniziare",
|
||||
"register_name": "registra un nome",
|
||||
"see_apps": "vedi le apps",
|
||||
"trade_qort": "scambia i QORT"
|
||||
}
|
||||
}
|
37
public/locales/ru/auth.json
Normal file
37
public/locales/ru/auth.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"account": {
|
||||
"your": "Ваш аккаунт",
|
||||
"account_many": "аккаунты",
|
||||
"account_one": "аккаунт"
|
||||
},
|
||||
"advanced_users": "для продвинутых пользователей",
|
||||
"apikey": {
|
||||
"alternative": "альтернатива: выбрать файл",
|
||||
"change": "изменить API-ключ",
|
||||
"enter": "введите API-ключ",
|
||||
"import": "импортировать API-ключ",
|
||||
"key": "API-ключ",
|
||||
"select_valid": "выберите действительный API-ключ"
|
||||
},
|
||||
"authenticate": "аутентификация",
|
||||
"build_version": "версия сборки",
|
||||
"create_account": "создать аккаунт",
|
||||
"download_account": "скачать аккаунт",
|
||||
"keep_secure": "Храните файл аккаунта в безопасности",
|
||||
"node": {
|
||||
"choose": "выбрать пользовательский узел",
|
||||
"custom_many": "пользовательские узлы",
|
||||
"use_custom": "использовать пользовательский узел",
|
||||
"use_local": "использовать локальный узел",
|
||||
"using": "используется узел",
|
||||
"using_public": "используется публичный узел"
|
||||
},
|
||||
"password": "пароль",
|
||||
"password_confirmation": "подтвердите пароль",
|
||||
"return_to_list": "вернуться к списку",
|
||||
"wallet": {
|
||||
"password_confirmation": "подтвердите пароль кошелька",
|
||||
"password": "пароль кошелька"
|
||||
},
|
||||
"welcome": "добро пожаловать в"
|
||||
}
|
71
public/locales/ru/core.json
Normal file
71
public/locales/ru/core.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"add": "добавить",
|
||||
"cancel": "отмена",
|
||||
"choose": "выбрать",
|
||||
"close": "закрыть",
|
||||
"continue": "продолжить",
|
||||
"core": {
|
||||
"block_height": "высота блока",
|
||||
"information": "информация ядра",
|
||||
"peers": "подключенные узлы",
|
||||
"version": "версия ядра"
|
||||
},
|
||||
"description": "описание",
|
||||
"edit": "редактировать",
|
||||
"export": "экспорт",
|
||||
"import": "импорт",
|
||||
"last_height": "последняя высота",
|
||||
"loading": "загрузка...",
|
||||
"logout": "выйти",
|
||||
"minting_status": "статус чеканки",
|
||||
"payment_notification": "уведомление о платеже",
|
||||
"price": "цена",
|
||||
"q_mail": "q-mail",
|
||||
"result": {
|
||||
"error": {
|
||||
"generic": "произошла ошибка",
|
||||
"incorrect_password": "неверный пароль",
|
||||
"save_qdn": "не удалось сохранить в QDN"
|
||||
},
|
||||
"status": {
|
||||
"minting": "(чеканка)",
|
||||
"not_minting": "(не чеканится)",
|
||||
"synchronized": "синхронизировано",
|
||||
"synchronizing": "синхронизация"
|
||||
},
|
||||
"success": {
|
||||
"publish_qdn": "успешно опубликовано в QDN"
|
||||
}
|
||||
},
|
||||
"save_options": {
|
||||
"no_pinned_changes": "у вас нет изменений в закреплённых приложениях",
|
||||
"overwrite_changes": "приложению не удалось загрузить ваши закреплённые приложения, сохранённые в QDN. Хотите перезаписать эти изменения?",
|
||||
"overwrite_qdn": "перезаписать в QDN",
|
||||
"publish_qdn": "хотите опубликовать свои настройки в QDN (зашифровано)?",
|
||||
"qdn": "использовать сохранение в QDN",
|
||||
"register_name": "вам необходимо зарегистрированное имя Qortal для сохранения закреплённых приложений в QDN.",
|
||||
"reset_pinned": "не устраивают текущие локальные изменения? Хотите сбросить до приложений по умолчанию?",
|
||||
"reset_qdn": "не устраивают текущие локальные изменения? Хотите сбросить до сохранённых в QDN приложений?",
|
||||
"revert_default": "сбросить до стандартных",
|
||||
"revert_qdn": "сбросить до QDN",
|
||||
"save_qdn": "сохранить в QDN",
|
||||
"save": "сохранить",
|
||||
"settings": "вы используете метод экспорта/импорта для сохранения настроек.",
|
||||
"unsaved_changes": "у вас есть несохранённые изменения в закреплённых приложениях. Сохраните их в QDN."
|
||||
},
|
||||
"settings": "настройки",
|
||||
"supply": "предложение",
|
||||
"theme": {
|
||||
"dark": "тёмная тема",
|
||||
"light": "светлая тема"
|
||||
},
|
||||
"title": "заголовок",
|
||||
"tutorial": "учебник",
|
||||
"user_lookup": "поиск пользователя",
|
||||
"wallet": {
|
||||
"backup_wallet": "резервная копия кошелька",
|
||||
"wallet": "кошелёк",
|
||||
"wallet_other": "кошельки"
|
||||
},
|
||||
"welcome": "добро пожаловать"
|
||||
}
|
21
public/locales/ru/tutorial.json
Normal file
21
public/locales/ru/tutorial.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"1_getting_started": "1. Начало работы",
|
||||
"2_overview": "2. Обзор",
|
||||
"3_groups": "3. Группы Qortal",
|
||||
"4_obtain_qort": "4. Получение QORT",
|
||||
"account_creation": "создание аккаунта",
|
||||
"important_info": "важная информация!",
|
||||
"apps": {
|
||||
"dashboard": "1. Панель приложений",
|
||||
"navigation": "2. Навигация по приложениям"
|
||||
},
|
||||
"initial": {
|
||||
"6_qort": "иметь не менее 6 QORT в кошельке",
|
||||
"explore": "исследовать",
|
||||
"general_chat": "общий чат",
|
||||
"getting_started": "начало работы",
|
||||
"register_name": "зарегистрировать имя",
|
||||
"see_apps": "посмотреть приложения",
|
||||
"trade_qort": "обмен QORT"
|
||||
}
|
||||
}
|
89
src/App.tsx
89
src/App.tsx
@ -136,6 +136,7 @@ import { QortPayment } from './components/QortPayment';
|
||||
import { GeneralNotifications } from './components/GeneralNotifications';
|
||||
import { PdfViewer } from './common/PdfViewer';
|
||||
import ThemeSelector from './components/Theme/ThemeSelector.tsx';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type extStates =
|
||||
| 'not-authenticated'
|
||||
@ -316,6 +317,8 @@ function App() {
|
||||
const [sendqortState, setSendqortState] = useState<any>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [isLoadingSendCoin, setIsLoadingSendCoin] = useState<boolean>(false);
|
||||
|
||||
const { t } = useTranslation(['auth', 'core']);
|
||||
const theme = useTheme();
|
||||
|
||||
const [
|
||||
@ -1550,9 +1553,10 @@ function App() {
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
LOGOUT
|
||||
{t('core:logout')}
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
@ -1589,9 +1593,10 @@ function App() {
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
SETTINGS
|
||||
{t('core:settings')}
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
@ -1628,9 +1633,10 @@ function App() {
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
USER LOOKUP
|
||||
{t('core:user_lookup')}
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
@ -1667,9 +1673,10 @@ function App() {
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
WALLETS
|
||||
{t('core:wallet_other')}
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
@ -1703,9 +1710,10 @@ function App() {
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
YOUR ACCOUNT
|
||||
{t('auth:account.your')}
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
@ -1813,9 +1821,10 @@ function App() {
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
MINTING STATUS
|
||||
{t('core:minting_status')}
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
@ -1857,9 +1866,10 @@ function App() {
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
TUTORIAL
|
||||
{t('core:tutorial')}
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
@ -1894,8 +1904,14 @@ function App() {
|
||||
>
|
||||
<Tooltip
|
||||
title={
|
||||
<span style={{ fontSize: '14px', fontWeight: 700 }}>
|
||||
BACKUP WALLET
|
||||
<span
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
{t('core:backup_wallet')}
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
@ -2083,6 +2099,7 @@ function App() {
|
||||
{messageQortalRequest?.text1}
|
||||
</TextP>
|
||||
</Box>
|
||||
|
||||
{messageQortalRequest?.text2 && (
|
||||
<>
|
||||
<Spacer height="10px" />
|
||||
@ -2124,6 +2141,7 @@ function App() {
|
||||
>
|
||||
{messageQortalRequest?.text3}
|
||||
</TextP>
|
||||
|
||||
<Spacer height="15px" />
|
||||
</Box>
|
||||
</>
|
||||
@ -2158,11 +2176,11 @@ function App() {
|
||||
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
lineHeight: 1.2,
|
||||
fontSize: '16px',
|
||||
fontWeight: 700,
|
||||
lineHeight: 1.2,
|
||||
maxWidth: '90%',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
{messageQortalRequest?.highlightedText}
|
||||
@ -2379,17 +2397,7 @@ function App() {
|
||||
>
|
||||
{sendqortState?.amount} QORT
|
||||
</TextP>
|
||||
{/* <Spacer height="29px" />
|
||||
|
||||
<CustomLabel htmlFor="standard-adornment-password">
|
||||
Confirm Wallet Password
|
||||
</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<PasswordField
|
||||
id="standard-adornment-password"
|
||||
value={paymentPassword}
|
||||
onChange={(e) => setPaymentPassword(e.target.value)}
|
||||
/> */}
|
||||
<Spacer height="29px" />
|
||||
<Box
|
||||
sx={{
|
||||
@ -2529,7 +2537,7 @@ function App() {
|
||||
setExtstate('create-wallet');
|
||||
}}
|
||||
>
|
||||
Create account
|
||||
{t('auth:create_account', { postProcess: 'capitalize' })}
|
||||
</CustomButton>
|
||||
</>
|
||||
)}
|
||||
@ -2624,7 +2632,7 @@ function App() {
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
Authenticate
|
||||
{t('auth:authenticate', { postProcess: 'capitalize' })}
|
||||
</TextP>
|
||||
</Box>
|
||||
|
||||
@ -2632,7 +2640,7 @@ function App() {
|
||||
|
||||
<>
|
||||
<CustomLabel htmlFor="standard-adornment-password">
|
||||
Wallet Password
|
||||
{t('auth:wallet.password', { postProcess: 'capitalize' })}
|
||||
</CustomLabel>
|
||||
|
||||
<Spacer height="5px" />
|
||||
@ -2656,7 +2664,8 @@ function App() {
|
||||
fontSize: '12px',
|
||||
}}
|
||||
>
|
||||
{'Using node: '} {currentNode?.url}
|
||||
{t('auth:node.using', { postProcess: 'capitalize' })}:{' '}
|
||||
{currentNode?.url}
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
@ -2667,7 +2676,7 @@ function App() {
|
||||
fontSize: '12px',
|
||||
}}
|
||||
>
|
||||
{'Using public node'}
|
||||
{t('auth:node.using_public', { postProcess: 'capitalize' })}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
@ -2675,7 +2684,7 @@ function App() {
|
||||
<Spacer height="20px" />
|
||||
|
||||
<CustomButton onClick={authenticateWallet}>
|
||||
Authenticate
|
||||
{t('auth:authenticate', { postProcess: 'capitalize' })}
|
||||
</CustomButton>
|
||||
|
||||
<ErrorText>{walletToBeDecryptedError}</ErrorText>
|
||||
@ -2703,7 +2712,9 @@ function App() {
|
||||
onClick={returnToMain}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Spacer height="10px" />
|
||||
|
||||
<div
|
||||
className="image-container"
|
||||
style={{
|
||||
@ -2731,7 +2742,7 @@ function App() {
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
Download Account
|
||||
{t('auth:download_account', { postProcess: 'capitalize' })}
|
||||
</TextP>
|
||||
</Box>
|
||||
|
||||
@ -2740,9 +2751,13 @@ function App() {
|
||||
{!walletToBeDownloaded && (
|
||||
<>
|
||||
<CustomLabel htmlFor="standard-adornment-password">
|
||||
Confirm Wallet Password
|
||||
{t('auth:wallet.password_confirmation', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</CustomLabel>
|
||||
|
||||
<Spacer height="5px" />
|
||||
|
||||
<PasswordField
|
||||
id="standard-adornment-password"
|
||||
value={walletToBeDownloadedPassword}
|
||||
@ -2750,9 +2765,13 @@ function App() {
|
||||
setWalletToBeDownloadedPassword(e.target.value)
|
||||
}
|
||||
/>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
<CustomButton onClick={confirmPasswordToDownload}>
|
||||
Confirm password
|
||||
{t('auth:password_confirmation', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</CustomButton>
|
||||
<ErrorText>{walletToBeDownloadedError}</ErrorText>
|
||||
</>
|
||||
@ -2764,11 +2783,15 @@ function App() {
|
||||
onClick={async () => {
|
||||
await saveFileToDiskFunc();
|
||||
await showInfo({
|
||||
message: `Keep your account file secure.`,
|
||||
message: t('auth:keep_secure', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
});
|
||||
}}
|
||||
>
|
||||
Download account
|
||||
{t('auth:download_account', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</CustomButton>
|
||||
</>
|
||||
)}
|
||||
@ -3024,7 +3047,7 @@ function App() {
|
||||
<Spacer height="17px" />
|
||||
|
||||
<CustomButton onClick={createAccountFunc}>
|
||||
Create Account
|
||||
{t('auth:create_account', { postProcess: 'capitalize' })}
|
||||
</CustomButton>
|
||||
</Box>
|
||||
<ErrorText>{walletToBeDownloadedError}</ErrorText>
|
||||
|
@ -30,6 +30,7 @@ import { cleanUrl, gateways } from '../background';
|
||||
import { GlobalContext } from '../App';
|
||||
import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip';
|
||||
import ThemeSelector from '../components/Theme/ThemeSelector';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const manifestData = {
|
||||
version: '0.5.3',
|
||||
@ -39,13 +40,14 @@ export const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||
<Tooltip {...props} classes={{ popper: className }} />
|
||||
))(({ theme }) => ({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
backgroundColor: '#232428',
|
||||
color: 'white',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
color: theme.palette.text.primary,
|
||||
maxWidth: 320,
|
||||
padding: '20px',
|
||||
fontSize: theme.typography.pxToRem(12),
|
||||
},
|
||||
}));
|
||||
|
||||
function removeTrailingSlash(url) {
|
||||
return url.replace(/\/+$/, '');
|
||||
}
|
||||
@ -84,6 +86,7 @@ export const NotAuthenticated = ({
|
||||
React.useState(null);
|
||||
const { showTutorial, hasSeenGettingStarted } = useContext(GlobalContext);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation(['auth', 'core']);
|
||||
|
||||
const importedApiKeyRef = useRef(null);
|
||||
const currentNodeRef = useRef(null);
|
||||
@ -183,6 +186,7 @@ export const NotAuthenticated = ({
|
||||
useEffect(() => {
|
||||
importedApiKeyRef.current = importedApiKey;
|
||||
}, [importedApiKey]);
|
||||
|
||||
useEffect(() => {
|
||||
currentNodeRef.current = currentNode;
|
||||
}, [currentNode]);
|
||||
@ -309,6 +313,7 @@ export const NotAuthenticated = ({
|
||||
} else if (currentNodeRef.current) {
|
||||
payload = currentNodeRef.current;
|
||||
}
|
||||
|
||||
let isValid = false;
|
||||
|
||||
const url = `${payload?.url}/admin/settings/localAuthBypassEnabled`;
|
||||
@ -345,7 +350,7 @@ export const NotAuthenticated = ({
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
'Failed to set API key:',
|
||||
error.message || 'An error occurred'
|
||||
error.message || t('core:error', { postProcess: 'capitalize' })
|
||||
);
|
||||
});
|
||||
} else {
|
||||
@ -354,7 +359,9 @@ export const NotAuthenticated = ({
|
||||
if (!fromStartUp) {
|
||||
setInfoSnack({
|
||||
type: 'error',
|
||||
message: 'Select a valid apikey',
|
||||
message: t('auth:apikey.select_valid', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
});
|
||||
setOpenSnack(true);
|
||||
}
|
||||
@ -377,7 +384,10 @@ export const NotAuthenticated = ({
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
'Failed to set API key:',
|
||||
error.message || 'An error occurred'
|
||||
error.message ||
|
||||
t('core:error', {
|
||||
postProcess: 'capitalize',
|
||||
})
|
||||
);
|
||||
});
|
||||
return;
|
||||
@ -385,7 +395,11 @@ export const NotAuthenticated = ({
|
||||
if (!fromStartUp) {
|
||||
setInfoSnack({
|
||||
type: 'error',
|
||||
message: error?.message || 'Select a valid apikey',
|
||||
message:
|
||||
error?.message ||
|
||||
t('auth:apikey.select_valid', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
});
|
||||
setOpenSnack(true);
|
||||
}
|
||||
@ -402,6 +416,7 @@ export const NotAuthenticated = ({
|
||||
const addCustomNode = () => {
|
||||
setMode('add-node');
|
||||
};
|
||||
|
||||
const saveCustomNodes = (myNodes, isFullListOfNodes) => {
|
||||
let nodes = [...(myNodes || [])];
|
||||
if (!isFullListOfNodes && customNodeToSaveIndex !== null) {
|
||||
@ -455,7 +470,9 @@ export const NotAuthenticated = ({
|
||||
>
|
||||
<img src={Logo1Dark} className="base-image" />
|
||||
</div>
|
||||
|
||||
<Spacer height="30px" />
|
||||
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
@ -463,7 +480,7 @@ export const NotAuthenticated = ({
|
||||
fontSize: '18px',
|
||||
}}
|
||||
>
|
||||
WELCOME TO
|
||||
{t('auth:welcome', { postProcess: 'capitalize' })}
|
||||
<TextSpan
|
||||
sx={{
|
||||
fontSize: '18px',
|
||||
@ -499,18 +516,15 @@ export const NotAuthenticated = ({
|
||||
transaction you make is linked to your ID, and this is where you
|
||||
manage all your QORT and other tradeable cryptocurrencies on
|
||||
Qortal.
|
||||
</Typography>
|
||||
</Typography>{' '}
|
||||
// TODO translate
|
||||
</React.Fragment>
|
||||
}
|
||||
>
|
||||
<CustomButton onClick={() => setExtstate('wallets')}>
|
||||
{/* <input {...getInputProps()} /> */}
|
||||
Accounts
|
||||
{t('auth:account.account_many', { postProcess: 'capitalize' })}
|
||||
</CustomButton>
|
||||
</HtmlTooltip>
|
||||
{/* <Tooltip title="Authenticate by importing your Qortal JSON file" arrow>
|
||||
<img src={Info} />
|
||||
</Tooltip> */}
|
||||
</Box>
|
||||
|
||||
<Spacer height="6px" />
|
||||
@ -534,7 +548,8 @@ export const NotAuthenticated = ({
|
||||
}}
|
||||
>
|
||||
New users start here!
|
||||
</Typography>
|
||||
</Typography>{' '}
|
||||
// TODO translate
|
||||
<Spacer height="10px" />
|
||||
<Typography
|
||||
color="inherit"
|
||||
@ -546,7 +561,8 @@ export const NotAuthenticated = ({
|
||||
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.
|
||||
</Typography>
|
||||
</Typography>{' '}
|
||||
// TODO translate
|
||||
</React.Fragment>
|
||||
}
|
||||
>
|
||||
@ -565,10 +581,11 @@ export const NotAuthenticated = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
Create account
|
||||
{t('auth:create_account', { postProcess: 'capitalize' })}
|
||||
</CustomButton>
|
||||
</HtmlTooltip>
|
||||
</Box>
|
||||
|
||||
<Spacer height="15px" />
|
||||
|
||||
<Typography
|
||||
@ -577,8 +594,10 @@ export const NotAuthenticated = ({
|
||||
visibility: !useLocalNode && 'hidden',
|
||||
}}
|
||||
>
|
||||
{'Using node: '} {currentNode?.url}
|
||||
{t('auth:node.using', { postProcess: 'capitalize' })}:{' '}
|
||||
{currentNode?.url}
|
||||
</Typography>
|
||||
|
||||
<>
|
||||
<Spacer height="15px" />
|
||||
<Box
|
||||
@ -603,7 +622,7 @@ export const NotAuthenticated = ({
|
||||
textDecoration: 'underline',
|
||||
}}
|
||||
>
|
||||
For advanced users
|
||||
{t('auth:advanced_users', { postProcess: 'capitalize' })}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
@ -628,7 +647,7 @@ export const NotAuthenticated = ({
|
||||
},
|
||||
'& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track':
|
||||
{
|
||||
backgroundColor: 'white', // Change track color when checked
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
}}
|
||||
checked={useLocalNode}
|
||||
@ -659,7 +678,11 @@ export const NotAuthenticated = ({
|
||||
disabled={false}
|
||||
/>
|
||||
}
|
||||
label={`Use ${isLocal ? 'Local' : 'Custom'} Node`}
|
||||
label={
|
||||
isLocal
|
||||
? t('auth:node.use_local', { postProcess: 'capitalize' })
|
||||
: t('auth:node.use_custom', { postProcess: 'capitalize' })
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
{currentNode?.url === 'http://127.0.0.1:12391' && (
|
||||
@ -670,14 +693,19 @@ export const NotAuthenticated = ({
|
||||
variant="contained"
|
||||
component="label"
|
||||
>
|
||||
{apiKey ? 'Change ' : 'Import '} apikey
|
||||
{apiKey
|
||||
? t('auth:node.use_local', { postProcess: 'capitalize' })
|
||||
: t('auth:apikey.import', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '12px',
|
||||
visibility: importedApiKey ? 'visible' : 'hidden',
|
||||
}}
|
||||
>{`api key : ${importedApiKey}`}</Typography>
|
||||
>
|
||||
{t('auth:apikey.key', { postProcess: 'capitalize' })}: $
|
||||
{importedApiKey}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
@ -688,19 +716,21 @@ export const NotAuthenticated = ({
|
||||
variant="contained"
|
||||
component="label"
|
||||
>
|
||||
Choose custom node
|
||||
{t('auth:node.choose', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
</>
|
||||
<Typography
|
||||
sx={{
|
||||
color: 'white',
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '12px',
|
||||
}}
|
||||
>
|
||||
Build version: {manifestData?.version}
|
||||
{t('auth:build_version', { postProcess: 'capitalize' })}:
|
||||
{manifestData?.version}
|
||||
</Typography>
|
||||
</Box>
|
||||
</>
|
||||
|
||||
<CustomizedSnackbars
|
||||
open={openSnack}
|
||||
setOpen={setOpenSnack}
|
||||
@ -714,7 +744,10 @@ export const NotAuthenticated = ({
|
||||
aria-describedby="alert-dialog-description"
|
||||
fullWidth
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{'Custom nodes'}</DialogTitle>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{' '}
|
||||
{t('auth:node.custom_many', { postProcess: 'capitalize' })}:
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box
|
||||
sx={{
|
||||
@ -742,7 +775,7 @@ export const NotAuthenticated = ({
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: 'white',
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
@ -783,7 +816,7 @@ export const NotAuthenticated = ({
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
Choose
|
||||
{t('core:choose', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
@ -799,7 +832,7 @@ export const NotAuthenticated = ({
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: 'white',
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
@ -842,8 +875,9 @@ export const NotAuthenticated = ({
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
Choose
|
||||
{t('core:choose', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => {
|
||||
@ -854,20 +888,20 @@ export const NotAuthenticated = ({
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
Edit
|
||||
{t('core:edit', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => {
|
||||
const nodesToSave = [
|
||||
...(customNodes || []),
|
||||
].filter((item) => item?.url !== node?.url);
|
||||
|
||||
saveCustomNodes(nodesToSave, true);
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
Remove
|
||||
{t('core:remove', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
@ -902,7 +936,14 @@ export const NotAuthenticated = ({
|
||||
)}
|
||||
</Box>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
{mode === 'list' && (
|
||||
<Button variant="contained" onClick={addCustomNode}>
|
||||
{t('core:add', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{mode === 'list' && (
|
||||
<>
|
||||
<Button
|
||||
@ -912,15 +953,10 @@ export const NotAuthenticated = ({
|
||||
}}
|
||||
autoFocus
|
||||
>
|
||||
Close
|
||||
{t('core:close', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{mode === 'list' && (
|
||||
<Button variant="contained" onClick={addCustomNode}>
|
||||
Add
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{mode === 'add-node' && (
|
||||
<>
|
||||
@ -931,7 +967,7 @@ export const NotAuthenticated = ({
|
||||
setCustomNodeToSaveIndex(null);
|
||||
}}
|
||||
>
|
||||
Return to list
|
||||
{t('auth:return_to_list', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@ -940,7 +976,7 @@ export const NotAuthenticated = ({
|
||||
onClick={() => saveCustomNodes(customNodes)}
|
||||
autoFocus
|
||||
>
|
||||
Save
|
||||
{t('core:save', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
@ -954,7 +990,9 @@ export const NotAuthenticated = ({
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{'Enter apikey'}</DialogTitle>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{t('auth:apikey.enter', { postProcess: 'capitalize' })}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box
|
||||
sx={{
|
||||
@ -972,7 +1010,7 @@ export const NotAuthenticated = ({
|
||||
variant="contained"
|
||||
component="label"
|
||||
>
|
||||
Alternative: File select
|
||||
{t('auth:apikey.alternative', { postProcess: 'capitalize' })}
|
||||
<input
|
||||
type="file"
|
||||
accept=".txt"
|
||||
@ -982,17 +1020,8 @@ export const NotAuthenticated = ({
|
||||
</Button>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setEnteredApiKey('');
|
||||
setShowSelectApiKey(false);
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={!enteredApiKey}
|
||||
@ -1036,7 +1065,17 @@ export const NotAuthenticated = ({
|
||||
}}
|
||||
autoFocus
|
||||
>
|
||||
Save
|
||||
{t('core:save', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setEnteredApiKey('');
|
||||
setShowSelectApiKey(false);
|
||||
}}
|
||||
>
|
||||
{t('core:close', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
|
@ -5,11 +5,14 @@ import syncingImg from '../assets/syncStatus/syncing.png';
|
||||
import { getBaseApiReact } from '../App';
|
||||
import '../styles/CoreSyncStatus.css';
|
||||
import { useTheme } from '@mui/material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const CoreSyncStatus = () => {
|
||||
const [nodeInfos, setNodeInfos] = useState({});
|
||||
const [coreInfos, setCoreInfos] = useState({});
|
||||
const [isUsingGateway, setIsUsingGateway] = useState(false);
|
||||
|
||||
const { t } = useTranslation(['auth', 'core']);
|
||||
const theme = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
@ -72,25 +75,25 @@ export const CoreSyncStatus = () => {
|
||||
: '';
|
||||
|
||||
let imagePath = syncingImg;
|
||||
let message = `Synchronizing`;
|
||||
let message = t('core:status.synchronizing', { postProcess: 'capitalize' });
|
||||
|
||||
if (isMintingPossible && !isUsingGateway) {
|
||||
imagePath = syncedMintingImg;
|
||||
message = `${isSynchronizing ? 'Synchronizing' : 'Synchronized'} ${'(Minting)'}`;
|
||||
message = `${t(`core:result.status.${isSynchronizing ? 'synchronizing' : 'synchronized'}`, { postProcess: 'capitalize' })} ${t('core:result.status.minting')}`;
|
||||
} else if (isSynchronizing === true && syncPercent === 99) {
|
||||
imagePath = syncingImg;
|
||||
} else if (isSynchronizing && !isMintingPossible && syncPercent === 100) {
|
||||
imagePath = syncingImg;
|
||||
message = `Synchronizing ${isUsingGateway ? '' : '(Not Minting)'}`;
|
||||
message = `${t('core:result.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.not_minting') : ''}`;
|
||||
} else if (!isSynchronizing && !isMintingPossible && syncPercent === 100) {
|
||||
imagePath = syncedImg;
|
||||
message = `Synchronized ${isUsingGateway ? '' : '(Not Minting)'}`;
|
||||
message = `${t('core:result.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.not_minting') : ''}`;
|
||||
} else if (isSynchronizing && isMintingPossible && syncPercent === 100) {
|
||||
imagePath = syncingImg;
|
||||
message = `Synchronizing ${isUsingGateway ? '' : '(Minting)'}`;
|
||||
message = `${t('core:result.status.synchronizing', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.minting') : ''}`;
|
||||
} else if (!isSynchronizing && isMintingPossible && syncPercent === 100) {
|
||||
imagePath = syncedMintingImg;
|
||||
message = `Synchronized ${isUsingGateway ? '' : '(Minting)'}`;
|
||||
message = `${t('core:result.status.synchronized', { postProcess: 'capitalize' })} ${!isUsingGateway ? t('core:result.status.minting') : ''}`;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -115,24 +118,24 @@ export const CoreSyncStatus = () => {
|
||||
top: '10px',
|
||||
}}
|
||||
>
|
||||
<h3>Core Information</h3>
|
||||
<h3>{t('core:core.information', { postProcess: 'capitalize' })}</h3>
|
||||
<h4 className="lineHeight">
|
||||
Core Version:{' '}
|
||||
{t('core:core.version', { postProcess: 'capitalize' })}:{' '}
|
||||
<span style={{ color: '#03a9f4' }}>{buildVersion}</span>
|
||||
</h4>
|
||||
<h4 className="lineHeight">{message}</h4>
|
||||
<h4 className="lineHeight">
|
||||
Block Height:{' '}
|
||||
{t('core:core.block_height', { postProcess: 'capitalize' })}:{' '}
|
||||
<span style={{ color: '#03a9f4' }}>{height || ''}</span>
|
||||
</h4>
|
||||
<h4 className="lineHeight">
|
||||
Connected Peers:{' '}
|
||||
{t('core:core.peers', { postProcess: 'capitalize' })}:{' '}
|
||||
<span style={{ color: '#03a9f4' }}>
|
||||
{numberOfConnections || ''}
|
||||
</span>
|
||||
</h4>
|
||||
<h4 className="lineHeight">
|
||||
Using public node:{' '}
|
||||
{t('auth:node.using_public', { postProcess: 'capitalize' })}:{' '}
|
||||
<span style={{ color: '#03a9f4' }}>
|
||||
{isUsingGateway?.toString()}
|
||||
</span>
|
||||
|
@ -1,129 +1,130 @@
|
||||
import { Box, ButtonBase, Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import ChatIcon from "@mui/icons-material/Chat";
|
||||
import qTradeLogo from "../../assets/Icons/q-trade-logo.webp";
|
||||
import AppsIcon from "@mui/icons-material/Apps";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { Box, ButtonBase, Typography, useTheme } from '@mui/material';
|
||||
import ChatIcon from '@mui/icons-material/Chat';
|
||||
import qTradeLogo from '../../assets/Icons/q-trade-logo.webp';
|
||||
import AppsIcon from '@mui/icons-material/Apps';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Explore = ({ setDesktopViewMode }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation(['core', 'tutorial']);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
flexWrap: 'wrap',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
"&:hover": { backgroundColor: "secondary.main" },
|
||||
transition: "all 0.1s ease-in-out",
|
||||
padding: "5px",
|
||||
borderRadius: "5px",
|
||||
gap: "5px",
|
||||
'&:hover': { backgroundColor: theme.palette.background.paper },
|
||||
borderRadius: '5px',
|
||||
gap: '5px',
|
||||
padding: '5px',
|
||||
transition: 'all 0.1s ease-in-out',
|
||||
}}
|
||||
onClick={async () => {
|
||||
executeEvent("addTab", {
|
||||
data: { service: "APP", name: "q-trade" },
|
||||
executeEvent('addTab', {
|
||||
data: { service: 'APP', name: 'q-trade' },
|
||||
});
|
||||
executeEvent("open-apps-mode", {});
|
||||
executeEvent('open-apps-mode', {});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
borderRadius: "50%",
|
||||
height: '30px'
|
||||
borderRadius: '50%',
|
||||
height: '30px',
|
||||
}}
|
||||
src={qTradeLogo}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
>
|
||||
Trade QORT
|
||||
{t('tutorial:initial.trade_qort', { postProcess: 'capitalize' })}
|
||||
</Typography>
|
||||
</ButtonBase>
|
||||
|
||||
<ButtonBase
|
||||
sx={{
|
||||
"&:hover": { backgroundColor: "secondary.main" },
|
||||
transition: "all 0.1s ease-in-out",
|
||||
padding: "5px",
|
||||
borderRadius: "5px",
|
||||
gap: "5px",
|
||||
'&:hover': { backgroundColor: theme.palette.background.paper },
|
||||
borderRadius: '5px',
|
||||
gap: '5px',
|
||||
padding: '5px',
|
||||
transition: 'all 0.1s ease-in-out',
|
||||
}}
|
||||
onClick={() => {
|
||||
setDesktopViewMode('apps')
|
||||
|
||||
setDesktopViewMode('apps');
|
||||
}}
|
||||
>
|
||||
<AppsIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
>
|
||||
See Apps
|
||||
{t('tutorial:initial.see_apps', { postProcess: 'capitalize' })}
|
||||
</Typography>
|
||||
</ButtonBase>
|
||||
|
||||
<ButtonBase
|
||||
sx={{
|
||||
"&:hover": { backgroundColor: "secondary.main" },
|
||||
transition: "all 0.1s ease-in-out",
|
||||
padding: "5px",
|
||||
borderRadius: "5px",
|
||||
gap: "5px",
|
||||
'&:hover': { backgroundColor: theme.palette.background.paper },
|
||||
borderRadius: '5px',
|
||||
gap: '5px',
|
||||
padding: '5px',
|
||||
transition: 'all 0.1s ease-in-out',
|
||||
}}
|
||||
onClick={async () => {
|
||||
executeEvent("openGroupMessage", {
|
||||
from: "0" ,
|
||||
executeEvent('openGroupMessage', {
|
||||
from: '0',
|
||||
});
|
||||
}}
|
||||
>
|
||||
<ChatIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
>
|
||||
General Chat
|
||||
{t('tutorial:initial.general_chat', { postProcess: 'capitalize' })}
|
||||
</Typography>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
"&:hover": { backgroundColor: "secondary.main" },
|
||||
transition: "all 0.1s ease-in-out",
|
||||
padding: "5px",
|
||||
borderRadius: "5px",
|
||||
gap: "5px",
|
||||
'&:hover': { backgroundColor: theme.palette.background.paper },
|
||||
transition: 'all 0.1s ease-in-out',
|
||||
padding: '5px',
|
||||
borderRadius: '5px',
|
||||
gap: '5px',
|
||||
}}
|
||||
onClick={async () => {
|
||||
executeEvent("openWalletsApp", {
|
||||
|
||||
});
|
||||
executeEvent('openWalletsApp', {});
|
||||
}}
|
||||
>
|
||||
<AccountBalanceWalletIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
>
|
||||
Wallets
|
||||
{t('core:wallet_other', { postProcess: 'capitalize' })}
|
||||
</Typography>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
|
@ -14,6 +14,7 @@ import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
|
||||
import { formatDate } from '../utils/time';
|
||||
import { useHandlePaymentNotification } from '../hooks/useHandlePaymentNotification';
|
||||
import { executeEvent } from '../utils/events';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const GeneralNotifications = ({ address }) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
@ -31,6 +32,7 @@ export const GeneralNotifications = ({ address }) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const { t } = useTranslation(['core']);
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
@ -48,9 +50,10 @@ export const GeneralNotifications = ({ address }) => {
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
PAYMENT NOTIFICATION
|
||||
{t('core:payment_notification')}
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
|
@ -91,7 +91,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
||||
if (!name) throw new Error('Please provide a name');
|
||||
if (!description) throw new Error('Please provide a description');
|
||||
|
||||
const fee = await getFee('CREATE_GROUP');
|
||||
const fee = await getFee('CREATE_GROUP'); // TODO translate
|
||||
await show({
|
||||
message: 'Would you like to perform an CREATE_GROUP transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
|
@ -101,7 +101,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
||||
const handleJoinGroup = async (group, isOpen) => {
|
||||
try {
|
||||
const groupId = group.groupId;
|
||||
const fee = await getFee('JOIN_GROUP');
|
||||
const fee = await getFee('JOIN_GROUP'); // TODO translate
|
||||
await show({
|
||||
message: 'Would you like to perform an JOIN_GROUP transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
|
@ -129,7 +129,7 @@ export const BlockedUsersModal = () => {
|
||||
executeEvent('updateChatMessagesWithBlocks', true);
|
||||
}
|
||||
} catch (error) {
|
||||
setOpenSnackGlobal(true);
|
||||
setOpenSnackGlobal(true); // TODO translate
|
||||
setInfoSnackCustom({
|
||||
type: 'error',
|
||||
message: error?.message || 'Unable to block user',
|
||||
|
@ -1,20 +1,19 @@
|
||||
import { useMemo } from "react";
|
||||
import DOMPurify from "dompurify";
|
||||
import "react-quill/dist/quill.snow.css";
|
||||
import "react-quill/dist/quill.core.css";
|
||||
import "react-quill/dist/quill.bubble.css";
|
||||
import { Box, styled } from "@mui/material";
|
||||
import { convertQortalLinks } from "../../../utils/qortalLink";
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import DOMPurify from 'dompurify';
|
||||
import 'react-quill/dist/quill.snow.css';
|
||||
import 'react-quill/dist/quill.core.css';
|
||||
import 'react-quill/dist/quill.bubble.css';
|
||||
import { Box, styled } from '@mui/material';
|
||||
import { convertQortalLinks } from '../../../utils/qortalLink';
|
||||
|
||||
const CrowdfundInlineContent = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
fontFamily: "Mulish",
|
||||
fontSize: "19px",
|
||||
display: 'flex',
|
||||
fontFamily: 'Mulish',
|
||||
fontSize: '19px',
|
||||
fontWeight: 400,
|
||||
letterSpacing: 0,
|
||||
color: theme.palette.text.primary,
|
||||
width: '100%'
|
||||
width: '100%',
|
||||
}));
|
||||
|
||||
export const DisplayHtml = ({ html, textColor }: any) => {
|
||||
@ -29,6 +28,7 @@ export const DisplayHtml = ({ html, textColor }: any) => {
|
||||
}, [html]);
|
||||
|
||||
if (!cleanContent) return null;
|
||||
|
||||
return (
|
||||
<CrowdfundInlineContent>
|
||||
<div
|
||||
@ -36,7 +36,7 @@ export const DisplayHtml = ({ html, textColor }: any) => {
|
||||
style={{
|
||||
color: textColor || 'white',
|
||||
fontWeight: 400,
|
||||
fontSize: '16px'
|
||||
fontSize: '16px',
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: cleanContent }}
|
||||
/>
|
||||
|
@ -45,7 +45,6 @@ import { formatDate, formatTimestamp } from '../../../utils/time';
|
||||
import LazyLoad from '../../../common/LazyLoad';
|
||||
import { delay } from '../../../utils/helpers';
|
||||
import { NewThread } from './NewThread';
|
||||
import { getBaseApi } from '../../../background';
|
||||
import {
|
||||
decryptPublishes,
|
||||
getTempPublish,
|
||||
@ -55,15 +54,11 @@ import CheckSVG from '../../../assets/svgs/Check.svg';
|
||||
import SortSVG from '../../../assets/svgs/Sort.svg';
|
||||
import ArrowDownSVG from '../../../assets/svgs/ArrowDown.svg';
|
||||
import { LoadingSnackbar } from '../../Snackbar/LoadingSnackbar';
|
||||
import {
|
||||
executeEvent,
|
||||
subscribeToEvent,
|
||||
unsubscribeFromEvent,
|
||||
} from '../../../utils/events';
|
||||
import { executeEvent } from '../../../utils/events';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App';
|
||||
import { WrapperUserAction } from '../../WrapperUserAction';
|
||||
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group';
|
||||
|
||||
const filterOptions = ['Recently active', 'Newest', 'Oldest'];
|
||||
|
||||
export const threadIdentifier = 'DOCUMENT';
|
||||
@ -183,7 +178,7 @@ export const GroupMail = ({
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
} finally {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
@ -266,7 +261,6 @@ export const GroupMail = ({
|
||||
} else {
|
||||
sorted = fullArrayMsg.sort((a: any, b: any) => a.created - b.created);
|
||||
}
|
||||
|
||||
setAllThreads(sorted);
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
@ -376,6 +370,7 @@ export const GroupMail = ({
|
||||
);
|
||||
setRecentThreads(sorted);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
// dispatch(setIsLoadingCustom(null));
|
||||
@ -444,7 +439,6 @@ export const GroupMail = ({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setMembers(members);
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
@ -754,7 +748,9 @@ export const GroupMail = ({
|
||||
>
|
||||
{thread?.threadData?.title}
|
||||
</ThreadSingleTitle>
|
||||
|
||||
<Spacer height="10px" />
|
||||
|
||||
{filterMode === 'Recently active' && (
|
||||
<div
|
||||
style={{
|
||||
@ -799,7 +795,7 @@ export const GroupMail = ({
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontSize: '12px',
|
||||
}}
|
||||
}} // TODO translate
|
||||
>
|
||||
Last page
|
||||
</Typography>
|
||||
|
@ -1,52 +1,47 @@
|
||||
import {
|
||||
AppBar,
|
||||
Button,
|
||||
Toolbar,
|
||||
Typography,
|
||||
Box,
|
||||
TextField,
|
||||
} from "@mui/material";
|
||||
import { styled } from "@mui/system";
|
||||
import { Typography, Box, TextField } from '@mui/material';
|
||||
import { styled } from '@mui/system';
|
||||
|
||||
export const InstanceContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
backgroundColor: "var(--color-instance)",
|
||||
height: "59px",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
backgroundColor: 'var(--color-instance)',
|
||||
height: '59px',
|
||||
flexShrink: 0,
|
||||
justifyContent: "space-between",
|
||||
justifyContent: 'space-between',
|
||||
}));
|
||||
|
||||
export const MailContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
height: "calc(100vh - 78px)",
|
||||
overflow: "hidden",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
height: 'calc(100vh - 78px)',
|
||||
overflow: 'hidden',
|
||||
}));
|
||||
|
||||
export const MailBody = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
height: "calc(100% - 59px)",
|
||||
// overflow: 'auto !important'
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
width: '100%',
|
||||
height: 'calc(100% - 59px)',
|
||||
}));
|
||||
|
||||
export const MailBodyInner = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "50%",
|
||||
height: "100%",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '50%',
|
||||
height: '100%',
|
||||
}));
|
||||
|
||||
export const MailBodyInnerHeader = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
height: "25px",
|
||||
marginTop: "50px",
|
||||
marginBottom: "35px",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "11px",
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '25px',
|
||||
marginTop: '50px',
|
||||
marginBottom: '35px',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: '11px',
|
||||
}));
|
||||
|
||||
export const MailBodyInnerScroll = styled(Box)`
|
||||
@ -84,163 +79,174 @@ export const MailBodyInnerScroll = styled(Box)`
|
||||
`;
|
||||
|
||||
export const ComposeContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "150px",
|
||||
alignItems: "center",
|
||||
gap: "7px",
|
||||
height: "100%",
|
||||
cursor: "pointer",
|
||||
transition: "0.2s background-color",
|
||||
justifyContent: "center",
|
||||
"&:hover": {
|
||||
backgroundColor: "rgba(67, 68, 72, 1)",
|
||||
display: 'flex',
|
||||
width: '150px',
|
||||
alignItems: 'center',
|
||||
gap: '7px',
|
||||
height: '100%',
|
||||
cursor: 'pointer',
|
||||
transition: '0.2s background-color',
|
||||
justifyContent: 'center',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(67, 68, 72, 1)',
|
||||
},
|
||||
}));
|
||||
|
||||
export const ComposeContainerBlank = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "150px",
|
||||
alignItems: "center",
|
||||
gap: "7px",
|
||||
height: "100%",
|
||||
display: 'flex',
|
||||
width: '150px',
|
||||
alignItems: 'center',
|
||||
gap: '7px',
|
||||
height: '100%',
|
||||
}));
|
||||
|
||||
export const ComposeP = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "15px",
|
||||
fontSize: '15px',
|
||||
fontWeight: 500,
|
||||
}));
|
||||
|
||||
export const ComposeIcon = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
cursor: "pointer",
|
||||
});
|
||||
export const ArrowDownIcon = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
cursor: "pointer",
|
||||
});
|
||||
export const MailIconImg = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
export const ComposeIcon = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
||||
export const MailMessageRowInfoImg = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
export const ArrowDownIcon = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
||||
export const MailIconImg = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
});
|
||||
|
||||
export const MailMessageRowInfoImg = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
});
|
||||
|
||||
export const SelectInstanceContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "17px",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '17px',
|
||||
}));
|
||||
|
||||
export const SelectInstanceContainerInner = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "3px",
|
||||
cursor: "pointer",
|
||||
padding: "8px",
|
||||
transition: "all 0.2s",
|
||||
"&:hover": {
|
||||
borderRadius: "8px",
|
||||
background: "#434448",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '3px',
|
||||
cursor: 'pointer',
|
||||
padding: '8px',
|
||||
transition: 'all 0.2s',
|
||||
'&:hover': {
|
||||
borderRadius: '8px',
|
||||
background: '#434448',
|
||||
},
|
||||
}));
|
||||
|
||||
export const SelectInstanceContainerFilterInner = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "3px",
|
||||
cursor: "pointer",
|
||||
padding: "8px",
|
||||
transition: "all 0.2s"
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '3px',
|
||||
cursor: 'pointer',
|
||||
padding: '8px',
|
||||
transition: 'all 0.2s',
|
||||
}));
|
||||
|
||||
|
||||
export const InstanceLabel = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
fontWeight: 500,
|
||||
color: "#FFFFFF33",
|
||||
color: '#FFFFFF33',
|
||||
}));
|
||||
|
||||
export const InstanceP = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
fontWeight: 500,
|
||||
}));
|
||||
|
||||
export const MailMessageRowContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
justifyContent: "space-between",
|
||||
borderRadius: "56px 5px 10px 56px",
|
||||
paddingRight: "15px",
|
||||
transition: "background 0.2s",
|
||||
gap: "10px",
|
||||
"&:hover": {
|
||||
background: "#434448",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
justifyContent: 'space-between',
|
||||
borderRadius: '56px 5px 10px 56px',
|
||||
paddingRight: '15px',
|
||||
transition: 'background 0.2s',
|
||||
gap: '10px',
|
||||
'&:hover': {
|
||||
background: '#434448',
|
||||
},
|
||||
}));
|
||||
|
||||
export const MailMessageRowProfile = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
justifyContent: "flex-start",
|
||||
gap: "10px",
|
||||
width: "50%",
|
||||
overflow: "hidden",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
justifyContent: 'flex-start',
|
||||
gap: '10px',
|
||||
width: '50%',
|
||||
overflow: 'hidden',
|
||||
}));
|
||||
|
||||
export const MailMessageRowInfo = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
justifyContent: "flex-start",
|
||||
gap: "7px",
|
||||
width: "50%",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
justifyContent: 'flex-start',
|
||||
gap: '7px',
|
||||
width: '50%',
|
||||
}));
|
||||
|
||||
export const MailMessageRowInfoStatusNotDecrypted = styled(Typography)(
|
||||
({ theme }) => ({
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
fontWeight: 900,
|
||||
textTransform: "uppercase",
|
||||
paddingTop: "2px",
|
||||
textTransform: 'uppercase',
|
||||
paddingTop: '2px',
|
||||
})
|
||||
);
|
||||
|
||||
export const MailMessageRowInfoStatusRead = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
fontWeight: 300,
|
||||
}));
|
||||
|
||||
export const MessageExtraInfo = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "2px",
|
||||
overflow: "hidden",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '2px',
|
||||
overflow: 'hidden',
|
||||
}));
|
||||
|
||||
export const MessageExtraName = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
fontWeight: 900,
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
}));
|
||||
|
||||
export const MessageExtraDate = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "15px",
|
||||
fontSize: '15px',
|
||||
fontWeight: 500,
|
||||
}));
|
||||
|
||||
export const MessagesContainer = styled(Box)(({ theme }) => ({
|
||||
width: "460px",
|
||||
maxWidth: "90%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "12px",
|
||||
width: '460px',
|
||||
maxWidth: '90%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '12px',
|
||||
}));
|
||||
|
||||
export const InstanceListParent = styled(Box)`
|
||||
@ -254,18 +260,21 @@ export const InstanceListParent = styled(Box)`
|
||||
background-color: var(--color-instance-popover-bg);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
`;
|
||||
|
||||
export const InstanceListHeader = styled(Box)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
background-color: var(--color-instance-popover-bg);
|
||||
`;
|
||||
|
||||
export const InstanceFooter = styled(Box)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
export const InstanceListContainer = styled(Box)`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@ -301,126 +310,132 @@ export const InstanceListContainer = styled(Box)`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const InstanceListContainerRowLabelContainer = styled(Box)(
|
||||
({ theme }) => ({
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
height: "50px",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
height: '50px',
|
||||
})
|
||||
);
|
||||
|
||||
export const InstanceListContainerRow = styled(Box)(({ theme }) => ({
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
height: "50px",
|
||||
cursor: "pointer",
|
||||
transition: "0.2s background",
|
||||
"&:hover": {
|
||||
background: "rgba(67, 68, 72, 1)",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
height: '50px',
|
||||
cursor: 'pointer',
|
||||
transition: '0.2s background',
|
||||
'&:hover': {
|
||||
background: 'rgba(67, 68, 72, 1)',
|
||||
},
|
||||
flexShrink: 0,
|
||||
}));
|
||||
|
||||
export const InstanceListContainerRowCheck = styled(Box)(({ theme }) => ({
|
||||
width: "47px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: '47px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}));
|
||||
|
||||
export const InstanceListContainerRowMain = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
paddingRight: "30px",
|
||||
overflow: "hidden",
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
paddingRight: '30px',
|
||||
overflow: 'hidden',
|
||||
}));
|
||||
|
||||
export const CloseParent = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '20px',
|
||||
}));
|
||||
|
||||
export const InstanceListContainerRowMainP = styled(Typography)(
|
||||
({ theme }) => ({
|
||||
fontWeight: 500,
|
||||
fontSize: "16px",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
fontSize: '16px',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
})
|
||||
);
|
||||
|
||||
export const InstanceListContainerRowCheckIcon = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
export const InstanceListContainerRowCheckIcon = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
});
|
||||
export const InstanceListContainerRowGroupIcon = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
export const InstanceListContainerRowGroupIcon = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
});
|
||||
export const TypeInAliasTextfield = styled(TextField)({
|
||||
width: "340px", // Adjust the width as needed
|
||||
borderRadius: "5px",
|
||||
backgroundColor: "rgba(30, 30, 32, 1)",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
width: '340px', // Adjust the width as needed
|
||||
borderRadius: '5px',
|
||||
backgroundColor: 'rgba(30, 30, 32, 1)',
|
||||
border: 'none',
|
||||
outline: 'none',
|
||||
input: {
|
||||
fontSize: 16,
|
||||
color: "white",
|
||||
"&::placeholder": {
|
||||
color: 'white',
|
||||
'&::placeholder': {
|
||||
fontSize: 16,
|
||||
color: "rgba(255, 255, 255, 0.2)",
|
||||
color: 'rgba(255, 255, 255, 0.2)',
|
||||
},
|
||||
border: "none",
|
||||
outline: "none",
|
||||
padding: "10px",
|
||||
border: 'none',
|
||||
outline: 'none',
|
||||
padding: '10px',
|
||||
},
|
||||
"& .MuiOutlinedInput-root": {
|
||||
"& fieldset": {
|
||||
border: "none",
|
||||
'& .MuiOutlinedInput-root': {
|
||||
'& fieldset': {
|
||||
border: 'none',
|
||||
},
|
||||
"&:hover fieldset": {
|
||||
border: "none",
|
||||
'&:hover fieldset': {
|
||||
border: 'none',
|
||||
},
|
||||
"&.Mui-focused fieldset": {
|
||||
border: "none",
|
||||
'&.Mui-focused fieldset': {
|
||||
border: 'none',
|
||||
},
|
||||
},
|
||||
"& .MuiInput-underline:before": {
|
||||
borderBottom: "none",
|
||||
'& .MuiInput-underline:before': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
"& .MuiInput-underline:hover:not(.Mui-disabled):before": {
|
||||
borderBottom: "none",
|
||||
'& .MuiInput-underline:hover:not(.Mui-disabled):before': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
"& .MuiInput-underline:after": {
|
||||
borderBottom: "none",
|
||||
'& .MuiInput-underline:after': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
export const NewMessageCloseImg = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
cursor: "pointer",
|
||||
export const NewMessageCloseImg = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
cursor: 'pointer',
|
||||
});
|
||||
export const NewMessageHeaderP = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "18px",
|
||||
fontSize: '18px',
|
||||
fontWeight: 600,
|
||||
}));
|
||||
|
||||
export const NewMessageInputRow = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
borderBottom: "3px solid rgba(237, 239, 241, 1)",
|
||||
width: "100%",
|
||||
paddingBottom: "6px",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
borderBottom: '3px solid rgba(237, 239, 241, 1)',
|
||||
width: '100%',
|
||||
paddingBottom: '6px',
|
||||
}));
|
||||
export const NewMessageInputLabelP = styled(Typography)`
|
||||
color: rgba(84, 84, 84, 0.7);
|
||||
@ -444,25 +459,25 @@ export const AliasLabelP = styled(Typography)`
|
||||
}
|
||||
`;
|
||||
export const NewMessageAliasContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "12px",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
}));
|
||||
export const AttachmentContainer = styled(Box)(({ theme }) => ({
|
||||
height: "36px",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: '36px',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}));
|
||||
|
||||
export const NewMessageAttachmentImg = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
cursor: "pointer",
|
||||
padding: "10px",
|
||||
border: "1px dashed #646464",
|
||||
export const NewMessageAttachmentImg = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
cursor: 'pointer',
|
||||
padding: '10px',
|
||||
border: '1px dashed #646464',
|
||||
});
|
||||
|
||||
export const NewMessageSendButton = styled(Box)`
|
||||
@ -582,34 +597,36 @@ export const ShowMessageButtonP = styled(Typography)`
|
||||
color: white;
|
||||
`;
|
||||
|
||||
export const ShowMessageButtonImg = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
cursor: "pointer",
|
||||
export const ShowMessageButtonImg = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
||||
export const MailAttachmentImg = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
export const MailAttachmentImg = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
});
|
||||
export const AliasAvatarImg = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
|
||||
export const AliasAvatarImg = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
});
|
||||
export const MoreImg = styled("img")({
|
||||
width: "auto",
|
||||
height: "auto",
|
||||
userSelect: "none",
|
||||
objectFit: "contain",
|
||||
transition: "0.2s all",
|
||||
"&:hover": {
|
||||
transform: "scale(1.3)",
|
||||
|
||||
export const MoreImg = styled('img')({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
userSelect: 'none',
|
||||
objectFit: 'contain',
|
||||
transition: '0.2s all',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.3)',
|
||||
},
|
||||
});
|
||||
|
||||
@ -625,17 +642,19 @@ export const MoreP = styled(Typography)`
|
||||
letter-spacing: -0.16px;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
export const ThreadContainerFullWidth = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
}));
|
||||
|
||||
export const ThreadContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
maxWidth: "95%",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
maxWidth: '95%',
|
||||
}));
|
||||
|
||||
export const GroupNameP = styled(Typography)`
|
||||
@ -648,7 +667,7 @@ export const GroupNameP = styled(Typography)`
|
||||
`;
|
||||
|
||||
export const AllThreadP = styled(Typography)`
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
@ -668,9 +687,10 @@ height: 76px;
|
||||
align-items: center;
|
||||
transition: 0.2s all;
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.20)
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
`;
|
||||
|
||||
export const SingleTheadMessageParent = styled(Box)`
|
||||
border-radius: 35px 4px 4px 35px;
|
||||
background: #434448;
|
||||
@ -680,22 +700,20 @@ cursor: pointer;
|
||||
margin-bottom: 5px;
|
||||
height: 76px;
|
||||
align-items: center;
|
||||
|
||||
`;
|
||||
|
||||
export const ThreadInfoColumn = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "170px",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '170px',
|
||||
gap: '2px',
|
||||
marginLeft: '10px',
|
||||
height: '100%',
|
||||
justifyContent: 'center'
|
||||
justifyContent: 'center',
|
||||
}));
|
||||
|
||||
|
||||
export const ThreadInfoColumnNameP = styled(Typography)`
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
@ -705,8 +723,9 @@ white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const ThreadInfoColumnbyP = styled('span')`
|
||||
color: rgba(255, 255, 255, 0.80);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
@ -715,15 +734,16 @@ line-height: normal;
|
||||
`;
|
||||
|
||||
export const ThreadInfoColumnTime = styled(Typography)`
|
||||
color: rgba(255, 255, 255, 0.80);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-family: Roboto;
|
||||
font-size: 15px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
`
|
||||
`;
|
||||
|
||||
export const ThreadSingleTitle = styled(Typography)`
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
font-family: Roboto;
|
||||
font-size: 23px;
|
||||
font-style: normal;
|
||||
@ -732,17 +752,19 @@ line-height: normal;
|
||||
white-space: wrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
`
|
||||
`;
|
||||
|
||||
export const ThreadSingleLastMessageP = styled(Typography)`
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
font-family: Roboto;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
`
|
||||
`;
|
||||
|
||||
export const ThreadSingleLastMessageSpanP = styled('span')`
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
font-family: Roboto;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
@ -754,24 +776,22 @@ export const GroupContainer = styled(Box)`
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
|
||||
|
||||
`
|
||||
`;
|
||||
|
||||
export const CloseContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "50px",
|
||||
overflow: "hidden",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
transition: "0.2s background-color",
|
||||
justifyContent: "center",
|
||||
display: 'flex',
|
||||
width: '50px',
|
||||
overflow: 'hidden',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
transition: '0.2s background-color',
|
||||
justifyContent: 'center',
|
||||
position: 'absolute',
|
||||
top: '0px',
|
||||
right: '0px',
|
||||
height: '50px',
|
||||
borderRadius: '0px 12px 0px 0px',
|
||||
"&:hover": {
|
||||
backgroundColor: "rgba(162, 31, 31, 1)",
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(162, 31, 31, 1)',
|
||||
},
|
||||
}));
|
@ -191,7 +191,7 @@ export const NewThread = ({
|
||||
}
|
||||
if (!groupInfo) {
|
||||
errorMsg = 'Cannot access group information';
|
||||
}
|
||||
} // TODO translate
|
||||
|
||||
// if (!description) missingFields.push('subject')
|
||||
if (missingFields.length > 0) {
|
||||
|
@ -1,18 +1,24 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { createEditor } from 'slate';
|
||||
import { withReact, Slate, Editable, RenderElementProps, RenderLeafProps } from 'slate-react';
|
||||
import {
|
||||
withReact,
|
||||
Slate,
|
||||
Editable,
|
||||
RenderElementProps,
|
||||
RenderLeafProps,
|
||||
} from 'slate-react';
|
||||
|
||||
type ExtendedRenderElementProps = RenderElementProps & { mode?: string }
|
||||
type ExtendedRenderElementProps = RenderElementProps & { mode?: string };
|
||||
|
||||
export const renderElement = ({
|
||||
attributes,
|
||||
children,
|
||||
element,
|
||||
mode
|
||||
mode,
|
||||
}: ExtendedRenderElementProps) => {
|
||||
switch (element.type) {
|
||||
case 'block-quote':
|
||||
return <blockquote {...attributes}>{children}</blockquote>
|
||||
return <blockquote {...attributes}>{children}</blockquote>;
|
||||
case 'heading-2':
|
||||
return (
|
||||
<h2
|
||||
@ -22,7 +28,7 @@ export const renderElement = ({
|
||||
>
|
||||
{children}
|
||||
</h2>
|
||||
)
|
||||
);
|
||||
case 'heading-3':
|
||||
return (
|
||||
<h3
|
||||
@ -32,21 +38,21 @@ export const renderElement = ({
|
||||
>
|
||||
{children}
|
||||
</h3>
|
||||
)
|
||||
);
|
||||
case 'code-block':
|
||||
return (
|
||||
<pre {...attributes} className="code-block">
|
||||
<code>{children}</code>
|
||||
</pre>
|
||||
)
|
||||
);
|
||||
case 'code-line':
|
||||
return <div {...attributes}>{children}</div>
|
||||
return <div {...attributes}>{children}</div>;
|
||||
case 'link':
|
||||
return (
|
||||
<a href={element.url} {...attributes}>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<p
|
||||
@ -56,24 +62,23 @@ export const renderElement = ({
|
||||
>
|
||||
{children}
|
||||
</p>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export const renderLeaf = ({ attributes, children, leaf }: RenderLeafProps) => {
|
||||
let el = children
|
||||
let el = children;
|
||||
|
||||
if (leaf.bold) {
|
||||
el = <strong>{el}</strong>
|
||||
el = <strong>{el}</strong>;
|
||||
}
|
||||
|
||||
if (leaf.italic) {
|
||||
el = <em>{el}</em>
|
||||
el = <em>{el}</em>;
|
||||
}
|
||||
|
||||
if (leaf.underline) {
|
||||
el = <u>{el}</u>
|
||||
el = <u>{el}</u>;
|
||||
}
|
||||
|
||||
if (leaf.link) {
|
||||
@ -81,39 +86,35 @@ export const renderLeaf = ({ attributes, children, leaf }: RenderLeafProps) => {
|
||||
<a href={leaf.link} {...attributes}>
|
||||
{el}
|
||||
</a>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return <span {...attributes}>{el}</span>
|
||||
}
|
||||
return <span {...attributes}>{el}</span>;
|
||||
};
|
||||
|
||||
interface ReadOnlySlateProps {
|
||||
content: any
|
||||
mode?: string
|
||||
content: any;
|
||||
mode?: string;
|
||||
}
|
||||
const ReadOnlySlate: React.FC<ReadOnlySlateProps> = ({ content, mode }) => {
|
||||
const [load, setLoad] = useState(false)
|
||||
const editor = useMemo(() => withReact(createEditor()), [])
|
||||
const value = useMemo(() => content, [content])
|
||||
const [load, setLoad] = useState(false);
|
||||
const editor = useMemo(() => withReact(createEditor()), []);
|
||||
const value = useMemo(() => content, [content]);
|
||||
|
||||
const performUpdate = useCallback(async () => {
|
||||
setLoad(true)
|
||||
setLoad(true);
|
||||
await new Promise<void>((res) => {
|
||||
setTimeout(() => {
|
||||
res()
|
||||
res();
|
||||
}, 250);
|
||||
})
|
||||
setLoad(false)
|
||||
}, [])
|
||||
});
|
||||
setLoad(false);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
performUpdate();
|
||||
}, [value]);
|
||||
|
||||
|
||||
|
||||
|
||||
performUpdate()
|
||||
}, [value])
|
||||
|
||||
if(load) return null
|
||||
if (load) return null;
|
||||
|
||||
return (
|
||||
<Slate editor={editor} value={value} onChange={() => {}}>
|
||||
@ -123,7 +124,7 @@ const ReadOnlySlate: React.FC<ReadOnlySlateProps> = ({ content, mode }) => {
|
||||
renderLeaf={renderLeaf}
|
||||
/>
|
||||
</Slate>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default ReadOnlySlate;
|
@ -1,8 +1,8 @@
|
||||
import React, { useState } from "react";
|
||||
import { Avatar, Box, IconButton } from "@mui/material";
|
||||
import DOMPurify from "dompurify";
|
||||
import { useState } from 'react';
|
||||
import { Avatar, Box, IconButton } from '@mui/material';
|
||||
import DOMPurify from 'dompurify';
|
||||
import FormatQuoteIcon from '@mui/icons-material/FormatQuote';
|
||||
import MoreSVG from '../../../assets/svgs/More.svg'
|
||||
import MoreSVG from '../../../assets/svgs/More.svg';
|
||||
|
||||
import {
|
||||
MoreImg,
|
||||
@ -11,20 +11,18 @@ import {
|
||||
ThreadInfoColumn,
|
||||
ThreadInfoColumnNameP,
|
||||
ThreadInfoColumnTime,
|
||||
} from "./Mail-styles";
|
||||
import { Spacer } from "../../../common/Spacer";
|
||||
import { DisplayHtml } from "./DisplayHtml";
|
||||
import { formatTimestampForum } from "../../../utils/time";
|
||||
import ReadOnlySlate from "./ReadOnlySlate";
|
||||
import { MessageDisplay } from "../../Chat/MessageDisplay";
|
||||
import { getBaseApi } from "../../../background";
|
||||
import { getBaseApiReact } from "../../../App";
|
||||
import { WrapperUserAction } from "../../WrapperUserAction";
|
||||
} from './Mail-styles';
|
||||
import { Spacer } from '../../../common/Spacer';
|
||||
import { formatTimestampForum } from '../../../utils/time';
|
||||
import ReadOnlySlate from './ReadOnlySlate';
|
||||
import { MessageDisplay } from '../../Chat/MessageDisplay';
|
||||
import { getBaseApiReact } from '../../../App';
|
||||
import { WrapperUserAction } from '../../WrapperUserAction';
|
||||
|
||||
export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
||||
const [expandAttachments, setExpandAttachments] = useState<boolean>(false);
|
||||
|
||||
let cleanHTML = "";
|
||||
let cleanHTML = '';
|
||||
if (message?.htmlContent) {
|
||||
cleanHTML = DOMPurify.sanitize(message.htmlContent);
|
||||
}
|
||||
@ -32,37 +30,49 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
||||
return (
|
||||
<SingleTheadMessageParent
|
||||
sx={{
|
||||
height: "auto",
|
||||
alignItems: "flex-start",
|
||||
cursor: "default",
|
||||
borderRadius: '35px 4px 4px 4px'
|
||||
height: 'auto',
|
||||
alignItems: 'flex-start',
|
||||
cursor: 'default',
|
||||
borderRadius: '35px 4px 4px 4px',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
width: '100%'
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
gap: "10px",
|
||||
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<WrapperUserAction disabled={myName === message?.name} address={undefined} name={message?.name}>
|
||||
<Avatar sx={{
|
||||
<WrapperUserAction
|
||||
disabled={myName === message?.name}
|
||||
address={undefined}
|
||||
name={message?.name}
|
||||
>
|
||||
<Avatar
|
||||
sx={{
|
||||
height: '50px',
|
||||
width: '50px'
|
||||
}} src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.name}/qortal_avatar?async=true`} alt={message?.name}>{message?.name?.charAt(0)}</Avatar>
|
||||
width: '50px',
|
||||
}}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.name}/qortal_avatar?async=true`}
|
||||
alt={message?.name}
|
||||
>
|
||||
{message?.name?.charAt(0)}
|
||||
</Avatar>
|
||||
</WrapperUserAction>
|
||||
<ThreadInfoColumn>
|
||||
|
||||
<WrapperUserAction disabled={myName === message?.name} address={undefined} name={message?.name}>
|
||||
<WrapperUserAction
|
||||
disabled={myName === message?.name}
|
||||
address={undefined}
|
||||
name={message?.name}
|
||||
>
|
||||
<ThreadInfoColumnNameP>{message?.name}</ThreadInfoColumnNameP>
|
||||
</WrapperUserAction>
|
||||
<ThreadInfoColumnTime>
|
||||
@ -71,37 +81,40 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
||||
</ThreadInfoColumn>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{message?.attachments?.length > 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
marginTop: "10px",
|
||||
width: '100%',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
{message?.attachments
|
||||
.map((file: any, index: number) => {
|
||||
const isFirst = index === 0
|
||||
{message?.attachments.map((file: any, index: number) => {
|
||||
const isFirst = index === 0;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: expandAttachments ? "flex" : !expandAttachments && isFirst ? 'flex' : 'none',
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
width: "100%",
|
||||
display: expandAttachments
|
||||
? 'flex'
|
||||
: !expandAttachments && isFirst
|
||||
? 'flex'
|
||||
: 'none',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
cursor: "pointer",
|
||||
width: "auto",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
cursor: 'pointer',
|
||||
width: 'auto',
|
||||
}}
|
||||
>
|
||||
{/* <FileElement
|
||||
@ -128,75 +141,84 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
||||
{message?.attachments?.length > 1 && isFirst && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
}}
|
||||
onClick={() => {
|
||||
setExpandAttachments(prev => !prev);
|
||||
setExpandAttachments((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
<MoreImg
|
||||
sx={{
|
||||
marginLeft: "5px",
|
||||
marginLeft: '5px',
|
||||
transform: expandAttachments
|
||||
? "rotate(180deg)"
|
||||
: "unset",
|
||||
? 'rotate(180deg)'
|
||||
: 'unset',
|
||||
}}
|
||||
src={MoreSVG}
|
||||
/>
|
||||
<MoreP>
|
||||
{expandAttachments ? 'hide' : `(${message?.attachments?.length - 1} more)`}
|
||||
|
||||
{expandAttachments
|
||||
? 'hide'
|
||||
: `(${message?.attachments?.length - 1} more)`}
|
||||
</MoreP>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
})
|
||||
}
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
{message?.reply?.textContentV2 && (
|
||||
<>
|
||||
<Box sx={{
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
opacity: 0.7,
|
||||
borderRadius: '5px',
|
||||
border: '1px solid gray',
|
||||
boxSizing: 'border-box',
|
||||
padding: '5px'
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
gap: "10px",
|
||||
|
||||
padding: '5px',
|
||||
}}
|
||||
>
|
||||
|
||||
<Avatar sx={{
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
sx={{
|
||||
height: '30px',
|
||||
width: '30px'
|
||||
}} src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.reply?.name}/qortal_avatar?async=true`} alt={message?.reply?.name}>{message?.reply?.name?.charAt(0)}</Avatar>
|
||||
width: '30px',
|
||||
}}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.reply?.name}/qortal_avatar?async=true`}
|
||||
alt={message?.reply?.name}
|
||||
>
|
||||
{message?.reply?.name?.charAt(0)}
|
||||
</Avatar>
|
||||
<ThreadInfoColumn>
|
||||
<ThreadInfoColumnNameP sx={{
|
||||
fontSize: '14px'
|
||||
}}>{message?.reply?.name}</ThreadInfoColumnNameP>
|
||||
|
||||
<ThreadInfoColumnNameP
|
||||
sx={{
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
{message?.reply?.name}
|
||||
</ThreadInfoColumnNameP>
|
||||
</ThreadInfoColumn>
|
||||
</Box>
|
||||
<MessageDisplay htmlContent={message?.reply?.textContentV2} />
|
||||
</Box>
|
||||
<Spacer height="20px" />
|
||||
</>
|
||||
|
||||
)}
|
||||
|
||||
{message?.textContent && (
|
||||
@ -208,22 +230,18 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
||||
{message?.htmlContent && (
|
||||
<div dangerouslySetInnerHTML={{ __html: cleanHTML }} />
|
||||
)}
|
||||
<Box sx={{
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end'
|
||||
}}>
|
||||
<IconButton
|
||||
onClick={() => openNewPostWithQuote(message)}
|
||||
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<IconButton onClick={() => openNewPostWithQuote(message)}>
|
||||
<FormatQuoteIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
|
||||
|
||||
</SingleTheadMessageParent>
|
||||
);
|
||||
};
|
||||
|
@ -1,32 +1,33 @@
|
||||
import React from "react";
|
||||
import ReactQuill, { Quill } from "react-quill";
|
||||
import "react-quill/dist/quill.snow.css";
|
||||
import ImageResize from "quill-image-resize-module-react";
|
||||
import './texteditor.css'
|
||||
Quill.register("modules/imageResize", ImageResize);
|
||||
import ReactQuill, { Quill } from 'react-quill';
|
||||
import 'react-quill/dist/quill.snow.css';
|
||||
import ImageResize from 'quill-image-resize-module-react';
|
||||
import './texteditor.css';
|
||||
|
||||
Quill.register('modules/imageResize', ImageResize);
|
||||
|
||||
const modules = {
|
||||
imageResize: {
|
||||
parchment: Quill.import("parchment"),
|
||||
modules: ["Resize", "DisplaySize"],
|
||||
parchment: Quill.import('parchment'),
|
||||
modules: ['Resize', 'DisplaySize'],
|
||||
},
|
||||
toolbar: [
|
||||
["bold", "italic", "underline", "strike"], // styled text
|
||||
["blockquote", "code-block"], // blocks
|
||||
['bold', 'italic', 'underline', 'strike'], // styled text
|
||||
['blockquote', 'code-block'], // blocks
|
||||
[{ header: 1 }, { header: 2 }], // custom button values
|
||||
[{ list: "ordered" }, { list: "bullet" }], // lists
|
||||
[{ script: "sub" }, { script: "super" }], // superscript/subscript
|
||||
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent
|
||||
[{ direction: "rtl" }], // text direction
|
||||
[{ size: ["small", false, "large", "huge"] }], // custom dropdown
|
||||
[{ list: 'ordered' }, { list: 'bullet' }], // lists
|
||||
[{ script: 'sub' }, { script: 'super' }], // superscript/subscript
|
||||
[{ indent: '-1' }, { indent: '+1' }], // outdent/indent
|
||||
[{ direction: 'rtl' }], // text direction
|
||||
[{ size: ['small', false, 'large', 'huge'] }], // custom dropdown
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }], // custom button values
|
||||
[{ color: [] }, { background: [] }], // dropdown with defaults
|
||||
[{ font: [] }], // font family
|
||||
[{ align: [] }], // text align
|
||||
["clean"], // remove formatting
|
||||
['clean'], // remove formatting
|
||||
// ["image"], // image
|
||||
],
|
||||
};
|
||||
|
||||
export const TextEditor = ({ inlineContent, setInlineContent }: any) => {
|
||||
return (
|
||||
<ReactQuill
|
||||
|
@ -467,6 +467,7 @@ export const Thread = ({
|
||||
}
|
||||
setMessages(fullArrayMsg);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
}
|
||||
},
|
||||
@ -700,7 +701,7 @@ export const Thread = ({
|
||||
);
|
||||
}}
|
||||
disabled={!hasPreviousPage}
|
||||
variant="contained"
|
||||
variant="contained" // TODO translate
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
|
@ -2218,7 +2218,8 @@ export const Group = ({
|
||||
}}
|
||||
>
|
||||
No group selected
|
||||
</Typography>
|
||||
</Typography>{' '}
|
||||
// TODO translate
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
@ -69,7 +69,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
}} // TODO translate
|
||||
>
|
||||
Group Invites{' '}
|
||||
{groupsWithJoinRequests?.length > 0 &&
|
||||
|
@ -82,6 +82,7 @@ export const GroupJoinRequests = ({
|
||||
);
|
||||
setGroupsWithJoinRequests(res);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@ -138,7 +139,7 @@ export const GroupJoinRequests = ({
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
}} // TODO translate
|
||||
>
|
||||
Join Requests{' '}
|
||||
{filteredJoinRequests?.filter((group) => group?.data?.length > 0)
|
||||
|
@ -1,22 +1,27 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Menu,
|
||||
MenuItem,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Badge,
|
||||
Box,
|
||||
} from "@mui/material";
|
||||
import ForumIcon from "@mui/icons-material/Forum";
|
||||
import GroupIcon from "@mui/icons-material/Group";
|
||||
import { ArrowDownIcon } from "../../assets/Icons/ArrowDownIcon";
|
||||
import { NotificationIcon2 } from "../../assets/Icons/NotificationIcon2";
|
||||
import { ChatIcon } from "../../assets/Icons/ChatIcon";
|
||||
import { ThreadsIcon } from "../../assets/Icons/ThreadsIcon";
|
||||
import { MembersIcon } from "../../assets/Icons/MembersIcon";
|
||||
} from '@mui/material';
|
||||
import { ArrowDownIcon } from '../../assets/Icons/ArrowDownIcon';
|
||||
import { NotificationIcon2 } from '../../assets/Icons/NotificationIcon2';
|
||||
import { ChatIcon } from '../../assets/Icons/ChatIcon';
|
||||
import { ThreadsIcon } from '../../assets/Icons/ThreadsIcon';
|
||||
import { MembersIcon } from '../../assets/Icons/MembersIcon';
|
||||
|
||||
export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers, goToAnnouncements, goToChat, hasUnreadChat, hasUnreadAnnouncements }) => {
|
||||
export const GroupMenu = ({
|
||||
setGroupSection,
|
||||
groupSection,
|
||||
setOpenManageMembers,
|
||||
goToAnnouncements,
|
||||
goToChat,
|
||||
hasUnreadChat,
|
||||
hasUnreadAnnouncements,
|
||||
}) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
@ -31,72 +36,103 @@ export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers,
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginTop: '14px',
|
||||
marginBottom: '14px'
|
||||
marginBottom: '14px',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
aria-controls={open ? "home-menu" : undefined}
|
||||
aria-controls={open ? 'home-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={open ? "true" : undefined}
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
onClick={handleClick}
|
||||
variant="contained"
|
||||
sx={{
|
||||
backgroundColor: "var(--bg-primary)",
|
||||
width: "148px",
|
||||
borderRadius: "5px",
|
||||
fontSize: "12px",
|
||||
backgroundColor: 'var(--bg-primary)',
|
||||
width: '148px',
|
||||
borderRadius: '5px',
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
color: "#fff",
|
||||
textTransform: "none",
|
||||
color: '#fff',
|
||||
textTransform: 'none',
|
||||
padding: '5px',
|
||||
height: '25px'
|
||||
height: '25px',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "6px",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
width: '100%'
|
||||
display: 'flex',
|
||||
gap: '6px',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "6px",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
gap: '6px',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{groupSection === "announcement" &&(
|
||||
<> <NotificationIcon2 color={hasUnreadAnnouncements || hasUnreadChat ? 'var(--danger)' : 'white'} /> {" Announcements"}</>
|
||||
{groupSection === 'announcement' && (
|
||||
<>
|
||||
{' '}
|
||||
<NotificationIcon2
|
||||
color={
|
||||
hasUnreadAnnouncements || hasUnreadChat
|
||||
? 'var(--danger)'
|
||||
: 'white'
|
||||
}
|
||||
/>{' '}
|
||||
{' Announcements'}
|
||||
</>
|
||||
)}
|
||||
{groupSection === "chat" &&(
|
||||
<> <ChatIcon color={hasUnreadAnnouncements || hasUnreadChat ? 'var(--danger)' : 'white'} /> {" Group Chats"}</>
|
||||
{groupSection === 'chat' && (
|
||||
<>
|
||||
{' '}
|
||||
<ChatIcon
|
||||
color={
|
||||
hasUnreadAnnouncements || hasUnreadChat
|
||||
? 'var(--danger)'
|
||||
: 'white'
|
||||
}
|
||||
/>{' '}
|
||||
{' Group Chats'}
|
||||
</>
|
||||
)}
|
||||
{groupSection === "forum" &&(
|
||||
<> <ThreadsIcon color={hasUnreadAnnouncements || hasUnreadChat ? 'var(--danger)' : 'white'} /> {" Threads"}</>
|
||||
{groupSection === 'forum' && (
|
||||
<>
|
||||
{' '}
|
||||
<ThreadsIcon
|
||||
color={
|
||||
hasUnreadAnnouncements || hasUnreadChat
|
||||
? 'var(--danger)'
|
||||
: 'white'
|
||||
}
|
||||
/>{' '}
|
||||
{' Threads'}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<ArrowDownIcon color="white" />
|
||||
</Box>
|
||||
</Button>
|
||||
|
||||
<Menu
|
||||
id="home-menu"
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
MenuListProps={{
|
||||
"aria-labelledby": "basic-button",
|
||||
'aria-labelledby': 'basic-button',
|
||||
}}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
@ -108,93 +144,109 @@ export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers,
|
||||
backgroundColor: 'var(--bg-primary)',
|
||||
color: '#fff',
|
||||
width: '148px',
|
||||
borderRadius: '5px'
|
||||
borderRadius: '5px',
|
||||
},
|
||||
},
|
||||
|
||||
}}
|
||||
sx={{
|
||||
marginTop: '10px'
|
||||
marginTop: '10px',
|
||||
}}
|
||||
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
goToChat()
|
||||
goToChat();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{
|
||||
|
||||
minWidth: '24px !important'
|
||||
}}>
|
||||
<ChatIcon color={hasUnreadChat ? 'var(--danger)' : "#fff"} />
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: '24px !important',
|
||||
}}
|
||||
>
|
||||
<ChatIcon color={hasUnreadChat ? 'var(--danger)' : '#fff'} />
|
||||
</ListItemIcon>
|
||||
<ListItemText sx={{
|
||||
"& .MuiTypography-root": {
|
||||
fontSize: "12px",
|
||||
<ListItemText
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
color: hasUnreadChat ? "var(--danger)" :"#fff"
|
||||
color: hasUnreadChat ? 'var(--danger)' : '#fff',
|
||||
},
|
||||
}} primary="Chat" />
|
||||
}}
|
||||
primary="Chat"
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
goToAnnouncements()
|
||||
goToAnnouncements();
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{
|
||||
|
||||
minWidth: '24px !important'
|
||||
}}>
|
||||
<NotificationIcon2 color={hasUnreadAnnouncements ? 'var(--danger)' : "#fff" } />
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: '24px !important',
|
||||
}}
|
||||
>
|
||||
<NotificationIcon2
|
||||
color={hasUnreadAnnouncements ? 'var(--danger)' : '#fff'}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText sx={{
|
||||
"& .MuiTypography-root": {
|
||||
fontSize: "12px",
|
||||
<ListItemText
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
color: hasUnreadAnnouncements ? "var(--danger)" :"#fff"
|
||||
color: hasUnreadAnnouncements ? 'var(--danger)' : '#fff',
|
||||
},
|
||||
}} primary="Announcements" />
|
||||
}}
|
||||
primary="Announcements"
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
setGroupSection("forum");
|
||||
setGroupSection('forum');
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{
|
||||
minWidth: '24px !important'
|
||||
}}>
|
||||
<ThreadsIcon color={"#fff"} />
|
||||
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: '24px !important',
|
||||
}}
|
||||
>
|
||||
<ThreadsIcon color={'#fff'} />
|
||||
</ListItemIcon>
|
||||
<ListItemText sx={{
|
||||
"& .MuiTypography-root": {
|
||||
fontSize: "12px",
|
||||
<ListItemText
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
},
|
||||
}} primary="Threads" />
|
||||
}}
|
||||
primary="Threads"
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
setOpenManageMembers(true)
|
||||
setOpenManageMembers(true);
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{
|
||||
minWidth: '24px !important'
|
||||
}}>
|
||||
<MembersIcon sx={{ color: "#fff" }} />
|
||||
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: '24px !important',
|
||||
}}
|
||||
>
|
||||
<MembersIcon sx={{ color: '#fff' }} />
|
||||
</ListItemIcon>
|
||||
<ListItemText sx={{
|
||||
"& .MuiTypography-root": {
|
||||
fontSize: "12px",
|
||||
<ListItemText
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
},
|
||||
}} primary="Members" />
|
||||
}}
|
||||
primary="Members"
|
||||
/>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Box>
|
||||
|
@ -1,112 +0,0 @@
|
||||
import { Box, Button, Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { ListOfThreadPostsWatched } from "./ListOfThreadPostsWatched";
|
||||
import { ThingsToDoInitial } from "./ThingsToDoInitial";
|
||||
import { GroupJoinRequests } from "./GroupJoinRequests";
|
||||
import { GroupInvites } from "./GroupInvites";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
|
||||
export const Home = ({
|
||||
refreshHomeDataFunc,
|
||||
myAddress,
|
||||
isLoadingGroups,
|
||||
balance,
|
||||
userInfo,
|
||||
groups,
|
||||
setGroupSection,
|
||||
setSelectedGroup,
|
||||
getTimestampEnterChat,
|
||||
setOpenManageMembers,
|
||||
setOpenAddGroup,
|
||||
setMobileViewMode,
|
||||
setDesktopViewMode
|
||||
}) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
overflow: "auto",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Spacer height="20px" />
|
||||
<Typography
|
||||
sx={{
|
||||
color: "rgba(255, 255, 255, 1)",
|
||||
fontWeight: 400,
|
||||
fontSize: userInfo?.name?.length > 15 ? "16px" : "20px",
|
||||
padding: '10px'
|
||||
}}
|
||||
>
|
||||
Welcome
|
||||
{userInfo?.name ? (
|
||||
<span
|
||||
style={{
|
||||
fontStyle: "italic",
|
||||
}}
|
||||
>{`, ${userInfo?.name}`}</span>
|
||||
) : null}
|
||||
</Typography>
|
||||
<Spacer height="26px" />
|
||||
|
||||
{/* <Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
justifyContent: "flex-start",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<RefreshIcon />}
|
||||
onClick={refreshHomeDataFunc}
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
Refresh home data
|
||||
</Button>
|
||||
</Box> */}
|
||||
{!isLoadingGroups && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<ThingsToDoInitial
|
||||
balance={balance}
|
||||
myAddress={myAddress}
|
||||
name={userInfo?.name}
|
||||
hasGroups={groups?.length !== 0}
|
||||
/>
|
||||
<ListOfThreadPostsWatched />
|
||||
|
||||
<GroupJoinRequests
|
||||
setGroupSection={setGroupSection}
|
||||
setSelectedGroup={setSelectedGroup}
|
||||
getTimestampEnterChat={getTimestampEnterChat}
|
||||
setOpenManageMembers={setOpenManageMembers}
|
||||
myAddress={myAddress}
|
||||
groups={groups}
|
||||
setMobileViewMode={setMobileViewMode}
|
||||
setDesktopViewMode={setDesktopViewMode}
|
||||
/>
|
||||
<GroupInvites
|
||||
setOpenAddGroup={setOpenAddGroup}
|
||||
myAddress={myAddress}
|
||||
groups={groups}
|
||||
setMobileViewMode={setMobileViewMode}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Spacer height="180px" />
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,16 +1,16 @@
|
||||
import { Box, Button, Divider, Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { ListOfThreadPostsWatched } from "./ListOfThreadPostsWatched";
|
||||
import { ThingsToDoInitial } from "./ThingsToDoInitial";
|
||||
import { GroupJoinRequests } from "./GroupJoinRequests";
|
||||
import { GroupInvites } from "./GroupInvites";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
import { ListOfGroupPromotions } from "./ListOfGroupPromotions";
|
||||
import { QortPrice } from "../Home/QortPrice";
|
||||
import ExploreIcon from "@mui/icons-material/Explore";
|
||||
import { Explore } from "../Explore/Explore";
|
||||
import { NewUsersCTA } from "../Home/NewUsersCTA";
|
||||
import { Box, Divider, Typography, useTheme } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { ThingsToDoInitial } from './ThingsToDoInitial';
|
||||
import { GroupJoinRequests } from './GroupJoinRequests';
|
||||
import { GroupInvites } from './GroupInvites';
|
||||
import { ListOfGroupPromotions } from './ListOfGroupPromotions';
|
||||
import { QortPrice } from '../Home/QortPrice';
|
||||
import ExploreIcon from '@mui/icons-material/Explore';
|
||||
import { Explore } from '../Explore/Explore';
|
||||
import { NewUsersCTA } from '../Home/NewUsersCTA';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const HomeDesktop = ({
|
||||
refreshHomeDataFunc,
|
||||
myAddress,
|
||||
@ -30,93 +30,97 @@ export const HomeDesktop = ({
|
||||
}) => {
|
||||
const [checked1, setChecked1] = React.useState(false);
|
||||
const [checked2, setChecked2] = React.useState(false);
|
||||
|
||||
const { t } = useTranslation(['core']);
|
||||
const theme = useTheme();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (balance && +balance >= 6) {
|
||||
setChecked1(true);
|
||||
}
|
||||
}, [balance]);
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (name) setChecked2(true);
|
||||
}, [name]);
|
||||
|
||||
|
||||
const isLoaded = React.useMemo(() => {
|
||||
if(userInfo !== null) return true
|
||||
return false
|
||||
}, [ userInfo])
|
||||
if (userInfo !== null) return true;
|
||||
return false;
|
||||
}, [userInfo]);
|
||||
|
||||
const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(() => {
|
||||
if(isLoaded && checked1 && checked2) return true
|
||||
return false
|
||||
}, [checked1, isLoaded, checked2])
|
||||
|
||||
if (isLoaded && checked1 && checked2) return true;
|
||||
return false;
|
||||
}, [checked1, isLoaded, checked2]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: desktopViewMode === "home" ? "flex" : "none",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
overflow: "auto",
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
display: desktopViewMode === 'home' ? 'flex' : 'none',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Spacer height="20px" />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
alignItems: "flex-start",
|
||||
maxWidth: "1036px",
|
||||
alignItems: 'flex-start',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
maxWidth: '1036px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "rgba(255, 255, 255, 1)",
|
||||
color: theme.palette.text.primary,
|
||||
fontWeight: 400,
|
||||
fontSize: userInfo?.name?.length > 15 ? "16px" : "20px",
|
||||
padding: "10px",
|
||||
fontSize: userInfo?.name?.length > 15 ? '16px' : '20px',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
Welcome
|
||||
{t('core:welcome', { postProcess: 'capitalize' })}
|
||||
{userInfo?.name ? (
|
||||
<span
|
||||
style={{
|
||||
fontStyle: "italic",
|
||||
fontStyle: 'italic',
|
||||
}}
|
||||
>{`, ${userInfo?.name}`}</span>
|
||||
) : null}
|
||||
</Typography>
|
||||
|
||||
<Spacer height="30px" />
|
||||
|
||||
{!isLoadingGroups && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '20px',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "column",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
gap: '20px',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "330px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '330px',
|
||||
}}
|
||||
>
|
||||
<ThingsToDoInitial
|
||||
@ -125,12 +129,12 @@ export const HomeDesktop = ({
|
||||
name={userInfo?.name}
|
||||
userInfo={userInfo}
|
||||
hasGroups={
|
||||
groups?.filter((item) => item?.groupId !== "0").length !== 0
|
||||
groups?.filter((item) => item?.groupId !== '0').length !== 0
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{desktopViewMode === "home" && (
|
||||
{desktopViewMode === 'home' && (
|
||||
<>
|
||||
{/* <Box sx={{
|
||||
width: '330px',
|
||||
@ -144,10 +148,10 @@ export const HomeDesktop = ({
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: "330px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '330px',
|
||||
}}
|
||||
>
|
||||
<GroupJoinRequests
|
||||
@ -161,12 +165,13 @@ export const HomeDesktop = ({
|
||||
setDesktopViewMode={setDesktopViewMode}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "330px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '330px',
|
||||
}}
|
||||
>
|
||||
<GroupInvites
|
||||
@ -178,7 +183,6 @@ export const HomeDesktop = ({
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
@ -189,58 +193,55 @@ export const HomeDesktop = ({
|
||||
{!isLoadingGroups && (
|
||||
<>
|
||||
<Spacer height="60px" />
|
||||
|
||||
<Divider
|
||||
color="secondary"
|
||||
sx={{
|
||||
width: "100%",
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<ExploreIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
ccolor: theme.palette.text.primary,
|
||||
}}
|
||||
/>{" "}
|
||||
/>{' '}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
>
|
||||
Explore
|
||||
</Typography>{" "}
|
||||
{t('tutorial:initial.explore', { postProcess: 'capitalize' })}
|
||||
</Typography>{' '}
|
||||
</Box>
|
||||
</Divider>
|
||||
{!hasDoneNameAndBalanceAndIsLoaded && (
|
||||
<Spacer height="40px" />
|
||||
)}
|
||||
|
||||
{!hasDoneNameAndBalanceAndIsLoaded && <Spacer height="40px" />}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '20px',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{hasDoneNameAndBalanceAndIsLoaded && (
|
||||
<ListOfGroupPromotions />
|
||||
|
||||
)}
|
||||
{hasDoneNameAndBalanceAndIsLoaded && <ListOfGroupPromotions />}
|
||||
|
||||
<Explore setDesktopViewMode={setDesktopViewMode} />
|
||||
</Box>
|
||||
|
||||
<NewUsersCTA balance={balance} />
|
||||
</>
|
||||
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Spacer height="26px" />
|
||||
|
||||
{/* <Box
|
||||
|
@ -1,13 +1,6 @@
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Input,
|
||||
MenuItem,
|
||||
Select,
|
||||
SelectChangeEvent,
|
||||
} from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Input, MenuItem, Select, SelectChangeEvent } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { Label } from './AddGroup';
|
||||
import { getFee } from '../../background';
|
||||
@ -34,6 +27,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
// TODO translate
|
||||
setInfoSnack({
|
||||
type: 'success',
|
||||
message: `Successfully invited ${value}. It may take a couple of minutes for the changes to propagate`,
|
||||
|
@ -1,16 +1,31 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Avatar, Box, Button, ListItem, ListItemAvatar, ListItemButton, ListItemText, Popover } from '@mui/material';
|
||||
import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Popover,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
AutoSizer,
|
||||
CellMeasurer,
|
||||
CellMeasurerCache,
|
||||
List,
|
||||
} from 'react-virtualized';
|
||||
import { getNameInfo } from './Group';
|
||||
import { getBaseApi, getFee } from '../../background';
|
||||
import { getFee } from '../../background';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
|
||||
export const getMemberInvites = async (groupNumber) => {
|
||||
const response = await fetch(`${getBaseApiReact()}/groups/bans/${groupNumber}?limit=0`);
|
||||
const response = await fetch(
|
||||
`${getBaseApiReact()}/groups/bans/${groupNumber}?limit=0`
|
||||
);
|
||||
const groupData = await response.json();
|
||||
return groupData;
|
||||
}
|
||||
};
|
||||
|
||||
const getNames = async (listOfMembers, includeNoNames) => {
|
||||
let members = [];
|
||||
@ -21,13 +36,13 @@ const getNames = async (listOfMembers, includeNoNames) => {
|
||||
if (name) {
|
||||
members.push({ ...member, name });
|
||||
} else if (includeNoNames) {
|
||||
members.push({ ...member, name: name || "" });
|
||||
members.push({ ...member, name: name || '' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return members;
|
||||
}
|
||||
};
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
@ -49,7 +64,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (groupId) {
|
||||
@ -69,14 +84,16 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
|
||||
const handleCancelBan = async (address) => {
|
||||
try {
|
||||
const fee = await getFee('CANCEL_GROUP_BAN')
|
||||
// TODO translate
|
||||
const fee = await getFee('CANCEL_GROUP_BAN');
|
||||
await show({
|
||||
message: "Would you like to perform a CANCEL_GROUP_BAN transaction?" ,
|
||||
publishFee: fee.fee + ' QORT'
|
||||
})
|
||||
setIsLoadingUnban(true)
|
||||
message: 'Would you like to perform a CANCEL_GROUP_BAN transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoadingUnban(true);
|
||||
new Promise((res, rej) => {
|
||||
window.sendMessage("cancelBan", {
|
||||
window
|
||||
.sendMessage('cancelBan', {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
@ -85,15 +102,16 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
res(response);
|
||||
setIsLoadingUnban(false);
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
message: "Successfully unbanned user. It may take a couple of minutes for the changes to propagate",
|
||||
type: 'success',
|
||||
message:
|
||||
'Successfully unbanned user. It may take a couple of minutes for the changes to propagate',
|
||||
});
|
||||
handlePopoverClose();
|
||||
setOpenSnack(true);
|
||||
return;
|
||||
}
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: response?.error,
|
||||
});
|
||||
setOpenSnack(true);
|
||||
@ -101,20 +119,18 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error.message || "An error occurred",
|
||||
type: 'error',
|
||||
message: error.message || 'An error occurred',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(error);
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
setIsLoadingUnban(false)
|
||||
}
|
||||
setIsLoadingUnban(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rowRenderer = ({ index, key, parent, style }) => {
|
||||
const member = bans[index];
|
||||
@ -135,36 +151,47 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
anchorEl={popoverAnchor}
|
||||
onClose={handlePopoverClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "center",
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "center",
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
style={{ marginTop: "8px" }}
|
||||
style={{ marginTop: '8px' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "325px",
|
||||
height: "250px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
padding: "10px",
|
||||
width: '325px',
|
||||
height: '250px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<LoadingButton loading={isLoadingUnban}
|
||||
<LoadingButton
|
||||
loading={isLoadingUnban}
|
||||
loadingPosition="start"
|
||||
variant="contained" onClick={()=> handleCancelBan(member?.offender)}>Cancel Ban</LoadingButton>
|
||||
variant="contained"
|
||||
onClick={() => handleCancelBan(member?.offender)}
|
||||
>
|
||||
Cancel Ban
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
</Popover>
|
||||
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
|
||||
<ListItemButton
|
||||
onClick={(event) => handlePopoverOpen(event, index)}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
alt={member?.name}
|
||||
src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
|
||||
src={
|
||||
member?.name
|
||||
? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={member?.name || member?.offender} />
|
||||
@ -179,7 +206,16 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
return (
|
||||
<div>
|
||||
<p>Ban list</p>
|
||||
<div style={{ position: 'relative', height: '500px', width: '100%', display: 'flex', flexDirection: 'column', flexShrink: 1 }}>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
height: '500px',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 1,
|
||||
}}
|
||||
>
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<List
|
||||
@ -196,4 +232,4 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -230,7 +230,7 @@ export const ListOfGroupPromotions = () => {
|
||||
.catch((error) => {
|
||||
rej(error.message || 'An error occurred');
|
||||
});
|
||||
});
|
||||
}); // TODO translate
|
||||
setInfoSnack({
|
||||
type: 'success',
|
||||
message:
|
||||
|
@ -1,16 +1,31 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Avatar, Box, Button, ListItem, ListItemAvatar, ListItemButton, ListItemText, Popover } from '@mui/material';
|
||||
import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Popover,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
AutoSizer,
|
||||
CellMeasurer,
|
||||
CellMeasurerCache,
|
||||
List,
|
||||
} from 'react-virtualized';
|
||||
import { getNameInfo } from './Group';
|
||||
import { getBaseApi, getFee } from '../../background';
|
||||
import { getFee } from '../../background';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
|
||||
export const getMemberInvites = async (groupNumber) => {
|
||||
const response = await fetch(`${getBaseApiReact()}/groups/invites/group/${groupNumber}?limit=0`);
|
||||
const response = await fetch(
|
||||
`${getBaseApiReact()}/groups/invites/group/${groupNumber}?limit=0`
|
||||
);
|
||||
const groupData = await response.json();
|
||||
return groupData;
|
||||
}
|
||||
};
|
||||
|
||||
const getNames = async (listOfMembers, includeNoNames) => {
|
||||
let members = [];
|
||||
@ -21,20 +36,25 @@ const getNames = async (listOfMembers, includeNoNames) => {
|
||||
if (name) {
|
||||
members.push({ ...member, name });
|
||||
} else if (includeNoNames) {
|
||||
members.push({ ...member, name: name || "" });
|
||||
members.push({ ...member, name: name || '' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return members;
|
||||
}
|
||||
};
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
defaultHeight: 50,
|
||||
});
|
||||
|
||||
export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
export const ListOfInvites = ({
|
||||
groupId,
|
||||
setInfoSnack,
|
||||
setOpenSnack,
|
||||
show,
|
||||
}) => {
|
||||
const [invites, setInvites] = useState([]);
|
||||
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to
|
||||
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
|
||||
@ -50,7 +70,7 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (groupId) {
|
||||
@ -70,22 +90,25 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
|
||||
|
||||
const handleCancelInvitation = async (address) => {
|
||||
try {
|
||||
const fee = await getFee('CANCEL_GROUP_INVITE')
|
||||
// TODO translate
|
||||
const fee = await getFee('CANCEL_GROUP_INVITE');
|
||||
await show({
|
||||
message: "Would you like to perform a CANCEL_GROUP_INVITE transaction?" ,
|
||||
publishFee: fee.fee + ' QORT'
|
||||
})
|
||||
setIsLoadingCancelInvite(true)
|
||||
message: 'Would you like to perform a CANCEL_GROUP_INVITE transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoadingCancelInvite(true);
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("cancelInvitationToGroup", {
|
||||
window
|
||||
.sendMessage('cancelInvitationToGroup', {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
message: "Successfully canceled invitation. It may take a couple of minutes for the changes to propagate",
|
||||
type: 'success',
|
||||
message:
|
||||
'Successfully canceled invitation. It may take a couple of minutes for the changes to propagate',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
handlePopoverClose();
|
||||
@ -94,7 +117,7 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
|
||||
return;
|
||||
}
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: response?.error,
|
||||
});
|
||||
setOpenSnack(true);
|
||||
@ -102,20 +125,18 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error.message || "An error occurred",
|
||||
type: 'error',
|
||||
message: error.message || 'An error occurred',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(error);
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
setIsLoadingCancelInvite(false)
|
||||
}
|
||||
setIsLoadingCancelInvite(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rowRenderer = ({ index, key, parent, style }) => {
|
||||
const member = invites[index];
|
||||
@ -136,36 +157,47 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
|
||||
anchorEl={popoverAnchor}
|
||||
onClose={handlePopoverClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "center",
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "center",
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
style={{ marginTop: "8px" }}
|
||||
style={{ marginTop: '8px' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "325px",
|
||||
height: "250px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
padding: "10px",
|
||||
width: '325px',
|
||||
height: '250px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<LoadingButton loading={isLoadingCancelInvite}
|
||||
<LoadingButton
|
||||
loading={isLoadingCancelInvite}
|
||||
loadingPosition="start"
|
||||
variant="contained" onClick={()=> handleCancelInvitation(member?.invitee)}>Cancel Invitation</LoadingButton>
|
||||
variant="contained"
|
||||
onClick={() => handleCancelInvitation(member?.invitee)}
|
||||
>
|
||||
Cancel Invitation
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
</Popover>
|
||||
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
|
||||
<ListItemButton
|
||||
onClick={(event) => handlePopoverOpen(event, index)}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
alt={member?.name}
|
||||
src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
|
||||
src={
|
||||
member?.name
|
||||
? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={member?.name || member?.invitee} />
|
||||
@ -180,7 +212,16 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
|
||||
return (
|
||||
<div>
|
||||
<p>Invitees list</p>
|
||||
<div style={{ position: 'relative', height: '500px', width: '100%', display: 'flex', flexDirection: 'column', flexShrink: 1 }}>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
height: '500px',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 1,
|
||||
}}
|
||||
>
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<List
|
||||
@ -197,4 +238,4 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -1,16 +1,31 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { Avatar, Box, Button, ListItem, ListItemAvatar, ListItemButton, ListItemText, Popover } from '@mui/material';
|
||||
import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
|
||||
import { useContext, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Popover,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
AutoSizer,
|
||||
CellMeasurer,
|
||||
CellMeasurerCache,
|
||||
List,
|
||||
} from 'react-virtualized';
|
||||
import { getNameInfo } from './Group';
|
||||
import { getBaseApi, getFee } from '../../background';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
|
||||
export const getMemberInvites = async (groupNumber) => {
|
||||
const response = await fetch(`${getBaseApiReact()}/groups/joinrequests/${groupNumber}?limit=0`);
|
||||
const response = await fetch(
|
||||
`${getBaseApiReact()}/groups/joinrequests/${groupNumber}?limit=0`
|
||||
);
|
||||
const groupData = await response.json();
|
||||
return groupData;
|
||||
}
|
||||
};
|
||||
|
||||
const getNames = async (listOfMembers, includeNoNames) => {
|
||||
let members = [];
|
||||
@ -19,24 +34,29 @@ const getNames = async (listOfMembers, includeNoNames) => {
|
||||
if (member.joiner) {
|
||||
const name = await getNameInfo(member.joiner);
|
||||
if (name) {
|
||||
members.push({ ...member, name: name || "" });
|
||||
members.push({ ...member, name: name || '' });
|
||||
} else if (includeNoNames) {
|
||||
members.push({ ...member, name: name || "" });
|
||||
members.push({ ...member, name: name || '' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return members;
|
||||
}
|
||||
};
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
defaultHeight: 50,
|
||||
});
|
||||
|
||||
export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
export const ListOfJoinRequests = ({
|
||||
groupId,
|
||||
setInfoSnack,
|
||||
setOpenSnack,
|
||||
show,
|
||||
}) => {
|
||||
const [invites, setInvites] = useState([]);
|
||||
const {txList, setTxList} = useContext(MyContext)
|
||||
const { txList, setTxList } = useContext(MyContext);
|
||||
|
||||
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
|
||||
@ -51,7 +71,7 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (groupId) {
|
||||
@ -71,14 +91,15 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
|
||||
|
||||
const handleAcceptJoinRequest = async (address) => {
|
||||
try {
|
||||
const fee = await getFee('GROUP_INVITE')
|
||||
const fee = await getFee('GROUP_INVITE'); // TODO translate
|
||||
await show({
|
||||
message: "Would you like to perform a GROUP_INVITE transaction?" ,
|
||||
publishFee: fee.fee + ' QORT'
|
||||
})
|
||||
setIsLoadingAccept(true)
|
||||
message: 'Would you like to perform a GROUP_INVITE transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoadingAccept(true);
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("inviteToGroup", {
|
||||
window
|
||||
.sendMessage('inviteToGroup', {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
inviteTime: 10800,
|
||||
@ -87,8 +108,9 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
|
||||
if (!response?.error) {
|
||||
setIsLoadingAccept(false);
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
message: "Successfully accepted join request. It may take a couple of minutes for the changes to propagate",
|
||||
type: 'success',
|
||||
message:
|
||||
'Successfully accepted join request. It may take a couple of minutes for the changes to propagate',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
handlePopoverClose();
|
||||
@ -111,7 +133,7 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
|
||||
}
|
||||
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: response?.error,
|
||||
});
|
||||
setOpenSnack(true);
|
||||
@ -119,25 +141,28 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error?.message || "An error occurred",
|
||||
type: 'error',
|
||||
message: error?.message || 'An error occurred',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(error);
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
setIsLoadingAccept(false)
|
||||
}
|
||||
setIsLoadingAccept(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rowRenderer = ({ index, key, parent, style }) => {
|
||||
const member = invites[index];
|
||||
const findJoinRequsetInTxList = txList?.find((tx)=> tx?.groupId === groupId && tx?.qortalAddress === member?.joiner && tx?.type === 'join-request-accept')
|
||||
if(findJoinRequsetInTxList) return null
|
||||
const findJoinRequsetInTxList = txList?.find(
|
||||
(tx) =>
|
||||
tx?.groupId === groupId &&
|
||||
tx?.qortalAddress === member?.joiner &&
|
||||
tx?.type === 'join-request-accept'
|
||||
);
|
||||
if (findJoinRequsetInTxList) return null;
|
||||
return (
|
||||
<CellMeasurer
|
||||
key={key}
|
||||
@ -154,36 +179,47 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
|
||||
anchorEl={popoverAnchor}
|
||||
onClose={handlePopoverClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "center",
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "center",
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
style={{ marginTop: "8px" }}
|
||||
style={{ marginTop: '8px' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "325px",
|
||||
height: "250px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
padding: "10px",
|
||||
width: '325px',
|
||||
height: '250px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<LoadingButton loading={isLoadingAccept}
|
||||
<LoadingButton
|
||||
loading={isLoadingAccept}
|
||||
loadingPosition="start"
|
||||
variant="contained" onClick={()=> handleAcceptJoinRequest(member?.joiner)}>Accept</LoadingButton>
|
||||
variant="contained"
|
||||
onClick={() => handleAcceptJoinRequest(member?.joiner)}
|
||||
>
|
||||
Accept
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
</Popover>
|
||||
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
|
||||
<ListItemButton
|
||||
onClick={(event) => handlePopoverOpen(event, index)}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
alt={member?.name}
|
||||
src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
|
||||
src={
|
||||
member?.name
|
||||
? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={member?.name || member?.joiner} />
|
||||
@ -198,7 +234,16 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
|
||||
return (
|
||||
<div>
|
||||
<p>Join request list</p>
|
||||
<div style={{ position: 'relative', height: '500px', width: '100%', display: 'flex', flexDirection: 'column', flexShrink: 1 }}>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
height: '500px',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 1,
|
||||
}}
|
||||
>
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<List
|
||||
@ -215,4 +260,4 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -1,29 +1,29 @@
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Popover,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import React, { useRef, useState } from "react";
|
||||
} from '@mui/material';
|
||||
import { useRef, useState } from 'react';
|
||||
import {
|
||||
AutoSizer,
|
||||
CellMeasurer,
|
||||
CellMeasurerCache,
|
||||
List,
|
||||
} from "react-virtualized";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { getBaseApi, getFee } from "../../background";
|
||||
import { getBaseApiReact } from "../../App";
|
||||
} from 'react-virtualized';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { getFee } from '../../background';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
defaultHeight: 50,
|
||||
});
|
||||
|
||||
const ListOfMembers = ({
|
||||
members,
|
||||
groupId,
|
||||
@ -40,7 +40,6 @@ const ListOfMembers = ({
|
||||
const [isLoadingMakeAdmin, setIsLoadingMakeAdmin] = useState(false);
|
||||
const [isLoadingRemoveAdmin, setIsLoadingRemoveAdmin] = useState(false);
|
||||
|
||||
|
||||
const listRef = useRef();
|
||||
|
||||
const handlePopoverOpen = (event, index) => {
|
||||
@ -55,23 +54,25 @@ const ListOfMembers = ({
|
||||
|
||||
const handleKick = async (address) => {
|
||||
try {
|
||||
const fee = await getFee("GROUP_KICK");
|
||||
const fee = await getFee('GROUP_KICK');
|
||||
await show({
|
||||
message: "Would you like to perform a GROUP_KICK transaction?",
|
||||
publishFee: fee.fee + " QORT",
|
||||
message: 'Would you like to perform a GROUP_KICK transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
|
||||
setIsLoadingKick(true);
|
||||
new Promise((res, rej) => {
|
||||
window.sendMessage("kickFromGroup", {
|
||||
window
|
||||
.sendMessage('kickFromGroup', {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
message: "Successfully kicked member from group. It may take a couple of minutes for the changes to propagate",
|
||||
type: 'success',
|
||||
message:
|
||||
'Successfully kicked member from group. It may take a couple of minutes for the changes to propagate',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
handlePopoverClose();
|
||||
@ -79,7 +80,7 @@ const ListOfMembers = ({
|
||||
return;
|
||||
}
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: response?.error,
|
||||
});
|
||||
setOpenSnack(true);
|
||||
@ -87,29 +88,30 @@ const ListOfMembers = ({
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error.message || "An error occurred",
|
||||
type: 'error',
|
||||
message: error.message || 'An error occurred',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(error);
|
||||
});
|
||||
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setIsLoadingKick(false);
|
||||
}
|
||||
};
|
||||
const handleBan = async (address) => {
|
||||
try {
|
||||
const fee = await getFee("GROUP_BAN");
|
||||
const fee = await getFee('GROUP_BAN'); // TODO translate
|
||||
await show({
|
||||
message: "Would you like to perform a GROUP_BAN transaction?",
|
||||
publishFee: fee.fee + " QORT",
|
||||
message: 'Would you like to perform a GROUP_BAN transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoadingBan(true);
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("banFromGroup", {
|
||||
window
|
||||
.sendMessage('banFromGroup', {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
rBanTime: 0,
|
||||
@ -117,8 +119,9 @@ const ListOfMembers = ({
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
message: "Successfully banned member from group. It may take a couple of minutes for the changes to propagate",
|
||||
type: 'success',
|
||||
message:
|
||||
'Successfully banned member from group. It may take a couple of minutes for the changes to propagate',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
handlePopoverClose();
|
||||
@ -126,7 +129,7 @@ const ListOfMembers = ({
|
||||
return;
|
||||
}
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: response?.error,
|
||||
});
|
||||
setOpenSnack(true);
|
||||
@ -134,13 +137,12 @@ const ListOfMembers = ({
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error.message || "An error occurred",
|
||||
type: 'error',
|
||||
message: error.message || 'An error occurred',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(error);
|
||||
});
|
||||
|
||||
});
|
||||
} catch (error) {
|
||||
} finally {
|
||||
@ -150,22 +152,24 @@ const ListOfMembers = ({
|
||||
|
||||
const makeAdmin = async (address) => {
|
||||
try {
|
||||
const fee = await getFee("ADD_GROUP_ADMIN");
|
||||
const fee = await getFee('ADD_GROUP_ADMIN');
|
||||
await show({
|
||||
message: "Would you like to perform a ADD_GROUP_ADMIN transaction?",
|
||||
publishFee: fee.fee + " QORT",
|
||||
message: 'Would you like to perform a ADD_GROUP_ADMIN transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoadingMakeAdmin(true);
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("makeAdmin", {
|
||||
window
|
||||
.sendMessage('makeAdmin', {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
message: "Successfully made member an admin. It may take a couple of minutes for the changes to propagate",
|
||||
type: 'success',
|
||||
message:
|
||||
'Successfully made member an admin. It may take a couple of minutes for the changes to propagate',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
handlePopoverClose();
|
||||
@ -173,7 +177,7 @@ const ListOfMembers = ({
|
||||
return;
|
||||
}
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: response?.error,
|
||||
});
|
||||
setOpenSnack(true);
|
||||
@ -181,13 +185,12 @@ const ListOfMembers = ({
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error.message || "An error occurred",
|
||||
type: 'error',
|
||||
message: error.message || 'An error occurred',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(error);
|
||||
});
|
||||
|
||||
});
|
||||
} catch (error) {
|
||||
} finally {
|
||||
@ -197,22 +200,24 @@ const ListOfMembers = ({
|
||||
|
||||
const removeAdmin = async (address) => {
|
||||
try {
|
||||
const fee = await getFee("REMOVE_GROUP_ADMIN");
|
||||
const fee = await getFee('REMOVE_GROUP_ADMIN');
|
||||
await show({
|
||||
message: "Would you like to perform a REMOVE_GROUP_ADMIN transaction?",
|
||||
publishFee: fee.fee + " QORT",
|
||||
message: 'Would you like to perform a REMOVE_GROUP_ADMIN transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoadingRemoveAdmin(true);
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("removeAdmin", {
|
||||
window
|
||||
.sendMessage('removeAdmin', {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
message: "Successfully removed member as an admin. It may take a couple of minutes for the changes to propagate",
|
||||
type: 'success',
|
||||
message:
|
||||
'Successfully removed member as an admin. It may take a couple of minutes for the changes to propagate',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
handlePopoverClose();
|
||||
@ -220,7 +225,7 @@ const ListOfMembers = ({
|
||||
return;
|
||||
}
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: response?.error,
|
||||
});
|
||||
setOpenSnack(true);
|
||||
@ -228,13 +233,12 @@ const ListOfMembers = ({
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error.message || "An error occurred",
|
||||
type: 'error',
|
||||
message: error.message || 'An error occurred',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(error);
|
||||
});
|
||||
|
||||
});
|
||||
} catch (error) {
|
||||
} finally {
|
||||
@ -260,24 +264,24 @@ const ListOfMembers = ({
|
||||
anchorEl={popoverAnchor}
|
||||
onClose={handlePopoverClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "center",
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "center",
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
style={{ marginTop: "8px" }}
|
||||
style={{ marginTop: '8px' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "325px",
|
||||
height: "250px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
padding: "10px",
|
||||
width: '325px',
|
||||
height: '250px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
{isOwner && (
|
||||
@ -336,21 +340,28 @@ const ListOfMembers = ({
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
alt={member?.name || member?.member}
|
||||
src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
|
||||
src={
|
||||
member?.name
|
||||
? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
id={""}
|
||||
id={''}
|
||||
primary={member?.name || member?.member}
|
||||
/>
|
||||
{member?.isAdmin && (
|
||||
<Typography sx={{
|
||||
<Typography
|
||||
sx={{
|
||||
color: 'white',
|
||||
marginLeft: 'auto'
|
||||
}}>Admin</Typography>
|
||||
marginLeft: 'auto',
|
||||
}}
|
||||
>
|
||||
Admin
|
||||
</Typography>
|
||||
)}
|
||||
</ListItemButton>
|
||||
|
||||
</ListItem>
|
||||
</div>
|
||||
)}
|
||||
@ -363,11 +374,11 @@ const ListOfMembers = ({
|
||||
<p>Member list</p>
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
height: "500px",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
position: 'relative',
|
||||
height: '500px',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 1,
|
||||
}}
|
||||
>
|
||||
|
@ -42,7 +42,7 @@ export const ListOfThreadPostsWatched = () => {
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || 'An error occurred');
|
||||
rej(error.message || 'An error occurred'); // TODO translate
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -105,7 +105,7 @@ export const ManageMembers = ({
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || 'An error occurred');
|
||||
rej(error.message || 'An error occurred'); // TODO translate
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -87,7 +87,7 @@ export const QMailMessages = ({ userName, userAddress }) => {
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || 'An error occurred');
|
||||
rej(error.message || 'An error occurred'); // TODO translate
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -79,7 +79,7 @@ export const Settings = ({ address, open, setOpen }) => {
|
||||
if (response?.error) {
|
||||
console.error('Error adding user settings:', response.error);
|
||||
} else {
|
||||
console.log('User settings added successfully');
|
||||
console.log('User settings added successfully'); // TODO translate
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
|
@ -4,10 +4,11 @@ import ListItem from '@mui/material/ListItem';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { Box, Typography, useTheme } from '@mui/material';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { QMailMessages } from './QMailMessages';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const ThingsToDoInitial = ({
|
||||
myAddress,
|
||||
@ -18,6 +19,8 @@ export const ThingsToDoInitial = ({
|
||||
}) => {
|
||||
const [checked1, setChecked1] = React.useState(false);
|
||||
const [checked2, setChecked2] = React.useState(false);
|
||||
const { t } = useTranslation(['core', 'tutorial']);
|
||||
const theme = useTheme();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (balance && +balance >= 6) {
|
||||
@ -72,7 +75,11 @@ export const ThingsToDoInitial = ({
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{!isLoaded ? 'Loading...' : 'Getting Started'}
|
||||
{!isLoaded
|
||||
? t('core:loading', { postProcess: 'capitalize' })
|
||||
: t('tutorial:initial.getting_started', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</Typography>
|
||||
|
||||
<Spacer height="10px" />
|
||||
@ -80,12 +87,12 @@ export const ThingsToDoInitial = ({
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: '322px',
|
||||
bgcolor: theme.palette.background.paper,
|
||||
borderRadius: '19px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
bgcolor: 'background.paper',
|
||||
padding: '20px',
|
||||
borderRadius: '19px',
|
||||
width: '322px',
|
||||
}}
|
||||
>
|
||||
{isLoaded && (
|
||||
@ -114,7 +121,9 @@ export const ThingsToDoInitial = ({
|
||||
fontWeight: 400,
|
||||
},
|
||||
}}
|
||||
primary={`Have at least 6 QORT in your wallet`}
|
||||
primary={t('tutorial:initial.6_qort', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
/>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
@ -183,7 +192,9 @@ export const ThingsToDoInitial = ({
|
||||
fontWeight: 400,
|
||||
},
|
||||
}}
|
||||
primary={`Register a name`}
|
||||
primary={t('tutorial:initial.register_name', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
/>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
|
@ -1,45 +1,57 @@
|
||||
import { Box, Button, ListItem, ListItemButton, ListItemText, Popover, Typography } from '@mui/material';
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react'
|
||||
import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
|
||||
import {
|
||||
Box,
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Popover,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { useContext, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
AutoSizer,
|
||||
CellMeasurer,
|
||||
CellMeasurerCache,
|
||||
List,
|
||||
} from 'react-virtualized';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { getBaseApi, getFee } from '../../background';
|
||||
import { getFee } from '../../background';
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
defaultHeight: 50,
|
||||
});
|
||||
|
||||
|
||||
|
||||
const getGroupInfo = async (groupId) => {
|
||||
const response = await fetch(`${getBaseApiReact()}/groups/` + groupId);
|
||||
const groupData = await response.json();
|
||||
|
||||
if (groupData) {
|
||||
return groupData
|
||||
}
|
||||
return groupData;
|
||||
}
|
||||
};
|
||||
export const getGroupNames = async (listOfGroups) => {
|
||||
let groups = [];
|
||||
if (listOfGroups && Array.isArray(listOfGroups)) {
|
||||
for (const group of listOfGroups) {
|
||||
|
||||
const groupInfo = await getGroupInfo(group.groupId);
|
||||
if (groupInfo) {
|
||||
groups.push({ ...group, ...groupInfo });
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
};
|
||||
|
||||
export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
const {txList, setTxList, show} = useContext(MyContext)
|
||||
export const UserListOfInvites = ({
|
||||
myAddress,
|
||||
setInfoSnack,
|
||||
setOpenSnack,
|
||||
}) => {
|
||||
const { txList, setTxList, show } = useContext(MyContext);
|
||||
const [invites, setInvites] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@ -49,20 +61,20 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
|
||||
const getRequests = async () => {
|
||||
try {
|
||||
const response = await fetch(`${getBaseApiReact()}/groups/invites/${myAddress}/?limit=0`);
|
||||
const response = await fetch(
|
||||
`${getBaseApiReact()}/groups/invites/${myAddress}/?limit=0`
|
||||
);
|
||||
const inviteData = await response.json();
|
||||
|
||||
const resMoreData = await getGroupNames(inviteData)
|
||||
const resMoreData = await getGroupNames(inviteData);
|
||||
setInvites(resMoreData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
getRequests();
|
||||
|
||||
}, []);
|
||||
|
||||
const handlePopoverOpen = (event, index) => {
|
||||
@ -77,17 +89,17 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
|
||||
const handleJoinGroup = async (groupId, groupName) => {
|
||||
try {
|
||||
|
||||
const fee = await getFee('JOIN_GROUP')
|
||||
const fee = await getFee('JOIN_GROUP'); // TODO translate
|
||||
await show({
|
||||
message: "Would you like to perform an JOIN_GROUP transaction?" ,
|
||||
publishFee: fee.fee + ' QORT'
|
||||
})
|
||||
message: 'Would you like to perform an JOIN_GROUP transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("joinGroup", {
|
||||
window
|
||||
.sendMessage('joinGroup', {
|
||||
groupId,
|
||||
})
|
||||
.then((response) => {
|
||||
@ -105,15 +117,16 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
]);
|
||||
res(response);
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
message: "Successfully requested to join group. It may take a couple of minutes for the changes to propagate",
|
||||
type: 'success',
|
||||
message:
|
||||
'Successfully requested to join group. It may take a couple of minutes for the changes to propagate',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
handlePopoverClose();
|
||||
return;
|
||||
}
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: response?.error,
|
||||
});
|
||||
setOpenSnack(true);
|
||||
@ -121,22 +134,18 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error.message || "An error occurred",
|
||||
type: 'error',
|
||||
message: error.message || 'An error occurred',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(error);
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const rowRenderer = ({ index, key, parent, style }) => {
|
||||
const invite = invites[index];
|
||||
@ -157,46 +166,61 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
anchorEl={popoverAnchor}
|
||||
onClose={handlePopoverClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "center",
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "center",
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
style={{ marginTop: "8px" }}
|
||||
style={{ marginTop: '8px' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "325px",
|
||||
height: "250px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
padding: "10px",
|
||||
width: '325px',
|
||||
height: '250px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<Typography>Join {invite?.groupName}</Typography>
|
||||
<LoadingButton
|
||||
loading={isLoading}
|
||||
loadingPosition="start"
|
||||
variant="contained" onClick={()=> handleJoinGroup(invite?.groupId, invite?.groupName)}>Join group</LoadingButton>
|
||||
variant="contained"
|
||||
onClick={() =>
|
||||
handleJoinGroup(invite?.groupId, invite?.groupName)
|
||||
}
|
||||
>
|
||||
Join group
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
</Popover>
|
||||
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
|
||||
<ListItemButton
|
||||
onClick={(event) => handlePopoverOpen(event, index)}
|
||||
>
|
||||
{invite?.isOpen === false && (
|
||||
<LockIcon sx={{
|
||||
color: 'var(--green)'
|
||||
}} />
|
||||
<LockIcon
|
||||
sx={{
|
||||
color: 'var(--green)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{invite?.isOpen === true && (
|
||||
<NoEncryptionGmailerrorredIcon sx={{
|
||||
color: 'var(--danger)'
|
||||
}} />
|
||||
<NoEncryptionGmailerrorredIcon
|
||||
sx={{
|
||||
color: 'var(--danger)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Spacer width="15px" />
|
||||
<ListItemText primary={invite?.groupName} secondary={invite?.description} />
|
||||
<ListItemText
|
||||
primary={invite?.groupName}
|
||||
secondary={invite?.description}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</div>
|
||||
@ -206,18 +230,20 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1
|
||||
}}>
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
<p>Invite list</p>
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
@ -237,4 +263,4 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -88,7 +88,7 @@ export const WalletsAppWrapper = () => {
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<Typography>Q-Wallets</Typography>
|
||||
<Typography>Q-Wallets</Typography> // TODO translate
|
||||
<ButtonBase onClick={handleClose}>
|
||||
<CloseIcon
|
||||
sx={{
|
||||
|
@ -1,12 +1,16 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { getBaseApiReactSocket, pauseAllQueues, resumeAllQueues } from '../../App';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import {
|
||||
getBaseApiReactSocket,
|
||||
pauseAllQueues,
|
||||
resumeAllQueues,
|
||||
} from '../../App';
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||
|
||||
export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
|
||||
const socketRef = useRef(null); // WebSocket reference
|
||||
const timeoutIdRef = useRef(null); // Timeout ID reference
|
||||
const groupSocketTimeoutRef = useRef(null); // Group Socket Timeout reference
|
||||
const initiateRef = useRef(null)
|
||||
const initiateRef = useRef(null);
|
||||
const forceCloseWebSocket = () => {
|
||||
if (socketRef.current) {
|
||||
clearTimeout(timeoutIdRef.current);
|
||||
@ -17,21 +21,20 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
|
||||
};
|
||||
|
||||
const logoutEventFunc = () => {
|
||||
forceCloseWebSocket()
|
||||
forceCloseWebSocket();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("logout-event", logoutEventFunc);
|
||||
subscribeToEvent('logout-event', logoutEventFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("logout-event", logoutEventFunc);
|
||||
unsubscribeFromEvent('logout-event', logoutEventFunc);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!myAddress) return; // Only proceed if myAddress is set
|
||||
|
||||
|
||||
const pingHeads = () => {
|
||||
try {
|
||||
if (socketRef.current?.readyState === WebSocket.OPEN) {
|
||||
@ -54,9 +57,8 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
|
||||
|
||||
try {
|
||||
if (!initiateRef.current) {
|
||||
setIsLoadingGroups(true)
|
||||
pauseAllQueues()
|
||||
|
||||
setIsLoadingGroups(true);
|
||||
pauseAllQueues();
|
||||
}
|
||||
const socketLink = `${getBaseApiReactSocket()}/websockets/chat/active/${currentAddress}?encoding=BASE64&haschatreference=false`;
|
||||
socketRef.current = new WebSocket(socketLink);
|
||||
@ -72,33 +74,45 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
|
||||
groupSocketTimeoutRef.current = setTimeout(pingHeads, 45000); // Ping every 45 seconds
|
||||
} else {
|
||||
if (!initiateRef.current) {
|
||||
setIsLoadingGroups(false)
|
||||
initiateRef.current = true
|
||||
resumeAllQueues()
|
||||
|
||||
setIsLoadingGroups(false);
|
||||
initiateRef.current = true;
|
||||
resumeAllQueues();
|
||||
}
|
||||
const data = JSON.parse(e.data);
|
||||
const copyGroups = [...(data?.groups || [])]
|
||||
const findIndex = copyGroups?.findIndex(item => item?.groupId === 0)
|
||||
const copyGroups = [...(data?.groups || [])];
|
||||
const findIndex = copyGroups?.findIndex(
|
||||
(item) => item?.groupId === 0
|
||||
);
|
||||
if (findIndex !== -1) {
|
||||
copyGroups[findIndex] = {
|
||||
...(copyGroups[findIndex] || {}),
|
||||
groupId: "0"
|
||||
groupId: '0',
|
||||
};
|
||||
}
|
||||
}
|
||||
const filteredGroups = copyGroups
|
||||
const sortedGroups = filteredGroups.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
|
||||
const sortedDirects = (data?.direct || []).filter(item =>
|
||||
item?.name !== 'extension-proxy' && item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH'
|
||||
).sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
|
||||
const filteredGroups = copyGroups;
|
||||
const sortedGroups = filteredGroups.sort(
|
||||
(a, b) => (b.timestamp || 0) - (a.timestamp || 0)
|
||||
);
|
||||
const sortedDirects = (data?.direct || [])
|
||||
.filter(
|
||||
(item) =>
|
||||
item?.name !== 'extension-proxy' &&
|
||||
item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH'
|
||||
)
|
||||
.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
|
||||
|
||||
window.sendMessage("handleActiveGroupDataFromSocket", {
|
||||
window
|
||||
.sendMessage('handleActiveGroupDataFromSocket', {
|
||||
groups: sortedGroups,
|
||||
directs: sortedDirects,
|
||||
}).catch((error) => {
|
||||
console.error("Failed to handle active group data from socket:", error.message || "An error occurred");
|
||||
})
|
||||
.catch((error) => {
|
||||
// TODO translate
|
||||
console.error(
|
||||
'Failed to handle active group data from socket:',
|
||||
error.message || 'An error occurred'
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing onmessage data:', error);
|
||||
@ -127,10 +141,8 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
initWebsocketMessageGroup(); // Initialize WebSocket on component mount
|
||||
|
||||
|
||||
return () => {
|
||||
forceCloseWebSocket(); // Clean up WebSocket on component unmount
|
||||
};
|
||||
|
@ -1,42 +1,34 @@
|
||||
import React, { useCallback, useEffect, useRef } from "react";
|
||||
import { getBaseApiReact } from "../../App";
|
||||
import { truncate } from "lodash";
|
||||
|
||||
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
export const useBlockedAddresses = () => {
|
||||
const userBlockedRef = useRef({})
|
||||
const userNamesBlockedRef = useRef({})
|
||||
const userBlockedRef = useRef({});
|
||||
const userNamesBlockedRef = useRef({});
|
||||
|
||||
const getAllBlockedUsers = useCallback(() => {
|
||||
|
||||
return {
|
||||
names: userNamesBlockedRef.current,
|
||||
addresses: userBlockedRef.current
|
||||
}
|
||||
}, [])
|
||||
addresses: userBlockedRef.current,
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isUserBlocked = useCallback((address, name) => {
|
||||
try {
|
||||
if(!address) return false
|
||||
if(userBlockedRef.current[address]) return true
|
||||
return false
|
||||
|
||||
|
||||
if (!address) return false;
|
||||
if (userBlockedRef.current[address]) return true;
|
||||
return false;
|
||||
} catch (error) {
|
||||
//error
|
||||
}
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchBlockedList = async () => {
|
||||
try {
|
||||
const response = await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'get',
|
||||
listName: `blockedAddresses`,
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
@ -47,21 +39,20 @@ export const useBlockedAddresses = () => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
})
|
||||
const blockedUsers = {}
|
||||
});
|
||||
const blockedUsers = {};
|
||||
response?.forEach((item) => {
|
||||
blockedUsers[item] = true
|
||||
})
|
||||
userBlockedRef.current = blockedUsers
|
||||
blockedUsers[item] = true;
|
||||
});
|
||||
userBlockedRef.current = blockedUsers;
|
||||
|
||||
const response2 = await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'get',
|
||||
listName: `blockedNames`,
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
@ -72,147 +63,132 @@ export const useBlockedAddresses = () => {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
})
|
||||
const blockedUsers2 = {}
|
||||
});
|
||||
const blockedUsers2 = {};
|
||||
response2?.forEach((item) => {
|
||||
blockedUsers2[item] = true
|
||||
})
|
||||
userNamesBlockedRef.current = blockedUsers2
|
||||
|
||||
|
||||
blockedUsers2[item] = true;
|
||||
});
|
||||
userNamesBlockedRef.current = blockedUsers2;
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
fetchBlockedList()
|
||||
}, [])
|
||||
};
|
||||
fetchBlockedList();
|
||||
}, []);
|
||||
|
||||
const removeBlockFromList = useCallback(async (address, name) => {
|
||||
if (name) {
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'remove',
|
||||
items: [name],
|
||||
listName: 'blockedNames'
|
||||
|
||||
listName: 'blockedNames',
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
|
||||
const copyObject = {...userNamesBlockedRef.current}
|
||||
delete copyObject[name]
|
||||
userNamesBlockedRef.current = copyObject
|
||||
|
||||
const copyObject = { ...userNamesBlockedRef.current };
|
||||
delete copyObject[name];
|
||||
userNamesBlockedRef.current = copyObject;
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
if (address) {
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'remove',
|
||||
items: [address],
|
||||
listName: 'blockedAddresses'
|
||||
|
||||
listName: 'blockedAddresses',
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
|
||||
const copyObject = {...userBlockedRef.current}
|
||||
delete copyObject[address]
|
||||
userBlockedRef.current = copyObject
|
||||
|
||||
const copyObject = { ...userBlockedRef.current };
|
||||
delete copyObject[address];
|
||||
userBlockedRef.current = copyObject;
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
const addToBlockList = useCallback(async (address, name) => {
|
||||
if (name) {
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'add',
|
||||
items: [name],
|
||||
listName: 'blockedNames'
|
||||
|
||||
listName: 'blockedNames',
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
const copyObject = {...userNamesBlockedRef.current}
|
||||
copyObject[name] = true
|
||||
userNamesBlockedRef.current = copyObject
|
||||
|
||||
const copyObject = { ...userNamesBlockedRef.current };
|
||||
copyObject[name] = true;
|
||||
userNamesBlockedRef.current = copyObject;
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
if (address) {
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'add',
|
||||
items: [address],
|
||||
listName: 'blockedAddresses'
|
||||
|
||||
listName: 'blockedAddresses',
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
|
||||
const copyObject = {...userBlockedRef.current}
|
||||
copyObject[address] = true
|
||||
userBlockedRef.current = copyObject
|
||||
const copyObject = { ...userBlockedRef.current };
|
||||
copyObject[address] = true;
|
||||
userBlockedRef.current = copyObject;
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
return {
|
||||
isUserBlocked,
|
||||
addToBlockList,
|
||||
removeBlockFromList,
|
||||
getAllBlockedUsers
|
||||
getAllBlockedUsers,
|
||||
};
|
||||
};
|
||||
|
@ -1,32 +1,30 @@
|
||||
import React, { useCallback, useRef } from "react";
|
||||
import { getBaseApiReact } from "../../App";
|
||||
|
||||
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
|
||||
export const useHandleUserInfo = () => {
|
||||
const userInfoRef = useRef({})
|
||||
|
||||
const userInfoRef = useRef({});
|
||||
|
||||
const getIndividualUserInfo = useCallback(async (address) => {
|
||||
try {
|
||||
if(!address) return null
|
||||
if(userInfoRef.current[address] !== undefined) return userInfoRef.current[address]
|
||||
if (!address) return null;
|
||||
if (userInfoRef.current[address] !== undefined)
|
||||
return userInfoRef.current[address];
|
||||
|
||||
const url = `${getBaseApiReact()}/addresses/${address}`;
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error("network error");
|
||||
throw new Error('network error');
|
||||
}
|
||||
const data = await response.json();
|
||||
userInfoRef.current = {
|
||||
...userInfoRef.current,
|
||||
[address]: data?.level
|
||||
}
|
||||
return data?.level
|
||||
[address]: data?.level,
|
||||
};
|
||||
return data?.level;
|
||||
} catch (error) {
|
||||
//error
|
||||
}
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
return {
|
||||
getIndividualUserInfo,
|
||||
|
@ -1,40 +1,40 @@
|
||||
import { Box, ButtonBase, Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { Box, ButtonBase, Typography } from '@mui/material';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
|
||||
export const NewUsersCTA = ({ balance }) => {
|
||||
if (balance === undefined || +balance > 0) return null;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Spacer height="40px" />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "320px",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
padding: "15px",
|
||||
outline: "1px solid gray",
|
||||
borderRadius: "4px",
|
||||
width: '320px',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '15px',
|
||||
outline: '1px solid gray',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
fontSize: "1.2rem",
|
||||
fontWeight: "bold",
|
||||
textAlign: 'center',
|
||||
fontSize: '1.2rem',
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
>
|
||||
Are you a new user?
|
||||
</Typography>
|
||||
</Typography>{' '}
|
||||
// TODO translate
|
||||
<Spacer height="20px" />
|
||||
<Typography>
|
||||
Please message us on Telegram or Discord if you need 4 QORT to start
|
||||
@ -43,25 +43,25 @@ export const NewUsersCTA = ({ balance }) => {
|
||||
<Spacer height="20px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
justifyContent: "center",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
textDecoration: "underline",
|
||||
textDecoration: 'underline',
|
||||
}}
|
||||
onClick={() => {
|
||||
if (window?.electronAPI?.openExternal) {
|
||||
window.electronAPI.openExternal(
|
||||
"https://link.qortal.dev/telegram-invite"
|
||||
'https://link.qortal.dev/telegram-invite'
|
||||
);
|
||||
} else {
|
||||
window.open(
|
||||
"https://link.qortal.dev/telegram-invite",
|
||||
"_blank"
|
||||
'https://link.qortal.dev/telegram-invite',
|
||||
'_blank'
|
||||
);
|
||||
}
|
||||
}}
|
||||
@ -70,15 +70,15 @@ export const NewUsersCTA = ({ balance }) => {
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
textDecoration: "underline",
|
||||
textDecoration: 'underline',
|
||||
}}
|
||||
onClick={() => {
|
||||
if (window?.electronAPI?.openExternal) {
|
||||
window.electronAPI.openExternal(
|
||||
"https://link.qortal.dev/discord-invite"
|
||||
'https://link.qortal.dev/discord-invite'
|
||||
);
|
||||
} else {
|
||||
window.open("https://link.qortal.dev/discord-invite", "_blank");
|
||||
window.open('https://link.qortal.dev/discord-invite', '_blank');
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { getBaseApiReact } from "../../App";
|
||||
import { Box, Tooltip, Typography } from "@mui/material";
|
||||
import { BarSpinner } from "../../common/Spinners/BarSpinner/BarSpinner";
|
||||
import { formatDate } from "../../utils/time";
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
import { Box, Tooltip, Typography, useTheme } from '@mui/material';
|
||||
import { BarSpinner } from '../../common/Spinners/BarSpinner/BarSpinner';
|
||||
import { formatDate } from '../../utils/time';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function getAverageLtcPerQort(trades) {
|
||||
let totalQort = 0;
|
||||
@ -38,6 +39,8 @@ export const QortPrice = () => {
|
||||
const [supply, setSupply] = useState(null);
|
||||
const [lastBlock, setLastBlock] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { t } = useTranslation(['core', 'tutorial']);
|
||||
const theme = useTheme();
|
||||
|
||||
const getPrice = useCallback(async () => {
|
||||
try {
|
||||
@ -101,64 +104,63 @@ export const QortPrice = () => {
|
||||
return () => clearInterval(interval);
|
||||
}, [getPrice]);
|
||||
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "column",
|
||||
width: "322px",
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
flexWrap: 'wrap',
|
||||
flexDirection: 'column',
|
||||
width: '322px',
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
title={
|
||||
<span style={{ color: "white", fontSize: "14px", fontWeight: 700 }}>
|
||||
<span style={{ color: 'white', fontSize: '14px', fontWeight: 700 }}>
|
||||
Based on the latest 20 trades
|
||||
</span>
|
||||
}
|
||||
placement="bottom"
|
||||
arrow
|
||||
sx={{ fontSize: "24" }}
|
||||
sx={{ fontSize: '24' }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#444444",
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: "#444444",
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "322px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: "10px",
|
||||
|
||||
justifyContent: "space-between",
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: '10px',
|
||||
justifyContent: 'space-between',
|
||||
width: '322px',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontWeight: "bold",
|
||||
fontSize: '1rem',
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
>
|
||||
Price
|
||||
{t('core:price', { postProcess: 'capitalize' })}
|
||||
</Typography>
|
||||
|
||||
{!ltcPerQort ? (
|
||||
<BarSpinner width="16px" color="white" />
|
||||
) : (
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
>
|
||||
{ltcPerQort} LTC/QORT
|
||||
@ -168,28 +170,28 @@ export const QortPrice = () => {
|
||||
</Tooltip>
|
||||
<Box
|
||||
sx={{
|
||||
width: "322px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: "10px",
|
||||
|
||||
justifyContent: "space-between",
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: '10px',
|
||||
justifyContent: 'space-between',
|
||||
width: '322px',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontWeight: "bold",
|
||||
fontSize: '1rem',
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
>
|
||||
Supply
|
||||
{t('core:supply', { postProcess: 'capitalize' })}
|
||||
</Typography>
|
||||
|
||||
{!supply ? (
|
||||
<BarSpinner width="16px" color="white" />
|
||||
) : (
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
>
|
||||
{supply} QORT
|
||||
@ -198,58 +200,56 @@ export const QortPrice = () => {
|
||||
</Box>
|
||||
<Tooltip
|
||||
title={
|
||||
<span style={{ color: "white", fontSize: "14px", fontWeight: 700 }}>
|
||||
<span style={{ color: 'white', fontSize: '14px', fontWeight: 700 }}>
|
||||
{lastBlock?.timestamp && formatDate(lastBlock?.timestamp)}
|
||||
</span>
|
||||
}
|
||||
placement="bottom"
|
||||
arrow
|
||||
sx={{ fontSize: "24" }}
|
||||
sx={{ fontSize: '24' }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#444444",
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: "#444444",
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "322px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: "10px",
|
||||
|
||||
justifyContent: "space-between",
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: '10px',
|
||||
justifyContent: 'space-between',
|
||||
width: '322px',
|
||||
}}
|
||||
>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontWeight: "bold",
|
||||
fontSize: '1rem',
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
>
|
||||
Last height
|
||||
{t('core:last_height', { postProcess: 'capitalize' })}
|
||||
</Typography>
|
||||
|
||||
{!lastBlock?.height ? (
|
||||
<BarSpinner width="16px" color="white" />
|
||||
) : (
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
>
|
||||
{lastBlock?.height}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
@ -206,7 +206,6 @@ export const Minting = ({
|
||||
window
|
||||
.sendMessage(
|
||||
'ADMIN_ACTION',
|
||||
|
||||
{
|
||||
type: 'addmintingaccount',
|
||||
value: val,
|
||||
@ -266,7 +265,7 @@ export const Minting = ({
|
||||
rej({ message: response.error });
|
||||
})
|
||||
.catch((error) => {
|
||||
rej({ message: error.message || 'An error occurred' });
|
||||
rej({ message: error.message || 'An error occurred' }); //TODO translate
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { useMemo } from 'react';
|
||||
import EmailIcon from '@mui/icons-material/Email';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { mailsAtom, qMailLastEnteredTimestampAtom } from '../atoms/global';
|
||||
import { isLessThanOneWeekOld } from './Group/QMailMessages';
|
||||
import { ButtonBase, Tooltip, useTheme } from '@mui/material';
|
||||
import { executeEvent } from '../utils/events';
|
||||
import { Mail } from '@mui/icons-material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const QMailStatus = () => {
|
||||
const { t } = useTranslation(['core']);
|
||||
const theme = useTheme();
|
||||
|
||||
const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState(
|
||||
@ -63,7 +64,9 @@ export const QMailStatus = () => {
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
Q-MAIL
|
||||
{t('core:q_mail', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import isEqual from 'lodash/isEqual'; // Import deep comparison utility
|
||||
import isEqual from 'lodash/isEqual'; // TODO Import deep comparison utility
|
||||
import {
|
||||
canSaveSettingToQdnAtom,
|
||||
hasSettingsChangedAtom,
|
||||
@ -26,6 +26,7 @@ import {
|
||||
base64ToUint8Array,
|
||||
uint8ArrayToObject,
|
||||
} from '../../backgroundFunctions/encryption';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const handleImportClick = async () => {
|
||||
const fileInput = document.createElement('input');
|
||||
@ -77,6 +78,8 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const { show } = useContext(MyContext);
|
||||
|
||||
const { t } = useTranslation(['core']);
|
||||
|
||||
const hasChanged = useMemo(() => {
|
||||
const newChanges = {
|
||||
sortablePinnedApps: pinnedApps.map((item) => {
|
||||
@ -146,8 +149,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
const fee = await getFee('ARBITRARY');
|
||||
|
||||
await show({
|
||||
message:
|
||||
'Would you like to publish your settings to QDN (encrypted) ?',
|
||||
message: t('core:save.publish_qnd', { postProcess: 'capitalize' }),
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
const response = await new Promise((res, rej) => {
|
||||
@ -165,7 +167,10 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || 'An error occurred');
|
||||
rej(
|
||||
error.message ||
|
||||
t('core:result.error.generic', { postProcess: 'capitalize' })
|
||||
);
|
||||
});
|
||||
});
|
||||
if (response?.identifier) {
|
||||
@ -173,7 +178,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
setSettingsQdnLastUpdated(Date.now());
|
||||
setInfoSnack({
|
||||
type: 'success',
|
||||
message: 'Sucessfully published to QDN',
|
||||
message: t('core:result.success.publish_qdn', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
});
|
||||
setOpenSnack(true);
|
||||
setAnchorEl(null);
|
||||
@ -182,7 +189,11 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
} catch (error) {
|
||||
setInfoSnack({
|
||||
type: 'error',
|
||||
message: error?.message || 'Unable to save to QDN',
|
||||
message:
|
||||
error?.message ||
|
||||
t('core:result.error.save_qdn', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
});
|
||||
setOpenSnack(true);
|
||||
} finally {
|
||||
@ -214,7 +225,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
{isDesktop ? (
|
||||
<IconWrapper
|
||||
disableWidth={disableWidth}
|
||||
label="Save"
|
||||
label={t('core:save_options.save', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
selected={false}
|
||||
>
|
||||
<SaveIcon
|
||||
@ -225,6 +238,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
<SaveIcon color={hasChanged && !isLoading ? '#5EB049' : undefined} />
|
||||
)}
|
||||
</ButtonBase>
|
||||
|
||||
<Popover
|
||||
open={!!anchorEl}
|
||||
anchorEl={anchorEl}
|
||||
@ -247,19 +261,19 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
{isUsingImportExportSettings && (
|
||||
<Box
|
||||
sx={{
|
||||
padding: '15px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 1,
|
||||
padding: '15px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
@ -267,8 +281,10 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
You are using the export/import way of saving settings.
|
||||
</Typography>
|
||||
{t('core:save_options.settings', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</Typography>{' '}
|
||||
<Spacer height="40px" />
|
||||
<Button
|
||||
size="small"
|
||||
@ -294,7 +310,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
Use QDN saving
|
||||
{t('core:save_options.qdn', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
@ -302,10 +320,10 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
{!isUsingImportExportSettings && (
|
||||
<Box
|
||||
sx={{
|
||||
padding: '15px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 1,
|
||||
padding: '15px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
@ -323,8 +341,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
You need a registered Qortal name to save your pinned apps to
|
||||
QDN.
|
||||
{t('core:save_options.register_name', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
@ -343,10 +362,13 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
You have unsaved changes to your pinned apps. Save them to
|
||||
QDN.
|
||||
{t('core:save_options.unsaved_changes', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</Typography>
|
||||
|
||||
<Spacer height="10px" />
|
||||
|
||||
<LoadingButton
|
||||
sx={{
|
||||
backgroundColor: 'var(--green)',
|
||||
@ -364,7 +386,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
onClick={saveToQdn}
|
||||
variant="contained"
|
||||
>
|
||||
Save to QDN
|
||||
{t('core:save_options.save_qdn', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</LoadingButton>
|
||||
<Spacer height="20px" />
|
||||
{!isNaN(settingsQdnLastUpdated) &&
|
||||
@ -375,8 +399,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
Don't like your current local changes? Would you
|
||||
like to reset to your saved QDN pinned apps?
|
||||
{t('core:save_options.reset_qdn', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</Typography>
|
||||
<Spacer height="10px" />
|
||||
<LoadingButton
|
||||
@ -396,7 +421,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
Revert to QDN
|
||||
{t('core:save_options.revert_qdn', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</LoadingButton>
|
||||
</>
|
||||
)}
|
||||
@ -408,8 +435,10 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
Don't like your current local changes? Would you
|
||||
like to reset to the default pinned apps?
|
||||
{' '}
|
||||
{t('core:save_options.reset_pinned', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</Typography>
|
||||
<Spacer height="10px" />
|
||||
<LoadingButton
|
||||
@ -417,7 +446,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
onClick={revertChanges}
|
||||
variant="contained"
|
||||
>
|
||||
Revert to default
|
||||
{t('core:save_options.revert_default', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</LoadingButton>
|
||||
</>
|
||||
)}
|
||||
@ -439,8 +470,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
The app was unable to download your existing QDN-saved
|
||||
pinned apps. Would you like to overwrite those changes?
|
||||
{t('core:save_options.overwrite_changes', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</Typography>
|
||||
<Spacer height="10px" />
|
||||
<LoadingButton
|
||||
@ -460,7 +492,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
Overwrite to QDN
|
||||
{t('core:save_options.overwrite_qdn', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
)}
|
||||
@ -478,7 +512,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
You currently do not have any changes to your pinned apps
|
||||
{t('core:save_options.no_pinned_changes', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
@ -533,8 +569,11 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Import
|
||||
{t('core:import', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</ButtonBase>
|
||||
|
||||
<ButtonBase
|
||||
onClick={async () => {
|
||||
try {
|
||||
@ -555,7 +594,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Export
|
||||
{t('core:export', {
|
||||
postProcess: 'capitalize',
|
||||
})}
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -2,8 +2,10 @@ import { useThemeContext } from './ThemeContext';
|
||||
import { IconButton, Tooltip } from '@mui/material';
|
||||
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const ThemeSelector = () => {
|
||||
const { t } = useTranslation(['core']);
|
||||
const { themeMode, toggleTheme } = useThemeContext();
|
||||
|
||||
return (
|
||||
@ -16,7 +18,17 @@ const ThemeSelector = () => {
|
||||
position: 'absolute',
|
||||
}}
|
||||
>
|
||||
<Tooltip title={themeMode === 'dark' ? 'Light mode' : 'Dark mode'}>
|
||||
<Tooltip
|
||||
title={
|
||||
themeMode === 'dark'
|
||||
? t('core:theme.light', {
|
||||
postProcess: 'capitalize',
|
||||
})
|
||||
: t('core:theme.light', {
|
||||
postProcess: 'capitalize',
|
||||
})
|
||||
}
|
||||
>
|
||||
<IconButton onClick={toggleTheme}>
|
||||
{themeMode === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
|
||||
</IconButton>
|
||||
|
@ -13,11 +13,13 @@ import {
|
||||
} from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { VideoPlayer } from '../Embeds/VideoPlayer';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const Tutorials = () => {
|
||||
const { openTutorialModal, setOpenTutorialModal } = useContext(GlobalContext);
|
||||
const [multiNumber, setMultiNumber] = useState(0);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation(['core', 'tutorial']);
|
||||
|
||||
const handleClose = () => {
|
||||
setOpenTutorialModal(null);
|
||||
@ -61,9 +63,7 @@ export const Tutorials = () => {
|
||||
})}
|
||||
</Tabs>
|
||||
|
||||
<DialogTitle sx={{ m: 0, p: 2 }}>
|
||||
{selectedTutorial?.title} {` Tutorial`}
|
||||
</DialogTitle>
|
||||
<DialogTitle sx={{ m: 0, p: 2 }}>{selectedTutorial?.title}</DialogTitle>
|
||||
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
@ -91,7 +91,7 @@ export const Tutorials = () => {
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={handleClose}>
|
||||
Close
|
||||
{t('core:close', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
@ -138,7 +138,7 @@ export const Tutorials = () => {
|
||||
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={handleClose}>
|
||||
Close
|
||||
{t('core:close', { postProcess: 'capitalize' })}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
@ -8,6 +8,7 @@ import navigationImg from './img/navigation.webp';
|
||||
import overviewImg from './img/overview.webp';
|
||||
import startedImg from './img/started.webp';
|
||||
import obtainingImg from './img/obtaining-qort.jpg';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const checkIfGatewayIsOnline = async () => {
|
||||
try {
|
||||
@ -27,9 +28,11 @@ const checkIfGatewayIsOnline = async () => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const useHandleTutorials = () => {
|
||||
const [openTutorialModal, setOpenTutorialModal] = useState<any>(null);
|
||||
const [shownTutorials, setShowTutorials] = useState(null);
|
||||
const { t } = useTranslation(['core', 'tutorial']);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
@ -104,7 +107,9 @@ export const useHandleTutorials = () => {
|
||||
setOpenTutorialModal({
|
||||
multi: [
|
||||
{
|
||||
title: '1. Getting Started',
|
||||
title: t('tutorial:1_getting_started', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
resource: {
|
||||
name: 'a-test',
|
||||
service: 'VIDEO',
|
||||
@ -113,7 +118,9 @@ export const useHandleTutorials = () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '2. Overview',
|
||||
title: t('tutorial:2_overview', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
resource: {
|
||||
name: 'a-test',
|
||||
service: 'VIDEO',
|
||||
@ -122,7 +129,9 @@ export const useHandleTutorials = () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '3. Qortal Groups',
|
||||
title: t('tutorial:3_groups', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
resource: {
|
||||
name: 'a-test',
|
||||
service: 'VIDEO',
|
||||
@ -131,7 +140,9 @@ export const useHandleTutorials = () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '4. Obtaining Qort',
|
||||
title: t('tutorial:4_obtain_qort', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
resource: {
|
||||
name: 'a-test',
|
||||
service: 'VIDEO',
|
||||
@ -151,7 +162,9 @@ export const useHandleTutorials = () => {
|
||||
setOpenTutorialModal({
|
||||
multi: [
|
||||
{
|
||||
title: '1. Apps Dashboard',
|
||||
title: t('tutorial:apps.dashboard', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
resource: {
|
||||
name: 'a-test',
|
||||
service: 'VIDEO',
|
||||
@ -160,7 +173,9 @@ export const useHandleTutorials = () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '2. Apps Navigation',
|
||||
title: t('tutorial:apps.navigation', {
|
||||
postProcess: 'capitalize',
|
||||
}),
|
||||
resource: {
|
||||
name: 'a-test',
|
||||
service: 'VIDEO',
|
||||
|
@ -6,6 +6,7 @@ import { MessageQueueProvider } from './MessageQueueContext.tsx';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { ThemeProvider } from './components/Theme/ThemeContext.tsx';
|
||||
import { CssBaseline } from '@mui/material';
|
||||
import '../i18n';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<>
|
||||
|
@ -130,10 +130,10 @@ export const CustomButton = styled(Box)(({ theme }) => ({
|
||||
transition: 'all 0.2s',
|
||||
width: 'fit-content',
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: '#fff',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
color: theme.palette.text.secondary,
|
||||
'svg path': {
|
||||
fill: '#fff',
|
||||
fill: theme.palette.background.paper,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
Loading…
x
Reference in New Issue
Block a user