mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-10 10:16:58 +00:00
Merge branch 'develop' into feature/add-theme
This commit is contained in:
commit
3d17303d84
14
README.md
14
README.md
@ -1,6 +1,6 @@
|
||||
# Qortal Hub - Desktop Interface for Qortal
|
||||
|
||||
Qortal Hub is the newest interface for Qortal, part of the 'Qortal Trifecta' series of new User Interfaces for the platform/network.
|
||||
Qortal Hub is the newest interface for Qortal, part of the 'Qortal Trifecta' series of new User Interfaces for the platform/network.
|
||||
|
||||
It is likely that Qortal Hub will become the new 'primary interface' for Qortal, and that the primary development focus surrounding Qortal Interface development, will be focused here instead of the previous 'qortal-ui' repo.
|
||||
|
||||
@ -8,14 +8,20 @@ It is likely that Qortal Hub will become the new 'primary interface' for Qortal,
|
||||
|
||||
Qortal Hub came along with the new Group Encryption methodologies applied, which provide **encrypted chat in Q-Chat for private groups.** Qortal Hub was the first to implement the new method of group encryption, which allows new users to see previously published data, unlike the previous group encryption methodology of things like 'threads' in Q-Mail.
|
||||
|
||||
Allowing new users to view older messages also comes along with a massive boost to the usability of the group encryption, and as such has been leveraged in multiple places inside Qortal Hub, Qortal Extension, and Qortal Go.
|
||||
Allowing new users to view older messages also comes along with a massive boost to the usability of the group encryption, and as such has been leveraged in multiple places inside Qortal Hub, Qortal Extension, and Qortal Go.
|
||||
|
||||
## Ease of Use Expanded
|
||||
|
||||
Qortal Hub has a focus on ease of use for new users. Providing both the ability to utlilize Qortal without needing to run a local node (though running a local node is still the recommended method to access Qortal), and multiple built-in (QDN-published) walk-thru videos (by Qortal Justin) that explain the various basics of any given section of the application. This allows new users to 'jump right in' to utilizing Qortal Hub, and Qortal overall, in a much more streamlined fashion than that which was previously required by the 'legacy UI' (qortal-ui).
|
||||
Qortal Hub has a focus on ease of use for new users. Providing both the ability to utlilize Qortal without needing to run a local node (though running a local node is still the recommended method to access Qortal), and multiple built-in (QDN-published) walk-thru videos (by Qortal Justin) that explain the various basics of any given section of the application. This allows new users to 'jump right in' to utilizing Qortal Hub, and Qortal overall, in a much more streamlined fashion than that which was previously required by the 'legacy UI' (qortal-ui).
|
||||
|
||||
Leveraging a redundant set of publicly accessible nodes provided by crowetic, Qortal Hub, Qortal Go, and Qortal Extension, all allow the use of Qortal without running a node, making it very simple to 'install and go' and start making use of the extensive functionality provided within the Qortal Ecosystem.
|
||||
Leveraging a redundant set of publicly accessible nodes provided by crowetic, Qortal Hub, Qortal Go, and Qortal Extension, all allow the use of Qortal without running a node, making it very simple to 'install and go' and start making use of the extensive functionality provided within the Qortal Ecosystem.
|
||||
|
||||
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']);
|
||||
|
||||
export const Explore = ({setDesktopViewMode}) => {
|
||||
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("open-apps-mode", {});
|
||||
}}
|
||||
executeEvent('addTab', {
|
||||
data: { service: 'APP', name: 'q-trade' },
|
||||
});
|
||||
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');
|
||||
}}
|
||||
onClick={()=> {
|
||||
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,21 +1,20 @@
|
||||
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",
|
||||
fontWeight: 400,
|
||||
letterSpacing: 0,
|
||||
color: theme.palette.text.primary,
|
||||
width: '100%'
|
||||
}));
|
||||
display: 'flex',
|
||||
fontFamily: 'Mulish',
|
||||
fontSize: '19px',
|
||||
fontWeight: 400,
|
||||
letterSpacing: 0,
|
||||
color: theme.palette.text.primary,
|
||||
width: '100%',
|
||||
}));
|
||||
|
||||
export const DisplayHtml = ({ html, textColor }: any) => {
|
||||
const cleanContent = useMemo(() => {
|
||||
@ -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,130 +667,131 @@ export const GroupNameP = styled(Typography)`
|
||||
`;
|
||||
|
||||
export const AllThreadP = styled(Typography)`
|
||||
color: #FFF;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 120%; /* 24px */
|
||||
letter-spacing: 0.15px;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 120%; /* 24px */
|
||||
letter-spacing: 0.15px;
|
||||
`;
|
||||
|
||||
export const SingleThreadParent = styled(Box)`
|
||||
border-radius: 35px 4px 4px 35px;
|
||||
position: relative;
|
||||
background: #434448;
|
||||
display: flex;
|
||||
padding: 13px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 5px;
|
||||
height: 76px;
|
||||
align-items:center;
|
||||
transition: 0.2s all;
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.20)
|
||||
}
|
||||
border-radius: 35px 4px 4px 35px;
|
||||
position: relative;
|
||||
background: #434448;
|
||||
display: flex;
|
||||
padding: 13px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 5px;
|
||||
height: 76px;
|
||||
align-items: center;
|
||||
transition: 0.2s all;
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
`;
|
||||
export const SingleTheadMessageParent = styled(Box)`
|
||||
border-radius: 35px 4px 4px 35px;
|
||||
background: #434448;
|
||||
display: flex;
|
||||
padding: 13px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 5px;
|
||||
height: 76px;
|
||||
align-items:center;
|
||||
|
||||
export const SingleTheadMessageParent = styled(Box)`
|
||||
border-radius: 35px 4px 4px 35px;
|
||||
background: #434448;
|
||||
display: flex;
|
||||
padding: 13px;
|
||||
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;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
line-height: normal;
|
||||
white-space: nowrap;
|
||||
color: #fff;
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
line-height: normal;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const ThreadInfoColumnbyP = styled('span')`
|
||||
color: rgba(255, 255, 255, 0.80);
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-family: Roboto;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
`;
|
||||
|
||||
export const ThreadInfoColumnTime = styled(Typography)`
|
||||
color: rgba(255, 255, 255, 0.80);
|
||||
font-family: Roboto;
|
||||
font-size: 15px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
`
|
||||
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;
|
||||
font-family: Roboto;
|
||||
font-size: 23px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
white-space: wrap;
|
||||
color: #fff;
|
||||
font-family: Roboto;
|
||||
font-size: 23px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
white-space: wrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
`
|
||||
`;
|
||||
|
||||
export const ThreadSingleLastMessageP = styled(Typography)`
|
||||
color: #FFF;
|
||||
font-family: Roboto;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
`
|
||||
color: #fff;
|
||||
font-family: Roboto;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
`;
|
||||
|
||||
export const ThreadSingleLastMessageSpanP = styled('span')`
|
||||
color: #FFF;
|
||||
font-family: Roboto;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
color: #fff;
|
||||
font-family: Roboto;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
`;
|
||||
|
||||
export const GroupContainer = styled(Box)`
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
|
||||
|
||||
`
|
||||
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 { createEditor } from 'slate';
|
||||
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)
|
||||
await new Promise<void>((res)=> {
|
||||
const performUpdate = useCallback(async () => {
|
||||
setLoad(true);
|
||||
await new Promise<void>((res) => {
|
||||
setTimeout(() => {
|
||||
res()
|
||||
res();
|
||||
}, 250);
|
||||
})
|
||||
setLoad(false)
|
||||
}, [])
|
||||
useEffect(()=> {
|
||||
});
|
||||
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;
|
||||
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,79 +30,94 @@ 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={{
|
||||
height: '50px',
|
||||
width: '50px'
|
||||
}} src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.name}/qortal_avatar?async=true`} alt={message?.name}>{message?.name?.charAt(0)}</Avatar>
|
||||
</WrapperUserAction>
|
||||
<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>
|
||||
</WrapperUserAction>
|
||||
<ThreadInfoColumn>
|
||||
|
||||
<WrapperUserAction disabled={myName === message?.name} address={undefined} name={message?.name}>
|
||||
<ThreadInfoColumnNameP>{message?.name}</ThreadInfoColumnNameP>
|
||||
<WrapperUserAction
|
||||
disabled={myName === message?.name}
|
||||
address={undefined}
|
||||
name={message?.name}
|
||||
>
|
||||
<ThreadInfoColumnNameP>{message?.name}</ThreadInfoColumnNameP>
|
||||
</WrapperUserAction>
|
||||
<ThreadInfoColumnTime>
|
||||
{formatTimestampForum(message?.created)}
|
||||
</ThreadInfoColumnTime>
|
||||
</ThreadInfoColumn>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{message?.attachments?.length > 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
marginTop: "10px",
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{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%",
|
||||
}}
|
||||
>
|
||||
{message?.attachments?.length > 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
{message?.attachments.map((file: any, index: number) => {
|
||||
const isFirst = index === 0;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
cursor: "pointer",
|
||||
width: "auto",
|
||||
display: expandAttachments
|
||||
? 'flex'
|
||||
: !expandAttachments && isFirst
|
||||
? 'flex'
|
||||
: 'none',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{/* <FileElement
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
cursor: 'pointer',
|
||||
width: 'auto',
|
||||
}}
|
||||
>
|
||||
{/* <FileElement
|
||||
fileInfo={{ ...file, mimeTypeSaved: file?.type }}
|
||||
title={file?.filename}
|
||||
mode="mail"
|
||||
@ -125,80 +138,89 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
||||
{file?.originalFilename || file?.filename}
|
||||
</Typography>
|
||||
</FileElement> */}
|
||||
{message?.attachments?.length > 1 && isFirst && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
}}
|
||||
onClick={() => {
|
||||
setExpandAttachments(prev => !prev);
|
||||
}}
|
||||
>
|
||||
<MoreImg
|
||||
{message?.attachments?.length > 1 && isFirst && (
|
||||
<Box
|
||||
sx={{
|
||||
marginLeft: "5px",
|
||||
transform: expandAttachments
|
||||
? "rotate(180deg)"
|
||||
: "unset",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
}}
|
||||
src={MoreSVG}
|
||||
/>
|
||||
<MoreP>
|
||||
{expandAttachments ? 'hide' : `(${message?.attachments?.length - 1} more)`}
|
||||
|
||||
</MoreP>
|
||||
</Box>
|
||||
)}
|
||||
onClick={() => {
|
||||
setExpandAttachments((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
<MoreImg
|
||||
sx={{
|
||||
marginLeft: '5px',
|
||||
transform: expandAttachments
|
||||
? 'rotate(180deg)'
|
||||
: 'unset',
|
||||
}}
|
||||
src={MoreSVG}
|
||||
/>
|
||||
<MoreP>
|
||||
{expandAttachments
|
||||
? 'hide'
|
||||
: `(${message?.attachments?.length - 1} more)`}
|
||||
</MoreP>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
})
|
||||
}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
{message?.reply?.textContentV2 && (
|
||||
<>
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
opacity: 0.7,
|
||||
borderRadius: '5px',
|
||||
border: '1px solid gray',
|
||||
boxSizing: 'border-box',
|
||||
padding: '5px'
|
||||
}}>
|
||||
<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",
|
||||
|
||||
}}
|
||||
>
|
||||
|
||||
<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>
|
||||
<ThreadInfoColumn>
|
||||
<ThreadInfoColumnNameP sx={{
|
||||
fontSize: '14px'
|
||||
}}>{message?.reply?.name}</ThreadInfoColumnNameP>
|
||||
|
||||
</ThreadInfoColumn>
|
||||
</Box>
|
||||
<MessageDisplay htmlContent={message?.reply?.textContentV2} />
|
||||
</Box>
|
||||
<Spacer height="20px" />
|
||||
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>
|
||||
<ThreadInfoColumn>
|
||||
<ThreadInfoColumnNameP
|
||||
sx={{
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
{message?.reply?.name}
|
||||
</ThreadInfoColumnNameP>
|
||||
</ThreadInfoColumn>
|
||||
</Box>
|
||||
<MessageDisplay htmlContent={message?.reply?.textContentV2} />
|
||||
</Box>
|
||||
<Spacer height="20px" />
|
||||
</>
|
||||
|
||||
)}
|
||||
|
||||
|
||||
{message?.textContent && (
|
||||
<ReadOnlySlate content={message.textContent} mode="mail" />
|
||||
)}
|
||||
@ -208,22 +230,18 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
|
||||
{message?.htmlContent && (
|
||||
<div dangerouslySetInnerHTML={{ __html: cleanHTML }} />
|
||||
)}
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end'
|
||||
}}>
|
||||
<IconButton
|
||||
onClick={() => openNewPostWithQuote(message)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<FormatQuoteIcon />
|
||||
</IconButton>
|
||||
<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>
|
||||
)}
|
||||
|
||||
@ -2235,39 +2236,39 @@ export const Group = ({
|
||||
: '0px',
|
||||
}}
|
||||
>
|
||||
<DesktopHeader
|
||||
isPrivate={isPrivate}
|
||||
selectedGroup={selectedGroup}
|
||||
groupSection={groupSection}
|
||||
isUnread={isUnread}
|
||||
goToAnnouncements={goToAnnouncements}
|
||||
isUnreadChat={isUnreadChat}
|
||||
goToChat={goToChat}
|
||||
goToThreads={goToThreads}
|
||||
setOpenManageMembers={setOpenManageMembers}
|
||||
groupChatHasUnread={groupChatHasUnread}
|
||||
groupsAnnHasUnread={groupsAnnHasUnread}
|
||||
directChatHasUnread={directChatHasUnread}
|
||||
chatMode={chatMode}
|
||||
openDrawerGroups={openDrawerGroups}
|
||||
goToHome={goToHome}
|
||||
setIsOpenDrawerProfile={setIsOpenDrawerProfile}
|
||||
mobileViewMode={mobileViewMode}
|
||||
setMobileViewMode={setMobileViewMode}
|
||||
setMobileViewModeKeepOpen={setMobileViewModeKeepOpen}
|
||||
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}
|
||||
hasUnreadDirects={directChatHasUnread}
|
||||
myName={userInfo?.name || null}
|
||||
isHome={groupSection === 'home'}
|
||||
isGroups={desktopSideView === 'groups'}
|
||||
isDirects={desktopSideView === 'directs'}
|
||||
setDesktopSideView={setDesktopSideView}
|
||||
hasUnreadAnnouncements={isUnread}
|
||||
isAnnouncement={groupSection === 'announcement'}
|
||||
isChat={groupSection === 'chat'}
|
||||
hasUnreadChat={isUnreadChat}
|
||||
setGroupSection={setGroupSection}
|
||||
isForum={groupSection === 'forum'}
|
||||
<DesktopHeader
|
||||
isPrivate={isPrivate}
|
||||
selectedGroup={selectedGroup}
|
||||
groupSection={groupSection}
|
||||
isUnread={isUnread}
|
||||
goToAnnouncements={goToAnnouncements}
|
||||
isUnreadChat={isUnreadChat}
|
||||
goToChat={goToChat}
|
||||
goToThreads={goToThreads}
|
||||
setOpenManageMembers={setOpenManageMembers}
|
||||
groupChatHasUnread={groupChatHasUnread}
|
||||
groupsAnnHasUnread={groupsAnnHasUnread}
|
||||
directChatHasUnread={directChatHasUnread}
|
||||
chatMode={chatMode}
|
||||
openDrawerGroups={openDrawerGroups}
|
||||
goToHome={goToHome}
|
||||
setIsOpenDrawerProfile={setIsOpenDrawerProfile}
|
||||
mobileViewMode={mobileViewMode}
|
||||
setMobileViewMode={setMobileViewMode}
|
||||
setMobileViewModeKeepOpen={setMobileViewModeKeepOpen}
|
||||
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}
|
||||
hasUnreadDirects={directChatHasUnread}
|
||||
myName={userInfo?.name || null}
|
||||
isHome={groupSection === 'home'}
|
||||
isGroups={desktopSideView === 'groups'}
|
||||
isDirects={desktopSideView === 'directs'}
|
||||
setDesktopSideView={setDesktopSideView}
|
||||
hasUnreadAnnouncements={isUnread}
|
||||
isAnnouncement={groupSection === 'announcement'}
|
||||
isChat={groupSection === 'chat'}
|
||||
hasUnreadChat={isUnreadChat}
|
||||
setGroupSection={setGroupSection}
|
||||
isForum={groupSection === 'forum'}
|
||||
/>
|
||||
|
||||
<Box
|
||||
@ -2536,7 +2537,7 @@ export const Group = ({
|
||||
isApps={desktopViewMode === 'apps'}
|
||||
desktopViewMode={desktopViewMode}
|
||||
/>
|
||||
|
||||
|
||||
<AppsDevMode
|
||||
toggleSideViewGroups={toggleSideViewGroups}
|
||||
toggleSideViewDirects={toggleSideViewDirects}
|
||||
@ -2554,7 +2555,7 @@ export const Group = ({
|
||||
desktopViewMode={desktopViewMode}
|
||||
isApps={desktopViewMode === 'apps'}
|
||||
/>
|
||||
|
||||
|
||||
<HomeDesktop
|
||||
name={userInfo?.name}
|
||||
refreshHomeDataFunc={refreshHomeDataFunc}
|
||||
@ -2571,7 +2572,7 @@ export const Group = ({
|
||||
setMobileViewMode={setMobileViewMode}
|
||||
setDesktopViewMode={setDesktopViewMode}
|
||||
desktopViewMode={desktopViewMode}
|
||||
/>
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<AuthenticatedContainerInnerRight
|
||||
|
@ -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,170 +36,217 @@ 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',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
backgroundColor: 'var(--bg-primary)',
|
||||
color: '#fff',
|
||||
width: '148px',
|
||||
borderRadius: '5px'
|
||||
},
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
backgroundColor: 'var(--bg-primary)',
|
||||
color: '#fff',
|
||||
width: '148px',
|
||||
borderRadius: '5px',
|
||||
},
|
||||
|
||||
}}
|
||||
sx={{
|
||||
marginTop: '10px'
|
||||
}}
|
||||
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
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",
|
||||
fontWeight: 600,
|
||||
color: hasUnreadChat ? "var(--danger)" :"#fff"
|
||||
},
|
||||
}} primary="Chat" />
|
||||
<ListItemText
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
color: hasUnreadChat ? 'var(--danger)' : '#fff',
|
||||
},
|
||||
}}
|
||||
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",
|
||||
fontWeight: 600,
|
||||
color: hasUnreadAnnouncements ? "var(--danger)" :"#fff"
|
||||
},
|
||||
}} primary="Announcements" />
|
||||
<ListItemText
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
color: hasUnreadAnnouncements ? 'var(--danger)' : '#fff',
|
||||
},
|
||||
}}
|
||||
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",
|
||||
fontWeight: 600,
|
||||
},
|
||||
}} primary="Threads" />
|
||||
<ListItemText
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
},
|
||||
}}
|
||||
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",
|
||||
fontWeight: 600,
|
||||
},
|
||||
}} primary="Members" />
|
||||
<ListItemText
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
},
|
||||
}}
|
||||
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);
|
||||
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])
|
||||
|
||||
const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(()=> {
|
||||
if(isLoaded && checked1 && checked2) return true
|
||||
return false
|
||||
}, [checked1, isLoaded, checked2])
|
||||
|
||||
|
||||
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]);
|
||||
|
||||
const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(() => {
|
||||
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',
|
||||
@ -140,107 +144,104 @@ export const HomeDesktop = ({
|
||||
}}>
|
||||
<ListOfThreadPostsWatched />
|
||||
</Box> */}
|
||||
{hasDoneNameAndBalanceAndIsLoaded && (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: "330px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<GroupJoinRequests
|
||||
setGroupSection={setGroupSection}
|
||||
setSelectedGroup={setSelectedGroup}
|
||||
getTimestampEnterChat={getTimestampEnterChat}
|
||||
setOpenManageMembers={setOpenManageMembers}
|
||||
myAddress={myAddress}
|
||||
groups={groups}
|
||||
setMobileViewMode={setMobileViewMode}
|
||||
setDesktopViewMode={setDesktopViewMode}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: "330px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<GroupInvites
|
||||
setOpenAddGroup={setOpenAddGroup}
|
||||
myAddress={myAddress}
|
||||
groups={groups}
|
||||
setMobileViewMode={setMobileViewMode}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
{hasDoneNameAndBalanceAndIsLoaded && (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '330px',
|
||||
}}
|
||||
>
|
||||
<GroupJoinRequests
|
||||
setGroupSection={setGroupSection}
|
||||
setSelectedGroup={setSelectedGroup}
|
||||
getTimestampEnterChat={getTimestampEnterChat}
|
||||
setOpenManageMembers={setOpenManageMembers}
|
||||
myAddress={myAddress}
|
||||
groups={groups}
|
||||
setMobileViewMode={setMobileViewMode}
|
||||
setDesktopViewMode={setDesktopViewMode}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '330px',
|
||||
}}
|
||||
>
|
||||
<GroupInvites
|
||||
setOpenAddGroup={setOpenAddGroup}
|
||||
myAddress={myAddress}
|
||||
groups={groups}
|
||||
setMobileViewMode={setMobileViewMode}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<QortPrice />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
||||
{!isLoadingGroups && (
|
||||
<>
|
||||
<Spacer height="60px" />
|
||||
<Divider
|
||||
color="secondary"
|
||||
sx={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<ExploreIcon
|
||||
|
||||
<Divider
|
||||
color="secondary"
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>{" "}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
Explore
|
||||
</Typography>{" "}
|
||||
</Box>
|
||||
</Divider>
|
||||
{!hasDoneNameAndBalanceAndIsLoaded && (
|
||||
<Spacer height="40px" />
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{hasDoneNameAndBalanceAndIsLoaded && (
|
||||
<ListOfGroupPromotions />
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<ExploreIcon
|
||||
sx={{
|
||||
ccolor: theme.palette.text.primary,
|
||||
}}
|
||||
/>{' '}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
>
|
||||
{t('tutorial:initial.explore', { postProcess: 'capitalize' })}
|
||||
</Typography>{' '}
|
||||
</Box>
|
||||
</Divider>
|
||||
|
||||
)}
|
||||
|
||||
<Explore setDesktopViewMode={setDesktopViewMode} />
|
||||
</Box>
|
||||
|
||||
<NewUsersCTA balance={balance} />
|
||||
{!hasDoneNameAndBalanceAndIsLoaded && <Spacer height="40px" />}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '20px',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{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 = [];
|
||||
@ -20,14 +35,14 @@ const getNames = async (listOfMembers, includeNoNames) => {
|
||||
const name = await getNameInfo(member.offender);
|
||||
if (name) {
|
||||
members.push({ ...member, name });
|
||||
} else if(includeNoNames){
|
||||
members.push({ ...member, name: name || "" });
|
||||
} else if (includeNoNames) {
|
||||
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) {
|
||||
@ -67,33 +82,36 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||
setOpenPopoverIndex(null);
|
||||
};
|
||||
|
||||
const handleCancelBan = async (address)=> {
|
||||
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)
|
||||
new Promise((res, rej)=> {
|
||||
window.sendMessage("cancelBan", {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
message: 'Would you like to perform a CANCEL_GROUP_BAN transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoadingUnban(true);
|
||||
new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage('cancelBan', {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
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,24 +119,22 @@ 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];
|
||||
|
||||
|
||||
return (
|
||||
<CellMeasurer
|
||||
key={key}
|
||||
@ -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
|
||||
<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 = [];
|
||||
@ -20,21 +35,26 @@ const getNames = async (listOfMembers, includeNoNames) => {
|
||||
const name = await getNameInfo(member.invitee);
|
||||
if (name) {
|
||||
members.push({ ...member, name });
|
||||
} else if(includeNoNames){
|
||||
members.push({ ...member, name: name || "" });
|
||||
} else if (includeNoNames) {
|
||||
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) {
|
||||
@ -68,24 +88,27 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
|
||||
setOpenPopoverIndex(null);
|
||||
};
|
||||
|
||||
const handleCancelInvitation = async (address)=> {
|
||||
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)
|
||||
await new Promise((res, rej)=> {
|
||||
window.sendMessage("cancelInvitationToGroup", {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
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', {
|
||||
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,24 +125,22 @@ 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];
|
||||
|
||||
|
||||
return (
|
||||
<CellMeasurer
|
||||
key={key}
|
||||
@ -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
|
||||
<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 || "" });
|
||||
} else if(includeNoNames){
|
||||
members.push({ ...member, name: name || "" });
|
||||
members.push({ ...member, name: name || '' });
|
||||
} else if (includeNoNames) {
|
||||
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) {
|
||||
@ -69,31 +89,33 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
|
||||
setOpenPopoverIndex(null);
|
||||
};
|
||||
|
||||
const handleAcceptJoinRequest = async (address)=> {
|
||||
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)
|
||||
await new Promise((res, rej)=> {
|
||||
window.sendMessage("inviteToGroup", {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
inviteTime: 10800,
|
||||
})
|
||||
message: 'Would you like to perform a GROUP_INVITE transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoadingAccept(true);
|
||||
await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage('inviteToGroup', {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
inviteTime: 10800,
|
||||
})
|
||||
.then((response) => {
|
||||
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();
|
||||
res(response);
|
||||
|
||||
|
||||
setTxList((prev) => [
|
||||
{
|
||||
...response,
|
||||
@ -106,12 +128,12 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
|
||||
},
|
||||
...prev,
|
||||
]);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
<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", {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
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,38 +88,40 @@ 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", {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
rBanTime: 0,
|
||||
})
|
||||
window
|
||||
.sendMessage('banFromGroup', {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
rBanTime: 0,
|
||||
})
|
||||
.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", {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
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", {
|
||||
groupId,
|
||||
qortalAddress: address,
|
||||
})
|
||||
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={{
|
||||
color: 'white',
|
||||
marginLeft: 'auto'
|
||||
}}>Admin</Typography>
|
||||
)}
|
||||
<Typography
|
||||
sx={{
|
||||
color: 'white',
|
||||
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,240 +1,266 @@
|
||||
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,
|
||||
});
|
||||
fixedWidth: true,
|
||||
defaultHeight: 50,
|
||||
});
|
||||
|
||||
|
||||
|
||||
const getGroupInfo = async (groupId)=> {
|
||||
const getGroupInfo = async (groupId) => {
|
||||
const response = await fetch(`${getBaseApiReact()}/groups/` + groupId);
|
||||
const groupData = await response.json();
|
||||
|
||||
if (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;
|
||||
return groupData;
|
||||
}
|
||||
|
||||
export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
const {txList, setTxList, show} = useContext(MyContext)
|
||||
const [invites, setInvites] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to
|
||||
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
|
||||
const listRef = useRef();
|
||||
|
||||
const getRequests = async () => {
|
||||
try {
|
||||
const response = await fetch(`${getBaseApiReact()}/groups/invites/${myAddress}/?limit=0`);
|
||||
const inviteData = await response.json();
|
||||
|
||||
const resMoreData = await getGroupNames(inviteData)
|
||||
setInvites(resMoreData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
};
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
getRequests();
|
||||
|
||||
}, []);
|
||||
|
||||
const handlePopoverOpen = (event, index) => {
|
||||
setPopoverAnchor(event.currentTarget);
|
||||
setOpenPopoverIndex(index);
|
||||
};
|
||||
|
||||
const handlePopoverClose = () => {
|
||||
setPopoverAnchor(null);
|
||||
setOpenPopoverIndex(null);
|
||||
};
|
||||
|
||||
const handleJoinGroup = async (groupId, groupName)=> {
|
||||
try {
|
||||
|
||||
const fee = await getFee('JOIN_GROUP')
|
||||
await show({
|
||||
message: "Would you like to perform an JOIN_GROUP transaction?" ,
|
||||
publishFee: fee.fee + ' QORT'
|
||||
})
|
||||
}
|
||||
return groups;
|
||||
};
|
||||
|
||||
setIsLoading(true);
|
||||
export const UserListOfInvites = ({
|
||||
myAddress,
|
||||
setInfoSnack,
|
||||
setOpenSnack,
|
||||
}) => {
|
||||
const { txList, setTxList, show } = useContext(MyContext);
|
||||
const [invites, setInvites] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
await new Promise((res, rej)=> {
|
||||
window.sendMessage("joinGroup", {
|
||||
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to
|
||||
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
|
||||
const listRef = useRef();
|
||||
|
||||
const getRequests = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${getBaseApiReact()}/groups/invites/${myAddress}/?limit=0`
|
||||
);
|
||||
const inviteData = await response.json();
|
||||
|
||||
const resMoreData = await getGroupNames(inviteData);
|
||||
setInvites(resMoreData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getRequests();
|
||||
}, []);
|
||||
|
||||
const handlePopoverOpen = (event, index) => {
|
||||
setPopoverAnchor(event.currentTarget);
|
||||
setOpenPopoverIndex(index);
|
||||
};
|
||||
|
||||
const handlePopoverClose = () => {
|
||||
setPopoverAnchor(null);
|
||||
setOpenPopoverIndex(null);
|
||||
};
|
||||
|
||||
const handleJoinGroup = async (groupId, groupName) => {
|
||||
try {
|
||||
const fee = await getFee('JOIN_GROUP'); // TODO translate
|
||||
await show({
|
||||
message: 'Would you like to perform an JOIN_GROUP transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage('joinGroup', {
|
||||
groupId,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
setTxList((prev) => [
|
||||
{
|
||||
...response,
|
||||
type: 'joined-group',
|
||||
label: `Joined Group ${groupName}: awaiting confirmation`,
|
||||
labelDone: `Joined Group ${groupName}: success!`,
|
||||
done: false,
|
||||
groupId,
|
||||
},
|
||||
...prev,
|
||||
]);
|
||||
res(response);
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
message: "Successfully requested to join group. It may take a couple of minutes for the changes to propagate",
|
||||
});
|
||||
setOpenSnack(true);
|
||||
handlePopoverClose();
|
||||
return;
|
||||
}
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
setTxList((prev) => [
|
||||
{
|
||||
...response,
|
||||
type: 'joined-group',
|
||||
label: `Joined Group ${groupName}: awaiting confirmation`,
|
||||
labelDone: `Joined Group ${groupName}: success!`,
|
||||
done: false,
|
||||
groupId,
|
||||
},
|
||||
...prev,
|
||||
]);
|
||||
res(response);
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: response?.error,
|
||||
type: 'success',
|
||||
message:
|
||||
'Successfully requested to join group. It may take a couple of minutes for the changes to propagate',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error.message || "An error occurred",
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(error);
|
||||
handlePopoverClose();
|
||||
return;
|
||||
}
|
||||
setInfoSnack({
|
||||
type: 'error',
|
||||
message: response?.error,
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
}
|
||||
setOpenSnack(true);
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
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];
|
||||
|
||||
return (
|
||||
<CellMeasurer
|
||||
key={key}
|
||||
cache={cache}
|
||||
parent={parent}
|
||||
columnIndex={0}
|
||||
rowIndex={index}
|
||||
>
|
||||
{({ measure }) => (
|
||||
<div style={style} onLoad={measure}>
|
||||
<ListItem disablePadding>
|
||||
<Popover
|
||||
open={openPopoverIndex === index}
|
||||
anchorEl={popoverAnchor}
|
||||
onClose={handlePopoverClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "center",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "center",
|
||||
}}
|
||||
style={{ marginTop: "8px" }}
|
||||
>
|
||||
<Box
|
||||
};
|
||||
|
||||
const rowRenderer = ({ index, key, parent, style }) => {
|
||||
const invite = invites[index];
|
||||
|
||||
return (
|
||||
<CellMeasurer
|
||||
key={key}
|
||||
cache={cache}
|
||||
parent={parent}
|
||||
columnIndex={0}
|
||||
rowIndex={index}
|
||||
>
|
||||
{({ measure }) => (
|
||||
<div style={style} onLoad={measure}>
|
||||
<ListItem disablePadding>
|
||||
<Popover
|
||||
open={openPopoverIndex === index}
|
||||
anchorEl={popoverAnchor}
|
||||
onClose={handlePopoverClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
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
|
||||
<Typography>Join {invite?.groupName}</Typography>
|
||||
<LoadingButton
|
||||
loading={isLoading}
|
||||
loadingPosition="start"
|
||||
variant="contained" onClick={()=> handleJoinGroup(invite?.groupId, invite?.groupName)}>Join group</LoadingButton>
|
||||
</Box>
|
||||
</Popover>
|
||||
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
|
||||
variant="contained"
|
||||
onClick={() =>
|
||||
handleJoinGroup(invite?.groupId, invite?.groupName)
|
||||
}
|
||||
>
|
||||
Join group
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
</Popover>
|
||||
<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)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Spacer width="15px" />
|
||||
<ListItemText
|
||||
primary={invite?.groupName}
|
||||
secondary={invite?.description}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</div>
|
||||
)}
|
||||
{invite?.isOpen === true && (
|
||||
<NoEncryptionGmailerrorredIcon sx={{
|
||||
color: 'var(--danger)'
|
||||
}} />
|
||||
)}
|
||||
<Spacer width="15px" />
|
||||
<ListItemText primary={invite?.groupName} secondary={invite?.description} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</div>
|
||||
)}
|
||||
</CellMeasurer>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
</CellMeasurer>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1
|
||||
}}>
|
||||
<p>Invite list</p>
|
||||
<div
|
||||
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,
|
||||
}}
|
||||
>
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<List
|
||||
ref={listRef}
|
||||
width={width}
|
||||
height={height}
|
||||
rowCount={invites.length}
|
||||
rowHeight={cache.rowHeight}
|
||||
rowRenderer={rowRenderer}
|
||||
deferredMeasurementCache={cache}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<List
|
||||
ref={listRef}
|
||||
width={width}
|
||||
height={height}
|
||||
rowCount={invites.length}
|
||||
rowHeight={cache.rowHeight}
|
||||
rowRenderer={rowRenderer}
|
||||
deferredMeasurementCache={cache}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</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) {
|
||||
@ -53,10 +56,9 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
|
||||
const currentAddress = myAddress;
|
||||
|
||||
try {
|
||||
if(!initiateRef.current) {
|
||||
setIsLoadingGroups(true)
|
||||
pauseAllQueues()
|
||||
|
||||
if (!initiateRef.current) {
|
||||
setIsLoadingGroups(true);
|
||||
pauseAllQueues();
|
||||
}
|
||||
const socketLink = `${getBaseApiReactSocket()}/websockets/chat/active/${currentAddress}?encoding=BASE64&haschatreference=false`;
|
||||
socketRef.current = new WebSocket(socketLink);
|
||||
@ -71,34 +73,46 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
|
||||
clearTimeout(timeoutIdRef.current);
|
||||
groupSocketTimeoutRef.current = setTimeout(pingHeads, 45000); // Ping every 45 seconds
|
||||
} else {
|
||||
if(!initiateRef.current) {
|
||||
setIsLoadingGroups(false)
|
||||
initiateRef.current = true
|
||||
resumeAllQueues()
|
||||
|
||||
if (!initiateRef.current) {
|
||||
setIsLoadingGroups(false);
|
||||
initiateRef.current = true;
|
||||
resumeAllQueues();
|
||||
}
|
||||
const data = JSON.parse(e.data);
|
||||
const copyGroups = [...(data?.groups || [])]
|
||||
const findIndex = copyGroups?.findIndex(item => item?.groupId === 0)
|
||||
if(findIndex !== -1){
|
||||
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", {
|
||||
groups: sortedGroups,
|
||||
directs: sortedDirects,
|
||||
}).catch((error) => {
|
||||
console.error("Failed to handle active group data from socket:", error.message || "An error occurred");
|
||||
window
|
||||
.sendMessage('handleActiveGroupDataFromSocket', {
|
||||
groups: sortedGroups,
|
||||
directs: sortedDirects,
|
||||
})
|
||||
.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,9 +141,7 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
initWebsocketMessageGroup(); // Initialize WebSocket on component mount
|
||||
|
||||
initWebsocketMessageGroup(); // Initialize WebSocket on component mount
|
||||
|
||||
return () => {
|
||||
forceCloseWebSocket(); // Clean up WebSocket on component unmount
|
||||
|
@ -1,218 +1,194 @@
|
||||
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 getAllBlockedUsers = useCallback(()=> {
|
||||
const userBlockedRef = useRef({});
|
||||
const userNamesBlockedRef = useRef({});
|
||||
|
||||
const getAllBlockedUsers = useCallback(() => {
|
||||
return {
|
||||
names: userNamesBlockedRef.current,
|
||||
addresses: userBlockedRef.current
|
||||
}
|
||||
}, [])
|
||||
addresses: userBlockedRef.current,
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isUserBlocked = useCallback((address, name)=> {
|
||||
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
|
||||
//error
|
||||
}
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
useEffect(()=> {
|
||||
const fetchBlockedList = async ()=> {
|
||||
useEffect(() => {
|
||||
const fetchBlockedList = async () => {
|
||||
try {
|
||||
const response = await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
const response = await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'get',
|
||||
listName: `blockedAddresses`,
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
});
|
||||
})
|
||||
const blockedUsers = {}
|
||||
response?.forEach((item)=> {
|
||||
blockedUsers[item] = true
|
||||
})
|
||||
userBlockedRef.current = blockedUsers
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
});
|
||||
const blockedUsers = {};
|
||||
response?.forEach((item) => {
|
||||
blockedUsers[item] = true;
|
||||
});
|
||||
userBlockedRef.current = blockedUsers;
|
||||
|
||||
const response2 = await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
const response2 = await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'get',
|
||||
listName: `blockedNames`,
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
});
|
||||
const blockedUsers2 = {};
|
||||
response2?.forEach((item) => {
|
||||
blockedUsers2[item] = true;
|
||||
});
|
||||
userNamesBlockedRef.current = blockedUsers2;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
fetchBlockedList();
|
||||
}, []);
|
||||
|
||||
const removeBlockFromList = useCallback(async (address, name) => {
|
||||
if (name) {
|
||||
await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'remove',
|
||||
items: [name],
|
||||
listName: 'blockedNames',
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
const copyObject = { ...userNamesBlockedRef.current };
|
||||
delete copyObject[name];
|
||||
userNamesBlockedRef.current = copyObject;
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
})
|
||||
const blockedUsers2 = {}
|
||||
response2?.forEach((item)=> {
|
||||
blockedUsers2[item] = true
|
||||
})
|
||||
userNamesBlockedRef.current = blockedUsers2
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
fetchBlockedList()
|
||||
}, [])
|
||||
|
||||
const removeBlockFromList = useCallback(async (address, name)=> {
|
||||
if(name){
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
type: 'remove',
|
||||
items: [name] ,
|
||||
listName: 'blockedNames'
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
|
||||
const copyObject = {...userNamesBlockedRef.current}
|
||||
delete copyObject[name]
|
||||
userNamesBlockedRef.current = copyObject
|
||||
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if(address){
|
||||
if (address) {
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'remove',
|
||||
items: [address],
|
||||
listName: 'blockedAddresses'
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
|
||||
const copyObject = {...userBlockedRef.current}
|
||||
delete copyObject[address]
|
||||
userBlockedRef.current = copyObject
|
||||
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}, [])
|
||||
listName: 'blockedAddresses',
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
const copyObject = { ...userBlockedRef.current };
|
||||
delete copyObject[address];
|
||||
userBlockedRef.current = copyObject;
|
||||
|
||||
const addToBlockList = useCallback(async (address, name)=> {
|
||||
if(name){
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((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'
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
const copyObject = {...userNamesBlockedRef.current}
|
||||
copyObject[name] = true
|
||||
userNamesBlockedRef.current = copyObject
|
||||
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
});
|
||||
})
|
||||
listName: 'blockedNames',
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
const copyObject = { ...userNamesBlockedRef.current };
|
||||
copyObject[name] = true;
|
||||
userNamesBlockedRef.current = copyObject;
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
});
|
||||
}
|
||||
if(address){
|
||||
if (address) {
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
window
|
||||
.sendMessage('listActions', {
|
||||
type: 'add',
|
||||
items: [address],
|
||||
listName: 'blockedAddresses'
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
|
||||
const copyObject = {...userBlockedRef.current}
|
||||
copyObject[address] = true
|
||||
userBlockedRef.current = copyObject
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
});
|
||||
})
|
||||
listName: 'blockedAddresses',
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
const copyObject = { ...userBlockedRef.current };
|
||||
copyObject[address] = true;
|
||||
userBlockedRef.current = copyObject;
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((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)=> {
|
||||
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");
|
||||
}
|
||||
const data = await response.json();
|
||||
userInfoRef.current = {
|
||||
...userInfoRef.current,
|
||||
[address]: data?.level
|
||||
}
|
||||
return data?.level
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error('network error');
|
||||
}
|
||||
const data = await response.json();
|
||||
userInfoRef.current = {
|
||||
...userInfoRef.current,
|
||||
[address]: data?.level,
|
||||
};
|
||||
return data?.level;
|
||||
} catch (error) {
|
||||
//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
|
||||
@ -197,60 +199,58 @@ export const QortPrice = () => {
|
||||
)}
|
||||
</Box>
|
||||
<Tooltip
|
||||
title={
|
||||
<span style={{ color: "white", fontSize: "14px", fontWeight: 700 }}>
|
||||
{lastBlock?.timestamp && formatDate(lastBlock?.timestamp)}
|
||||
</span>
|
||||
}
|
||||
placement="bottom"
|
||||
arrow
|
||||
sx={{ fontSize: "24" }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#444444",
|
||||
},
|
||||
title={
|
||||
<span style={{ color: 'white', fontSize: '14px', fontWeight: 700 }}>
|
||||
{lastBlock?.timestamp && formatDate(lastBlock?.timestamp)}
|
||||
</span>
|
||||
}
|
||||
placement="bottom"
|
||||
arrow
|
||||
sx={{ fontSize: '24' }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: "#444444",
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "322px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: "10px",
|
||||
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
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>
|
||||
</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