From efdbb4788b274052e21ea96b6138935d44ae5f2f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 00:56:13 +0800 Subject: [PATCH 01/51] chore(main): release 1.1.4 (#1859) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..7cb7467187 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,26 @@ +# Changelog + +## [1.2.0](https://github.com/paritytech/polkadot-staking-dashboard/compare/v1.1.3...v1.2.0) (2024-01-28) + + +### Features + +* **ci:** add markdown-link-check ([#1865](https://github.com/paritytech/polkadot-staking-dashboard/issues/1865)) ([7e7134e](https://github.com/paritytech/polkadot-staking-dashboard/commit/7e7134ea2a42ec56d095e0809a4eaa05f94ad793)) +* **fix:** account for staking rate in average reward rate ([#1886](https://github.com/paritytech/polkadot-staking-dashboard/issues/1886)) ([9938620](https://github.com/paritytech/polkadot-staking-dashboard/commit/9938620ee417bd42761a6e63828e3dd3ab8e7ee2)) +* **refacor:** Pool config to bootstrap app state ([#1900](https://github.com/paritytech/polkadot-staking-dashboard/issues/1900)) ([8f51a86](https://github.com/paritytech/polkadot-staking-dashboard/commit/8f51a8672b843b7cd7172144e2157148d03325ca)) +* **refactor:** Move balance syncing to static class, `activeBalances` in UI only. ([#1858](https://github.com/paritytech/polkadot-staking-dashboard/issues/1858)) ([a372487](https://github.com/paritytech/polkadot-staking-dashboard/commit/a3724879f377c38baf0e9b04e03749e0e2f65ee0)) +* **refactor:** Move Polkawatch to inline instantiation ([#1890](https://github.com/paritytech/polkadot-staking-dashboard/issues/1890)) ([d7b88bd](https://github.com/paritytech/polkadot-staking-dashboard/commit/d7b88bd57701c16ad143a6e2b70897b5086f82f6)) +* **refactor:** Move staking metrics `payee` to active balances ([#1904](https://github.com/paritytech/polkadot-staking-dashboard/issues/1904)) ([7b692e0](https://github.com/paritytech/polkadot-staking-dashboard/commit/7b692e06006448604929ec23fe510a7c7492141b)) +* **refactor:** network metrics to static, bootstrap all state before subscribe ([#1896](https://github.com/paritytech/polkadot-staking-dashboard/issues/1896)) ([08a813c](https://github.com/paritytech/polkadot-staking-dashboard/commit/08a813c7d47d2055984f7d32c1d8b34bec9791f4)) +* **refactor:** remove unneeded bonded getters ([7108e55](https://github.com/paritytech/polkadot-staking-dashboard/commit/7108e55d3f1aeeb07cdaa7cdb5608425b0d6641a)) +* **refactor:** rm `ExtrinsicsProvider`, nonces to `TxMeta` ([4194150](https://github.com/paritytech/polkadot-staking-dashboard/commit/41941509cb0d930d53f3fc98a12736b0356c6846)) +* **refactor:** Staking metrics to bootstrap and API ([#1905](https://github.com/paritytech/polkadot-staking-dashboard/issues/1905)) ([f6c0b93](https://github.com/paritytech/polkadot-staking-dashboard/commit/f6c0b93fc5fcaf2961c91cdeae770d5503766fe8)) +* **refactor:** Subscan refactor, remove fetching from Providers ([#1878](https://github.com/paritytech/polkadot-staking-dashboard/issues/1878)) ([57e2a1b](https://github.com/paritytech/polkadot-staking-dashboard/commit/57e2a1bed952b41441635dc9eba02b79d63d3fc0)) + + +### Bug Fixes + +* balances no accounts sync ([#1889](https://github.com/paritytech/polkadot-staking-dashboard/issues/1889)) ([5316283](https://github.com/paritytech/polkadot-staking-dashboard/commit/5316283029f9a28003bb25814623f768f3294fb8)) +* Render safe guards, pool useEffect fixes ([#1906](https://github.com/paritytech/polkadot-staking-dashboard/issues/1906)) ([7c0212f](https://github.com/paritytech/polkadot-staking-dashboard/commit/7c0212f428e56b2eba9c735a6299c3661dd47b25)) +* roll back Substrate Connect ([#1899](https://github.com/paritytech/polkadot-staking-dashboard/issues/1899)) ([33b5671](https://github.com/paritytech/polkadot-staking-dashboard/commit/33b5671caf5ec09240e164e82618efb15deebe81)) +* unlock Chunk unit type ([afe9b1c](https://github.com/paritytech/polkadot-staking-dashboard/commit/afe9b1c0256eb9840a375f1d7574895a07ba0f4a)) From 633abe54f11465dd0292dfb9624ae79b064f3738 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:54:05 +0000 Subject: [PATCH 02/51] chore(deps): bump i18next from 23.7.20 to 23.8.1 (#1907) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 68bd35c10e..4aef006cad 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "date-fns": "^3.3.1", "framer-motion": "^11.0.3", "html5-qrcode": "^2.3.8", - "i18next": "^23.7.20", + "i18next": "^23.8.1", "i18next-browser-languagedetector": "^7.2.0", "lodash.throttle": "^4.1.1", "qrcode-generator": "1.4.4", diff --git a/yarn.lock b/yarn.lock index 43401fcc94..99ec1ae4d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4830,12 +4830,12 @@ __metadata: languageName: node linkType: hard -"i18next@npm:^23.7.20": - version: 23.7.20 - resolution: "i18next@npm:23.7.20" +"i18next@npm:^23.8.1": + version: 23.8.1 + resolution: "i18next@npm:23.8.1" dependencies: "@babel/runtime": "npm:^7.23.2" - checksum: 95460fff030860af7b5c65485c966fc5894372bff09f0f2f6b3fb72f053763d0b253aba977f52ddf7e32f763bf463efcf025da17a108cb1010b85fe3bd278cbd + checksum: f74ca2ead03e71bc8ca353c89e842b75042a52b52dcc5dfc14092469dcbc5d61f220fef536dfa95520de42baad0caa86c7a8abc1313a057ece9ba08ad8598bbf languageName: node linkType: hard @@ -6349,7 +6349,7 @@ __metadata: framer-motion: "npm:^11.0.3" gh-pages: "npm:^6.1.1" html5-qrcode: "npm:^2.3.8" - i18next: "npm:^23.7.20" + i18next: "npm:^23.8.1" i18next-browser-languagedetector: "npm:^7.2.0" lodash.throttle: "npm:^4.1.1" prettier: "npm:^3.2.4" From 04d3f73e75c48c1ababd51a9e8e8fa1c232fdd24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:54:36 +0000 Subject: [PATCH 03/51] chore(deps-dev): bump @typescript-eslint/eslint-plugin from 6.19.1 to 6.20.0 (#1908) --- package.json | 2 +- yarn.lock | 90 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 4aef006cad..f797cceac8 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@types/react-helmet": "^6.1.11", "@types/react-scroll": "^1.8.10", "@types/styled-components": "^5.1.34", - "@typescript-eslint/eslint-plugin": "^6.19.1", + "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.19.1", "@vitejs/plugin-react-swc": "^3.5.0", "eslint": "^8.56.0", diff --git a/yarn.lock b/yarn.lock index 99ec1ae4d3..b60317a557 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2290,15 +2290,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/eslint-plugin@npm:6.19.1" +"@typescript-eslint/eslint-plugin@npm:^6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/eslint-plugin@npm:6.20.0" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.19.1" - "@typescript-eslint/type-utils": "npm:6.19.1" - "@typescript-eslint/utils": "npm:6.19.1" - "@typescript-eslint/visitor-keys": "npm:6.19.1" + "@typescript-eslint/scope-manager": "npm:6.20.0" + "@typescript-eslint/type-utils": "npm:6.20.0" + "@typescript-eslint/utils": "npm:6.20.0" + "@typescript-eslint/visitor-keys": "npm:6.20.0" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -2311,7 +2311,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 01f1d643219b51bfad76734c6eb19a480309f0e66877ddc00bca89368e5aee3eb907e366977a8d11094c49e807773f5cfba306c861cd690d70044a7925173823 + checksum: 5020faac39be476de056342f58f2bf68bb788f230e2fa4a2e27ceab8a5187dc450beba7333b0aa741a43aeaff45a117558132953f9390b5eca4c2cc004fde716 languageName: node linkType: hard @@ -2343,12 +2343,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/type-utils@npm:6.19.1" +"@typescript-eslint/scope-manager@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/scope-manager@npm:6.20.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.19.1" - "@typescript-eslint/utils": "npm:6.19.1" + "@typescript-eslint/types": "npm:6.20.0" + "@typescript-eslint/visitor-keys": "npm:6.20.0" + checksum: f6768ed2dcd2d1771d55ed567ff392a6569ffd683a26500067509dd41769f8838c43686460fe7337144f324fd063df33f5d5646d44e5df4998ceffb3ad1fb790 + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/type-utils@npm:6.20.0" + dependencies: + "@typescript-eslint/typescript-estree": "npm:6.20.0" + "@typescript-eslint/utils": "npm:6.20.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -2356,7 +2366,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 78c185c64a8c92d7b5f2132ef4880b974a2e07e9ae7913ad53e327972af540a8a8bf75bc319c8aaa82445615e2680f3c85736ee67aa174a5ba91798fe5068f95 + checksum: 8f622fbb14268f1d00b2948f995b570f0ef82be02c12be41d90385290a56ea0dbd34d855d6a5aff100b57f3bdd300ff0c300f16c78f12d6064f7ae6e34fd71bf languageName: node linkType: hard @@ -2367,6 +2377,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/types@npm:6.20.0" + checksum: 37589003b0e06f83c1945e3748e91af85918cfd997766894642a08e6f355f611cfe11df4e7632dda96e3a9b3441406283fe834ab0906cf81ea97fd43ca2aebe3 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:6.19.1": version: 6.19.1 resolution: "@typescript-eslint/typescript-estree@npm:6.19.1" @@ -2386,20 +2403,39 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/utils@npm:6.19.1" +"@typescript-eslint/typescript-estree@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/typescript-estree@npm:6.20.0" + dependencies: + "@typescript-eslint/types": "npm:6.20.0" + "@typescript-eslint/visitor-keys": "npm:6.20.0" + debug: "npm:^4.3.4" + globby: "npm:^11.1.0" + is-glob: "npm:^4.0.3" + minimatch: "npm:9.0.3" + semver: "npm:^7.5.4" + ts-api-utils: "npm:^1.0.1" + peerDependenciesMeta: + typescript: + optional: true + checksum: 551f13445a303882d9fc0fbe14ef8507eb8414253fd87a5f13d2e324b5280b626421a238b8ec038e628bc80128dc06c057757f668738e82e64d5b39a9083c27d + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/utils@npm:6.20.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.19.1" - "@typescript-eslint/types": "npm:6.19.1" - "@typescript-eslint/typescript-estree": "npm:6.19.1" + "@typescript-eslint/scope-manager": "npm:6.20.0" + "@typescript-eslint/types": "npm:6.20.0" + "@typescript-eslint/typescript-estree": "npm:6.20.0" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 5fa58a32722e9915bfe8433fda2f46be894352549e8406acc4e29a04a8ddb0ea5988fddda2a3145f8952129a267cb51b666206b30489d2ff36b7911f540f1d57 + checksum: 0a8ede3d80a365b52ae96d88e4a9f6e6abf3569c6b60ff9f42ff900cd843ae7c5493cd95f8f2029d90bb0acbf31030980206af98e581d760d6d41e0f80e9fb86 languageName: node linkType: hard @@ -2413,6 +2449,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/visitor-keys@npm:6.20.0" + dependencies: + "@typescript-eslint/types": "npm:6.20.0" + eslint-visitor-keys: "npm:^3.4.1" + checksum: 852d938f2e5d57200cf62733b42e73a369f797b097d17e8fd3fffd0f7315c3b9e1863eed60bb8d57d6535a3b7f1980f645f96ec6d513950f182bfa8107b33fab + languageName: node + linkType: hard + "@ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" @@ -6326,7 +6372,7 @@ __metadata: "@types/react-helmet": "npm:^6.1.11" "@types/react-scroll": "npm:^1.8.10" "@types/styled-components": "npm:^5.1.34" - "@typescript-eslint/eslint-plugin": "npm:^6.19.1" + "@typescript-eslint/eslint-plugin": "npm:^6.20.0" "@typescript-eslint/parser": "npm:^6.19.1" "@vitejs/plugin-react-swc": "npm:^3.5.0" "@zondax/ledger-substrate": "npm:^0.41.3" From 9010e28f960042400d48b30d396f730512c6d272 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:58:44 +0000 Subject: [PATCH 04/51] chore(deps-dev): bump @typescript-eslint/parser from 6.19.1 to 6.20.0 (#1909) --- package.json | 2 +- yarn.lock | 66 ++++++++-------------------------------------------- 2 files changed, 11 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index f797cceac8..622b4a9bb5 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "@types/react-scroll": "^1.8.10", "@types/styled-components": "^5.1.34", "@typescript-eslint/eslint-plugin": "^6.20.0", - "@typescript-eslint/parser": "^6.19.1", + "@typescript-eslint/parser": "^6.20.0", "@vitejs/plugin-react-swc": "^3.5.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", diff --git a/yarn.lock b/yarn.lock index b60317a557..addb279af2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2315,31 +2315,21 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/parser@npm:6.19.1" - dependencies: - "@typescript-eslint/scope-manager": "npm:6.19.1" - "@typescript-eslint/types": "npm:6.19.1" - "@typescript-eslint/typescript-estree": "npm:6.19.1" - "@typescript-eslint/visitor-keys": "npm:6.19.1" +"@typescript-eslint/parser@npm:^6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/parser@npm:6.20.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:6.20.0" + "@typescript-eslint/types": "npm:6.20.0" + "@typescript-eslint/typescript-estree": "npm:6.20.0" + "@typescript-eslint/visitor-keys": "npm:6.20.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 442e860fbc4786fe999205528cc74b31d933008e170a707ddaec0c9e2c374f62c36c8d05d3dd446c9ceb802f2b403806d72c78ffd97867cf1672028b754b6262 - languageName: node - linkType: hard - -"@typescript-eslint/scope-manager@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/scope-manager@npm:6.19.1" - dependencies: - "@typescript-eslint/types": "npm:6.19.1" - "@typescript-eslint/visitor-keys": "npm:6.19.1" - checksum: a81315b4a2888343d3be781fe8d6b4c229c656d7bf1bd74bc44a89bba96bb6a10a0319d301f24ca91adb898374eaadbd38979e6567ac9085b5d7076163794281 + checksum: d84ad5e2282b1096c80dedb903c83ecc31eaf7be1aafcb14c18d9ec2d4a319f2fd1e5a9038b944d9f42c36c1c57add5e4292d4026ca7d3d5441d41286700d402 languageName: node linkType: hard @@ -2370,13 +2360,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/types@npm:6.19.1" - checksum: b8f75df157ca383e5bd6c07276fbeed6ff775e1354260a1653777749c0d71626fb29be5d36c9570e2c5cfaa5db62deaae20aa4be8a2d7d753782ab66d88e007f - languageName: node - linkType: hard - "@typescript-eslint/types@npm:6.20.0": version: 6.20.0 resolution: "@typescript-eslint/types@npm:6.20.0" @@ -2384,25 +2367,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/typescript-estree@npm:6.19.1" - dependencies: - "@typescript-eslint/types": "npm:6.19.1" - "@typescript-eslint/visitor-keys": "npm:6.19.1" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - minimatch: "npm:9.0.3" - semver: "npm:^7.5.4" - ts-api-utils: "npm:^1.0.1" - peerDependenciesMeta: - typescript: - optional: true - checksum: dec16f873084e9eeb1a696dff82c42164e75908221f7868d900ad7b7fcec6fc62a9a7dddb8bc17c78c19bf35f07acee81b3778b20b9735ffdaeee732ecb643d3 - languageName: node - linkType: hard - "@typescript-eslint/typescript-estree@npm:6.20.0": version: 6.20.0 resolution: "@typescript-eslint/typescript-estree@npm:6.20.0" @@ -2439,16 +2403,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/visitor-keys@npm:6.19.1" - dependencies: - "@typescript-eslint/types": "npm:6.19.1" - eslint-visitor-keys: "npm:^3.4.1" - checksum: b0370a9bc6fd8d243aa8b7ccd1657ec2fbd25ceb7b067aac64322f03aa0f64b97444b13b0946f52a53d6bc5edd43e0b447f72160be4a5b72e073c1d3679b6b4c - languageName: node - linkType: hard - "@typescript-eslint/visitor-keys@npm:6.20.0": version: 6.20.0 resolution: "@typescript-eslint/visitor-keys@npm:6.20.0" @@ -6373,7 +6327,7 @@ __metadata: "@types/react-scroll": "npm:^1.8.10" "@types/styled-components": "npm:^5.1.34" "@typescript-eslint/eslint-plugin": "npm:^6.20.0" - "@typescript-eslint/parser": "npm:^6.19.1" + "@typescript-eslint/parser": "npm:^6.20.0" "@vitejs/plugin-react-swc": "npm:^3.5.0" "@zondax/ledger-substrate": "npm:^0.41.3" bignumber.js: "npm:^9.1.2" From 525659116d105214d5d5f620d38396a923dd742f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 21:18:55 +0000 Subject: [PATCH 05/51] chore(deps-dev): bump vite-bundle-visualizer from 1.0.0 to 1.0.1 (#1910) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 622b4a9bb5..3a5378be6a 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "sass": "^1.70.0", "typescript": "^5.3.3", "vite": "^5.0.12", - "vite-bundle-visualizer": "^1.0.0", + "vite-bundle-visualizer": "^1.0.1", "vite-plugin-checker": "^0.6.2", "vite-plugin-eslint": "^1.8.1", "vite-plugin-svgr": "^4.2.0", diff --git a/yarn.lock b/yarn.lock index addb279af2..cbf8db5a1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6369,7 +6369,7 @@ __metadata: typescript: "npm:^5.3.3" usehooks-ts: "npm:2.10.0" vite: "npm:^5.0.12" - vite-bundle-visualizer: "npm:^1.0.0" + vite-bundle-visualizer: "npm:^1.0.1" vite-plugin-checker: "npm:^0.6.2" vite-plugin-eslint: "npm:^1.8.1" vite-plugin-svgr: "npm:^4.2.0" @@ -8032,9 +8032,9 @@ __metadata: languageName: node linkType: hard -"vite-bundle-visualizer@npm:^1.0.0": - version: 1.0.0 - resolution: "vite-bundle-visualizer@npm:1.0.0" +"vite-bundle-visualizer@npm:^1.0.1": + version: 1.0.1 + resolution: "vite-bundle-visualizer@npm:1.0.1" dependencies: cac: "npm:^6.7.14" import-from-esm: "npm:^1.3.3" @@ -8042,7 +8042,7 @@ __metadata: tmp: "npm:^0.2.1" bin: vite-bundle-visualizer: bin.js - checksum: 9113c78bf47bcc673cdf8487c2023a2679d8ec78c4fff2a4c6be83bbe0f3596b1fa5c7fdf4fe7b9abd487cf8b4cf10dbd956183da019de4b72ce9a94385225a6 + checksum: 686585028eef05d9134c86cdc5b98e8dc792e10340f6ea3cf1263bdf5b16c3fc29189d6291a49e921696f052155e1de97b7ba3452716965adbc55e25b62b584a languageName: node linkType: hard From c453dd1e07016b46425938a26615138d361819de Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 31 Jan 2024 09:48:27 +0800 Subject: [PATCH 06/51] fix: stricter member count fetching (#1911) --- src/contexts/Pools/ActivePools/index.tsx | 29 ++++++++++++++---------- src/static/APIController/index.ts | 8 +++---- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/contexts/Pools/ActivePools/index.tsx b/src/contexts/Pools/ActivePools/index.tsx index d78ea15b81..2fc96d62e8 100644 --- a/src/contexts/Pools/ActivePools/index.tsx +++ b/src/contexts/Pools/ActivePools/index.tsx @@ -42,8 +42,8 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { const { membership } = usePoolMemberships(); const { createAccounts } = usePoolsConfig(); const { activeAccount } = useActiveAccounts(); + const { getMembersOfPoolFromNode } = usePoolMembers(); const { getAccountPools, bondedPools } = useBondedPools(); - const { getMembersOfPoolFromNode, poolMembersNode } = usePoolMembers(); // Determine active pools to subscribe to. const accountPools = useMemo(() => { @@ -80,7 +80,7 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { const [selectedPoolMemberCount, setSelectedPoolMemberCount] = useState(0); - const fetchingMemberCount = useRef(false); + const fetchingMemberCount = useRef('unsynced'); // Store whether active pool data has been synced. this will be true if no active pool exists for // the active account. We just need confirmation this is the case. @@ -91,8 +91,8 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { // present). const [selectedPoolId, setSelectedPoolId] = useState(null); + // Get the `activePool` of the active account. const getActivePoolMembership = () => - // get the activePool that the active account activePoolsRef.current.find((a) => { const p = membership?.poolId ? String(membership.poolId) : '0'; return String(a.id) === p; @@ -491,12 +491,15 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { return; } // If `Subscan` plugin is enabled, fetch member count directly from the API. - if (pluginEnabled('subscan') && !fetchingMemberCount.current) { - fetchingMemberCount.current = true; + if ( + pluginEnabled('subscan') && + fetchingMemberCount.current === 'unsynced' + ) { + fetchingMemberCount.current = 'syncing'; const poolDetails = await SubscanController.handleFetchPoolDetails( selectedActivePool.id ); - fetchingMemberCount.current = false; + fetchingMemberCount.current = 'synced'; setSelectedPoolMemberCount(poolDetails?.member_count || 0); return; } @@ -507,16 +510,15 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { ); }; - // Re-sync when number of accountRoles change. This can happen when bondedPools sync, when roles - // are edited within the dashboard, or when pool membership changes. + // Re-sync when number of `accountRoles` change. This can happen when `bondedPools` sync, when + // roles are edited within the dashboard, or when pool membership changes. useEffectIgnoreInitial(() => { unsubscribeActivePools(); unsubscribePoolNominations(); setStateWithRef('unsynced', setSynced, syncedRef); }, [activeAccount, accountPools.length]); - // when we are subscribed to all active pools, syncing is considered - // completed. + // When we are subscribed to all active pools, syncing is considered completed. useEffectIgnoreInitial(() => { if (unsubNominations.current.length === accountPools.length) { setStateWithRef('synced', setSynced, syncedRef); @@ -524,10 +526,13 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { }, [accountPools, unsubNominations.current]); // Fetch pool member count. We use `membership` as a dependency as the member count could change - // in the UI when active account's membership changes. + // in the UI when active account's membership changes. NOTE: Do not have `poolMembersNode` as a + // dependency - could trigger many re-renders if value is constantly changing - more suited as a + // custom event. useEffect(() => { + fetchingMemberCount.current = 'unsynced'; getMemberCount(); - }, [activeAccount, getSelectedActivePool(), membership, poolMembersNode]); + }, [activeAccount, getSelectedActivePool()?.id, membership?.poolId]); // unsubscribe all on component unmount. useEffect( diff --git a/src/static/APIController/index.ts b/src/static/APIController/index.ts index c617970ede..2e45a9d9c0 100644 --- a/src/static/APIController/index.ts +++ b/src/static/APIController/index.ts @@ -647,11 +647,9 @@ export class APIController { await this.disconnect(); } else { // Update block number verification data. - this._blockNumberVerify.minBlockNumber = String( - new BigNumber(this._blockNumber).plus( - this.MIN_EXPECTED_BLOCKS_PER_VERIFY - ) - ).toString(); + this._blockNumberVerify.minBlockNumber = new BigNumber(this._blockNumber) + .plus(this.MIN_EXPECTED_BLOCKS_PER_VERIFY) + .toString(); } }; From 42c0638f464662e741085966a886bb2fdf00c4b2 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 31 Jan 2024 10:12:22 +0800 Subject: [PATCH 07/51] feat(refactor): create pool accounts to hooks (#1913) --- src/Providers.tsx | 5 +- src/contexts/Pools/ActivePools/index.tsx | 6 +- src/contexts/Pools/BondedPools/index.tsx | 8 +- .../defaults.ts | 9 +- src/contexts/Pools/FavoritePools/index.tsx | 69 ++++++++++++ src/contexts/Pools/FavoritePools/types.ts | 8 ++ src/contexts/Pools/PoolsConfig/index.tsx | 103 ------------------ src/contexts/Pools/PoolsConfig/types.ts | 11 -- src/contexts/Validators/types.ts | 4 +- .../Hooks/useCreatePoolAccounts/index.tsx | 42 +++++++ src/library/ListItem/Labels/FavoritePool.tsx | 4 +- src/modals/UnlockChunks/Forms.tsx | 8 +- src/pages/Pools/Home/Favorites/index.tsx | 4 +- src/pages/Pools/Home/index.tsx | 4 +- 14 files changed, 144 insertions(+), 141 deletions(-) rename src/contexts/Pools/{PoolsConfig => FavoritePools}/defaults.ts (63%) create mode 100644 src/contexts/Pools/FavoritePools/index.tsx create mode 100644 src/contexts/Pools/FavoritePools/types.ts delete mode 100644 src/contexts/Pools/PoolsConfig/index.tsx delete mode 100644 src/contexts/Pools/PoolsConfig/types.ts create mode 100644 src/library/Hooks/useCreatePoolAccounts/index.tsx diff --git a/src/Providers.tsx b/src/Providers.tsx index 1088748b67..6bc3ef2378 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -23,7 +23,7 @@ import { ActivePoolsProvider } from 'contexts/Pools/ActivePools'; import { BondedPoolsProvider } from 'contexts/Pools/BondedPools'; import { PoolMembersProvider } from 'contexts/Pools/PoolMembers'; import { PoolMembershipsProvider } from 'contexts/Pools/PoolMemberships'; -import { PoolsConfigProvider } from 'contexts/Pools/PoolsConfig'; +import { FavoritePoolsProvider } from 'contexts/Pools/FavoritePools'; import { ProxiesProvider } from 'contexts/Proxies'; import { SetupProvider } from 'contexts/Setup'; import { StakingProvider } from 'contexts/Staking'; @@ -47,7 +47,6 @@ import { ImportedAccountsProvider } from 'contexts/Connect/ImportedAccounts'; import { PoolPerformanceProvider } from 'contexts/Pools/PoolPerformance'; import { ExternalAccountsProvider } from 'contexts/Connect/ExternalAccounts'; -// Embed providers from hook. export const Providers = () => { const { network, @@ -76,7 +75,7 @@ export const Providers = () => { BondedProvider, BalancesProvider, StakingProvider, - PoolsConfigProvider, + FavoritePoolsProvider, BondedPoolsProvider, PoolMembershipsProvider, PoolMembersProvider, diff --git a/src/contexts/Pools/ActivePools/index.tsx b/src/contexts/Pools/ActivePools/index.tsx index 2fc96d62e8..eb7e5f5a83 100644 --- a/src/contexts/Pools/ActivePools/index.tsx +++ b/src/contexts/Pools/ActivePools/index.tsx @@ -21,12 +21,12 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useApi } from '../../Api'; import { useBondedPools } from '../BondedPools'; import { usePoolMemberships } from '../PoolMemberships'; -import { usePoolsConfig } from '../PoolsConfig'; import * as defaults from './defaults'; import { usePoolMembers } from '../PoolMembers'; import type { ActivePool, ActivePoolsContextState, PoolTargets } from './types'; import type { PoolAddresses } from '../BondedPools/types'; import { SubscanController } from 'static/SubscanController'; +import { useCreatePoolAccounts } from 'library/Hooks/useCreatePoolAccounts'; export const ActivePoolsContext = createContext( defaults.defaultActivePoolContext @@ -40,8 +40,8 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { const { eraStakers } = useStaking(); const { pluginEnabled } = usePlugins(); const { membership } = usePoolMemberships(); - const { createAccounts } = usePoolsConfig(); const { activeAccount } = useActiveAccounts(); + const createPoolAccounts = useCreatePoolAccounts(); const { getMembersOfPoolFromNode } = usePoolMembers(); const { getAccountPools, bondedPools } = useBondedPools(); @@ -155,7 +155,7 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { return; } - const addresses: PoolAddresses = createAccounts(poolId); + const addresses: PoolAddresses = createPoolAccounts(poolId); // new active pool subscription const subscribeActivePool = async (id: number) => { diff --git a/src/contexts/Pools/BondedPools/index.tsx b/src/contexts/Pools/BondedPools/index.tsx index 8b6ac450f3..63875cc0c4 100644 --- a/src/contexts/Pools/BondedPools/index.tsx +++ b/src/contexts/Pools/BondedPools/index.tsx @@ -18,8 +18,8 @@ import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import type { AnyJson } from '@polkadot-cloud/react/types'; import { useApi } from '../../Api'; -import { usePoolsConfig } from '../PoolsConfig'; import { defaultBondedPoolsContext } from './defaults'; +import { useCreatePoolAccounts } from 'library/Hooks/useCreatePoolAccounts'; export const BondedPoolsContext = createContext( defaultBondedPoolsContext @@ -35,7 +35,7 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { activeEra, poolsConfig: { lastPoolId }, } = useApi(); - const { createAccounts } = usePoolsConfig(); + const createPoolAccounts = useCreatePoolAccounts(); const { getNominationsStatusFromTargets } = useStaking(); // Store bonded pools. @@ -135,7 +135,7 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { } return { id, - addresses: createAccounts(id), + addresses: createPoolAccounts(id), ...bondedPool, }; }; @@ -192,7 +192,7 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { const getPoolWithAddresses = (id: number, pool: BondedPool) => ({ ...pool, id, - addresses: createAccounts(id), + addresses: createPoolAccounts(id), }); const getBondedPool = (poolId: MaybePool) => diff --git a/src/contexts/Pools/PoolsConfig/defaults.ts b/src/contexts/Pools/FavoritePools/defaults.ts similarity index 63% rename from src/contexts/Pools/PoolsConfig/defaults.ts rename to src/contexts/Pools/FavoritePools/defaults.ts index 9e3acafb74..43780e79f1 100644 --- a/src/contexts/Pools/PoolsConfig/defaults.ts +++ b/src/contexts/Pools/FavoritePools/defaults.ts @@ -2,14 +2,13 @@ // SPDX-License-Identifier: GPL-3.0-only /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */ -import type { PoolsConfigContextState } from './types'; +import type { FavoritePoolsContextState } from './types'; import type { PoolAddresses } from '../BondedPools/types'; -export const defaultPoolsConfigContext: PoolsConfigContextState = { - addFavorite: () => {}, - removeFavorite: () => {}, - createAccounts: () => poolAddresses, +export const defaultFavoritePoolsContext: FavoritePoolsContextState = { favorites: [], + addFavorite: (address: string) => {}, + removeFavorite: (address: string) => {}, }; export const poolAddresses: PoolAddresses = { diff --git a/src/contexts/Pools/FavoritePools/index.tsx b/src/contexts/Pools/FavoritePools/index.tsx new file mode 100644 index 0000000000..237154c1d0 --- /dev/null +++ b/src/contexts/Pools/FavoritePools/index.tsx @@ -0,0 +1,69 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { ReactNode } from 'react'; +import { createContext, useContext, useState } from 'react'; +import type { FavoritePoolsContextState } from './types'; +import { useNetwork } from 'contexts/Network'; +import { defaultFavoritePoolsContext } from './defaults'; + +export const FavoritePoolsContext = createContext( + defaultFavoritePoolsContext +); + +export const useFavoritePools = () => useContext(FavoritePoolsContext); + +export const FavoritePoolsProvider = ({ + children, +}: { + children: ReactNode; +}) => { + const { network } = useNetwork(); + + // Get favorite pools from local storage. + const getLocalFavorites = () => { + const localFavorites = localStorage.getItem(`${network}_favorite_pools`); + return localFavorites !== null ? JSON.parse(localFavorites) : []; + }; + + // Stores the user's favorite pools. + const [favorites, setFavorites] = useState(getLocalFavorites()); + + // Adds a favorite validator. + const addFavorite = (address: string) => { + const newFavorites = Object.assign(favorites); + if (!newFavorites.includes(address)) { + newFavorites.push(address); + } + + localStorage.setItem( + `${network}_favorite_pools`, + JSON.stringify(newFavorites) + ); + setFavorites([...newFavorites]); + }; + + // Removes a favorite pool if they exist. + const removeFavorite = (address: string) => { + const newFavorites = Object.assign(favorites).filter( + (validator: string) => validator !== address + ); + localStorage.setItem( + `${network}_favorite_pools`, + JSON.stringify(newFavorites) + ); + setFavorites([...newFavorites]); + }; + + return ( + + {children} + + ); +}; diff --git a/src/contexts/Pools/FavoritePools/types.ts b/src/contexts/Pools/FavoritePools/types.ts new file mode 100644 index 0000000000..08aeadfa05 --- /dev/null +++ b/src/contexts/Pools/FavoritePools/types.ts @@ -0,0 +1,8 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +export interface FavoritePoolsContextState { + favorites: string[]; + addFavorite: (address: string) => void; + removeFavorite: (address: string) => void; +} diff --git a/src/contexts/Pools/PoolsConfig/index.tsx b/src/contexts/Pools/PoolsConfig/index.tsx deleted file mode 100644 index a7f9e9da30..0000000000 --- a/src/contexts/Pools/PoolsConfig/index.tsx +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { bnToU8a, u8aConcat } from '@polkadot/util'; -import BigNumber from 'bignumber.js'; -import BN from 'bn.js'; -import type { ReactNode } from 'react'; -import { createContext, useContext, useState } from 'react'; -import { EmptyH256, ModPrefix, U32Opts } from 'consts'; -import type { PoolsConfigContextState } from './types'; -import { useNetwork } from 'contexts/Network'; -import { useApi } from '../../Api'; -import { defaultPoolsConfigContext } from './defaults'; - -export const PoolsConfigContext = createContext( - defaultPoolsConfigContext -); - -export const usePoolsConfig = () => useContext(PoolsConfigContext); - -export const PoolsConfigProvider = ({ children }: { children: ReactNode }) => { - const { network } = useNetwork(); - const { api, consts } = useApi(); - const { poolsPalletId } = consts; - - // get favorite pools from local storage. - const getLocalFavorites = () => { - const localFavorites = localStorage.getItem(`${network}_favorite_pools`); - return localFavorites !== null ? JSON.parse(localFavorites) : []; - }; - - // stores the user's favorite pools - const [favorites, setFavorites] = useState(getLocalFavorites()); - - // Adds a favorite validator. - const addFavorite = (address: string) => { - const newFavorites = Object.assign(favorites); - if (!newFavorites.includes(address)) { - newFavorites.push(address); - } - - localStorage.setItem( - `${network}_favorite_pools`, - JSON.stringify(newFavorites) - ); - setFavorites([...newFavorites]); - }; - - /* - * Removes a favorite validator if they exist. - */ - const removeFavorite = (address: string) => { - let newFavorites = Object.assign(favorites); - newFavorites = newFavorites.filter( - (validator: string) => validator !== address - ); - localStorage.setItem( - `${network}_favorite_pools`, - JSON.stringify(newFavorites) - ); - setFavorites([...newFavorites]); - }; - - // Helper: generates pool stash and reward accounts. assumes poolsPalletId is synced. - const createAccounts = (poolId: number) => { - const poolIdBigNumber = new BigNumber(poolId); - return { - stash: createAccount(poolIdBigNumber, 0), - reward: createAccount(poolIdBigNumber, 1), - }; - }; - - const createAccount = (poolId: BigNumber, index: number): string => { - if (!api) { - return ''; - } - return api.registry - .createType( - 'AccountId32', - u8aConcat( - ModPrefix, - poolsPalletId, - new Uint8Array([index]), - bnToU8a(new BN(poolId.toString()), U32Opts), - EmptyH256 - ) - ) - .toString(); - }; - - return ( - - {children} - - ); -}; diff --git a/src/contexts/Pools/PoolsConfig/types.ts b/src/contexts/Pools/PoolsConfig/types.ts deleted file mode 100644 index c893e303bf..0000000000 --- a/src/contexts/Pools/PoolsConfig/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import type { PoolAddresses } from '../BondedPools/types'; - -export interface PoolsConfigContextState { - addFavorite: (a: string) => void; - removeFavorite: (a: string) => void; - createAccounts: (p: number) => PoolAddresses; - favorites: string[]; -} diff --git a/src/contexts/Validators/types.ts b/src/contexts/Validators/types.ts index bcca9d4081..11fb95806b 100644 --- a/src/contexts/Validators/types.ts +++ b/src/contexts/Validators/types.ts @@ -36,8 +36,8 @@ export interface AverageEraValidatorReward { } export interface FavoriteValidatorsContextInterface { - addFavorite: (a: string) => void; - removeFavorite: (a: string) => void; + addFavorite: (address: string) => void; + removeFavorite: (address: string) => void; favorites: string[]; favoritesList: Validator[] | null; } diff --git a/src/library/Hooks/useCreatePoolAccounts/index.tsx b/src/library/Hooks/useCreatePoolAccounts/index.tsx new file mode 100644 index 0000000000..a1a7708c77 --- /dev/null +++ b/src/library/Hooks/useCreatePoolAccounts/index.tsx @@ -0,0 +1,42 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { bnToU8a, u8aConcat } from '@polkadot/util'; +import BigNumber from 'bignumber.js'; +import { BN } from 'bn.js'; +import { EmptyH256, ModPrefix, U32Opts } from 'consts'; +import { useApi } from 'contexts/Api'; + +export const useCreatePoolAccounts = () => { + const { api, consts } = useApi(); + const { poolsPalletId } = consts; + + // Generates pool stash and reward accounts. Assumes `poolsPalletId` is synced. + const createPoolAccounts = (poolId: number) => { + const poolIdBigNumber = new BigNumber(poolId); + return { + stash: createAccount(poolIdBigNumber, 0), + reward: createAccount(poolIdBigNumber, 1), + }; + }; + + const createAccount = (poolId: BigNumber, index: number): string => { + if (!api) { + return ''; + } + return api.registry + .createType( + 'AccountId32', + u8aConcat( + ModPrefix, + poolsPalletId, + new Uint8Array([index]), + bnToU8a(new BN(poolId.toString()), U32Opts), + EmptyH256 + ) + ) + .toString(); + }; + + return createPoolAccounts; +}; diff --git a/src/library/ListItem/Labels/FavoritePool.tsx b/src/library/ListItem/Labels/FavoritePool.tsx index 35bbf40c32..ab435100f1 100644 --- a/src/library/ListItem/Labels/FavoritePool.tsx +++ b/src/library/ListItem/Labels/FavoritePool.tsx @@ -5,7 +5,7 @@ import { faHeart as faHeartRegular } from '@fortawesome/free-regular-svg-icons'; import { faHeart } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useTranslation } from 'react-i18next'; -import { usePoolsConfig } from 'contexts/Pools/PoolsConfig'; +import { useFavoritePools } from 'contexts/Pools/FavoritePools'; import { useTooltip } from 'contexts/Tooltip'; import { TooltipTrigger } from 'library/ListItem/Wrappers'; import type { FavoriteProps } from '../types'; @@ -14,7 +14,7 @@ import { NotificationsController } from 'static/NotificationsController'; export const FavoritePool = ({ address }: FavoriteProps) => { const { t } = useTranslation('library'); const { setTooltipTextAndOpen } = useTooltip(); - const { favorites, addFavorite, removeFavorite } = usePoolsConfig(); + const { favorites, addFavorite, removeFavorite } = useFavoritePools(); const isFavorite = favorites.includes(address); diff --git a/src/modals/UnlockChunks/Forms.tsx b/src/modals/UnlockChunks/Forms.tsx index d7f2be9e12..6bc5ab9428 100644 --- a/src/modals/UnlockChunks/Forms.tsx +++ b/src/modals/UnlockChunks/Forms.tsx @@ -19,7 +19,7 @@ import { useActivePools } from 'contexts/Pools/ActivePools'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; -import { usePoolsConfig } from 'contexts/Pools/PoolsConfig'; +import { useFavoritePools } from 'contexts/Pools/FavoritePools'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; @@ -40,12 +40,12 @@ export const Forms = forwardRef( const { networkData: { units, unit }, } = useNetwork(); - const { activeAccount } = useActiveAccounts(); - const { removeFavorite: removeFavoritePool } = usePoolsConfig(); const { membership } = usePoolMemberships(); + const { activeAccount } = useActiveAccounts(); + const { removePoolMember } = usePoolMembers(); const { selectedActivePool } = useActivePools(); const { removeFromBondedPools } = useBondedPools(); - const { removePoolMember } = usePoolMembers(); + const { removeFavorite: removeFavoritePool } = useFavoritePools(); const { setModalStatus, config: { options }, diff --git a/src/pages/Pools/Home/Favorites/index.tsx b/src/pages/Pools/Home/Favorites/index.tsx index 3f3316a1e8..09be7c4c67 100644 --- a/src/pages/Pools/Home/Favorites/index.tsx +++ b/src/pages/Pools/Home/Favorites/index.tsx @@ -6,7 +6,7 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { usePoolsConfig } from 'contexts/Pools/PoolsConfig'; +import { useFavoritePools } from 'contexts/Pools/FavoritePools'; import { useUi } from 'contexts/UI'; import { CardWrapper } from 'library/Card/Wrappers'; import { PoolList } from 'library/PoolList/Default'; @@ -19,7 +19,7 @@ export const PoolFavorites = () => { const { isReady } = useApi(); const { isPoolSyncing } = useUi(); const { bondedPools } = useBondedPools(); - const { favorites, removeFavorite } = usePoolsConfig(); + const { favorites, removeFavorite } = useFavoritePools(); // Store local favorite list and update when favorites list is mutated. const [favoritesList, setFavoritesList] = useState([]); diff --git a/src/pages/Pools/Home/index.tsx b/src/pages/Pools/Home/index.tsx index 0155caffd8..364aef4e27 100644 --- a/src/pages/Pools/Home/index.tsx +++ b/src/pages/Pools/Home/index.tsx @@ -10,7 +10,7 @@ import { useBondedPools } from 'contexts/Pools/BondedPools'; import { CardWrapper } from 'library/Card/Wrappers'; import { PoolList } from 'library/PoolList/Default'; import { StatBoxList } from 'library/StatBoxList'; -import { usePoolsConfig } from 'contexts/Pools/PoolsConfig'; +import { useFavoritePools } from 'contexts/Pools/FavoritePools'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { PoolListProvider } from 'library/PoolList/context'; @@ -29,9 +29,9 @@ import { useApi } from 'contexts/Api'; export const HomeInner = () => { const { t } = useTranslation('pages'); + const { favorites } = useFavoritePools(); const { openModal } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); - const { favorites } = usePoolsConfig(); const { activeTab, setActiveTab } = usePoolsTabs(); const { counterForBondedPools } = useApi().poolsConfig; const { bondedPools, getAccountPools } = useBondedPools(); From 4e469041aeafcddd51e2abbc36d2a453529a4bff Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 31 Jan 2024 10:28:39 +0800 Subject: [PATCH 08/51] feat(refactor): Hooks to own folder (#1914) --- docs/CONTRIBUTING.md | 2 +- src/Providers.tsx | 2 +- src/canvas/ManageNominations/index.tsx | 2 +- .../PoolMembers/Prompts/UnbondMember.tsx | 8 +++--- .../PoolMembers/Prompts/WithdrawMember.tsx | 4 +-- src/contexts/Balances/index.tsx | 2 +- src/contexts/FastUnstake/index.tsx | 2 +- src/contexts/Pools/ActivePools/index.tsx | 2 +- src/contexts/Pools/BondedPools/index.tsx | 2 +- src/contexts/TxMeta/index.tsx | 2 +- .../Validators/ValidatorEntries/index.tsx | 2 +- .../useActiveBalances/index.tsx | 0 .../useAverageRewardRate/defaults.ts | 0 .../useAverageRewardRate/index.tsx | 0 .../useAverageRewardRate/types.ts | 0 .../Hooks => hooks}/useBatchCall/index.tsx | 2 +- .../Hooks => hooks}/useBlockNumber/index.tsx | 0 .../useBondGreatestFee/index.tsx | 0 .../Hooks => hooks}/useBuildPayload/index.tsx | 0 .../useCreatePoolAccounts/index.tsx | 0 .../useDotLottieButton/index.tsx | 0 .../Hooks => hooks}/useEraTimeLeft/index.tsx | 0 .../Hooks => hooks}/useErasPerDay/index.tsx | 0 .../useErasToTimeLeft/index.tsx | 0 .../useFillVariables/index.tsx | 0 .../Hooks => hooks}/useFillVariables/types.ts | 0 .../useNominationStatus/index.tsx | 0 .../useOutsideAlerter}/index.tsx | 25 ++----------------- .../Hooks => hooks}/usePayeeConfig/index.tsx | 0 .../usePoolCommission/index.tsx | 0 .../Hooks => hooks}/usePoolFilters/index.tsx | 0 .../Hooks => hooks}/usePrices/index.tsx | 2 +- .../useProxySupported/index.tsx | 0 .../useSignerWarnings/index.tsx | 0 .../Hooks => hooks}/useSize/index.tsx | 0 .../useSubmitExtrinsic/index.tsx | 0 .../useSubmitExtrinsic/types.ts | 0 .../Hooks => hooks}/useSubscanData/index.tsx | 0 .../Hooks => hooks}/useTimeLeft/defaults.ts | 0 .../Hooks => hooks}/useTimeLeft/index.tsx | 0 .../Hooks => hooks}/useTimeLeft/types.ts | 0 .../Hooks => hooks}/useTimeLeft/utils.ts | 0 .../Hooks => hooks}/useUnitPrice/index.tsx | 0 .../Hooks => hooks}/useUnstaking/index.tsx | 0 .../useValidatorFilters/index.tsx | 0 src/hooks/withProviders/index.tsx | 22 ++++++++++++++++ src/library/Countdown/types.ts | 2 +- src/library/GenerateNominations/index.tsx | 2 +- .../GenerateNominations/useFetchMethods.tsx | 2 +- src/library/Help/index.tsx | 2 +- src/library/List/Selectable.tsx | 2 +- src/library/Menu/index.tsx | 2 +- src/library/NetworkBar/index.tsx | 2 +- src/library/Nominations/index.tsx | 2 +- src/library/Pool/index.tsx | 2 +- src/library/PoolList/Default.tsx | 2 +- src/library/SideMenu/Primary/index.tsx | 2 +- src/library/SideMenu/index.tsx | 2 +- src/library/StatBoxList/types.ts | 2 +- src/library/UpdateHeader/index.tsx | 2 +- .../ValidatorList/FilterValidators.tsx | 2 +- .../ValidatorList/Filters/FilterBadges.tsx | 2 +- src/library/ValidatorList/OrderValidators.tsx | 2 +- src/library/ValidatorList/index.tsx | 4 +-- src/modals/Accounts/index.tsx | 2 +- src/modals/BalanceTest/index.tsx | 4 +-- src/modals/Bond/index.tsx | 6 ++--- src/modals/ChangePoolRoles/index.tsx | 2 +- src/modals/ClaimPayouts/Forms.tsx | 6 ++--- src/modals/ClaimReward/index.tsx | 4 +-- src/modals/JoinPool/index.tsx | 8 +++--- src/modals/ManageFastUnstake/index.tsx | 6 ++--- .../Forms/ClaimCommission/index.tsx | 4 +-- .../ManagePool/Forms/LeavePool/index.tsx | 8 +++--- .../Forms/ManageCommission/index.tsx | 6 ++--- .../ManagePool/Forms/RenamePool/index.tsx | 4 +-- .../Forms/SetClaimPermission/index.tsx | 4 +-- .../ManagePool/Forms/SetPoolState/index.tsx | 4 +-- src/modals/StopNominations/index.tsx | 4 +-- src/modals/Unbond/index.tsx | 8 +++--- src/modals/UnlockChunks/Chunk.tsx | 6 ++--- src/modals/UnlockChunks/Forms.tsx | 4 +-- src/modals/UnlockChunks/Overview.tsx | 6 ++--- src/modals/Unstake/index.tsx | 10 ++++---- src/modals/UpdateController/index.tsx | 4 +-- src/modals/UpdatePayee/index.tsx | 6 ++--- src/modals/ValidatorGeo/index.tsx | 2 +- src/modals/ValidatorMetrics/index.tsx | 2 +- src/pages/Nominate/Active/ManageBond.tsx | 2 +- .../Active/Status/NominationStatus.tsx | 4 +-- .../Active/Status/PayoutDestinationStatus.tsx | 4 +-- src/pages/Nominate/Active/UnstakePrompts.tsx | 2 +- src/pages/Nominate/Active/index.tsx | 2 +- src/pages/Nominate/Setup/Payee/index.tsx | 2 +- src/pages/Nominate/Setup/Summary/index.tsx | 6 ++--- src/pages/Overview/BalanceChart.tsx | 2 +- src/pages/Overview/NetworkSats/index.tsx | 2 +- src/pages/Overview/Payouts.tsx | 4 +-- .../Overview/StakeStatus/Tips/Syncing.tsx | 2 +- src/pages/Overview/StakeStatus/Tips/index.tsx | 2 +- .../Overview/Stats/ActiveEraTimeLeft.tsx | 6 ++--- .../Overview/Stats/AveragelRewardRate.tsx | 2 +- src/pages/Payouts/index.tsx | 4 +-- src/pages/Pools/Create/Summary/index.tsx | 4 +-- src/pages/Pools/Home/PoolStats/index.tsx | 2 +- src/pages/Pools/Home/Status/PoolStatus.tsx | 2 +- 106 files changed, 147 insertions(+), 146 deletions(-) rename src/{library/Hooks => hooks}/useActiveBalances/index.tsx (100%) rename src/{library/Hooks => hooks}/useAverageRewardRate/defaults.ts (100%) rename src/{library/Hooks => hooks}/useAverageRewardRate/index.tsx (100%) rename src/{library/Hooks => hooks}/useAverageRewardRate/types.ts (100%) rename src/{library/Hooks => hooks}/useBatchCall/index.tsx (93%) rename src/{library/Hooks => hooks}/useBlockNumber/index.tsx (100%) rename src/{library/Hooks => hooks}/useBondGreatestFee/index.tsx (100%) rename src/{library/Hooks => hooks}/useBuildPayload/index.tsx (100%) rename src/{library/Hooks => hooks}/useCreatePoolAccounts/index.tsx (100%) rename src/{library/Hooks => hooks}/useDotLottieButton/index.tsx (100%) rename src/{library/Hooks => hooks}/useEraTimeLeft/index.tsx (100%) rename src/{library/Hooks => hooks}/useErasPerDay/index.tsx (100%) rename src/{library/Hooks => hooks}/useErasToTimeLeft/index.tsx (100%) rename src/{library/Hooks => hooks}/useFillVariables/index.tsx (100%) rename src/{library/Hooks => hooks}/useFillVariables/types.ts (100%) rename src/{library/Hooks => hooks}/useNominationStatus/index.tsx (100%) rename src/{library/Hooks => hooks/useOutsideAlerter}/index.tsx (56%) rename src/{library/Hooks => hooks}/usePayeeConfig/index.tsx (100%) rename src/{library/Hooks => hooks}/usePoolCommission/index.tsx (100%) rename src/{library/Hooks => hooks}/usePoolFilters/index.tsx (100%) rename src/{library/Hooks => hooks}/usePrices/index.tsx (97%) rename src/{library/Hooks => hooks}/useProxySupported/index.tsx (100%) rename src/{library/Hooks => hooks}/useSignerWarnings/index.tsx (100%) rename src/{library/Hooks => hooks}/useSize/index.tsx (100%) rename src/{library/Hooks => hooks}/useSubmitExtrinsic/index.tsx (100%) rename src/{library/Hooks => hooks}/useSubmitExtrinsic/types.ts (100%) rename src/{library/Hooks => hooks}/useSubscanData/index.tsx (100%) rename src/{library/Hooks => hooks}/useTimeLeft/defaults.ts (100%) rename src/{library/Hooks => hooks}/useTimeLeft/index.tsx (100%) rename src/{library/Hooks => hooks}/useTimeLeft/types.ts (100%) rename src/{library/Hooks => hooks}/useTimeLeft/utils.ts (100%) rename src/{library/Hooks => hooks}/useUnitPrice/index.tsx (100%) rename src/{library/Hooks => hooks}/useUnstaking/index.tsx (100%) rename src/{library/Hooks => hooks}/useValidatorFilters/index.tsx (100%) create mode 100644 src/hooks/withProviders/index.tsx diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 0808168b7c..23b60e3bea 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -57,7 +57,7 @@ Going from the top-most component, the component hierarchy is set up as follows: - `src/index.tsx`: DOM render, of little interest. - `src/App.tsx`: wraps `` in the theme provider context and determines the active network from local storage. -- `src/Providers.tsx`: imports and wraps `` with all the contexts using a withProviders hook. We also wrap styled component's theme provider context here to make the theme configuration work. +- `src/Providers.tsx`: imports and wraps `` with all the contexts using a `withProviders` hook. We also wrap styled component's theme provider context here to make the theme configuration work. - `src/Router.tsx`: contains react router ``'s, in addition to the major app presentational components. ## Development Patterns diff --git a/src/Providers.tsx b/src/Providers.tsx index 6bc3ef2378..ede98153e6 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -39,13 +39,13 @@ import { APIProvider } from 'contexts/Api'; import { ThemedRouter } from 'Themes'; import type { AnyJson } from 'types'; import type { FC } from 'react'; -import { withProviders } from 'library/Hooks'; import { OtherAccountsProvider } from 'contexts/Connect/OtherAccounts'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { DappName } from 'consts'; import { ImportedAccountsProvider } from 'contexts/Connect/ImportedAccounts'; import { PoolPerformanceProvider } from 'contexts/Pools/PoolPerformance'; import { ExternalAccountsProvider } from 'contexts/Connect/ExternalAccounts'; +import { withProviders } from 'hooks/withProviders'; export const Providers = () => { const { diff --git a/src/canvas/ManageNominations/index.tsx b/src/canvas/ManageNominations/index.tsx index a8b515513f..27b50c348e 100644 --- a/src/canvas/ManageNominations/index.tsx +++ b/src/canvas/ManageNominations/index.tsx @@ -15,7 +15,7 @@ import { useApi } from 'contexts/Api'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { usePrompt } from 'contexts/Prompt'; import { useHelp } from 'contexts/Help'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useBonded } from 'contexts/Bonded'; import { useActivePools } from 'contexts/Pools/ActivePools'; diff --git a/src/canvas/PoolMembers/Prompts/UnbondMember.tsx b/src/canvas/PoolMembers/Prompts/UnbondMember.tsx index 1c81c19f38..2aabaf9a2f 100644 --- a/src/canvas/PoolMembers/Prompts/UnbondMember.tsx +++ b/src/canvas/PoolMembers/Prompts/UnbondMember.tsx @@ -21,10 +21,10 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { Warning } from 'library/Form/Warning'; -import { useErasToTimeLeft } from 'library/Hooks/useErasToTimeLeft'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; -import { timeleftAsString } from 'library/Hooks/useTimeLeft/utils'; +import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; +import { timeleftAsString } from 'hooks/useTimeLeft/utils'; import { SubmitTx } from 'library/SubmitTx'; import { StaticNote } from 'modals/Utils/StaticNote'; import { useNetwork } from 'contexts/Network'; diff --git a/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx b/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx index 2d812d1b74..643a1fddf1 100644 --- a/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx +++ b/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx @@ -20,8 +20,8 @@ import { useState } from 'react'; import { useApi } from 'contexts/Api'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/contexts/Balances/index.tsx b/src/contexts/Balances/index.tsx index 8d5414d9b4..67aa8402d7 100644 --- a/src/contexts/Balances/index.tsx +++ b/src/contexts/Balances/index.tsx @@ -11,7 +11,7 @@ import { useEventListener } from 'usehooks-ts'; import { isCustomEvent } from 'static/utils'; import { BalancesController } from 'static/BalancesController'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useActiveBalances } from 'library/Hooks/useActiveBalances'; +import { useActiveBalances } from 'hooks/useActiveBalances'; import { useBonded } from 'contexts/Bonded'; export const BalancesContext = createContext( diff --git a/src/contexts/FastUnstake/index.tsx b/src/contexts/FastUnstake/index.tsx index 313302a546..e86e8598e2 100644 --- a/src/contexts/FastUnstake/index.tsx +++ b/src/contexts/FastUnstake/index.tsx @@ -15,7 +15,7 @@ import { useStaking } from 'contexts/Staking'; import type { AnyApi, AnyJson, MaybeAddress } from 'types'; import Worker from 'workers/stakers?worker'; import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; -import { useNominationStatus } from 'library/Hooks/useNominationStatus'; +import { useNominationStatus } from 'hooks/useNominationStatus'; import { validateLocalExposure } from 'contexts/Validators/Utils'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/contexts/Pools/ActivePools/index.tsx b/src/contexts/Pools/ActivePools/index.tsx index eb7e5f5a83..868d985363 100644 --- a/src/contexts/Pools/ActivePools/index.tsx +++ b/src/contexts/Pools/ActivePools/index.tsx @@ -26,7 +26,7 @@ import { usePoolMembers } from '../PoolMembers'; import type { ActivePool, ActivePoolsContextState, PoolTargets } from './types'; import type { PoolAddresses } from '../BondedPools/types'; import { SubscanController } from 'static/SubscanController'; -import { useCreatePoolAccounts } from 'library/Hooks/useCreatePoolAccounts'; +import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; export const ActivePoolsContext = createContext( defaults.defaultActivePoolContext diff --git a/src/contexts/Pools/BondedPools/index.tsx b/src/contexts/Pools/BondedPools/index.tsx index 63875cc0c4..b6e5bcccc9 100644 --- a/src/contexts/Pools/BondedPools/index.tsx +++ b/src/contexts/Pools/BondedPools/index.tsx @@ -19,7 +19,7 @@ import { useNetwork } from 'contexts/Network'; import type { AnyJson } from '@polkadot-cloud/react/types'; import { useApi } from '../../Api'; import { defaultBondedPoolsContext } from './defaults'; -import { useCreatePoolAccounts } from 'library/Hooks/useCreatePoolAccounts'; +import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; export const BondedPoolsContext = createContext( defaultBondedPoolsContext diff --git a/src/contexts/TxMeta/index.tsx b/src/contexts/TxMeta/index.tsx index d46886e84b..6701169d5a 100644 --- a/src/contexts/TxMeta/index.tsx +++ b/src/contexts/TxMeta/index.tsx @@ -13,7 +13,7 @@ import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import * as defaults from './defaults'; import type { TxMetaContextInterface } from './types'; import { useEffectIgnoreInitial } from '@polkadot-cloud/react'; -import { useActiveBalances } from 'library/Hooks/useActiveBalances'; +import { useActiveBalances } from 'hooks/useActiveBalances'; import { useApi } from 'contexts/Api'; export const TxMetaContext = createContext( diff --git a/src/contexts/Validators/ValidatorEntries/index.tsx b/src/contexts/Validators/ValidatorEntries/index.tsx index c95e568b7d..325a161888 100644 --- a/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/src/contexts/Validators/ValidatorEntries/index.tsx @@ -34,7 +34,7 @@ import { } from './defaults'; import { getLocalEraValidators, setLocalEraValidators } from '../Utils'; import type { ValidatorEntry } from '@polkadot-cloud/assets/types'; -import { useErasPerDay } from 'library/Hooks/useErasPerDay'; +import { useErasPerDay } from 'hooks/useErasPerDay'; export const ValidatorsContext = createContext( defaultValidatorsContext diff --git a/src/library/Hooks/useActiveBalances/index.tsx b/src/hooks/useActiveBalances/index.tsx similarity index 100% rename from src/library/Hooks/useActiveBalances/index.tsx rename to src/hooks/useActiveBalances/index.tsx diff --git a/src/library/Hooks/useAverageRewardRate/defaults.ts b/src/hooks/useAverageRewardRate/defaults.ts similarity index 100% rename from src/library/Hooks/useAverageRewardRate/defaults.ts rename to src/hooks/useAverageRewardRate/defaults.ts diff --git a/src/library/Hooks/useAverageRewardRate/index.tsx b/src/hooks/useAverageRewardRate/index.tsx similarity index 100% rename from src/library/Hooks/useAverageRewardRate/index.tsx rename to src/hooks/useAverageRewardRate/index.tsx diff --git a/src/library/Hooks/useAverageRewardRate/types.ts b/src/hooks/useAverageRewardRate/types.ts similarity index 100% rename from src/library/Hooks/useAverageRewardRate/types.ts rename to src/hooks/useAverageRewardRate/types.ts diff --git a/src/library/Hooks/useBatchCall/index.tsx b/src/hooks/useBatchCall/index.tsx similarity index 93% rename from src/library/Hooks/useBatchCall/index.tsx rename to src/hooks/useBatchCall/index.tsx index de3684cbfb..8243018fc2 100644 --- a/src/library/Hooks/useBatchCall/index.tsx +++ b/src/hooks/useBatchCall/index.tsx @@ -4,7 +4,7 @@ import { useApi } from 'contexts/Api'; import type { AnyApi, MaybeAddress } from 'types'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useProxySupported } from '../useProxySupported'; +import { useProxySupported } from 'hooks/useProxySupported'; export const useBatchCall = () => { const { api } = useApi(); diff --git a/src/library/Hooks/useBlockNumber/index.tsx b/src/hooks/useBlockNumber/index.tsx similarity index 100% rename from src/library/Hooks/useBlockNumber/index.tsx rename to src/hooks/useBlockNumber/index.tsx diff --git a/src/library/Hooks/useBondGreatestFee/index.tsx b/src/hooks/useBondGreatestFee/index.tsx similarity index 100% rename from src/library/Hooks/useBondGreatestFee/index.tsx rename to src/hooks/useBondGreatestFee/index.tsx diff --git a/src/library/Hooks/useBuildPayload/index.tsx b/src/hooks/useBuildPayload/index.tsx similarity index 100% rename from src/library/Hooks/useBuildPayload/index.tsx rename to src/hooks/useBuildPayload/index.tsx diff --git a/src/library/Hooks/useCreatePoolAccounts/index.tsx b/src/hooks/useCreatePoolAccounts/index.tsx similarity index 100% rename from src/library/Hooks/useCreatePoolAccounts/index.tsx rename to src/hooks/useCreatePoolAccounts/index.tsx diff --git a/src/library/Hooks/useDotLottieButton/index.tsx b/src/hooks/useDotLottieButton/index.tsx similarity index 100% rename from src/library/Hooks/useDotLottieButton/index.tsx rename to src/hooks/useDotLottieButton/index.tsx diff --git a/src/library/Hooks/useEraTimeLeft/index.tsx b/src/hooks/useEraTimeLeft/index.tsx similarity index 100% rename from src/library/Hooks/useEraTimeLeft/index.tsx rename to src/hooks/useEraTimeLeft/index.tsx diff --git a/src/library/Hooks/useErasPerDay/index.tsx b/src/hooks/useErasPerDay/index.tsx similarity index 100% rename from src/library/Hooks/useErasPerDay/index.tsx rename to src/hooks/useErasPerDay/index.tsx diff --git a/src/library/Hooks/useErasToTimeLeft/index.tsx b/src/hooks/useErasToTimeLeft/index.tsx similarity index 100% rename from src/library/Hooks/useErasToTimeLeft/index.tsx rename to src/hooks/useErasToTimeLeft/index.tsx diff --git a/src/library/Hooks/useFillVariables/index.tsx b/src/hooks/useFillVariables/index.tsx similarity index 100% rename from src/library/Hooks/useFillVariables/index.tsx rename to src/hooks/useFillVariables/index.tsx diff --git a/src/library/Hooks/useFillVariables/types.ts b/src/hooks/useFillVariables/types.ts similarity index 100% rename from src/library/Hooks/useFillVariables/types.ts rename to src/hooks/useFillVariables/types.ts diff --git a/src/library/Hooks/useNominationStatus/index.tsx b/src/hooks/useNominationStatus/index.tsx similarity index 100% rename from src/library/Hooks/useNominationStatus/index.tsx rename to src/hooks/useNominationStatus/index.tsx diff --git a/src/library/Hooks/index.tsx b/src/hooks/useOutsideAlerter/index.tsx similarity index 56% rename from src/library/Hooks/index.tsx rename to src/hooks/useOutsideAlerter/index.tsx index 6d33b8e908..e37a1625d8 100644 --- a/src/library/Hooks/index.tsx +++ b/src/hooks/useOutsideAlerter/index.tsx @@ -1,13 +1,10 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { FC, RefObject } from 'react'; -import { useEffect } from 'react'; +import { useEffect, type RefObject } from 'react'; import type { AnyFunction, AnyJson } from 'types'; -/* - * A hook that alerts clicks outside of the passed ref. - */ +// A hook that alerts clicks outside of the passed ref. export const useOutsideAlerter = ( ref: RefObject, callback: AnyFunction, @@ -30,21 +27,3 @@ export const useOutsideAlerter = ( }; }, [ref]); }; - -// A pure function that applies an arbitrary amount of context providers to a wrapped -// component. -export const withProviders = ( - providers: (FC | [FC, AnyJson])[], - Wrapped: FC -) => - providers.reduceRight( - (acc, prov) => { - if (Array.isArray(prov)) { - const Provider = prov[0]; - return {acc}; - } - const Provider = prov; - return {acc}; - }, - - ); diff --git a/src/library/Hooks/usePayeeConfig/index.tsx b/src/hooks/usePayeeConfig/index.tsx similarity index 100% rename from src/library/Hooks/usePayeeConfig/index.tsx rename to src/hooks/usePayeeConfig/index.tsx diff --git a/src/library/Hooks/usePoolCommission/index.tsx b/src/hooks/usePoolCommission/index.tsx similarity index 100% rename from src/library/Hooks/usePoolCommission/index.tsx rename to src/hooks/usePoolCommission/index.tsx diff --git a/src/library/Hooks/usePoolFilters/index.tsx b/src/hooks/usePoolFilters/index.tsx similarity index 100% rename from src/library/Hooks/usePoolFilters/index.tsx rename to src/hooks/usePoolFilters/index.tsx diff --git a/src/library/Hooks/usePrices/index.tsx b/src/hooks/usePrices/index.tsx similarity index 97% rename from src/library/Hooks/usePrices/index.tsx rename to src/hooks/usePrices/index.tsx index cedd229226..b4e9be8657 100644 --- a/src/library/Hooks/usePrices/index.tsx +++ b/src/hooks/usePrices/index.tsx @@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'react'; import { usePlugins } from 'contexts/Plugins'; -import { useUnitPrice } from 'library/Hooks/useUnitPrice'; +import { useUnitPrice } from 'hooks/useUnitPrice'; import { useNetwork } from 'contexts/Network'; import type { AnyJson } from 'types'; diff --git a/src/library/Hooks/useProxySupported/index.tsx b/src/hooks/useProxySupported/index.tsx similarity index 100% rename from src/library/Hooks/useProxySupported/index.tsx rename to src/hooks/useProxySupported/index.tsx diff --git a/src/library/Hooks/useSignerWarnings/index.tsx b/src/hooks/useSignerWarnings/index.tsx similarity index 100% rename from src/library/Hooks/useSignerWarnings/index.tsx rename to src/hooks/useSignerWarnings/index.tsx diff --git a/src/library/Hooks/useSize/index.tsx b/src/hooks/useSize/index.tsx similarity index 100% rename from src/library/Hooks/useSize/index.tsx rename to src/hooks/useSize/index.tsx diff --git a/src/library/Hooks/useSubmitExtrinsic/index.tsx b/src/hooks/useSubmitExtrinsic/index.tsx similarity index 100% rename from src/library/Hooks/useSubmitExtrinsic/index.tsx rename to src/hooks/useSubmitExtrinsic/index.tsx diff --git a/src/library/Hooks/useSubmitExtrinsic/types.ts b/src/hooks/useSubmitExtrinsic/types.ts similarity index 100% rename from src/library/Hooks/useSubmitExtrinsic/types.ts rename to src/hooks/useSubmitExtrinsic/types.ts diff --git a/src/library/Hooks/useSubscanData/index.tsx b/src/hooks/useSubscanData/index.tsx similarity index 100% rename from src/library/Hooks/useSubscanData/index.tsx rename to src/hooks/useSubscanData/index.tsx diff --git a/src/library/Hooks/useTimeLeft/defaults.ts b/src/hooks/useTimeLeft/defaults.ts similarity index 100% rename from src/library/Hooks/useTimeLeft/defaults.ts rename to src/hooks/useTimeLeft/defaults.ts diff --git a/src/library/Hooks/useTimeLeft/index.tsx b/src/hooks/useTimeLeft/index.tsx similarity index 100% rename from src/library/Hooks/useTimeLeft/index.tsx rename to src/hooks/useTimeLeft/index.tsx diff --git a/src/library/Hooks/useTimeLeft/types.ts b/src/hooks/useTimeLeft/types.ts similarity index 100% rename from src/library/Hooks/useTimeLeft/types.ts rename to src/hooks/useTimeLeft/types.ts diff --git a/src/library/Hooks/useTimeLeft/utils.ts b/src/hooks/useTimeLeft/utils.ts similarity index 100% rename from src/library/Hooks/useTimeLeft/utils.ts rename to src/hooks/useTimeLeft/utils.ts diff --git a/src/library/Hooks/useUnitPrice/index.tsx b/src/hooks/useUnitPrice/index.tsx similarity index 100% rename from src/library/Hooks/useUnitPrice/index.tsx rename to src/hooks/useUnitPrice/index.tsx diff --git a/src/library/Hooks/useUnstaking/index.tsx b/src/hooks/useUnstaking/index.tsx similarity index 100% rename from src/library/Hooks/useUnstaking/index.tsx rename to src/hooks/useUnstaking/index.tsx diff --git a/src/library/Hooks/useValidatorFilters/index.tsx b/src/hooks/useValidatorFilters/index.tsx similarity index 100% rename from src/library/Hooks/useValidatorFilters/index.tsx rename to src/hooks/useValidatorFilters/index.tsx diff --git a/src/hooks/withProviders/index.tsx b/src/hooks/withProviders/index.tsx new file mode 100644 index 0000000000..881b852d37 --- /dev/null +++ b/src/hooks/withProviders/index.tsx @@ -0,0 +1,22 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { FC } from 'react'; +import type { AnyJson } from 'types'; + +// A pure function that applies an arbitrary amount of context providers to a wrapped component. +export const withProviders = ( + providers: (FC | [FC, AnyJson])[], + Wrapped: FC +) => + providers.reduceRight( + (acc, prov) => { + if (Array.isArray(prov)) { + const Provider = prov[0]; + return {acc}; + } + const Provider = prov; + return {acc}; + }, + + ); diff --git a/src/library/Countdown/types.ts b/src/library/Countdown/types.ts index cf96ae2caa..2333bed2f0 100644 --- a/src/library/Countdown/types.ts +++ b/src/library/Countdown/types.ts @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { TimeLeftFormatted } from 'library/Hooks/useTimeLeft/types'; +import type { TimeLeftFormatted } from 'hooks/useTimeLeft/types'; export interface CountdownProps { timeleft: TimeLeftFormatted; diff --git a/src/library/GenerateNominations/index.tsx b/src/library/GenerateNominations/index.tsx index 98725e9f6f..fe14ed1c20 100644 --- a/src/library/GenerateNominations/index.tsx +++ b/src/library/GenerateNominations/index.tsx @@ -13,7 +13,7 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { useUnstaking } from 'hooks/useUnstaking'; import { SelectableWrapper } from 'library/List'; import { SelectItems } from 'library/SelectItems'; import { SelectItem } from 'library/SelectItems/Item'; diff --git a/src/library/GenerateNominations/useFetchMethods.tsx b/src/library/GenerateNominations/useFetchMethods.tsx index be991a5763..1c1d401701 100644 --- a/src/library/GenerateNominations/useFetchMethods.tsx +++ b/src/library/GenerateNominations/useFetchMethods.tsx @@ -5,7 +5,7 @@ import { shuffle } from '@polkadot-cloud/utils'; import { useFavoriteValidators } from 'contexts/Validators/FavoriteValidators'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; import type { Validator } from 'contexts/Validators/types'; -import { useValidatorFilters } from 'library/Hooks/useValidatorFilters'; +import { useValidatorFilters } from 'hooks/useValidatorFilters'; import type { AddNominationsType } from './types'; export const useFetchMehods = () => { diff --git a/src/library/Help/index.tsx b/src/library/Help/index.tsx index 3c64fd5c71..c143afdc5f 100644 --- a/src/library/Help/index.tsx +++ b/src/library/Help/index.tsx @@ -20,7 +20,7 @@ import type { ExternalItems, HelpItem, } from 'contexts/Help/types'; -import { useFillVariables } from 'library/Hooks/useFillVariables'; +import { useFillVariables } from 'hooks/useFillVariables'; import { Definition } from './Items/Definition'; import { External } from './Items/External'; import { ActiveDefinition } from './Items/ActiveDefinition'; diff --git a/src/library/List/Selectable.tsx b/src/library/List/Selectable.tsx index cf30a41304..2326f41474 100644 --- a/src/library/List/Selectable.tsx +++ b/src/library/List/Selectable.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import { useTranslation } from 'react-i18next'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { useUnstaking } from 'hooks/useUnstaking'; import { ButtonMonoInvert } from '@polkadot-cloud/react'; import { SelectableWrapper } from '.'; import { useList } from './context'; diff --git a/src/library/Menu/index.tsx b/src/library/Menu/index.tsx index 35143b3da6..cf88b899ca 100644 --- a/src/library/Menu/index.tsx +++ b/src/library/Menu/index.tsx @@ -3,9 +3,9 @@ import { useEffect, useRef } from 'react'; import { useMenu } from 'contexts/Menu'; -import { useOutsideAlerter } from 'library/Hooks'; import { ItemWrapper, Wrapper } from './Wrappers'; import type { AnyJson } from 'types'; +import { useOutsideAlerter } from 'hooks/useOutsideAlerter'; export const Menu = () => { const menu = useMenu(); diff --git a/src/library/NetworkBar/index.tsx b/src/library/NetworkBar/index.tsx index 5ae90369b3..11edd1c46f 100644 --- a/src/library/NetworkBar/index.tsx +++ b/src/library/NetworkBar/index.tsx @@ -6,7 +6,7 @@ import { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { usePlugins } from 'contexts/Plugins'; -import { usePrices } from 'library/Hooks/usePrices'; +import { usePrices } from 'hooks/usePrices'; import { useNetwork } from 'contexts/Network'; import { Status } from './Status'; import { Summary, Wrapper } from './Wrappers'; diff --git a/src/library/Nominations/index.tsx b/src/library/Nominations/index.tsx index d617f66fd8..61d381c96e 100644 --- a/src/library/Nominations/index.tsx +++ b/src/library/Nominations/index.tsx @@ -11,7 +11,7 @@ import { useStaking } from 'contexts/Staking'; import { useUi } from 'contexts/UI'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { useUnstaking } from 'hooks/useUnstaking'; import { ValidatorList } from 'library/ValidatorList'; import type { MaybeAddress } from 'types'; import { useOverlay } from '@polkadot-cloud/react/hooks'; diff --git a/src/library/Pool/index.tsx b/src/library/Pool/index.tsx index 277a6e6970..d0d31bd86d 100644 --- a/src/library/Pool/index.tsx +++ b/src/library/Pool/index.tsx @@ -12,7 +12,7 @@ import { useBondedPools } from 'contexts/Pools/BondedPools'; import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useUi } from 'contexts/UI'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; -import { usePoolCommission } from 'library/Hooks/usePoolCommission'; +import { usePoolCommission } from 'hooks/usePoolCommission'; import { FavoritePool } from 'library/ListItem/Labels/FavoritePool'; import { PoolBonded } from 'library/ListItem/Labels/PoolBonded'; import { PoolCommission } from 'library/ListItem/Labels/PoolCommission'; diff --git a/src/library/PoolList/Default.tsx b/src/library/PoolList/Default.tsx index a735786dde..861d296de2 100644 --- a/src/library/PoolList/Default.tsx +++ b/src/library/PoolList/Default.tsx @@ -15,7 +15,7 @@ import { useBondedPools } from 'contexts/Pools/BondedPools'; import { useTheme } from 'contexts/Themes'; import { useUi } from 'contexts/UI'; import { Tabs } from 'library/Filter/Tabs'; -import { usePoolFilters } from 'library/Hooks/usePoolFilters'; +import { usePoolFilters } from 'hooks/usePoolFilters'; import { FilterHeaderWrapper, List, diff --git a/src/library/SideMenu/Primary/index.tsx b/src/library/SideMenu/Primary/index.tsx index f7c825623e..a726e228a5 100644 --- a/src/library/SideMenu/Primary/index.tsx +++ b/src/library/SideMenu/Primary/index.tsx @@ -5,7 +5,7 @@ import { faCircle } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Link } from 'react-router-dom'; import { useUi } from 'contexts/UI'; -import { useDotLottieButton } from 'library/Hooks/useDotLottieButton'; +import { useDotLottieButton } from 'hooks/useDotLottieButton'; import type { PrimaryProps } from '../types'; import { Wrapper } from './Wrappers'; diff --git a/src/library/SideMenu/index.tsx b/src/library/SideMenu/index.tsx index 589f54853a..8da42525df 100644 --- a/src/library/SideMenu/index.tsx +++ b/src/library/SideMenu/index.tsx @@ -20,13 +20,13 @@ import LanguageSVG from 'img/language.svg?react'; import LogoGithubSVG from 'img/logo-github.svg?react'; import MoonOutlineSVG from 'img/moon-outline.svg?react'; import SunnyOutlineSVG from 'img/sunny-outline.svg?react'; -import { useOutsideAlerter } from 'library/Hooks'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { Heading } from './Heading/Heading'; import { Main } from './Main'; import { Secondary } from './Secondary'; import { ConnectionSymbol, Separator, Wrapper } from './Wrapper'; +import { useOutsideAlerter } from 'hooks/useOutsideAlerter'; export const SideMenu = () => { const { t } = useTranslation('base'); diff --git a/src/library/StatBoxList/types.ts b/src/library/StatBoxList/types.ts index 04cc087ce5..d02708df03 100644 --- a/src/library/StatBoxList/types.ts +++ b/src/library/StatBoxList/types.ts @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { TimeLeftFormatted } from 'library/Hooks/useTimeLeft/types'; +import type { TimeLeftFormatted } from 'hooks/useTimeLeft/types'; export interface NumberProps { label: string; diff --git a/src/library/UpdateHeader/index.tsx b/src/library/UpdateHeader/index.tsx index 3dbf180cad..e8fe1f6ca5 100644 --- a/src/library/UpdateHeader/index.tsx +++ b/src/library/UpdateHeader/index.tsx @@ -3,7 +3,7 @@ import { faAnglesRight } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { usePayeeConfig } from 'library/Hooks/usePayeeConfig'; +import { usePayeeConfig } from 'hooks/usePayeeConfig'; import { Wrapper } from './Wrapper'; interface UpdateHeaderProps { diff --git a/src/library/ValidatorList/FilterValidators.tsx b/src/library/ValidatorList/FilterValidators.tsx index 9cfffb6519..df2d180f7d 100644 --- a/src/library/ValidatorList/FilterValidators.tsx +++ b/src/library/ValidatorList/FilterValidators.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { Title } from 'library/Prompt/Title'; import { FilterListButton, FilterListWrapper } from 'library/Prompt/Wrappers'; import { useFilters } from 'contexts/Filters'; -import { useValidatorFilters } from '../Hooks/useValidatorFilters'; +import { useValidatorFilters } from '../../hooks/useValidatorFilters'; export const FilterValidators = () => { const { t } = useTranslation('library'); diff --git a/src/library/ValidatorList/Filters/FilterBadges.tsx b/src/library/ValidatorList/Filters/FilterBadges.tsx index 9d28aa20dc..c9cd987efd 100644 --- a/src/library/ValidatorList/Filters/FilterBadges.tsx +++ b/src/library/ValidatorList/Filters/FilterBadges.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { useFilters } from 'contexts/Filters'; import { Container } from 'library/Filter/Container'; import { Item } from 'library/Filter/Item'; -import { useValidatorFilters } from 'library/Hooks/useValidatorFilters'; +import { useValidatorFilters } from 'hooks/useValidatorFilters'; export const FilterBadges = () => { const { t } = useTranslation('library'); diff --git a/src/library/ValidatorList/OrderValidators.tsx b/src/library/ValidatorList/OrderValidators.tsx index 9e7ae3451c..31034e4a07 100644 --- a/src/library/ValidatorList/OrderValidators.tsx +++ b/src/library/ValidatorList/OrderValidators.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { Title } from 'library/Prompt/Title'; import { FilterListButton, FilterListWrapper } from 'library/Prompt/Wrappers'; import { useFilters } from 'contexts/Filters'; -import { useValidatorFilters } from '../Hooks/useValidatorFilters'; +import { useValidatorFilters } from '../../hooks/useValidatorFilters'; export const OrderValidators = () => { const { t } = useTranslation('library'); diff --git a/src/library/ValidatorList/index.tsx b/src/library/ValidatorList/index.tsx index e22e520b66..3cb24d4ccc 100644 --- a/src/library/ValidatorList/index.tsx +++ b/src/library/ValidatorList/index.tsx @@ -28,9 +28,9 @@ import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; -import { useNominationStatus } from 'library/Hooks/useNominationStatus'; +import { useNominationStatus } from 'hooks/useNominationStatus'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { useValidatorFilters } from '../Hooks/useValidatorFilters'; +import { useValidatorFilters } from '../../hooks/useValidatorFilters'; import { ListProvider, useList } from '../List/context'; import type { ValidatorListProps } from './types'; import { FilterHeaders } from './Filters/FilterHeaders'; diff --git a/src/modals/Accounts/index.tsx b/src/modals/Accounts/index.tsx index 9f1da77ca1..668635123b 100644 --- a/src/modals/Accounts/index.tsx +++ b/src/modals/Accounts/index.tsx @@ -31,7 +31,7 @@ import type { AccountNotStaking, } from './types'; import type { ImportedAccount } from '@polkadot-cloud/react/types'; -import { useActiveBalances } from 'library/Hooks/useActiveBalances'; +import { useActiveBalances } from 'hooks/useActiveBalances'; import type { MaybeAddress } from 'types'; import { useTransferOptions } from 'contexts/TransferOptions'; import BigNumber from 'bignumber.js'; diff --git a/src/modals/BalanceTest/index.tsx b/src/modals/BalanceTest/index.tsx index 56fd912dee..498b7c3d9c 100644 --- a/src/modals/BalanceTest/index.tsx +++ b/src/modals/BalanceTest/index.tsx @@ -6,8 +6,8 @@ import { unitToPlanck } from '@polkadot-cloud/utils'; import { useApi } from 'contexts/Api'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useTxMeta } from 'contexts/TxMeta'; -import { useBatchCall } from 'library/Hooks/useBatchCall'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useBatchCall } from 'hooks/useBatchCall'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useEffect } from 'react'; diff --git a/src/modals/Bond/index.tsx b/src/modals/Bond/index.tsx index bdc20b8bc9..662e9f13fd 100644 --- a/src/modals/Bond/index.tsx +++ b/src/modals/Bond/index.tsx @@ -11,9 +11,9 @@ import { useActivePools } from 'contexts/Pools/ActivePools'; import { useTransferOptions } from 'contexts/TransferOptions'; import { BondFeedback } from 'library/Form/Bond/BondFeedback'; import { Warning } from 'library/Form/Warning'; -import { useBondGreatestFee } from 'library/Hooks/useBondGreatestFee'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useBondGreatestFee } from 'hooks/useBondGreatestFee'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; diff --git a/src/modals/ChangePoolRoles/index.tsx b/src/modals/ChangePoolRoles/index.tsx index 53845ead90..e2870bb7ad 100644 --- a/src/modals/ChangePoolRoles/index.tsx +++ b/src/modals/ChangePoolRoles/index.tsx @@ -5,7 +5,7 @@ import { ModalPadding } from '@polkadot-cloud/react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; diff --git a/src/modals/ClaimPayouts/Forms.tsx b/src/modals/ClaimPayouts/Forms.tsx index cb82a47818..433ff146b0 100644 --- a/src/modals/ClaimPayouts/Forms.tsx +++ b/src/modals/ClaimPayouts/Forms.tsx @@ -15,11 +15,11 @@ import { forwardRef, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from '@polkadot-cloud/react/hooks'; -import { useBatchCall } from 'library/Hooks/useBatchCall'; +import { useBatchCall } from 'hooks/useBatchCall'; import type { AnyApi } from 'types'; import { usePayouts } from 'contexts/Payouts'; import { useNetwork } from 'contexts/Network'; diff --git a/src/modals/ClaimReward/index.tsx b/src/modals/ClaimReward/index.tsx index 3166cf0272..9402eb43ae 100644 --- a/src/modals/ClaimReward/index.tsx +++ b/src/modals/ClaimReward/index.tsx @@ -9,8 +9,8 @@ import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; diff --git a/src/modals/JoinPool/index.tsx b/src/modals/JoinPool/index.tsx index a54a0bb2cb..a156060472 100644 --- a/src/modals/JoinPool/index.tsx +++ b/src/modals/JoinPool/index.tsx @@ -14,10 +14,10 @@ import { useTransferOptions } from 'contexts/TransferOptions'; import { useTxMeta } from 'contexts/TxMeta'; import { BondFeedback } from 'library/Form/Bond/BondFeedback'; import { ClaimPermissionInput } from 'library/Form/ClaimPermissionInput'; -import { useBatchCall } from 'library/Hooks/useBatchCall'; -import { useBondGreatestFee } from 'library/Hooks/useBondGreatestFee'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useBatchCall } from 'hooks/useBatchCall'; +import { useBondGreatestFee } from 'hooks/useBondGreatestFee'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from '@polkadot-cloud/react/hooks'; diff --git a/src/modals/ManageFastUnstake/index.tsx b/src/modals/ManageFastUnstake/index.tsx index c491deee12..bfe812366b 100644 --- a/src/modals/ManageFastUnstake/index.tsx +++ b/src/modals/ManageFastUnstake/index.tsx @@ -16,9 +16,9 @@ import { useBonded } from 'contexts/Bonded'; import { useFastUnstake } from 'contexts/FastUnstake'; import { useTransferOptions } from 'contexts/TransferOptions'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; +import { useUnstaking } from 'hooks/useUnstaking'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; diff --git a/src/modals/ManagePool/Forms/ClaimCommission/index.tsx b/src/modals/ManagePool/Forms/ClaimCommission/index.tsx index 6bdd978a5d..f609d7e884 100644 --- a/src/modals/ManagePool/Forms/ClaimCommission/index.tsx +++ b/src/modals/ManagePool/Forms/ClaimCommission/index.tsx @@ -17,8 +17,8 @@ import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; diff --git a/src/modals/ManagePool/Forms/LeavePool/index.tsx b/src/modals/ManagePool/Forms/LeavePool/index.tsx index 38343a0bcc..5d83a4505e 100644 --- a/src/modals/ManagePool/Forms/LeavePool/index.tsx +++ b/src/modals/ManagePool/Forms/LeavePool/index.tsx @@ -22,10 +22,10 @@ import { useApi } from 'contexts/Api'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { useTransferOptions } from 'contexts/TransferOptions'; import { Warning } from 'library/Form/Warning'; -import { useErasToTimeLeft } from 'library/Hooks/useErasToTimeLeft'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; -import { timeleftAsString } from 'library/Hooks/useTimeLeft/utils'; +import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; +import { timeleftAsString } from 'hooks/useTimeLeft/utils'; import { SubmitTx } from 'library/SubmitTx'; import { StaticNote } from 'modals/Utils/StaticNote'; import { useOverlay } from '@polkadot-cloud/react/hooks'; diff --git a/src/modals/ManagePool/Forms/ManageCommission/index.tsx b/src/modals/ManagePool/Forms/ManageCommission/index.tsx index aa0a88380a..a9d5a8b154 100644 --- a/src/modals/ManagePool/Forms/ManageCommission/index.tsx +++ b/src/modals/ManagePool/Forms/ManageCommission/index.tsx @@ -18,9 +18,9 @@ import { useHelp } from 'contexts/Help'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { Warning } from 'library/Form/Warning'; -import { useBatchCall } from 'library/Hooks/useBatchCall'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useBatchCall } from 'hooks/useBatchCall'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import 'rc-slider/assets/index.css'; import { useOverlay } from '@polkadot-cloud/react/hooks'; diff --git a/src/modals/ManagePool/Forms/RenamePool/index.tsx b/src/modals/ManagePool/Forms/RenamePool/index.tsx index 3d968a1ea4..a856d249be 100644 --- a/src/modals/ManagePool/Forms/RenamePool/index.tsx +++ b/src/modals/ManagePool/Forms/RenamePool/index.tsx @@ -15,8 +15,8 @@ import { useApi } from 'contexts/Api'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx b/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx index e8a94f8d8c..a898fbef64 100644 --- a/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx +++ b/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx @@ -15,8 +15,8 @@ import { useActivePools } from 'contexts/Pools/ActivePools'; import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { ClaimPermissionInput } from 'library/Form/ClaimPermissionInput'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/modals/ManagePool/Forms/SetPoolState/index.tsx b/src/modals/ManagePool/Forms/SetPoolState/index.tsx index c51634a849..06f3f65a2a 100644 --- a/src/modals/ManagePool/Forms/SetPoolState/index.tsx +++ b/src/modals/ManagePool/Forms/SetPoolState/index.tsx @@ -15,8 +15,8 @@ import { useApi } from 'contexts/Api'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/modals/StopNominations/index.tsx b/src/modals/StopNominations/index.tsx index 5c2977072d..1574c822eb 100644 --- a/src/modals/StopNominations/index.tsx +++ b/src/modals/StopNominations/index.tsx @@ -12,8 +12,8 @@ import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; diff --git a/src/modals/Unbond/index.tsx b/src/modals/Unbond/index.tsx index 0059337e17..ab8054391a 100644 --- a/src/modals/Unbond/index.tsx +++ b/src/modals/Unbond/index.tsx @@ -14,10 +14,10 @@ import { useTransferOptions } from 'contexts/TransferOptions'; import { useTxMeta } from 'contexts/TxMeta'; import { UnbondFeedback } from 'library/Form/Unbond/UnbondFeedback'; import { Warning } from 'library/Form/Warning'; -import { useErasToTimeLeft } from 'library/Hooks/useErasToTimeLeft'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; -import { timeleftAsString } from 'library/Hooks/useTimeLeft/utils'; +import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; +import { timeleftAsString } from 'hooks/useTimeLeft/utils'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { StaticNote } from 'modals/Utils/StaticNote'; diff --git a/src/modals/UnlockChunks/Chunk.tsx b/src/modals/UnlockChunks/Chunk.tsx index 641a852ae0..9aa149b153 100644 --- a/src/modals/UnlockChunks/Chunk.tsx +++ b/src/modals/UnlockChunks/Chunk.tsx @@ -8,9 +8,9 @@ import { fromUnixTime } from 'date-fns'; import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Countdown } from 'library/Countdown'; -import { useErasToTimeLeft } from 'library/Hooks/useErasToTimeLeft'; -import { useTimeLeft } from 'library/Hooks/useTimeLeft'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; +import { useTimeLeft } from 'hooks/useTimeLeft'; +import { useUnstaking } from 'hooks/useUnstaking'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { ChunkWrapper } from './Wrappers'; diff --git a/src/modals/UnlockChunks/Forms.tsx b/src/modals/UnlockChunks/Forms.tsx index 6bc5ab9428..58928fc755 100644 --- a/src/modals/UnlockChunks/Forms.tsx +++ b/src/modals/UnlockChunks/Forms.tsx @@ -21,8 +21,8 @@ import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useFavoritePools } from 'contexts/Pools/FavoritePools'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; diff --git a/src/modals/UnlockChunks/Overview.tsx b/src/modals/UnlockChunks/Overview.tsx index 6ee5505e7c..f40e08fe67 100644 --- a/src/modals/UnlockChunks/Overview.tsx +++ b/src/modals/UnlockChunks/Overview.tsx @@ -11,9 +11,9 @@ import type { Dispatch, ForwardedRef, SetStateAction } from 'react'; import { forwardRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useErasToTimeLeft } from 'library/Hooks/useErasToTimeLeft'; -import { timeleftAsString } from 'library/Hooks/useTimeLeft/utils'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; +import { timeleftAsString } from 'hooks/useTimeLeft/utils'; +import { useUnstaking } from 'hooks/useUnstaking'; import { StatWrapper, StatsWrapper } from 'library/Modal/Wrappers'; import { StaticNote } from 'modals/Utils/StaticNote'; import type { AnyJson, BondFor } from 'types'; diff --git a/src/modals/Unstake/index.tsx b/src/modals/Unstake/index.tsx index b657a82a9d..167c59fb89 100644 --- a/src/modals/Unstake/index.tsx +++ b/src/modals/Unstake/index.tsx @@ -14,11 +14,11 @@ import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; import { useTransferOptions } from 'contexts/TransferOptions'; import { Warning } from 'library/Form/Warning'; -import { useBatchCall } from 'library/Hooks/useBatchCall'; -import { useErasToTimeLeft } from 'library/Hooks/useErasToTimeLeft'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; -import { timeleftAsString } from 'library/Hooks/useTimeLeft/utils'; +import { useBatchCall } from 'hooks/useBatchCall'; +import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; +import { timeleftAsString } from 'hooks/useTimeLeft/utils'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { StaticNote } from 'modals/Utils/StaticNote'; diff --git a/src/modals/UpdateController/index.tsx b/src/modals/UpdateController/index.tsx index bfae68f50c..34036489f1 100644 --- a/src/modals/UpdateController/index.tsx +++ b/src/modals/UpdateController/index.tsx @@ -6,8 +6,8 @@ import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; import { Warning } from 'library/Form/Warning'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; diff --git a/src/modals/UpdatePayee/index.tsx b/src/modals/UpdatePayee/index.tsx index 30f86becc5..8107720e23 100644 --- a/src/modals/UpdatePayee/index.tsx +++ b/src/modals/UpdatePayee/index.tsx @@ -9,9 +9,9 @@ import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; import type { PayeeConfig, PayeeOptions } from 'contexts/Setup/types'; import { Warning } from 'library/Form/Warning'; -import { usePayeeConfig } from 'library/Hooks/usePayeeConfig'; -import { useSignerWarnings } from 'library/Hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { usePayeeConfig } from 'hooks/usePayeeConfig'; +import { useSignerWarnings } from 'hooks/useSignerWarnings'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Title } from 'library/Modal/Title'; import { PayeeInput } from 'library/PayeeInput'; import { SelectItems } from 'library/SelectItems'; diff --git a/src/modals/ValidatorGeo/index.tsx b/src/modals/ValidatorGeo/index.tsx index b9f26db2f3..5c254d816c 100644 --- a/src/modals/ValidatorGeo/index.tsx +++ b/src/modals/ValidatorGeo/index.tsx @@ -10,7 +10,7 @@ import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; import { GeoDonut } from 'library/Graphs/GeoDonut'; import { formatSize } from 'library/Graphs/Utils'; import { GraphWrapper } from 'library/Graphs/Wrapper'; -import { useSize } from 'library/Hooks/useSize'; +import { useSize } from 'hooks/useSize'; import { Title } from 'library/Modal/Title'; import { StatusLabel } from 'library/StatusLabel'; import { PolkawatchApi, type ValidatorDetail } from '@polkawatch/ddp-client'; diff --git a/src/modals/ValidatorMetrics/index.tsx b/src/modals/ValidatorMetrics/index.tsx index bbaa2213cb..09399602aa 100644 --- a/src/modals/ValidatorMetrics/index.tsx +++ b/src/modals/ValidatorMetrics/index.tsx @@ -12,7 +12,7 @@ import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; import { EraPoints as EraPointsGraph } from 'library/Graphs/EraPoints'; import { formatSize } from 'library/Graphs/Utils'; import { GraphWrapper } from 'library/Graphs/Wrapper'; -import { useSize } from 'library/Hooks/useSize'; +import { useSize } from 'hooks/useSize'; import { Title } from 'library/Modal/Title'; import { StatWrapper, StatsWrapper } from 'library/Modal/Wrappers'; import { StatusLabel } from 'library/StatusLabel'; diff --git a/src/pages/Nominate/Active/ManageBond.tsx b/src/pages/Nominate/Active/ManageBond.tsx index 6ae2db6bf4..616d055f9b 100644 --- a/src/pages/Nominate/Active/ManageBond.tsx +++ b/src/pages/Nominate/Active/ManageBond.tsx @@ -17,7 +17,7 @@ import { useStaking } from 'contexts/Staking'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useUi } from 'contexts/UI'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { useUnstaking } from 'hooks/useUnstaking'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { BondedChart } from 'library/BarChart/BondedChart'; import { useNetwork } from 'contexts/Network'; diff --git a/src/pages/Nominate/Active/Status/NominationStatus.tsx b/src/pages/Nominate/Active/Status/NominationStatus.tsx index da609b7456..c1ec31d9d4 100644 --- a/src/pages/Nominate/Active/Status/NominationStatus.tsx +++ b/src/pages/Nominate/Active/Status/NominationStatus.tsx @@ -13,8 +13,8 @@ import { useFastUnstake } from 'contexts/FastUnstake'; import { useSetup } from 'contexts/Setup'; import { useStaking } from 'contexts/Staking'; import { useUi } from 'contexts/UI'; -import { useNominationStatus } from 'library/Hooks/useNominationStatus'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { useNominationStatus } from 'hooks/useNominationStatus'; +import { useUnstaking } from 'hooks/useUnstaking'; import { Stat } from 'library/Stat'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx b/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx index e9a0e3056c..c55b4e4343 100644 --- a/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx +++ b/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx @@ -5,8 +5,8 @@ import { faGear, faWallet } from '@fortawesome/free-solid-svg-icons'; import { useTranslation } from 'react-i18next'; import { useStaking } from 'contexts/Staking'; import { useUi } from 'contexts/UI'; -import { usePayeeConfig } from 'library/Hooks/usePayeeConfig'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { usePayeeConfig } from 'hooks/usePayeeConfig'; +import { useUnstaking } from 'hooks/useUnstaking'; import { Stat } from 'library/Stat'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/pages/Nominate/Active/UnstakePrompts.tsx b/src/pages/Nominate/Active/UnstakePrompts.tsx index c3b8230e86..abcfc26ae2 100644 --- a/src/pages/Nominate/Active/UnstakePrompts.tsx +++ b/src/pages/Nominate/Active/UnstakePrompts.tsx @@ -9,7 +9,7 @@ import { useTheme } from 'contexts/Themes'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useUi } from 'contexts/UI'; import { CardWrapper } from 'library/Card/Wrappers'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { useUnstaking } from 'hooks/useUnstaking'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/pages/Nominate/Active/index.tsx b/src/pages/Nominate/Active/index.tsx index a98e757078..13e6d234c4 100644 --- a/src/pages/Nominate/Active/index.tsx +++ b/src/pages/Nominate/Active/index.tsx @@ -14,7 +14,7 @@ import { useHelp } from 'contexts/Help'; import { useStaking } from 'contexts/Staking'; import { useUi } from 'contexts/UI'; import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; -import { useUnstaking } from 'library/Hooks/useUnstaking'; +import { useUnstaking } from 'hooks/useUnstaking'; import { StatBoxList } from 'library/StatBoxList'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/pages/Nominate/Setup/Payee/index.tsx b/src/pages/Nominate/Setup/Payee/index.tsx index 3f9e9ad554..7721d38050 100644 --- a/src/pages/Nominate/Setup/Payee/index.tsx +++ b/src/pages/Nominate/Setup/Payee/index.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'; import { useSetup } from 'contexts/Setup'; import type { PayeeOptions } from 'contexts/Setup/types'; import { Spacer } from 'library/Form/Wrappers'; -import { usePayeeConfig } from 'library/Hooks/usePayeeConfig'; +import { usePayeeConfig } from 'hooks/usePayeeConfig'; import { PayeeInput } from 'library/PayeeInput'; import { SelectItems } from 'library/SelectItems'; import { SelectItem } from 'library/SelectItems/Item'; diff --git a/src/pages/Nominate/Setup/Summary/index.tsx b/src/pages/Nominate/Setup/Summary/index.tsx index 5170413f98..28718d09ec 100644 --- a/src/pages/Nominate/Setup/Summary/index.tsx +++ b/src/pages/Nominate/Setup/Summary/index.tsx @@ -8,9 +8,9 @@ import BigNumber from 'bignumber.js'; import { useTranslation } from 'react-i18next'; import { useSetup } from 'contexts/Setup'; import { Warning } from 'library/Form/Warning'; -import { useBatchCall } from 'library/Hooks/useBatchCall'; -import { usePayeeConfig } from 'library/Hooks/usePayeeConfig'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useBatchCall } from 'hooks/useBatchCall'; +import { usePayeeConfig } from 'hooks/usePayeeConfig'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Header } from 'library/SetupSteps/Header'; import { MotionContainer } from 'library/SetupSteps/MotionContainer'; import type { SetupStepProps } from 'library/SetupSteps/types'; diff --git a/src/pages/Overview/BalanceChart.tsx b/src/pages/Overview/BalanceChart.tsx index e49d9bc1d5..b6390e61e7 100644 --- a/src/pages/Overview/BalanceChart.tsx +++ b/src/pages/Overview/BalanceChart.tsx @@ -18,7 +18,7 @@ import { BarSegment } from 'library/BarChart/BarSegment'; import { LegendItem } from 'library/BarChart/LegendItem'; import { Bar, BarChartWrapper, Legend } from 'library/BarChart/Wrappers'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; -import { usePrices } from 'library/Hooks/usePrices'; +import { usePrices } from 'hooks/usePrices'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/pages/Overview/NetworkSats/index.tsx b/src/pages/Overview/NetworkSats/index.tsx index 34fa727d85..90b53fb3bd 100644 --- a/src/pages/Overview/NetworkSats/index.tsx +++ b/src/pages/Overview/NetworkSats/index.tsx @@ -8,7 +8,7 @@ import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; import { StatsHead } from 'library/StatsHead'; import { Announcements } from './Announcements'; import { Wrapper } from './Wrappers'; -import { useAverageRewardRate } from 'library/Hooks/useAverageRewardRate'; +import { useAverageRewardRate } from 'hooks/useAverageRewardRate'; import { useApi } from 'contexts/Api'; export const NetworkStats = () => { diff --git a/src/pages/Overview/Payouts.tsx b/src/pages/Overview/Payouts.tsx index e6d8b27ded..5edbf0e000 100644 --- a/src/pages/Overview/Payouts.tsx +++ b/src/pages/Overview/Payouts.tsx @@ -10,9 +10,9 @@ import { PayoutBar } from 'library/Graphs/PayoutBar'; import { PayoutLine } from 'library/Graphs/PayoutLine'; import { formatRewardsForGraphs, formatSize } from 'library/Graphs/Utils'; import { GraphWrapper } from 'library/Graphs/Wrapper'; -import { useSize } from 'library/Hooks/useSize'; +import { useSize } from 'hooks/useSize'; import { StatusLabel } from 'library/StatusLabel'; -import { useSubscanData } from 'library/Hooks/useSubscanData'; +import { useSubscanData } from 'hooks/useSubscanData'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; import { Odometer } from '@polkadot-cloud/react'; import { locales } from 'locale'; diff --git a/src/pages/Overview/StakeStatus/Tips/Syncing.tsx b/src/pages/Overview/StakeStatus/Tips/Syncing.tsx index 810e8010de..2e6aeeb385 100644 --- a/src/pages/Overview/StakeStatus/Tips/Syncing.tsx +++ b/src/pages/Overview/StakeStatus/Tips/Syncing.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import { useTranslation } from 'react-i18next'; -import { useDotLottieButton } from 'library/Hooks/useDotLottieButton'; +import { useDotLottieButton } from 'hooks/useDotLottieButton'; import { ItemInnerWrapper, ItemWrapper, ItemsWrapper } from './Wrappers'; export const Syncing = () => { diff --git a/src/pages/Overview/StakeStatus/Tips/index.tsx b/src/pages/Overview/StakeStatus/Tips/index.tsx index 1e37580289..b030e4f5ec 100644 --- a/src/pages/Overview/StakeStatus/Tips/index.tsx +++ b/src/pages/Overview/StakeStatus/Tips/index.tsx @@ -12,7 +12,7 @@ import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useStaking } from 'contexts/Staking'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useUi } from 'contexts/UI'; -import { useFillVariables } from 'library/Hooks/useFillVariables'; +import { useFillVariables } from 'hooks/useFillVariables'; import type { AnyJson } from 'types'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/pages/Overview/Stats/ActiveEraTimeLeft.tsx b/src/pages/Overview/Stats/ActiveEraTimeLeft.tsx index 12432baafc..ee0c49703b 100644 --- a/src/pages/Overview/Stats/ActiveEraTimeLeft.tsx +++ b/src/pages/Overview/Stats/ActiveEraTimeLeft.tsx @@ -5,9 +5,9 @@ import BigNumber from 'bignumber.js'; import { fromUnixTime } from 'date-fns'; import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { useEraTimeLeft } from 'library/Hooks/useEraTimeLeft'; -import { useTimeLeft } from 'library/Hooks/useTimeLeft'; -import { fromNow } from 'library/Hooks/useTimeLeft/utils'; +import { useEraTimeLeft } from 'hooks/useEraTimeLeft'; +import { useTimeLeft } from 'hooks/useTimeLeft'; +import { fromNow } from 'hooks/useTimeLeft/utils'; import { Timeleft } from 'library/StatBoxList/Timeleft'; import { useApi } from 'contexts/Api'; diff --git a/src/pages/Overview/Stats/AveragelRewardRate.tsx b/src/pages/Overview/Stats/AveragelRewardRate.tsx index 03504afae8..8aa67d16b8 100644 --- a/src/pages/Overview/Stats/AveragelRewardRate.tsx +++ b/src/pages/Overview/Stats/AveragelRewardRate.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import { Text } from 'library/StatBoxList/Text'; -import { useAverageRewardRate } from 'library/Hooks/useAverageRewardRate'; +import { useAverageRewardRate } from 'hooks/useAverageRewardRate'; import { useTranslation } from 'react-i18next'; export const AverageRewardRateStat = () => { diff --git a/src/pages/Payouts/index.tsx b/src/pages/Payouts/index.tsx index bfe4967d78..d84f9aaa9e 100644 --- a/src/pages/Payouts/index.tsx +++ b/src/pages/Payouts/index.tsx @@ -14,14 +14,14 @@ import { PayoutBar } from 'library/Graphs/PayoutBar'; import { PayoutLine } from 'library/Graphs/PayoutLine'; import { formatSize } from 'library/Graphs/Utils'; import { GraphWrapper } from 'library/Graphs/Wrapper'; -import { useSize } from 'library/Hooks/useSize'; +import { useSize } from 'hooks/useSize'; import { StatBoxList } from 'library/StatBoxList'; import { StatusLabel } from 'library/StatusLabel'; import type { AnySubscan, PageProps } from 'types'; import { PluginLabel } from 'library/PluginLabel'; import { PayoutList } from './PayoutList'; import { LastEraPayoutStat } from './Stats/LastEraPayout'; -import { useSubscanData } from 'library/Hooks/useSubscanData'; +import { useSubscanData } from 'hooks/useSubscanData'; import { SubscanController } from 'static/SubscanController'; import { locales } from 'locale'; diff --git a/src/pages/Pools/Create/Summary/index.tsx b/src/pages/Pools/Create/Summary/index.tsx index 5518b8e9a1..f3c9ef004c 100644 --- a/src/pages/Pools/Create/Summary/index.tsx +++ b/src/pages/Pools/Create/Summary/index.tsx @@ -10,8 +10,8 @@ import { useBondedPools } from 'contexts/Pools/BondedPools'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useSetup } from 'contexts/Setup'; import { Warning } from 'library/Form/Warning'; -import { useBatchCall } from 'library/Hooks/useBatchCall'; -import { useSubmitExtrinsic } from 'library/Hooks/useSubmitExtrinsic'; +import { useBatchCall } from 'hooks/useBatchCall'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Header } from 'library/SetupSteps/Header'; import { MotionContainer } from 'library/SetupSteps/MotionContainer'; import type { SetupStepProps } from 'library/SetupSteps/types'; diff --git a/src/pages/Pools/Home/PoolStats/index.tsx b/src/pages/Pools/Home/PoolStats/index.tsx index 512c64848e..41a08d5f7a 100644 --- a/src/pages/Pools/Home/PoolStats/index.tsx +++ b/src/pages/Pools/Home/PoolStats/index.tsx @@ -6,7 +6,7 @@ import BigNumber from 'bignumber.js'; import { useTranslation } from 'react-i18next'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; -import { usePoolCommission } from 'library/Hooks/usePoolCommission'; +import { usePoolCommission } from 'hooks/usePoolCommission'; import { StatsHead } from 'library/StatsHead'; import { useNetwork } from 'contexts/Network'; import { Announcements } from './Announcements'; diff --git a/src/pages/Pools/Home/Status/PoolStatus.tsx b/src/pages/Pools/Home/Status/PoolStatus.tsx index ad5ecfd39b..d1e530a6e3 100644 --- a/src/pages/Pools/Home/Status/PoolStatus.tsx +++ b/src/pages/Pools/Home/Status/PoolStatus.tsx @@ -8,7 +8,7 @@ import { import { useTranslation } from 'react-i18next'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { useUi } from 'contexts/UI'; -import { useNominationStatus } from 'library/Hooks/useNominationStatus'; +import { useNominationStatus } from 'hooks/useNominationStatus'; import { Stat } from 'library/Stat'; export const PoolStatus = () => { From 764a880e0640416a5ad88b69d2e84887624b8f11 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 31 Jan 2024 11:32:56 +0800 Subject: [PATCH 09/51] feat(refactor): Pool memberships to active balances (#1915) --- src/Providers.tsx | 2 - .../PoolMembers/Prompts/UnbondMember.tsx | 2 +- .../PoolMembers/Prompts/WithdrawMember.tsx | 2 +- src/contexts/Balances/defaults.ts | 1 + src/contexts/Balances/index.tsx | 15 +- src/contexts/Balances/types.ts | 3 + src/contexts/Pools/ActivePools/index.tsx | 6 +- .../Pools/PoolMemberships/defaults.ts | 12 - src/contexts/Pools/PoolMemberships/index.tsx | 210 ------------------ src/contexts/Pools/PoolMemberships/types.ts | 35 --- src/contexts/Pools/types.ts | 20 ++ src/contexts/Setup/index.tsx | 6 +- src/contexts/TransferOptions/index.tsx | 9 +- src/hooks/useActiveBalances/index.tsx | 13 ++ .../Form/ClaimPermissionInput/index.tsx | 23 +- src/library/Form/types.ts | 8 + src/library/Graphs/PayoutBar.tsx | 8 +- src/library/Graphs/PayoutLine.tsx | 9 +- src/library/Pool/index.tsx | 5 +- src/library/SideMenu/Main.tsx | 8 +- src/library/StatusLabel/index.tsx | 10 +- src/locale/cn/base.json | 6 - src/locale/cn/library.json | 6 + src/locale/en/base.json | 6 - src/locale/en/library.json | 6 + src/modals/AccountPoolRoles/index.tsx | 10 +- src/modals/Accounts/index.tsx | 11 +- src/modals/Accounts/types.ts | 2 +- src/modals/JoinPool/index.tsx | 2 +- .../Forms/SetClaimPermission/index.tsx | 8 +- src/modals/UnlockChunks/Forms.tsx | 7 +- src/pages/Overview/StakeStatus/Tips/index.tsx | 9 +- .../Pools/Home/Status/useStatusButtons.tsx | 5 +- src/static/BalancesController/index.ts | 63 ++++++ 34 files changed, 223 insertions(+), 325 deletions(-) delete mode 100644 src/contexts/Pools/PoolMemberships/defaults.ts delete mode 100644 src/contexts/Pools/PoolMemberships/index.tsx delete mode 100644 src/contexts/Pools/PoolMemberships/types.ts diff --git a/src/Providers.tsx b/src/Providers.tsx index ede98153e6..1c146d3262 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -22,7 +22,6 @@ import { PluginsProvider } from 'contexts/Plugins'; import { ActivePoolsProvider } from 'contexts/Pools/ActivePools'; import { BondedPoolsProvider } from 'contexts/Pools/BondedPools'; import { PoolMembersProvider } from 'contexts/Pools/PoolMembers'; -import { PoolMembershipsProvider } from 'contexts/Pools/PoolMemberships'; import { FavoritePoolsProvider } from 'contexts/Pools/FavoritePools'; import { ProxiesProvider } from 'contexts/Proxies'; import { SetupProvider } from 'contexts/Setup'; @@ -77,7 +76,6 @@ export const Providers = () => { StakingProvider, FavoritePoolsProvider, BondedPoolsProvider, - PoolMembershipsProvider, PoolMembersProvider, ActivePoolsProvider, TransferOptionsProvider, diff --git a/src/canvas/PoolMembers/Prompts/UnbondMember.tsx b/src/canvas/PoolMembers/Prompts/UnbondMember.tsx index 2aabaf9a2f..456bb4f070 100644 --- a/src/canvas/PoolMembers/Prompts/UnbondMember.tsx +++ b/src/canvas/PoolMembers/Prompts/UnbondMember.tsx @@ -30,7 +30,7 @@ import { StaticNote } from 'modals/Utils/StaticNote'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { usePrompt } from 'contexts/Prompt'; -import type { PoolMembership } from 'contexts/Pools/PoolMemberships/types'; +import type { PoolMembership } from 'contexts/Pools/types'; import { Title } from 'library/Prompt/Title'; export const UnbondMember = ({ diff --git a/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx b/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx index 643a1fddf1..e4846b0f09 100644 --- a/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx +++ b/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx @@ -25,7 +25,7 @@ import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import type { PoolMembership } from 'contexts/Pools/PoolMemberships/types'; +import type { PoolMembership } from 'contexts/Pools/types'; import { usePrompt } from 'contexts/Prompt'; import { Title } from 'library/Prompt/Title'; import { useTranslation } from 'react-i18next'; diff --git a/src/contexts/Balances/defaults.ts b/src/contexts/Balances/defaults.ts index eb87b3d1c0..1250663702 100644 --- a/src/contexts/Balances/defaults.ts +++ b/src/contexts/Balances/defaults.ts @@ -17,5 +17,6 @@ export const defaultBalancesContext: BalancesContextInterface = { getBalance: (address) => defaultBalance, getLedger: (source) => defaultLedger, getPayee: (address) => defaultPayee, + getPoolMembership: (address) => null, balancesInitialSynced: false, }; diff --git a/src/contexts/Balances/index.tsx b/src/contexts/Balances/index.tsx index 67aa8402d7..a22510601f 100644 --- a/src/contexts/Balances/index.tsx +++ b/src/contexts/Balances/index.tsx @@ -27,10 +27,16 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { const controller = getBondedAccount(activeAccount); // Listen to balance updates for the active account, active proxy and controller.. - const { activeBalances, getLocks, getBalance, getLedger, getPayee } = - useActiveBalances({ - accounts: [activeAccount, activeProxy, controller], - }); + const { + activeBalances, + getLocks, + getBalance, + getLedger, + getPayee, + getPoolMembership, + } = useActiveBalances({ + accounts: [activeAccount, activeProxy, controller], + }); // Store whether balances for all imported accounts have been synced on initial page load. const [balancesInitialSynced, setBalancesInitialSynced] = @@ -92,6 +98,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { getBalance, getLedger, getPayee, + getPoolMembership, balancesInitialSynced, }} > diff --git a/src/contexts/Balances/types.ts b/src/contexts/Balances/types.ts index 0762e2ca5e..660e5d297c 100644 --- a/src/contexts/Balances/types.ts +++ b/src/contexts/Balances/types.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import type BigNumber from 'bignumber.js'; +import type { PoolMembership } from 'contexts/Pools/types'; import type { PayeeConfig } from 'contexts/Setup/types'; import type { MaybeAddress } from 'types'; @@ -12,6 +13,7 @@ export interface BalancesContextInterface { getBalance: (address: MaybeAddress) => Balance; getLedger: (source: ActiveLedgerSource) => Ledger; getPayee: (address: MaybeAddress) => PayeeConfig; + getPoolMembership: (address: MaybeAddress) => PoolMembership | null; balancesInitialSynced: boolean; } @@ -21,6 +23,7 @@ export interface ActiveBalance { ledger: Ledger; balances: Balances; payee: PayeeConfig; + poolMembership: PoolMembership; } export interface Balances { diff --git a/src/contexts/Pools/ActivePools/index.tsx b/src/contexts/Pools/ActivePools/index.tsx index 868d985363..20d0e13dd2 100644 --- a/src/contexts/Pools/ActivePools/index.tsx +++ b/src/contexts/Pools/ActivePools/index.tsx @@ -20,13 +20,13 @@ import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useApi } from '../../Api'; import { useBondedPools } from '../BondedPools'; -import { usePoolMemberships } from '../PoolMemberships'; import * as defaults from './defaults'; import { usePoolMembers } from '../PoolMembers'; import type { ActivePool, ActivePoolsContextState, PoolTargets } from './types'; import type { PoolAddresses } from '../BondedPools/types'; import { SubscanController } from 'static/SubscanController'; import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; +import { useBalances } from 'contexts/Balances'; export const ActivePoolsContext = createContext( defaults.defaultActivePoolContext @@ -39,12 +39,14 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { const { api, isReady } = useApi(); const { eraStakers } = useStaking(); const { pluginEnabled } = usePlugins(); - const { membership } = usePoolMemberships(); + const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const createPoolAccounts = useCreatePoolAccounts(); const { getMembersOfPoolFromNode } = usePoolMembers(); const { getAccountPools, bondedPools } = useBondedPools(); + const membership = getPoolMembership(activeAccount); + // Determine active pools to subscribe to. const accountPools = useMemo(() => { const newAccountPools = Object.keys(getAccountPools(activeAccount) || {}); diff --git a/src/contexts/Pools/PoolMemberships/defaults.ts b/src/contexts/Pools/PoolMemberships/defaults.ts deleted file mode 100644 index d2ba9f0d63..0000000000 --- a/src/contexts/Pools/PoolMemberships/defaults.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import type { PoolMembership, PoolMembershipsContextState } from './types'; - -export const poolMembership: PoolMembership | null = null; - -export const defaultPoolMembershipsContext: PoolMembershipsContextState = { - memberships: [], - membership: null, - claimPermissionConfig: [], -}; diff --git a/src/contexts/Pools/PoolMemberships/index.tsx b/src/contexts/Pools/PoolMemberships/index.tsx deleted file mode 100644 index 90e1801ee6..0000000000 --- a/src/contexts/Pools/PoolMemberships/index.tsx +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { rmCommas, setStateWithRef } from '@polkadot-cloud/utils'; -import BigNumber from 'bignumber.js'; -import type { ReactNode } from 'react'; -import { createContext, useContext, useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import type { - ClaimPermissionConfig, - PoolMembership, - PoolMembershipsContextState, -} from './types'; -import type { AnyApi, Fn } from 'types'; -import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; -import { useNetwork } from 'contexts/Network'; -import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; -import { useApi } from '../../Api'; -import * as defaults from './defaults'; - -export const PoolMembershipsContext = - createContext( - defaults.defaultPoolMembershipsContext - ); - -export const usePoolMemberships = () => useContext(PoolMembershipsContext); - -export const PoolMembershipsProvider = ({ - children, -}: { - children: ReactNode; -}) => { - const { t } = useTranslation('base'); - const { network } = useNetwork(); - const { api, isReady } = useApi(); - const { accounts } = useImportedAccounts(); - const { activeAccount } = useActiveAccounts(); - - // Stores pool memberships for the imported accounts. - const [poolMemberships, setPoolMemberships] = useState([]); - const poolMembershipsRef = useRef(poolMemberships); - - // Stores pool membership unsubs. - const unsubs = useRef([]); - - // subscribe to account pool memberships - const getPoolMemberships = async () => { - Promise.all( - accounts.map(({ address }) => subscribeToPoolMembership(address)) - ); - }; - - // unsubscribe from all pool memberships - const unsubAll = () => { - Object.values(unsubs.current).forEach((v: Fn) => v()); - }; - - // subscribe to an account's pool membership - const subscribeToPoolMembership = async (address: string) => { - if (!api) { - return undefined; - } - - const unsub = await api.queryMulti( - [ - [api.query.nominationPools.poolMembers, address], - [api.query.nominationPools.claimPermissions, address], - ], - ([poolMember, claimPermission]) => { - handleMembership(poolMember, claimPermission); - } - ); - - const handleMembership = async ( - poolMember: AnyApi, - claimPermission?: AnyApi - ) => { - let membership = poolMember?.unwrapOr(undefined)?.toHuman(); - - if (membership) { - // format pool's unlocking chunks - const unbondingEras: AnyApi = membership.unbondingEras; - - const unlocking = []; - for (const [e, v] of Object.entries(unbondingEras || {})) { - unlocking.push({ - era: Number(rmCommas(e as string)), - value: new BigNumber(rmCommas(v as string)), - }); - } - membership.points = membership.points - ? rmCommas(membership.points) - : '0'; - - const balance = - ( - await api.call.nominationPoolsApi.pointsToBalance( - membership.poolId, - membership.points - ) - )?.toString() || '0'; - - membership = { - ...membership, - balance: new BigNumber(balance), - address, - unlocking, - claimPermission: claimPermission?.toString() || 'Permissioned', - }; - - // remove stale membership if it's already in list, and add to memberships. - setStateWithRef( - Object.values(poolMembershipsRef.current) - .filter((m) => m.address !== address) - .concat(membership), - setPoolMemberships, - poolMembershipsRef - ); - } else { - // no membership: remove account membership if present. - setStateWithRef( - Object.values(poolMembershipsRef.current).filter( - (m) => m.address !== address - ), - setPoolMemberships, - poolMembershipsRef - ); - } - }; - - unsubs.current = unsubs.current.concat(unsub); - return unsub; - }; - - // gets the membership of the active account - const getActiveAccountPoolMembership = () => { - if (!activeAccount) { - return defaults.poolMembership; - } - const poolMembership = poolMembershipsRef.current.find( - (m) => m.address === activeAccount - ); - if (poolMembership === undefined) { - return defaults.poolMembership; - } - return poolMembership; - }; - - const claimPermissionConfig: ClaimPermissionConfig[] = [ - { - label: t('allowCompound'), - value: 'PermissionlessCompound', - description: t('allowAnyoneCompound'), - }, - { - label: t('allowWithdraw'), - value: 'PermissionlessWithdraw', - description: t('allowAnyoneWithdraw'), - }, - { - label: t('allowAll'), - value: 'PermissionlessAll', - description: t('allowAnyoneCompoundWithdraw'), - }, - ]; - - // Reset and re-sync pool memberships on network change. - // re-sync pool memberships when accounts update. - useEffectIgnoreInitial(() => { - if (isReady) { - (() => { - // NOTE: resetting memberships here. - setStateWithRef([], setPoolMemberships, poolMembershipsRef); - unsubAll(); - getPoolMemberships(); - })(); - } - }, [network, isReady]); - - // re-sync pool memberships when accounts update. - useEffectIgnoreInitial(() => { - if (isReady) { - (() => { - unsubAll(); - getPoolMemberships(); - })(); - } - }, [isReady, accounts]); - - // Unsubscribe from pool memberships on unmount. - useEffect( - () => () => { - unsubAll(); - }, - [] - ); - - return ( - - {children} - - ); -}; diff --git a/src/contexts/Pools/PoolMemberships/types.ts b/src/contexts/Pools/PoolMemberships/types.ts deleted file mode 100644 index 31a547f049..0000000000 --- a/src/contexts/Pools/PoolMemberships/types.ts +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import type BigNumber from 'bignumber.js'; -import type { PoolUnlocking } from '../ActivePools/types'; - -export interface PoolMembershipsContextState { - memberships: PoolMembership[]; - membership: PoolMembership | null; - claimPermissionConfig: ClaimPermissionConfig[]; -} - -export type ClaimPermission = - | 'Permissioned' - | 'PermissionlessCompound' - | 'PermissionlessWithdraw' - | 'PermissionlessAll'; - -export interface PoolMembership { - address: string; - poolId: number; - points: string; - balance: BigNumber; - lastRecordedRewardCounter: string; - unbondingEras: Record; - claimPermission: ClaimPermission; - unlocking: PoolUnlocking[]; -} - -// PoolMembers types -export interface ClaimPermissionConfig { - label: string; - value: ClaimPermission; - description: string; -} diff --git a/src/contexts/Pools/types.ts b/src/contexts/Pools/types.ts index 778499a71e..7a3cd25b36 100644 --- a/src/contexts/Pools/types.ts +++ b/src/contexts/Pools/types.ts @@ -1,2 +1,22 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only + +import type BigNumber from 'bignumber.js'; +import type { PoolUnlocking } from './ActivePools/types'; + +export type ClaimPermission = + | 'Permissioned' + | 'PermissionlessCompound' + | 'PermissionlessWithdraw' + | 'PermissionlessAll'; + +export interface PoolMembership { + address: string; + poolId: number; + points: string; + balance: BigNumber; + lastRecordedRewardCounter: string; + unbondingEras: Record; + claimPermission: ClaimPermission; + unlocking: PoolUnlocking[]; +} diff --git a/src/contexts/Setup/index.tsx b/src/contexts/Setup/index.tsx index 381142b3cc..fea2df4bee 100644 --- a/src/contexts/Setup/index.tsx +++ b/src/contexts/Setup/index.tsx @@ -8,7 +8,6 @@ import { } from '@polkadot-cloud/utils'; import type { ReactNode } from 'react'; import { createContext, useContext, useState } from 'react'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import type { BondFor, MaybeAddress } from 'types'; import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; @@ -29,6 +28,7 @@ import type { PoolSetups, SetupContextInterface, } from './types'; +import { useBalances } from 'contexts/Balances'; export const SetupContext = createContext(defaultSetupContext); @@ -42,8 +42,10 @@ export const SetupProvider = ({ children }: { children: ReactNode }) => { networkData: { units }, } = useNetwork(); const { accounts } = useImportedAccounts(); + const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); - const { membership: poolMembership } = usePoolMemberships(); + + const poolMembership = getPoolMembership(activeAccount); // is the user actively on the setup page const [onNominatorSetup, setOnNominatorSetup] = useState(false); diff --git a/src/contexts/TransferOptions/index.tsx b/src/contexts/TransferOptions/index.tsx index b4fd7cd81a..d43850ae2f 100644 --- a/src/contexts/TransferOptions/index.tsx +++ b/src/contexts/TransferOptions/index.tsx @@ -6,7 +6,6 @@ import type { ReactNode } from 'react'; import { createContext, useContext, useState } from 'react'; import { useApi } from 'contexts/Api'; import { useBalances } from 'contexts/Balances'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import type { MaybeAddress } from 'types'; import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; @@ -26,15 +25,15 @@ export const TransferOptionsProvider = ({ }: { children: ReactNode; }) => { - const { consts, activeEra } = useApi(); - const { membership } = usePoolMemberships(); - const { activeAccount } = useActiveAccounts(); const { network, networkData: { units, defaultFeeReserve }, } = useNetwork(); - const { getLedger, getBalance, getLocks } = useBalances(); + const { consts, activeEra } = useApi(); + const { activeAccount } = useActiveAccounts(); + const { getLedger, getBalance, getLocks, getPoolMembership } = useBalances(); const { existentialDeposit } = consts; + const membership = getPoolMembership(activeAccount); // A user-configurable reserve amount to be used to pay for transaction fees. const [feeReserve, setFeeReserve] = useState( diff --git a/src/hooks/useActiveBalances/index.tsx b/src/hooks/useActiveBalances/index.tsx index fec7573781..219fc8bd77 100644 --- a/src/hooks/useActiveBalances/index.tsx +++ b/src/hooks/useActiveBalances/index.tsx @@ -23,6 +23,7 @@ import { defaultPayee, } from 'static/BalancesController/defaults'; import type { PayeeConfig } from 'contexts/Setup/types'; +import type { PoolMembership } from 'contexts/Pools/types'; export const useActiveBalances = ({ accounts, @@ -102,6 +103,17 @@ export const useActiveBalances = ({ return defaultPayee; }; + // Gets an active balance's pool membership. + const getPoolMembership = (address: MaybeAddress): PoolMembership | null => { + if (address) { + const maybePoolMembership = activeBalances[address]?.poolMembership; + if (maybePoolMembership) { + return maybePoolMembership; + } + } + return null; + }; + // Gets the amount of balance reserved for existential deposit. const getEdReserved = ( address: MaybeAddress, @@ -177,6 +189,7 @@ export const useActiveBalances = ({ getBalance, getLedger, getPayee, + getPoolMembership, getEdReserved, }; }; diff --git a/src/library/Form/ClaimPermissionInput/index.tsx b/src/library/Form/ClaimPermissionInput/index.tsx index 561e6130f2..22bdc57d29 100644 --- a/src/library/Form/ClaimPermissionInput/index.tsx +++ b/src/library/Form/ClaimPermissionInput/index.tsx @@ -4,9 +4,9 @@ import { ActionItem } from '@polkadot-cloud/react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { TabWrapper, TabsWrapper } from 'library/Filter/Wrappers'; -import type { ClaimPermission } from 'contexts/Pools/PoolMemberships/types'; +import type { ClaimPermission } from 'contexts/Pools/types'; +import type { ClaimPermissionConfig } from '../types'; export interface ClaimPermissionInputProps { current: ClaimPermission | undefined; @@ -22,7 +22,24 @@ export const ClaimPermissionInput = ({ disabled = false, }: ClaimPermissionInputProps) => { const { t } = useTranslation('library'); - const { claimPermissionConfig } = usePoolMemberships(); + + const claimPermissionConfig: ClaimPermissionConfig[] = [ + { + label: t('allowCompound'), + value: 'PermissionlessCompound', + description: t('allowAnyoneCompound'), + }, + { + label: t('allowWithdraw'), + value: 'PermissionlessWithdraw', + description: t('allowAnyoneWithdraw'), + }, + { + label: t('allowAll'), + value: 'PermissionlessAll', + description: t('allowAnyoneCompoundWithdraw'), + }, + ]; // Updated claim permission value const [selected, setSelected] = useState( diff --git a/src/library/Form/types.ts b/src/library/Form/types.ts index 0e6124ff0a..879de6ccca 100644 --- a/src/library/Form/types.ts +++ b/src/library/Form/types.ts @@ -8,6 +8,7 @@ import type { ExternalAccount, } from '@polkadot-cloud/react/types'; import type { BondFor, MaybeAddress } from 'types'; +import type { ClaimPermission } from 'contexts/Pools/types'; export interface ExtensionAccountItem extends ExtensionAccount { active?: boolean; @@ -87,3 +88,10 @@ export interface NominateStatusBarProps { export interface WarningProps { text: string; } + +// PoolMembers types +export interface ClaimPermissionConfig { + label: string; + value: ClaimPermission; + description: string; +} diff --git a/src/library/Graphs/PayoutBar.tsx b/src/library/Graphs/PayoutBar.tsx index bc06d39fa2..a7f2a91828 100644 --- a/src/library/Graphs/PayoutBar.tsx +++ b/src/library/Graphs/PayoutBar.tsx @@ -17,7 +17,6 @@ import { format, fromUnixTime } from 'date-fns'; import { Bar } from 'react-chartjs-2'; import { useTranslation } from 'react-i18next'; import { DefaultLocale } from 'consts'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useStaking } from 'contexts/Staking'; import { useTheme } from 'contexts/Themes'; import { useUi } from 'contexts/UI'; @@ -27,6 +26,8 @@ import type { AnyJson, AnySubscan } from 'types'; import { useNetwork } from 'contexts/Network'; import type { PayoutBarProps } from './types'; import { formatRewardsForGraphs } from './Utils'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useBalances } from 'contexts/Balances'; ChartJS.register( CategoryScale, @@ -48,7 +49,10 @@ export const PayoutBar = ({ const { mode } = useTheme(); const { isSyncing } = useUi(); const { inSetup } = useStaking(); - const { membership } = usePoolMemberships(); + const { getPoolMembership } = useBalances(); + const { activeAccount } = useActiveAccounts(); + + const membership = getPoolMembership(activeAccount); const { unit, units, colors } = useNetwork().networkData; const notStaking = !isSyncing && inSetup() && !membership; diff --git a/src/library/Graphs/PayoutLine.tsx b/src/library/Graphs/PayoutLine.tsx index ce2dfb61ec..3244a5a5a0 100644 --- a/src/library/Graphs/PayoutLine.tsx +++ b/src/library/Graphs/PayoutLine.tsx @@ -14,7 +14,6 @@ import { } from 'chart.js'; import { Line } from 'react-chartjs-2'; import { useTranslation } from 'react-i18next'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useStaking } from 'contexts/Staking'; import { useTheme } from 'contexts/Themes'; import { useUi } from 'contexts/UI'; @@ -27,6 +26,8 @@ import { combineRewards, formatRewardsForGraphs, } from './Utils'; +import { useBalances } from 'contexts/Balances'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; ChartJS.register( CategoryScale, @@ -49,9 +50,11 @@ export const PayoutLine = ({ const { mode } = useTheme(); const { isSyncing } = useUi(); const { inSetup } = useStaking(); - const { unit, units, colors } = useNetwork().networkData; - const { membership: poolMembership } = usePoolMemberships(); + const { getPoolMembership } = useBalances(); + const { activeAccount } = useActiveAccounts(); + const { unit, units, colors } = useNetwork().networkData; + const poolMembership = getPoolMembership(activeAccount); const notStaking = !isSyncing && inSetup() && !poolMembership; const poolingOnly = !isSyncing && inSetup() && poolMembership !== null; diff --git a/src/library/Pool/index.tsx b/src/library/Pool/index.tsx index d0d31bd86d..863d182d46 100644 --- a/src/library/Pool/index.tsx +++ b/src/library/Pool/index.tsx @@ -9,7 +9,6 @@ import { useTranslation } from 'react-i18next'; import { useMenu } from 'contexts/Menu'; import type { NotificationText } from 'static/NotificationsController/types'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useUi } from 'contexts/UI'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; import { usePoolCommission } from 'hooks/usePoolCommission'; @@ -34,6 +33,7 @@ import type { PoolProps } from './types'; import { Rewards } from './Rewards'; import { NotificationsController } from 'static/NotificationsController'; import type { MenuItem } from 'contexts/Menu/types'; +import { useBalances } from 'contexts/Balances'; export const Pool = ({ pool }: PoolProps) => { const { t } = useTranslation('library'); @@ -42,13 +42,14 @@ export const Pool = ({ pool }: PoolProps) => { const { validators } = useValidators(); const { setActiveTab } = usePoolsTabs(); const { openModal } = useOverlay().modal; - const { membership } = usePoolMemberships(); + const { getPoolMembership } = useBalances(); const { poolsNominations } = useBondedPools(); const { activeAccount } = useActiveAccounts(); const { isReadOnlyAccount } = useImportedAccounts(); const { getCurrentCommission } = usePoolCommission(); const { setMenuPosition, setMenuItems, open } = useMenu(); + const membership = getPoolMembership(activeAccount); const currentCommission = getCurrentCommission(id); // get metadata from pools metabatch diff --git a/src/library/SideMenu/Main.tsx b/src/library/SideMenu/Main.tsx index 29b76beab9..7b927557a9 100644 --- a/src/library/SideMenu/Main.tsx +++ b/src/library/SideMenu/Main.tsx @@ -7,7 +7,6 @@ import { useLocation } from 'react-router-dom'; import { PageCategories, PagesConfig } from 'config/pages'; import { PolkadotUrl } from 'consts'; import { useBonded } from 'contexts/Bonded'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useSetup } from 'contexts/Setup'; import type { SetupContextInterface } from 'contexts/Setup/types'; import { useStaking } from 'contexts/Staking'; @@ -21,6 +20,7 @@ import { Heading } from './Heading/Heading'; import { Primary } from './Primary'; import { LogoWrapper } from './Wrapper'; import type { AnyJson } from '@polkadot-cloud/react/types'; +import { useBalances } from 'contexts/Balances'; export const Main = () => { const { t, i18n } = useTranslation('base'); @@ -28,10 +28,9 @@ export const Main = () => { const { pathname } = useLocation(); const { getBondedAccount } = useBonded(); const { accounts } = useImportedAccounts(); + const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const { inSetup: inNominatorSetup, addressDifferentToStash } = useStaking(); - const { membership } = usePoolMemberships(); - const controller = getBondedAccount(activeAccount); const { onNominatorSetup, onPoolSetup, @@ -39,6 +38,9 @@ export const Main = () => { getNominatorSetupPercent, }: SetupContextInterface = useSetup(); const { isSyncing, sideMenuMinimised }: UIContextInterface = useUi(); + + const membership = getPoolMembership(activeAccount); + const controller = getBondedAccount(activeAccount); const controllerDifferentToStash = addressDifferentToStash(controller); const [pageConfig, setPageConfig] = useState({ diff --git a/src/library/StatusLabel/index.tsx b/src/library/StatusLabel/index.tsx index 99aa359784..5013ede23d 100644 --- a/src/library/StatusLabel/index.tsx +++ b/src/library/StatusLabel/index.tsx @@ -6,11 +6,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { ButtonHelp } from '@polkadot-cloud/react'; import { useHelp } from 'contexts/Help'; import { usePlugins } from 'contexts/Plugins'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useStaking } from 'contexts/Staking'; import { useUi } from 'contexts/UI'; import { Wrapper } from './Wrapper'; import type { StatusLabelProps } from './types'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useBalances } from 'contexts/Balances'; export const StatusLabel = ({ title, @@ -21,10 +22,13 @@ export const StatusLabel = ({ status = 'sync_or_setup', }: StatusLabelProps) => { const { isSyncing } = useUi(); + const { openHelp } = useHelp(); const { plugins } = usePlugins(); const { inSetup } = useStaking(); - const { membership } = usePoolMemberships(); - const { openHelp } = useHelp(); + const { getPoolMembership } = useBalances(); + const { activeAccount } = useActiveAccounts(); + + const membership = getPoolMembership(activeAccount); // syncing or not staking if (status === 'sync_or_setup') { diff --git a/src/locale/cn/base.json b/src/locale/cn/base.json index cf19eb8a9a..7bfd52d8a1 100644 --- a/src/locale/cn/base.json +++ b/src/locale/cn/base.json @@ -1,12 +1,6 @@ { "base": { "active": "激活", - "allowAll": "允许所有", - "allowAnyoneCompound": "允许任何人代表您复利收益", - "allowAnyoneCompoundWithdraw": "允许任何人代表您复利或取出收益", - "allowAnyoneWithdraw": "允许任何人代表您取出收益", - "allowCompound": "允许复利", - "allowWithdraw": "允许取出收益", "community": "社区", "feedback": "反馈", "goTo": "查看", diff --git a/src/locale/cn/library.json b/src/locale/cn/library.json index 469b4ee9a9..f712e8e461 100644 --- a/src/locale/cn/library.json +++ b/src/locale/cn/library.json @@ -14,6 +14,12 @@ "address": "地址", "addressCopiedToClipboard": "复制到剪贴板的地址", "all": "全部", + "allowAll": "允许所有", + "allowAnyoneCompound": "允许任何人代表您复利收益", + "allowAnyoneCompoundWithdraw": "允许任何人代表您复利或取出收益", + "allowAnyoneWithdraw": "允许任何人代表您取出收益", + "allowCompound": "允许复利", + "allowWithdraw": "允许取出收益", "alreadyImported": "地址已导入", "asAPoolMember": "作为提名池成员", "asThePoolDepositor": "作为提名池存款人", diff --git a/src/locale/en/base.json b/src/locale/en/base.json index 60d343dae8..4466bfa4e3 100644 --- a/src/locale/en/base.json +++ b/src/locale/en/base.json @@ -1,12 +1,6 @@ { "base": { "active": "Active", - "allowAll": "Allow All", - "allowAnyoneCompound": "Allow anyone to compound rewards on your behalf.", - "allowAnyoneCompoundWithdraw": "Allow anyone to compound or withdraw rewards on your behalf.", - "allowAnyoneWithdraw": "Allow anyone to withdraw rewards on your behalf.", - "allowCompound": "Allow Compound", - "allowWithdraw": "Allow Withdraw", "community": "Community", "feedback": "Feedback", "goTo": "Go To", diff --git a/src/locale/en/library.json b/src/locale/en/library.json index d210eec6ae..286b88be1c 100644 --- a/src/locale/en/library.json +++ b/src/locale/en/library.json @@ -14,6 +14,12 @@ "address": "Address", "addressCopiedToClipboard": "Address Copied to Clipboard", "all": "All", + "allowAll": "Allow All", + "allowAnyoneCompound": "Allow anyone to compound rewards on your behalf.", + "allowAnyoneCompoundWithdraw": "Allow anyone to compound or withdraw rewards on your behalf.", + "allowAnyoneWithdraw": "Allow anyone to withdraw rewards on your behalf.", + "allowCompound": "Allow Compound", + "allowWithdraw": "Allow Withdraw", "alreadyImported": "Address Already Imported", "asAPoolMember": "as a pool member.", "asThePoolDepositor": "as the pool depositor.", diff --git a/src/modals/AccountPoolRoles/index.tsx b/src/modals/AccountPoolRoles/index.tsx index 07cedf694e..beb49bd4be 100644 --- a/src/modals/AccountPoolRoles/index.tsx +++ b/src/modals/AccountPoolRoles/index.tsx @@ -6,19 +6,23 @@ import { ButtonOption, ModalPadding, Polkicon } from '@polkadot-cloud/react'; import { useTranslation } from 'react-i18next'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { Title } from 'library/Modal/Title'; import { useStatusButtons } from 'pages/Pools/Home/Status/useStatusButtons'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { ContentWrapper } from './Wrappers'; +import { useBalances } from 'contexts/Balances'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; export const AccountPoolRoles = () => { const { t } = useTranslation('modals'); - const { options } = useOverlay().modal.config; + const { getPoolMembership } = useBalances(); const { getAccountPools } = useBondedPools(); - const { membership } = usePoolMemberships(); + const { options } = useOverlay().modal.config; + const { activeAccount } = useActiveAccounts(); + const { who } = options; const accountPools = getAccountPools(who); + const membership = getPoolMembership(activeAccount); const totalAccountPools = Object.entries(accountPools).length; const { label } = useStatusButtons(); diff --git a/src/modals/Accounts/index.tsx b/src/modals/Accounts/index.tsx index 668635123b..a86c564909 100644 --- a/src/modals/Accounts/index.tsx +++ b/src/modals/Accounts/index.tsx @@ -17,7 +17,6 @@ import { useEffectIgnoreInitial, useOverlay, } from '@polkadot-cloud/react/hooks'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useProxies } from 'contexts/Proxies'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; @@ -45,7 +44,6 @@ export const Accounts = () => { const { getDelegates } = useProxies(); const { bondedAccounts } = useBonded(); const { extensionsStatus } = useExtensions(); - const { memberships } = usePoolMemberships(); const { replaceModal, status: modalStatus, @@ -61,9 +59,10 @@ export const Accounts = () => { useState(accounts); // Listen to balance updates for entire accounts list. - const { getLocks, getBalance, getEdReserved } = useActiveBalances({ - accounts: localAccounts.map(({ address }) => address), - }); + const { getLocks, getBalance, getEdReserved, getPoolMembership } = + useActiveBalances({ + accounts: localAccounts.map(({ address }) => address), + }); // Calculate transferrable balance of an address. const getTransferrableBalance = (address: MaybeAddress) => { @@ -111,7 +110,7 @@ export const Accounts = () => { })); } - const poolMember = memberships.find((m) => m.address === address) ?? null; + const poolMember = getPoolMembership(address); // Check if nominating. if ( diff --git a/src/modals/Accounts/types.ts b/src/modals/Accounts/types.ts index 0a1dabc3db..3397e9485b 100644 --- a/src/modals/Accounts/types.ts +++ b/src/modals/Accounts/types.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import type BigNumber from 'bignumber.js'; -import type { PoolMembership } from 'contexts/Pools/PoolMemberships/types'; +import type { PoolMembership } from 'contexts/Pools/types'; import type { Proxy } from 'contexts/Proxies/types'; import type { MaybeAddress } from 'types'; diff --git a/src/modals/JoinPool/index.tsx b/src/modals/JoinPool/index.tsx index a156060472..b18199c65e 100644 --- a/src/modals/JoinPool/index.tsx +++ b/src/modals/JoinPool/index.tsx @@ -23,7 +23,7 @@ import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import type { ClaimPermission } from 'contexts/Pools/PoolMemberships/types'; +import type { ClaimPermission } from 'contexts/Pools/types'; export const JoinPool = () => { const { t } = useTranslation('modals'); diff --git a/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx b/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx index a898fbef64..f390b3c703 100644 --- a/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx +++ b/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx @@ -12,7 +12,6 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useActivePools } from 'contexts/Pools/ActivePools'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { ClaimPermissionInput } from 'library/Form/ClaimPermissionInput'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; @@ -20,7 +19,8 @@ import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import type { ClaimPermission } from 'contexts/Pools/PoolMemberships/types'; +import type { ClaimPermission } from 'contexts/Pools/types'; +import { useBalances } from 'contexts/Balances'; export const SetClaimPermission = ({ setSection, @@ -31,11 +31,13 @@ export const SetClaimPermission = ({ }) => { const { t } = useTranslation('modals'); const { api } = useApi(); + const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const { setModalStatus } = useOverlay().modal; const { isOwner, isMember } = useActivePools(); const { getSignerWarnings } = useSignerWarnings(); - const { membership } = usePoolMemberships(); + + const membership = getPoolMembership(activeAccount); // Valid to submit transaction. const [valid, setValid] = useState(false); diff --git a/src/modals/UnlockChunks/Forms.tsx b/src/modals/UnlockChunks/Forms.tsx index 58928fc755..3accd990bc 100644 --- a/src/modals/UnlockChunks/Forms.tsx +++ b/src/modals/UnlockChunks/Forms.tsx @@ -18,7 +18,6 @@ import { useBonded } from 'contexts/Bonded'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useFavoritePools } from 'contexts/Pools/FavoritePools'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; @@ -29,6 +28,7 @@ import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { ContentWrapper } from './Wrappers'; import type { FormsProps } from './types'; +import { useBalances } from 'contexts/Balances'; export const Forms = forwardRef( ( @@ -40,19 +40,20 @@ export const Forms = forwardRef( const { networkData: { units, unit }, } = useNetwork(); - const { membership } = usePoolMemberships(); const { activeAccount } = useActiveAccounts(); const { removePoolMember } = usePoolMembers(); const { selectedActivePool } = useActivePools(); const { removeFromBondedPools } = useBondedPools(); - const { removeFavorite: removeFavoritePool } = useFavoritePools(); const { setModalStatus, config: { options }, } = useOverlay().modal; const { getBondedAccount } = useBonded(); + const { getPoolMembership } = useBalances(); const { getSignerWarnings } = useSignerWarnings(); + const { removeFavorite: removeFavoritePool } = useFavoritePools(); + const membership = getPoolMembership(activeAccount); const { bondFor, poolClosure } = options || {}; const { historyDepth } = consts; const controller = getBondedAccount(activeAccount); diff --git a/src/pages/Overview/StakeStatus/Tips/index.tsx b/src/pages/Overview/StakeStatus/Tips/index.tsx index b030e4f5ec..e4c9eeb142 100644 --- a/src/pages/Overview/StakeStatus/Tips/index.tsx +++ b/src/pages/Overview/StakeStatus/Tips/index.tsx @@ -8,7 +8,6 @@ import { useTranslation } from 'react-i18next'; import { TipsConfig } from 'config/tips'; import { DefaultLocale, TipsThresholdMedium, TipsThresholdSmall } from 'consts'; import { useActivePools } from 'contexts/Pools/ActivePools'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useStaking } from 'contexts/Staking'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useUi } from 'contexts/UI'; @@ -22,6 +21,7 @@ import { Syncing } from './Syncing'; import { TipsWrapper } from './Wrappers'; import type { TipDisplay } from './types'; import { useApi } from 'contexts/Api'; +import { useBalances } from 'contexts/Balances'; export const Tips = () => { const { i18n, t } = useTranslation(); @@ -29,14 +29,15 @@ export const Tips = () => { const { isNetworkSyncing } = useUi(); const { activeAccount } = useActiveAccounts(); const { fillVariables } = useFillVariables(); - const { membership } = usePoolMemberships(); const { stakingMetrics: { minNominatorBond }, } = useApi(); - - const { isNominating } = useStaking(); const { isOwner } = useActivePools(); + const { isNominating } = useStaking(); + const { getPoolMembership } = useBalances(); const { feeReserve, getTransferOptions } = useTransferOptions(); + + const membership = getPoolMembership(activeAccount); const transferOptions = getTransferOptions(activeAccount); // multiple tips per row is currently turned off. diff --git a/src/pages/Pools/Home/Status/useStatusButtons.tsx b/src/pages/Pools/Home/Status/useStatusButtons.tsx index 1ee9a59649..fcc63e51be 100644 --- a/src/pages/Pools/Home/Status/useStatusButtons.tsx +++ b/src/pages/Pools/Home/Status/useStatusButtons.tsx @@ -6,12 +6,12 @@ import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; import { useSetup } from 'contexts/Setup'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { usePoolsTabs } from '../context'; +import { useBalances } from 'contexts/Balances'; export const useStatusButtons = () => { const { t } = useTranslation('pages'); @@ -22,12 +22,13 @@ export const useStatusButtons = () => { const { isOwner } = useActivePools(); const { setActiveTab } = usePoolsTabs(); const { bondedPools } = useBondedPools(); - const { membership } = usePoolMemberships(); + const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const { getTransferOptions } = useTransferOptions(); const { isReadOnlyAccount } = useImportedAccounts(); const { setOnPoolSetup, getPoolSetupPercent } = useSetup(); + const membership = getPoolMembership(activeAccount); const { active } = getTransferOptions(activeAccount).pool; const poolSetupPercent = getPoolSetupPercent(activeAccount); diff --git a/src/static/BalancesController/index.ts b/src/static/BalancesController/index.ts index 14d7bf1c96..f188270c12 100644 --- a/src/static/BalancesController/index.ts +++ b/src/static/BalancesController/index.ts @@ -13,6 +13,7 @@ import type { UnlockChunkRaw, } from 'contexts/Balances/types'; import type { PayeeConfig, PayeeOptions } from 'contexts/Setup/types'; +import type { PoolMembership } from 'contexts/Pools/types'; export class BalancesController { // ------------------------------------------------------ @@ -31,6 +32,9 @@ export class BalancesController { // Account payees, populated by api callbacks. static payees: Record = {}; + // Account pool membership and claim commissions, populated by api callbacks. + static poolMemberships: Record = {}; + // Unsubscribe objects. static _unsubs: Record = {}; @@ -59,17 +63,28 @@ export class BalancesController { [api.query.system.account, address], [api.query.balances.locks, address], [api.query.staking.payee, address], + [api.query.nominationPools.poolMembers, address], + [api.query.nominationPools.claimPermissions, address], ], async ([ ledgerResult, accountResult, locksResult, payeeResult, + poolMembersResult, + claimPermissionsResult, ]): Promise => { this.handleLedgerCallback(address, ledgerResult); this.handleAccountCallback(address, accountResult, locksResult); this.handlePayeeCallback(address, payeeResult); + // NOTE: async; contains runtime call for pending rewards. + await this.handlePoolMembershipCallback( + address, + poolMembersResult, + claimPermissionsResult + ); + // Send updated account state back to UI. document.dispatchEvent( new CustomEvent('new-account-balance', { @@ -78,6 +93,7 @@ export class BalancesController { ledger: this.ledgers[address], balances: this.balances[address], payee: this.payees[address], + poolMembership: this.poolMemberships[address], }, }) ); @@ -183,11 +199,56 @@ export class BalancesController { this.payees[address] = payeeFinal; }; + // Handle pool membership and claim commission callback. + static handlePoolMembershipCallback = async ( + address: string, + poolMembersResult: AnyApi, + claimPermissionsResult: AnyApi + ): Promise => { + // If pool membership is `null`, remove pool membership data from class data and exit early. + // This skips claim permission data as well as user would not have claim permissions if they are + // not in a pool. + const membership = poolMembersResult?.unwrapOr(undefined)?.toHuman(); + if (!membership) { + delete this.poolMemberships[address]; + return; + } + + // Format pool membership data. + const unlocking = Object.entries(membership?.unbondingEras || {}).map( + ([e, v]) => ({ + era: Number(rmCommas(e as string)), + value: new BigNumber(rmCommas(v as string)), + }) + ); + membership.points = rmCommas(membership?.points || '0'); + const balance = new BigNumber( + ( + await APIController.api.call.nominationPoolsApi.pointsToBalance( + membership.poolId, + membership.points + ) + )?.toString() || '0' + ); + const claimPermission = + claimPermissionsResult?.toString() || 'Permissioned'; + + // Persist formatted pool membership data to class. + this.poolMemberships[address] = { + ...membership, + address, + balance, + claimPermission, + unlocking, + }; + }; + // Gets an `ActiveBalance` from class members for the given address if it exists. static getAccountBalances = (address: string): ActiveBalance | undefined => { const ledger = this.ledgers[address]; const balances = this.balances[address]; const payee = this.payees[address]; + const poolMembership = this.poolMemberships[address]; // Account info has not synced yet. Note that `ledger` may not exist and therefore cannot be // tested. @@ -198,6 +259,7 @@ export class BalancesController { ledger, balances, payee, + poolMembership, }; }; @@ -214,6 +276,7 @@ export class BalancesController { this.ledgers = {}; this.balances = {}; this.payees = {}; + this.poolMemberships = {}; this._unsubs = {}; }; From f55f70f165a26615ad3046f30ad4ca50ca62ba96 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 31 Jan 2024 21:50:04 +0800 Subject: [PATCH 10/51] feat(refactor): Use identities hook (#1916) --- src/Providers.tsx | 2 - src/contexts/Identities/defaults.ts | 10 - src/contexts/Identities/index.tsx | 196 ------------------ src/contexts/Identities/types.ts | 9 - src/contexts/Pools/ActivePools/defaults.ts | 1 + src/contexts/Pools/ActivePools/index.tsx | 22 ++ src/contexts/Pools/ActivePools/types.ts | 5 + .../Validators/ValidatorEntries/index.tsx | 69 +----- src/contexts/Validators/types.ts | 2 +- src/hooks/useIdentities/index.tsx | 76 +++++++ src/library/Pool/types.ts | 5 + src/pages/Pools/Create/PoolRoles/index.tsx | 1 - src/pages/Pools/Home/index.tsx | 5 +- src/pages/Pools/PoolAccount/index.tsx | 25 +-- src/pages/Pools/Roles/index.tsx | 23 +- src/pages/Pools/Roles/types.ts | 1 - src/pages/Pools/types.ts | 4 +- 17 files changed, 130 insertions(+), 326 deletions(-) delete mode 100644 src/contexts/Identities/defaults.ts delete mode 100644 src/contexts/Identities/index.tsx delete mode 100644 src/contexts/Identities/types.ts create mode 100644 src/hooks/useIdentities/index.tsx diff --git a/src/Providers.tsx b/src/Providers.tsx index 1c146d3262..79d17bbf19 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -14,7 +14,6 @@ import { FiltersProvider } from 'contexts/Filters'; import { LedgerHardwareProvider } from 'contexts/Hardware/Ledger/LedgerHardware'; import { VaultAccountsProvider } from 'contexts/Hardware/Vault/VaultAccounts'; import { HelpProvider } from 'contexts/Help'; -import { IdentitiesProvider } from 'contexts/Identities'; import { MenuProvider } from 'contexts/Menu'; import { MigrateProvider } from 'contexts/Migrate'; import { PromptProvider } from 'contexts/Prompt'; @@ -70,7 +69,6 @@ export const Providers = () => { ProxiesProvider, HelpProvider, PluginsProvider, - IdentitiesProvider, BondedProvider, BalancesProvider, StakingProvider, diff --git a/src/contexts/Identities/defaults.ts b/src/contexts/Identities/defaults.ts deleted file mode 100644 index b1a3f5e3d3..0000000000 --- a/src/contexts/Identities/defaults.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only -/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */ - -import type { IdentitiesContextInterface } from './types'; - -export const defaultIdentitiesContext: IdentitiesContextInterface = { - fetchIdentitiesMetaBatch: (k, v, r) => {}, - meta: {}, -}; diff --git a/src/contexts/Identities/index.tsx b/src/contexts/Identities/index.tsx deleted file mode 100644 index a0b1caa806..0000000000 --- a/src/contexts/Identities/index.tsx +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { setStateWithRef } from '@polkadot-cloud/utils'; -import type { ReactNode } from 'react'; -import { createContext, useContext, useEffect, useRef, useState } from 'react'; -import type { AnyApi, AnyMetaBatch } from 'types'; -import { useApi } from '../Api'; -import { defaultIdentitiesContext } from './defaults'; -import type { IdentitiesContextInterface } from './types'; - -export const IdentitiesContext = createContext( - defaultIdentitiesContext -); - -export const useIdentities = () => useContext(IdentitiesContext); - -export const IdentitiesProvider = ({ children }: { children: ReactNode }) => { - const { isReady, api } = useApi(); - - // stores the meta data batches for validator lists - const [identitiesMetaBatches, setIdentitiesMetaBatch] = - useState({}); - const identitiesMetaBatchesRef = useRef(identitiesMetaBatches); - - // stores the meta batch subscriptions for validator lists - const identitiesSubsRef = useRef({}); - - // unsubscribe from any validator meta batches - useEffect( - () => () => { - Object.values(identitiesSubsRef.current).map((batch: AnyMetaBatch) => - Object.entries(batch).map(([, v]: AnyApi) => v()) - ); - }, - [] - ); - - /* - Fetches a new batch of subscribed accounts metadata. Stores the returning - metadata alongside the unsubscribe function in state. - structure: - { - key: { - [ - { - addresses [], - identities: [], - } - ] - }, - }; - */ - const fetchIdentitiesMetaBatch = async ( - key: string, - addresses: string[], - refetch = false - ) => { - if (!isReady || !api) { - return; - } - - if (!addresses.length) { - return; - } - - if (!refetch) { - // if already exists, do not re-fetch - if (identitiesMetaBatchesRef.current[key] !== undefined) { - return; - } - } else { - // tidy up if existing batch exists - const updatedMetaBatches: AnyMetaBatch = { - ...identitiesMetaBatchesRef.current, - }; - delete updatedMetaBatches[key]; - setStateWithRef( - updatedMetaBatches, - setIdentitiesMetaBatch, - identitiesMetaBatchesRef - ); - if (identitiesSubsRef.current[key] !== undefined) { - for (const unsub of identitiesSubsRef.current[key]) { - unsub(); - } - } - } - - // store batch addresses - const batchesUpdated = Object.assign(identitiesMetaBatchesRef.current); - batchesUpdated[key] = {}; - batchesUpdated[key].addresses = addresses; - setStateWithRef( - { ...batchesUpdated }, - setIdentitiesMetaBatch, - identitiesMetaBatchesRef - ); - - const subscribeToIdentities = async (addr: string[]) => { - const unsub = await api.query.identity.identityOf.multi( - addr, - (_identities) => { - const identities = []; - for (const _identity of _identities) { - identities.push(_identity.toHuman()); - } - const updated = Object.assign(identitiesMetaBatchesRef.current); - updated[key].identities = identities; - setStateWithRef( - { ...updated }, - setIdentitiesMetaBatch, - identitiesMetaBatchesRef - ); - } - ); - return unsub; - }; - - const subscribeToSuperIdentities = async (addr: string[]) => { - const unsub = await api.query.identity.superOf.multi( - addr, - async (result) => { - // determine where supers exist - const supers: AnyApi = []; - const supersWithIdentity: AnyApi = []; - - for (let i = 0; i < result.length; i++) { - const item = result[i].toHuman(); - supers.push(item); - if (item !== null) { - supersWithIdentity.push(i); - } - } - - // get supers one-off multi query - const query = supers - .filter((s: AnyApi) => s !== null) - .map((s: AnyApi) => s[0]); - - ( - await api.query.identity.identityOf.multi( - query, - (_identities) => { - for (let j = 0; j < _identities.length; j++) { - const identity = _identities[j].toHuman(); - // inject identity into super array - supers[supersWithIdentity[j]].identity = identity; - } - } - ) - )(); - - const updated = Object.assign(identitiesMetaBatchesRef.current); - updated[key].supers = supers; - setStateWithRef( - { ...updated }, - setIdentitiesMetaBatch, - identitiesMetaBatchesRef - ); - } - ); - return unsub; - }; - - await Promise.all([ - subscribeToIdentities(addresses), - subscribeToSuperIdentities(addresses), - ]).then((unsubs: AnyApi) => { - addMetaBatchUnsubs(key, unsubs); - }); - }; - - /* - * Helper function to add mataBatch unsubs by key. - */ - const addMetaBatchUnsubs = (key: string, unsubs: AnyApi) => { - const identityUnsubs = identitiesSubsRef.current; - const keyUnsubs = identityUnsubs[key] ?? []; - - keyUnsubs.push(...unsubs); - identityUnsubs[key] = keyUnsubs; - identitiesSubsRef.current = identityUnsubs; - }; - - return ( - - {children} - - ); -}; diff --git a/src/contexts/Identities/types.ts b/src/contexts/Identities/types.ts deleted file mode 100644 index 4af71a55d9..0000000000 --- a/src/contexts/Identities/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import type { AnyMetaBatch } from 'types'; - -export interface IdentitiesContextInterface { - fetchIdentitiesMetaBatch: (k: string, v: string[], r?: boolean) => void; - meta: AnyMetaBatch; -} diff --git a/src/contexts/Pools/ActivePools/defaults.ts b/src/contexts/Pools/ActivePools/defaults.ts index 042f7d84f7..32444c8da3 100644 --- a/src/contexts/Pools/ActivePools/defaults.ts +++ b/src/contexts/Pools/ActivePools/defaults.ts @@ -29,6 +29,7 @@ export const bondedPool: ActiveBondedPool = { root: '', bouncer: '', }, + roleIdentities: { identities: {}, supers: {} }, }; export const rewardPool: RewardPool = { diff --git a/src/contexts/Pools/ActivePools/index.tsx b/src/contexts/Pools/ActivePools/index.tsx index 20d0e13dd2..45c913ae10 100644 --- a/src/contexts/Pools/ActivePools/index.tsx +++ b/src/contexts/Pools/ActivePools/index.tsx @@ -27,6 +27,7 @@ import type { PoolAddresses } from '../BondedPools/types'; import { SubscanController } from 'static/SubscanController'; import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; import { useBalances } from 'contexts/Balances'; +import { useIdentities } from 'hooks/useIdentities'; export const ActivePoolsContext = createContext( defaults.defaultActivePoolContext @@ -39,6 +40,7 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { const { api, isReady } = useApi(); const { eraStakers } = useStaking(); const { pluginEnabled } = usePlugins(); + const { fetchIdentities } = useIdentities(); const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const createPoolAccounts = useCreatePoolAccounts(); @@ -171,7 +173,27 @@ export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { const balance = accountData.data; bondedPool = bondedPool?.unwrapOr(undefined)?.toHuman(); rewardPool = rewardPool?.unwrapOr(undefined)?.toHuman(); + if (rewardPool && bondedPool) { + // Fetch identities & super identities for roles and expand `bondedPool` state to store + // them. + const { roles } = bondedPool; + const roleAddresses: string[] = []; + if (roles.root) { + roleAddresses.push(roles.root); + } + if (roles.depositor) { + roleAddresses.push(roles.depositor); + } + if (roles.nominator) { + roleAddresses.push(roles.nominator); + } + if (roles.bouncer) { + roleAddresses.push(roles.bouncer); + } + + bondedPool.roleIdentities = await fetchIdentities(roleAddresses); + const rewardAccountBalance = balance?.free; const pendingRewards = await fetchPendingRewards(); const pool = { diff --git a/src/contexts/Pools/ActivePools/types.ts b/src/contexts/Pools/ActivePools/types.ts index 14c096559f..04a82a89f2 100644 --- a/src/contexts/Pools/ActivePools/types.ts +++ b/src/contexts/Pools/ActivePools/types.ts @@ -6,6 +6,7 @@ import type { NominationStatuses, PoolAddresses } from '../BondedPools/types'; import type { MaybeAddress } from '@polkadot-cloud/react/types'; import type { AnyJson, Sync } from 'types'; import type { Nominations } from 'contexts/Bonded/types'; +import type { Identity, SuperIdentity } from 'contexts/Validators/types'; export interface ActivePoolsContextState { isBonding: () => boolean; @@ -40,6 +41,10 @@ export interface ActiveBondedPool { points: string; memberCounter: string; roles: PoolRoles; + roleIdentities: { + identities: Record; + supers: Record; + }; state: PoolState; } diff --git a/src/contexts/Validators/ValidatorEntries/index.tsx b/src/contexts/Validators/ValidatorEntries/index.tsx index 325a161888..f01c5f7290 100644 --- a/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/src/contexts/Validators/ValidatorEntries/index.tsx @@ -21,7 +21,7 @@ import type { Identity, Validator, ValidatorAddresses, - ValidatorSuper, + SuperIdentity, ValidatorListEntry, ValidatorsContextInterface, ValidatorEraPointHistory, @@ -35,6 +35,7 @@ import { import { getLocalEraValidators, setLocalEraValidators } from '../Utils'; import type { ValidatorEntry } from '@polkadot-cloud/assets/types'; import { useErasPerDay } from 'hooks/useErasPerDay'; +import { useIdentities } from 'hooks/useIdentities'; export const ValidatorsContext = createContext( defaultValidatorsContext @@ -52,6 +53,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { } = useApi(); const { activeEra } = useApi(); const { stakers } = useStaking().eraStakers; + const { fetchIdentities } = useIdentities(); const { poolNominations } = useActivePools(); const { activeAccount } = useActiveAccounts(); const { erasPerDay, maxSupportedDays } = useErasPerDay(); @@ -70,7 +72,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Store validator super identity data. const [validatorSupers, setValidatorSupers] = useState< - Record + Record >({}); // Stores the currently active validator set. @@ -360,10 +362,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { setValidators(shuffle(validatorEntries)); const addresses = validatorEntries.map(({ address }) => address); - const [identities, supers] = await Promise.all([ - fetchValidatorIdentities(addresses), - fetchValidatorSupers(addresses), - ]); + const { identities, supers } = await fetchIdentities(addresses); setValidatorIdentities(identities); setValidatorSupers(supers); setValidatorsFetched('synced'); @@ -418,64 +417,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { return formatted; }; - // Fetches validator identities. - const fetchValidatorIdentities = async (addresses: string[]) => { - if (!api) { - return {}; - } - - const identities: AnyApi[] = ( - await api.query.identity.identityOf.multi(addresses) - ).map((identity) => identity.toHuman()); - - return Object.fromEntries( - Object.entries( - Object.fromEntries(identities.map((k, i) => [addresses[i], k])) - ).filter(([, v]) => v !== null) - ); - }; - - // Fetch validator super accounts and their identities. - const fetchValidatorSupers = async (addresses: string[]) => { - if (!api) { - return {}; - } - - const supersRaw: AnyApi[] = ( - await api.query.identity.superOf.multi(addresses) - ).map((superOf) => superOf.toHuman()); - - const supers = Object.fromEntries( - Object.entries( - Object.fromEntries( - supersRaw.map((k, i) => [ - addresses[i], - { - superOf: k, - }, - ]) - ) - ).filter(([, { superOf }]) => superOf !== null) - ); - - const superIdentities = ( - await api.query.identity.identityOf.multi( - Object.values(supers).map(({ superOf }) => superOf[0]) - ) - ).map((superIdentity) => superIdentity.toHuman()); - - const supersWithIdentity = Object.fromEntries( - Object.entries(supers).map(([k, v]: AnyApi, i) => [ - k, - { - ...v, - identity: superIdentities[i], - }, - ]) - ); - return supersWithIdentity; - }; - // Gets era points for a validator const getValidatorPointsFromEras = (startEra: BigNumber, address: string) => { startEra = BigNumber.max(startEra, 1); diff --git a/src/contexts/Validators/types.ts b/src/contexts/Validators/types.ts index 11fb95806b..26d9b6ec85 100644 --- a/src/contexts/Validators/types.ts +++ b/src/contexts/Validators/types.ts @@ -48,7 +48,7 @@ export interface Identity { judgements: AnyJson[]; } -export interface ValidatorSuper { +export interface SuperIdentity { identity: Identity; superOf: [string, { Raw: string }]; } diff --git a/src/hooks/useIdentities/index.tsx b/src/hooks/useIdentities/index.tsx new file mode 100644 index 0000000000..b1cefcb98d --- /dev/null +++ b/src/hooks/useIdentities/index.tsx @@ -0,0 +1,76 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { AnyApi } from '@polkadot-cloud/react/types'; +import { useApi } from 'contexts/Api'; + +export const useIdentities = () => { + const { api } = useApi(); + + // Fetches validator identities. + const fetchValidatorIdentities = async (addresses: string[]) => { + if (!api) { + return {}; + } + + const result = (await api.query.identity.identityOf.multi(addresses)).map( + (identity) => identity.toHuman() + ); + return Object.fromEntries( + result.map((k, i) => [addresses[i], k]).filter(([, v]) => v !== null) + ); + }; + + // Fetch an array of super accounts and their identities. + const fetchValidatorSupers = async (addresses: string[]) => { + if (!api) { + return {}; + } + + const supersRaw = (await api.query.identity.superOf.multi(addresses)).map( + (superOf) => superOf.toHuman() + ); + + const supers = Object.fromEntries( + supersRaw + .map((k, i) => [ + addresses[i], + { + superOf: k, + }, + ]) + .filter(([, { superOf }]: AnyApi) => superOf !== null) + ); + + const superIdentities = ( + await api.query.identity.identityOf.multi( + Object.values(supers).map(({ superOf }: AnyApi) => superOf[0]) + ) + ).map((superIdentity) => superIdentity.toHuman()); + + const supersWithIdentity = Object.fromEntries( + Object.entries(supers).map(([k, v]: AnyApi, i) => [ + k, + { + ...v, + identity: superIdentities[i], + }, + ]) + ); + return supersWithIdentity; + }; + + // Fetches both identities and super identities from an array of addresses. + const fetchIdentities = async (addresses: string[]) => { + const [identities, supers] = await Promise.all([ + fetchValidatorIdentities(addresses), + fetchValidatorSupers(addresses), + ]); + + return { identities, supers }; + }; + + return { + fetchIdentities, + }; +}; diff --git a/src/library/Pool/types.ts b/src/library/Pool/types.ts index 2e843fe69d..2a18b9f90f 100644 --- a/src/library/Pool/types.ts +++ b/src/library/Pool/types.ts @@ -3,6 +3,7 @@ import type { PoolRoles, PoolState } from 'contexts/Pools/ActivePools/types'; import type { PoolAddresses } from 'contexts/Pools/BondedPools/types'; +import type { Identity, SuperIdentity } from 'contexts/Validators/types'; import type { DisplayFor } from 'types'; export interface PoolProps { @@ -16,6 +17,10 @@ export interface Pool { id: number; state: PoolState; roles: PoolRoles; + roleIdentities: { + identities: Record; + supers: Record; + }; } export interface RewardProps { diff --git a/src/pages/Pools/Create/PoolRoles/index.tsx b/src/pages/Pools/Create/PoolRoles/index.tsx index 4ac8d29d25..75d6b42b04 100644 --- a/src/pages/Pools/Create/PoolRoles/index.tsx +++ b/src/pages/Pools/Create/PoolRoles/index.tsx @@ -81,7 +81,6 @@ export const PoolRoles = ({ section }: SetupStepProps) => { { - + diff --git a/src/pages/Pools/PoolAccount/index.tsx b/src/pages/Pools/PoolAccount/index.tsx index 193b67fd1d..007b1f2e8e 100644 --- a/src/pages/Pools/PoolAccount/index.tsx +++ b/src/pages/Pools/PoolAccount/index.tsx @@ -6,7 +6,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { ellipsisFn, remToUnit } from '@polkadot-cloud/utils'; import { motion } from 'framer-motion'; import { useTranslation } from 'react-i18next'; -import { useIdentities } from 'contexts/Identities'; import type { NotificationText } from 'static/NotificationsController/types'; import { Polkicon } from '@polkadot-cloud/react'; import { getIdentityDisplay } from 'library/ValidatorList/ValidatorItem/Utils'; @@ -14,25 +13,17 @@ import type { PoolAccountProps } from '../types'; import { Wrapper } from './Wrapper'; import { NotificationsController } from 'static/NotificationsController'; -export const PoolAccount = ({ - address, - batchKey, - batchIndex, -}: PoolAccountProps) => { +export const PoolAccount = ({ address, pool }: PoolAccountProps) => { const { t } = useTranslation('pages'); - const { meta } = useIdentities(); - const identities = meta[batchKey]?.identities || []; - const supers = meta[batchKey]?.supers || []; + const roleIdentities = pool?.bondedPool?.roleIdentities; + const identities = roleIdentities?.identities || {}; + const supers = roleIdentities?.supers || {}; + const synced = roleIdentities !== undefined; - const identitiesSynced = identities.length > 0 || false; - const supersSynced = supers.length > 0 || false; - const synced = identitiesSynced && supersSynced; - - const display = getIdentityDisplay( - identities[batchIndex], - supers[batchIndex] - ); + const display = address + ? getIdentityDisplay(identities[address], supers[address]) + : null; let notification: NotificationText | null = null; if (address !== null) { diff --git a/src/pages/Pools/Roles/index.tsx b/src/pages/Pools/Roles/index.tsx index 915dd51696..efc31c950b 100644 --- a/src/pages/Pools/Roles/index.tsx +++ b/src/pages/Pools/Roles/index.tsx @@ -15,7 +15,6 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useHelp } from 'contexts/Help'; -import { useIdentities } from 'contexts/Identities'; import { useActivePools } from 'contexts/Pools/ActivePools'; import { useUi } from 'contexts/UI'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; @@ -27,10 +26,8 @@ import { RolesWrapper } from '../Home/ManagePool/Wrappers'; import { PoolAccount } from '../PoolAccount'; import { RoleEditInput } from './RoleEditInput'; import type { RoleEditEntry, RolesProps } from './types'; -import type { MaybeAddress } from '@polkadot-cloud/react/types'; export const Roles = ({ - batchKey, defaultRoles, setters = [], inline = false, @@ -44,7 +41,6 @@ export const Roles = ({ const { openModal } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); const { isReadOnlyAccount } = useImportedAccounts(); - const { fetchIdentitiesMetaBatch } = useIdentities(); const { isOwner, selectedActivePool } = useActivePools(); const { id } = selectedActivePool || { id: 0 }; const roles = defaultRoles; @@ -69,17 +65,11 @@ export const Roles = ({ // store whether roles are being edited const [isEditing, setIsEditing] = useState(false); - // store role accounts - const [accounts, setAccounts] = useState( - Object.values(roles) - ); - // is this the initial fetch const [fetched, setFetched] = useState(false); // update default roles on account switch useEffect(() => { - setAccounts(Object.values(roles)); setIsEditing(false); setRoleEdits(initialiseEdits); setFetched(false); @@ -89,7 +79,6 @@ export const Roles = ({ useEffect(() => { if (isReady && !fetched) { setFetched(true); - fetchIdentitiesMetaBatch(batchKey, Object.values(roles), true); } }, [isReady, fetched]); @@ -201,8 +190,7 @@ export const Roles = ({

{t('pools.depositor')}

@@ -218,8 +206,7 @@ export const Roles = ({ ) : ( )} @@ -236,8 +223,7 @@ export const Roles = ({ ) : ( )} @@ -254,8 +240,7 @@ export const Roles = ({ ) : ( )} diff --git a/src/pages/Pools/Roles/types.ts b/src/pages/Pools/Roles/types.ts index a07d789c56..17d960bb3a 100644 --- a/src/pages/Pools/Roles/types.ts +++ b/src/pages/Pools/Roles/types.ts @@ -5,7 +5,6 @@ import type { PoolRoles } from 'contexts/Pools/ActivePools/types'; import type { AnyFunction } from 'types'; export interface RolesProps { - batchKey: string; defaultRoles: PoolRoles; listenIsValid?: AnyFunction; setters?: AnyFunction; diff --git a/src/pages/Pools/types.ts b/src/pages/Pools/types.ts index 6ca44855e7..b558aecc3d 100644 --- a/src/pages/Pools/types.ts +++ b/src/pages/Pools/types.ts @@ -1,12 +1,12 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only +import type { ActivePool } from 'contexts/Pools/ActivePools/types'; import type { ListFormat } from 'library/PoolList/types'; export interface PoolAccountProps { address: string | null; - batchKey: string; - batchIndex: number; + pool: ActivePool | null; } export interface PoolsTabsContextInterface { From ca237750cd5beceb8a0141f329e725ebb334195b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:33:21 +0000 Subject: [PATCH 11/51] chore(deps): bump @ledgerhq/hw-transport-webhid from 6.28.2 to 6.28.3 (#1917) --- package.json | 2 +- yarn.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 3a5378be6a..3b1671a46c 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@fortawesome/free-regular-svg-icons": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", - "@ledgerhq/hw-transport-webhid": "^6.28.2", + "@ledgerhq/hw-transport-webhid": "^6.28.3", "@polkadot-cloud/assets": "^0.3.4", "@polkadot-cloud/core": "^1.2.4", "@polkadot-cloud/react": "^0.3.8", diff --git a/yarn.lock b/yarn.lock index cbf8db5a1e..95b1a69359 100644 --- a/yarn.lock +++ b/yarn.lock @@ -918,15 +918,15 @@ __metadata: languageName: node linkType: hard -"@ledgerhq/hw-transport-webhid@npm:^6.28.2": - version: 6.28.2 - resolution: "@ledgerhq/hw-transport-webhid@npm:6.28.2" +"@ledgerhq/hw-transport-webhid@npm:^6.28.3": + version: 6.28.3 + resolution: "@ledgerhq/hw-transport-webhid@npm:6.28.3" dependencies: "@ledgerhq/devices": "npm:^8.2.0" "@ledgerhq/errors": "npm:^6.16.1" - "@ledgerhq/hw-transport": "npm:^6.30.2" + "@ledgerhq/hw-transport": "npm:^6.30.3" "@ledgerhq/logs": "npm:^6.12.0" - checksum: 9529ba663470cfe8adb3d6808096982eb34ba341729db4dedc4b91185486fb38d178b418771b7d606be63652966c96e74b7076cdd311ccb34538ad71a88a8365 + checksum: b2017d283f2030e28ab663fc7f032db66ef3bfefb29ceb4cecf29561f7ed54f9e0c17c1e1e30a09ee9957ed8f1b8bada7391c4030554370a122a6654979600b8 languageName: node linkType: hard @@ -942,15 +942,15 @@ __metadata: languageName: node linkType: hard -"@ledgerhq/hw-transport@npm:^6.30.2": - version: 6.30.2 - resolution: "@ledgerhq/hw-transport@npm:6.30.2" +"@ledgerhq/hw-transport@npm:^6.30.3": + version: 6.30.3 + resolution: "@ledgerhq/hw-transport@npm:6.30.3" dependencies: "@ledgerhq/devices": "npm:^8.2.0" "@ledgerhq/errors": "npm:^6.16.1" "@ledgerhq/logs": "npm:^6.12.0" events: "npm:^3.3.0" - checksum: 2c3811e8c2a0d1844cb1ed608f0ded074ee15cd8e69dbdaa380d503c70abd4179b67137ea6dd48c78707818f0bc031f63d27d0d074eb0b885431dab884ecfb64 + checksum: e50b9f99d6127b641d293d33b5141f55e59a8a14afb231e7d5c696e234a210ca94a6573a442548384ac4afa315b60b9c3ff7cff29f3df5e00f9cb59385801d70 languageName: node linkType: hard @@ -6306,7 +6306,7 @@ __metadata: "@fortawesome/free-regular-svg-icons": "npm:^6.5.1" "@fortawesome/free-solid-svg-icons": "npm:^6.5.1" "@fortawesome/react-fontawesome": "npm:^0.2.0" - "@ledgerhq/hw-transport-webhid": "npm:^6.28.2" + "@ledgerhq/hw-transport-webhid": "npm:^6.28.3" "@ledgerhq/logs": "npm:^6.12.0" "@polkadot-cloud/assets": "npm:^0.3.4" "@polkadot-cloud/core": "npm:^1.2.4" From e4d1a30e4571423b99ca7f26360906fe2ae52dcd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:34:34 +0000 Subject: [PATCH 12/51] chore(deps-dev): bump @vitejs/plugin-react-swc from 3.5.0 to 3.6.0 (#1918) --- package.json | 2 +- yarn.lock | 100 +++++++++++++++++++++++++-------------------------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index 3b1671a46c..0c61d35b52 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "@types/styled-components": "^5.1.34", "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", - "@vitejs/plugin-react-swc": "^3.5.0", + "@vitejs/plugin-react-swc": "^3.6.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.1", diff --git a/yarn.lock b/yarn.lock index 95b1a69359..d08c2dc3b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1987,90 +1987,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-darwin-arm64@npm:1.3.105" +"@swc/core-darwin-arm64@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-darwin-arm64@npm:1.3.107" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-darwin-x64@npm:1.3.105" +"@swc/core-darwin-x64@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-darwin-x64@npm:1.3.107" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.105" +"@swc/core-linux-arm-gnueabihf@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.107" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.105" +"@swc/core-linux-arm64-gnu@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.107" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.105" +"@swc/core-linux-arm64-musl@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.107" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.105" +"@swc/core-linux-x64-gnu@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.107" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-x64-musl@npm:1.3.105" +"@swc/core-linux-x64-musl@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-x64-musl@npm:1.3.107" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.105" +"@swc/core-win32-arm64-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.107" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.105" +"@swc/core-win32-ia32-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.107" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.105" +"@swc/core-win32-x64-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.107" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@swc/core@npm:^1.3.96": - version: 1.3.105 - resolution: "@swc/core@npm:1.3.105" +"@swc/core@npm:^1.3.107": + version: 1.3.107 + resolution: "@swc/core@npm:1.3.107" dependencies: - "@swc/core-darwin-arm64": "npm:1.3.105" - "@swc/core-darwin-x64": "npm:1.3.105" - "@swc/core-linux-arm-gnueabihf": "npm:1.3.105" - "@swc/core-linux-arm64-gnu": "npm:1.3.105" - "@swc/core-linux-arm64-musl": "npm:1.3.105" - "@swc/core-linux-x64-gnu": "npm:1.3.105" - "@swc/core-linux-x64-musl": "npm:1.3.105" - "@swc/core-win32-arm64-msvc": "npm:1.3.105" - "@swc/core-win32-ia32-msvc": "npm:1.3.105" - "@swc/core-win32-x64-msvc": "npm:1.3.105" + "@swc/core-darwin-arm64": "npm:1.3.107" + "@swc/core-darwin-x64": "npm:1.3.107" + "@swc/core-linux-arm-gnueabihf": "npm:1.3.107" + "@swc/core-linux-arm64-gnu": "npm:1.3.107" + "@swc/core-linux-arm64-musl": "npm:1.3.107" + "@swc/core-linux-x64-gnu": "npm:1.3.107" + "@swc/core-linux-x64-musl": "npm:1.3.107" + "@swc/core-win32-arm64-msvc": "npm:1.3.107" + "@swc/core-win32-ia32-msvc": "npm:1.3.107" + "@swc/core-win32-x64-msvc": "npm:1.3.107" "@swc/counter": "npm:^0.1.1" "@swc/types": "npm:^0.1.5" peerDependencies: @@ -2099,7 +2099,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 719283fa1bb7e25a9e3bfb4bb2fca8dec98a1cbbcd53cdd6a8cc9d9e2ac701d0fe409b2f3f9b6ddd466b2c1592cdae9c2302a114036bb5adc08e8f95549543e0 + checksum: 1f5c3b42443f7437e8b46621db6078babf292cc0855d83b2c45f43fd57a7af098243d9f5e2cdebc5fd5219ec8d9c0429cc17601497d7e301336d104618f775b2 languageName: node linkType: hard @@ -2420,14 +2420,14 @@ __metadata: languageName: node linkType: hard -"@vitejs/plugin-react-swc@npm:^3.5.0": - version: 3.5.0 - resolution: "@vitejs/plugin-react-swc@npm:3.5.0" +"@vitejs/plugin-react-swc@npm:^3.6.0": + version: 3.6.0 + resolution: "@vitejs/plugin-react-swc@npm:3.6.0" dependencies: - "@swc/core": "npm:^1.3.96" + "@swc/core": "npm:^1.3.107" peerDependencies: vite: ^4 || ^5 - checksum: 8a0c61fd08224a8945f7190a33ff0ab563548200f0841f7d9ef4a41260d9fcd70bc75fcd5cfef2915fe1e81642e36a3c158fa2b48ba6626e19ba7da61330b2c1 + checksum: aae7c02f390559d0fbfb6285f1ba80917493d2c4979315f62f90fa06fb19b0b40362717fac035cac726575fdb120f66c4094f27bea846e2009686d15bc8637ae languageName: node linkType: hard @@ -6328,7 +6328,7 @@ __metadata: "@types/styled-components": "npm:^5.1.34" "@typescript-eslint/eslint-plugin": "npm:^6.20.0" "@typescript-eslint/parser": "npm:^6.20.0" - "@vitejs/plugin-react-swc": "npm:^3.5.0" + "@vitejs/plugin-react-swc": "npm:^3.6.0" "@zondax/ledger-substrate": "npm:^0.41.3" bignumber.js: "npm:^9.1.2" bn.js: "npm:^5.2.1" From 457b95a40974a16bf22399920fd9a56d1ab8803d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:37:01 +0000 Subject: [PATCH 13/51] chore(deps): bump i18next from 23.8.1 to 23.8.2 (#1919) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 0c61d35b52..c25a95b27e 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "date-fns": "^3.3.1", "framer-motion": "^11.0.3", "html5-qrcode": "^2.3.8", - "i18next": "^23.8.1", + "i18next": "^23.8.2", "i18next-browser-languagedetector": "^7.2.0", "lodash.throttle": "^4.1.1", "qrcode-generator": "1.4.4", diff --git a/yarn.lock b/yarn.lock index d08c2dc3b7..67ef78789a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4830,12 +4830,12 @@ __metadata: languageName: node linkType: hard -"i18next@npm:^23.8.1": - version: 23.8.1 - resolution: "i18next@npm:23.8.1" +"i18next@npm:^23.8.2": + version: 23.8.2 + resolution: "i18next@npm:23.8.2" dependencies: "@babel/runtime": "npm:^7.23.2" - checksum: f74ca2ead03e71bc8ca353c89e842b75042a52b52dcc5dfc14092469dcbc5d61f220fef536dfa95520de42baad0caa86c7a8abc1313a057ece9ba08ad8598bbf + checksum: c16ccee81bc1e096fec8d10000de42f10137f7cd27b295eca22492cb174b364ebca72327b2e6be066c4308c79bf72c8585d1c7cde2aedf556e3f423af60cd66c languageName: node linkType: hard @@ -6349,7 +6349,7 @@ __metadata: framer-motion: "npm:^11.0.3" gh-pages: "npm:^6.1.1" html5-qrcode: "npm:^2.3.8" - i18next: "npm:^23.8.1" + i18next: "npm:^23.8.2" i18next-browser-languagedetector: "npm:^7.2.0" lodash.throttle: "npm:^4.1.1" prettier: "npm:^3.2.4" From 93240737b6c56bb13446af585d755e980d57bd3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:38:26 +0000 Subject: [PATCH 14/51] chore(deps-dev): bump vite-plugin-checker from 0.6.2 to 0.6.3 (#1920) --- package.json | 2 +- yarn.lock | 28 ++++++++++------------------ 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index c25a95b27e..e2f4b2530f 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "typescript": "^5.3.3", "vite": "^5.0.12", "vite-bundle-visualizer": "^1.0.1", - "vite-plugin-checker": "^0.6.2", + "vite-plugin-checker": "^0.6.3", "vite-plugin-eslint": "^1.8.1", "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^4.3.1", diff --git a/yarn.lock b/yarn.lock index 67ef78789a..23391c9c34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5488,10 +5488,10 @@ __metadata: languageName: node linkType: hard -"lodash.debounce@npm:^4.0.8": - version: 4.0.8 - resolution: "lodash.debounce@npm:4.0.8" - checksum: 762998a63e095412b6099b8290903e0a8ddcb353ac6e2e0f2d7e7d03abd4275fe3c689d88960eb90b0dde4f177554d51a690f22a343932ecbc50a5d111849987 +"lodash-es@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash-es@npm:4.17.21" + checksum: fb407355f7e6cd523a9383e76e6b455321f0f153a6c9625e21a8827d10c54c2a2341bd2ae8d034358b60e07325e1330c14c224ff582d04612a46a4f0479ff2f2 languageName: node linkType: hard @@ -5502,13 +5502,6 @@ __metadata: languageName: node linkType: hard -"lodash.pick@npm:^4.4.0": - version: 4.4.0 - resolution: "lodash.pick@npm:4.4.0" - checksum: a04c460b95d1aaa44e9513d1dacf72ea74d838da843e45831de9de64c303f13cdde1859702a6f4dcef417816898ffd47c6ae0614c957ac70245bed2809b8d2e2 - languageName: node - linkType: hard - "lodash.throttle@npm:^4.1.1": version: 4.1.1 resolution: "lodash.throttle@npm:4.1.1" @@ -6370,7 +6363,7 @@ __metadata: usehooks-ts: "npm:2.10.0" vite: "npm:^5.0.12" vite-bundle-visualizer: "npm:^1.0.1" - vite-plugin-checker: "npm:^0.6.2" + vite-plugin-checker: "npm:^0.6.3" vite-plugin-eslint: "npm:^1.8.1" vite-plugin-svgr: "npm:^4.2.0" vite-tsconfig-paths: "npm:^4.3.1" @@ -8061,9 +8054,9 @@ __metadata: languageName: node linkType: hard -"vite-plugin-checker@npm:^0.6.2": - version: 0.6.2 - resolution: "vite-plugin-checker@npm:0.6.2" +"vite-plugin-checker@npm:^0.6.3": + version: 0.6.3 + resolution: "vite-plugin-checker@npm:0.6.3" dependencies: "@babel/code-frame": "npm:^7.12.13" ansi-escapes: "npm:^4.3.0" @@ -8072,8 +8065,7 @@ __metadata: commander: "npm:^8.0.0" fast-glob: "npm:^3.2.7" fs-extra: "npm:^11.1.0" - lodash.debounce: "npm:^4.0.8" - lodash.pick: "npm:^4.4.0" + lodash-es: "npm:^4.17.21" npm-run-path: "npm:^4.0.1" semver: "npm:^7.5.0" strip-ansi: "npm:^6.0.0" @@ -8109,7 +8101,7 @@ __metadata: optional: true vue-tsc: optional: true - checksum: 8c991c63b61e52fc69a22033aa34c6cb698d6af7dd7505a9fecb62c6e8486bc6f9667c23f5ad6f7289d9d2780bdb4711740433a5cabc27c4c7d763d028d54b2a + checksum: 3315b0a14d503ae2f563029f8bf98c5f7c587ae2463b443035a22f9f847a5a59001b43e689da20b51c0a36f3eaf7df46e3b8fd47405ac629ed5b2c4859f5f847 languageName: node linkType: hard From 02104f41c1df7207056de51bafce246f0f1a9697 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:42:56 +0000 Subject: [PATCH 15/51] chore(deps): bump usehooks-ts from 2.10.0 to 2.11.0 (#1921) --- package.json | 2 +- yarn.lock | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index e2f4b2530f..9b427d7bf7 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "react-router-dom": "^6.21.3", "react-scroll": "^1.9.0", "styled-components": "^6.1.8", - "usehooks-ts": "2.10.0" + "usehooks-ts": "2.11.0" }, "devDependencies": { "@ledgerhq/logs": "^6.12.0", diff --git a/yarn.lock b/yarn.lock index 23391c9c34..0db867c76f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6360,7 +6360,7 @@ __metadata: sass: "npm:^1.70.0" styled-components: "npm:^6.1.8" typescript: "npm:^5.3.3" - usehooks-ts: "npm:2.10.0" + usehooks-ts: "npm:2.11.0" vite: "npm:^5.0.12" vite-bundle-visualizer: "npm:^1.0.1" vite-plugin-checker: "npm:^0.6.3" @@ -7979,13 +7979,16 @@ __metadata: languageName: node linkType: hard -"usehooks-ts@npm:2.10.0": - version: 2.10.0 - resolution: "usehooks-ts@npm:2.10.0" +"usehooks-ts@npm:2.11.0": + version: 2.11.0 + resolution: "usehooks-ts@npm:2.11.0" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: cb5b3ab4f9269b103bfbd85ef624b0dc10205015134233626a0ea2d4b9e2849e401ca3c167f3876fcd51387751696746bd8e8b7460402923a80354245a3d5202 + lodash.debounce: ^4 + react: ^16.8.0 || ^17 || ^18 + peerDependenciesMeta: + lodash.debounce: + optional: true + checksum: ca748fb72eea1728d1ba8de8fc9cc3be7cd33a0b274293b775773193fc5074b4d650ced4bacbb408c32825354afdddfb06a50283d08c6783dbb1c4a31a4f39e6 languageName: node linkType: hard From ba15327332abc618b4399142fd3d82efef7c693f Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 1 Feb 2024 22:31:35 +0800 Subject: [PATCH 16/51] chore: add peer dep --- package.json | 2 + yarn.lock | 432 ++++++++++++++++++++++++--------------------------- 2 files changed, 203 insertions(+), 231 deletions(-) diff --git a/package.json b/package.json index 9b427d7bf7..83ae91023b 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "html5-qrcode": "^2.3.8", "i18next": "^23.8.2", "i18next-browser-languagedetector": "^7.2.0", + "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1", "qrcode-generator": "1.4.4", "rc-slider": "^10.5.0", @@ -63,6 +64,7 @@ "devDependencies": { "@ledgerhq/logs": "^6.12.0", "@types/chroma-js": "^2.4.3", + "@types/lodash.debounce": "^4", "@types/lodash.throttle": "^4.1.9", "@types/react": "^18.2.48", "@types/react-dom": "^18.2.17", diff --git a/yarn.lock b/yarn.lock index 0db867c76f..b9b81762ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,7 +22,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.23.5": version: 7.23.5 resolution: "@babel/code-frame@npm:7.23.5" dependencies: @@ -40,25 +40,25 @@ __metadata: linkType: hard "@babel/core@npm:^7.21.3": - version: 7.23.7 - resolution: "@babel/core@npm:7.23.7" + version: 7.23.9 + resolution: "@babel/core@npm:7.23.9" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.23.5" "@babel/generator": "npm:^7.23.6" "@babel/helper-compilation-targets": "npm:^7.23.6" "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helpers": "npm:^7.23.7" - "@babel/parser": "npm:^7.23.6" - "@babel/template": "npm:^7.22.15" - "@babel/traverse": "npm:^7.23.7" - "@babel/types": "npm:^7.23.6" + "@babel/helpers": "npm:^7.23.9" + "@babel/parser": "npm:^7.23.9" + "@babel/template": "npm:^7.23.9" + "@babel/traverse": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 38c9934973d384ed83369712978453eac91dc3f22167404dbdb272b64f602e74728a6f37012c53ee57e521b8ae2da60097f050497d9b6a212d28b59cdfb2cd1d + checksum: 03883300bf1252ab4c9ba5b52f161232dd52873dbe5cde9289bb2bb26e935c42682493acbac9194a59a3b6cbd17f4c4c84030db8d6d482588afe64531532ff9b languageName: node linkType: hard @@ -176,14 +176,14 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.23.7": - version: 7.23.8 - resolution: "@babel/helpers@npm:7.23.8" +"@babel/helpers@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/helpers@npm:7.23.9" dependencies: - "@babel/template": "npm:^7.22.15" - "@babel/traverse": "npm:^7.23.7" - "@babel/types": "npm:^7.23.6" - checksum: d9fce49278a31aaa017a40c1fcdaa450999c49e33582cce8138058c58b1acbe3a2d2488f010f28e91dedf0d35795ea32f0ee18745bbb6c7f54052ae0fd7e6a3f + "@babel/template": "npm:^7.23.9" + "@babel/traverse": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + checksum: f69fd0aca96a6fb8bd6dd044cd8a5c0f1851072d4ce23355345b9493c4032e76d1217f86b70df795e127553cf7f3fcd1587ede9d1b03b95e8b62681ca2165b87 languageName: node linkType: hard @@ -198,38 +198,38 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.6": - version: 7.23.6 - resolution: "@babel/parser@npm:7.23.6" +"@babel/parser@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/parser@npm:7.23.9" bin: parser: ./bin/babel-parser.js - checksum: 6f76cd5ccae1fa9bcab3525b0865c6222e9c1d22f87abc69f28c5c7b2c8816a13361f5bd06bddbd5faf903f7320a8feba02545c981468acec45d12a03db7755e + checksum: 7df97386431366d4810538db4b9ec538f4377096f720c0591c7587a16f6810e62747e9fbbfa1ff99257fd4330035e4fb1b5b77c7bd3b97ce0d2e3780a6618975 languageName: node linkType: hard "@babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2": - version: 7.23.8 - resolution: "@babel/runtime@npm:7.23.8" + version: 7.23.9 + resolution: "@babel/runtime@npm:7.23.9" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: ba5e8fbb32ef04f6cab5e89c54a0497c2fde7b730595cc1af93496270314f13ff2c6a9360fdb2f0bdd4d6b376752ce3cf85642bd6b876969a6a62954934c2df8 + checksum: e71205fdd7082b2656512cc98e647d9ea7e222e4fe5c36e9e5adc026446fcc3ba7b3cdff8b0b694a0b78bb85db83e7b1e3d4c56ef90726682b74f13249cf952d languageName: node linkType: hard -"@babel/template@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/template@npm:7.22.15" +"@babel/template@npm:^7.22.15, @babel/template@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/template@npm:7.23.9" dependencies: - "@babel/code-frame": "npm:^7.22.13" - "@babel/parser": "npm:^7.22.15" - "@babel/types": "npm:^7.22.15" - checksum: 9312edd37cf1311d738907003f2aa321a88a42ba223c69209abe4d7111db019d321805504f606c7fd75f21c6cf9d24d0a8223104cd21ebd207e241b6c551f454 + "@babel/code-frame": "npm:^7.23.5" + "@babel/parser": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + checksum: 0e8b60119433787742bc08ae762bbd8d6755611c4cabbcb7627b292ec901a55af65d93d1c88572326069efb64136ef151ec91ffb74b2df7689bbab237030833a languageName: node linkType: hard -"@babel/traverse@npm:^7.23.7": - version: 7.23.7 - resolution: "@babel/traverse@npm:7.23.7" +"@babel/traverse@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/traverse@npm:7.23.9" dependencies: "@babel/code-frame": "npm:^7.23.5" "@babel/generator": "npm:^7.23.6" @@ -237,22 +237,22 @@ __metadata: "@babel/helper-function-name": "npm:^7.23.0" "@babel/helper-hoist-variables": "npm:^7.22.5" "@babel/helper-split-export-declaration": "npm:^7.22.6" - "@babel/parser": "npm:^7.23.6" - "@babel/types": "npm:^7.23.6" + "@babel/parser": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: e32fceb4249beec2bde83968ddffe17444221c1ee5cd18c543a2feaf94e3ca83f2a4dfbc2dcca87cf226e0105973e0fe3717063a21e982a9de9945615ab3f3f5 + checksum: d1615d1d02f04d47111a7ea4446a1a6275668ca39082f31d51f08380de9502e19862be434eaa34b022ce9a17dbb8f9e2b73a746c654d9575f3a680a7ffdf5630 languageName: node linkType: hard -"@babel/types@npm:^7.21.3, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.8.3": - version: 7.23.6 - resolution: "@babel/types@npm:7.23.6" +"@babel/types@npm:^7.21.3, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.23.9, @babel/types@npm:^7.8.3": + version: 7.23.9 + resolution: "@babel/types@npm:7.23.9" dependencies: "@babel/helper-string-parser": "npm:^7.23.4" "@babel/helper-validator-identifier": "npm:^7.22.20" to-fast-properties: "npm:^2.0.0" - checksum: 42cefce8a68bd09bb5828b4764aa5586c53c60128ac2ac012e23858e1c179347a4aac9c66fc577994fbf57595227611c5ec8270bf0cfc94ff033bbfac0550b70 + checksum: edc7bb180ce7e4d2aea10c6972fb10474341ac39ba8fdc4a27ffb328368dfdfbf40fca18e441bbe7c483774500d5c05e222cec276c242e952853dcaf4eb884f7 languageName: node linkType: hard @@ -362,163 +362,163 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/aix-ppc64@npm:0.19.11" +"@esbuild/aix-ppc64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/aix-ppc64@npm:0.19.12" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/android-arm64@npm:0.19.11" +"@esbuild/android-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-arm64@npm:0.19.12" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/android-arm@npm:0.19.11" +"@esbuild/android-arm@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-arm@npm:0.19.12" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/android-x64@npm:0.19.11" +"@esbuild/android-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-x64@npm:0.19.12" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/darwin-arm64@npm:0.19.11" +"@esbuild/darwin-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/darwin-arm64@npm:0.19.12" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/darwin-x64@npm:0.19.11" +"@esbuild/darwin-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/darwin-x64@npm:0.19.12" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/freebsd-arm64@npm:0.19.11" +"@esbuild/freebsd-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/freebsd-arm64@npm:0.19.12" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/freebsd-x64@npm:0.19.11" +"@esbuild/freebsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/freebsd-x64@npm:0.19.12" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-arm64@npm:0.19.11" +"@esbuild/linux-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-arm64@npm:0.19.12" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-arm@npm:0.19.11" +"@esbuild/linux-arm@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-arm@npm:0.19.12" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-ia32@npm:0.19.11" +"@esbuild/linux-ia32@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-ia32@npm:0.19.12" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-loong64@npm:0.19.11" +"@esbuild/linux-loong64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-loong64@npm:0.19.12" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-mips64el@npm:0.19.11" +"@esbuild/linux-mips64el@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-mips64el@npm:0.19.12" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-ppc64@npm:0.19.11" +"@esbuild/linux-ppc64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-ppc64@npm:0.19.12" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-riscv64@npm:0.19.11" +"@esbuild/linux-riscv64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-riscv64@npm:0.19.12" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-s390x@npm:0.19.11" +"@esbuild/linux-s390x@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-s390x@npm:0.19.12" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/linux-x64@npm:0.19.11" +"@esbuild/linux-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-x64@npm:0.19.12" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/netbsd-x64@npm:0.19.11" +"@esbuild/netbsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/netbsd-x64@npm:0.19.12" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/openbsd-x64@npm:0.19.11" +"@esbuild/openbsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/openbsd-x64@npm:0.19.12" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/sunos-x64@npm:0.19.11" +"@esbuild/sunos-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/sunos-x64@npm:0.19.12" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/win32-arm64@npm:0.19.11" +"@esbuild/win32-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-arm64@npm:0.19.12" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/win32-ia32@npm:0.19.11" +"@esbuild/win32-ia32@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-ia32@npm:0.19.12" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.19.11": - version: 0.19.11 - resolution: "@esbuild/win32-x64@npm:0.19.11" +"@esbuild/win32-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-x64@npm:0.19.12" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -930,19 +930,7 @@ __metadata: languageName: node linkType: hard -"@ledgerhq/hw-transport@npm:^6.27.1": - version: 6.30.1 - resolution: "@ledgerhq/hw-transport@npm:6.30.1" - dependencies: - "@ledgerhq/devices": "npm:^8.2.0" - "@ledgerhq/errors": "npm:^6.16.1" - "@ledgerhq/logs": "npm:^6.12.0" - events: "npm:^3.3.0" - checksum: ef53219330296f81f84fc116debd27218ee97da4477aa68a58a1baae9f1d5e0fa220d293a40c0415f60a977634efa7ebe6c8bce84ff817a898b0aa6a7719a8f1 - languageName: node - linkType: hard - -"@ledgerhq/hw-transport@npm:^6.30.3": +"@ledgerhq/hw-transport@npm:^6.27.1, @ledgerhq/hw-transport@npm:^6.30.3": version: 6.30.3 resolution: "@ledgerhq/hw-transport@npm:6.30.3" dependencies: @@ -962,9 +950,9 @@ __metadata: linkType: hard "@lit-labs/ssr-dom-shim@npm:^1.0.0, @lit-labs/ssr-dom-shim@npm:^1.1.0": - version: 1.1.2 - resolution: "@lit-labs/ssr-dom-shim@npm:1.1.2" - checksum: e51c7c156317ac95cac8d534d8608ac2a9dda7441f14f73e9e66a995d277851a90315324fe74690d1169a66dce645ed9674a8f5a9a467d183156de1c87549b23 + version: 1.2.0 + resolution: "@lit-labs/ssr-dom-shim@npm:1.2.0" + checksum: 016168cf6901ab343462c13fb168dda6d549f8b42680aa394e6b7cd0af7cce51271e00dbfa5bbbe388912bf89cbb8f941a21cc3ec9bf95d6a84b6241aa9e5a72 languageName: node linkType: hard @@ -1136,28 +1124,14 @@ __metadata: languageName: node linkType: hard -"@polkadot-cloud/assets@npm:^0.3.2": - version: 0.3.2 - resolution: "@polkadot-cloud/assets@npm:0.3.2" - checksum: 8447ae961912b36c2b9e69990aea69967ee220eecbd598c8ed60482dc2d501fa81f1c31f550eea23f84d9f179d41baaae25b794018589d682fdcebed5a2e8136 - languageName: node - linkType: hard - -"@polkadot-cloud/assets@npm:^0.3.4": +"@polkadot-cloud/assets@npm:^0.3.2, @polkadot-cloud/assets@npm:^0.3.4": version: 0.3.4 resolution: "@polkadot-cloud/assets@npm:0.3.4" checksum: 3ea2ee71930fa39541831aab17b634159bc23fab1e53916d97d57ebd8591c4fdfcc282f4e67078b9907f181dba91e126693b8e424e0b461cff7d03a80db5ab79 languageName: node linkType: hard -"@polkadot-cloud/core@npm:^1.2.2": - version: 1.2.2 - resolution: "@polkadot-cloud/core@npm:1.2.2" - checksum: 4718100f3cdd9bff444e264c6b65b1549a11bdfc279612168977484c18af7d3a87e955f35636f6c4557c722a8636095e5ec4aff5a035550e7bcd39460388bec2 - languageName: node - linkType: hard - -"@polkadot-cloud/core@npm:^1.2.4": +"@polkadot-cloud/core@npm:^1.2.2, @polkadot-cloud/core@npm:^1.2.4": version: 1.2.4 resolution: "@polkadot-cloud/core@npm:1.2.4" checksum: fcc033b5fb4611af39402e22348b3dbed7ba5f610123d8094eda72da5d3427069ce5da515cf61cde9e4f340e32a90087c0797bcb18e2d3632682f627199f0d3a @@ -1190,21 +1164,7 @@ __metadata: languageName: node linkType: hard -"@polkadot-cloud/utils@npm:^0.2.1": - version: 0.2.1 - resolution: "@polkadot-cloud/utils@npm:0.2.1" - dependencies: - "@polkadot/keyring": "npm:^12.6.2" - "@polkadot/util": "npm:^12.6.2" - bignumber.js: "npm:^9.1.1" - peerDependencies: - "@polkadot/keyring": ^12.6.2 - "@polkadot/util": ^12.6.2 - checksum: c1b47f1501e5f88aecee221c1a7ecfe801ed07c84b5c5c27b92677b5f21ce878ed308804618488611736bb46dc39a682c44eec346d9549503478b5dd50e98fa0 - languageName: node - linkType: hard - -"@polkadot-cloud/utils@npm:^0.2.3": +"@polkadot-cloud/utils@npm:^0.2.1, @polkadot-cloud/utils@npm:^0.2.3": version: 0.2.3 resolution: "@polkadot-cloud/utils@npm:0.2.3" dependencies: @@ -2174,6 +2134,15 @@ __metadata: languageName: node linkType: hard +"@types/lodash.debounce@npm:^4": + version: 4.0.9 + resolution: "@types/lodash.debounce@npm:4.0.9" + dependencies: + "@types/lodash": "npm:*" + checksum: 9fbb24e5e52616faf60ba5c82d8c6517f4b86fc6e9ab353b4c56c0760f63d9bf53af3f2d8f6c37efa48090359fb96dba1087d497758511f6c40677002191d042 + languageName: node + linkType: hard + "@types/lodash.throttle@npm:^4.1.9": version: 4.1.9 resolution: "@types/lodash.throttle@npm:4.1.9" @@ -2191,11 +2160,11 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 20.11.5 - resolution: "@types/node@npm:20.11.5" + version: 20.11.15 + resolution: "@types/node@npm:20.11.15" dependencies: undici-types: "npm:~5.26.4" - checksum: 6d18cec852f5cfbed3ec42b5c01c026e7a3f9da540d6e3d6738d4cee9979fb308cf27b6df7ba40a6553e7bc82e678f0ef53ba6e6ad52e5b86bd97b7783c2a42c + checksum: 7dfab4208fedc02e9584c619551906f46ade7955bb929b1e32e354a50522eb532d6bfb2844fdaad2c8dca03be84a590674460c64cb101e1a33bb318e1ec448d4 languageName: node linkType: hard @@ -2234,13 +2203,13 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:^18.2.48": - version: 18.2.48 - resolution: "@types/react@npm:18.2.48" + version: 18.2.51 + resolution: "@types/react@npm:18.2.51" dependencies: "@types/prop-types": "npm:*" "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: 7e89f18ea2928b1638f564b156d692894dcb9352a7e0a807873c97e858abe1f23dbd165a25dd088a991344e973fdeef88ba5724bfb64504b74072cbc9c220c3a + checksum: 409f684dc1cb03ed542e1f185da7e84f81186d7945119adb553fe0e4e28bad3468213fce9096e1867b358c87fb57fc80b53a3e544a3779f7080ddde7c1b99eb1 languageName: node linkType: hard @@ -2772,9 +2741,9 @@ __metadata: linkType: hard "available-typed-arrays@npm:^1.0.5": - version: 1.0.5 - resolution: "available-typed-arrays@npm:1.0.5" - checksum: c4df567ca72d2754a6cbad20088f5f98b1065b3360178169fa9b44ea101af62c0f423fc3854fa820fd6895b6b9171b8386e71558203103ff8fc2ad503fdcc660 + version: 1.0.6 + resolution: "available-typed-arrays@npm:1.0.6" + checksum: e427360e68ccb19fa0ea48421f393ef3f3d2c9c561f6a8a0704ff472d58024be3e4101f00565445ba453de723a76f4a650defbacaea663c3fb3be8b8f842e955 languageName: node linkType: hard @@ -2938,16 +2907,16 @@ __metadata: linkType: hard "browserslist@npm:^4.22.2": - version: 4.22.2 - resolution: "browserslist@npm:4.22.2" + version: 4.22.3 + resolution: "browserslist@npm:4.22.3" dependencies: - caniuse-lite: "npm:^1.0.30001565" - electron-to-chromium: "npm:^1.4.601" + caniuse-lite: "npm:^1.0.30001580" + electron-to-chromium: "npm:^1.4.648" node-releases: "npm:^2.0.14" update-browserslist-db: "npm:^1.0.13" bin: browserslist: cli.js - checksum: 2a331aab90503130043ca41dd5d281fa1e89d5e076d07a2d75e76bf4d693bd56e73d5abcd8c4f39119da6328d450578c216cf1cd5c99b82d8a90a2ae6271b465 + checksum: 5a1f673ce0d6e61a68369835a6b66e199669bde02c3bed5ec51e77598d8daafd91719dba55b15af2021b9ad0bbaa94951fd702eb71087449eb28be8002815ece languageName: node linkType: hard @@ -3049,10 +3018,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001565": - version: 1.0.30001579 - resolution: "caniuse-lite@npm:1.0.30001579" - checksum: 4003970f8d01a5fa314e39f4a21751dc750a530f3d19aed225e18e8e02892b590b8b0debfa0961eae9bc0e49b77bfb17cf30d2469540e428a8305e3cc9164fb8 +"caniuse-lite@npm:^1.0.30001580": + version: 1.0.30001582 + resolution: "caniuse-lite@npm:1.0.30001582" + checksum: 6fe9dede07b61755a1ca55e7e6381d113a166942196c98828038524eb0adc8f0001d492a7c0e81a568a6c6edfe76051e0a0dc5a435d72a77d351144be31998ec languageName: node linkType: hard @@ -3490,10 +3459,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.601": - version: 1.4.642 - resolution: "electron-to-chromium@npm:1.4.642" - checksum: 1a45c745e4cfa6dd0a756ca7df8295dd227b80a6d3552ccedd9665525aedab53d5335e8b97d64c02a6b10b0c773f8b9c20877875b3a3eb2428330f14d3892f27 +"electron-to-chromium@npm:^1.4.648": + version: 1.4.653 + resolution: "electron-to-chromium@npm:1.4.653" + checksum: 22c9dbe6d9a756caf7909d87fd93d00ca0043158a82f1ba87e855e09de295da6d3c1346feab89e49119b5043bb4c4f56f868fd184e6a7654b088bd089b451b3a languageName: node linkType: hard @@ -3692,32 +3661,32 @@ __metadata: linkType: hard "esbuild@npm:^0.19.3": - version: 0.19.11 - resolution: "esbuild@npm:0.19.11" - dependencies: - "@esbuild/aix-ppc64": "npm:0.19.11" - "@esbuild/android-arm": "npm:0.19.11" - "@esbuild/android-arm64": "npm:0.19.11" - "@esbuild/android-x64": "npm:0.19.11" - "@esbuild/darwin-arm64": "npm:0.19.11" - "@esbuild/darwin-x64": "npm:0.19.11" - "@esbuild/freebsd-arm64": "npm:0.19.11" - "@esbuild/freebsd-x64": "npm:0.19.11" - "@esbuild/linux-arm": "npm:0.19.11" - "@esbuild/linux-arm64": "npm:0.19.11" - "@esbuild/linux-ia32": "npm:0.19.11" - "@esbuild/linux-loong64": "npm:0.19.11" - "@esbuild/linux-mips64el": "npm:0.19.11" - "@esbuild/linux-ppc64": "npm:0.19.11" - "@esbuild/linux-riscv64": "npm:0.19.11" - "@esbuild/linux-s390x": "npm:0.19.11" - "@esbuild/linux-x64": "npm:0.19.11" - "@esbuild/netbsd-x64": "npm:0.19.11" - "@esbuild/openbsd-x64": "npm:0.19.11" - "@esbuild/sunos-x64": "npm:0.19.11" - "@esbuild/win32-arm64": "npm:0.19.11" - "@esbuild/win32-ia32": "npm:0.19.11" - "@esbuild/win32-x64": "npm:0.19.11" + version: 0.19.12 + resolution: "esbuild@npm:0.19.12" + dependencies: + "@esbuild/aix-ppc64": "npm:0.19.12" + "@esbuild/android-arm": "npm:0.19.12" + "@esbuild/android-arm64": "npm:0.19.12" + "@esbuild/android-x64": "npm:0.19.12" + "@esbuild/darwin-arm64": "npm:0.19.12" + "@esbuild/darwin-x64": "npm:0.19.12" + "@esbuild/freebsd-arm64": "npm:0.19.12" + "@esbuild/freebsd-x64": "npm:0.19.12" + "@esbuild/linux-arm": "npm:0.19.12" + "@esbuild/linux-arm64": "npm:0.19.12" + "@esbuild/linux-ia32": "npm:0.19.12" + "@esbuild/linux-loong64": "npm:0.19.12" + "@esbuild/linux-mips64el": "npm:0.19.12" + "@esbuild/linux-ppc64": "npm:0.19.12" + "@esbuild/linux-riscv64": "npm:0.19.12" + "@esbuild/linux-s390x": "npm:0.19.12" + "@esbuild/linux-x64": "npm:0.19.12" + "@esbuild/netbsd-x64": "npm:0.19.12" + "@esbuild/openbsd-x64": "npm:0.19.12" + "@esbuild/sunos-x64": "npm:0.19.12" + "@esbuild/win32-arm64": "npm:0.19.12" + "@esbuild/win32-ia32": "npm:0.19.12" + "@esbuild/win32-x64": "npm:0.19.12" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -3767,7 +3736,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 0fd913124089e26d30ec30f73b94d4ef9607935251df3253f869106980a5d4c78aa517738c8746abe6e933262e91a77d31427ce468ed8fc7fe498a20f7f92fbc + checksum: 0f2d21ffe24ebead64843f87c3aebe2e703a5ed9feb086a0728b24907fac2eb9923e4a79857d3df9059c915739bd7a870dd667972eae325c67f478b592b8582d languageName: node linkType: hard @@ -4177,11 +4146,11 @@ __metadata: linkType: hard "fastq@npm:^1.6.0": - version: 1.16.0 - resolution: "fastq@npm:1.16.0" + version: 1.17.0 + resolution: "fastq@npm:1.17.0" dependencies: reusify: "npm:^1.0.4" - checksum: 38c1b49adba639af020727284a02af021acab764efd7f088bc31364e8a5b01ce9031eb6c5f7f304019b8267c3b7c236e79d6904884f50f94f83b1700b8a6619a + checksum: 0a90ed46ccd6a858a32e297bd35f4249ba6544899b905d08f33a6ba36459041639161fa15bc85a38afa98dd44fb2fa2969419a879721a1395d3e24668a7732c7 languageName: node linkType: hard @@ -4856,16 +4825,16 @@ __metadata: linkType: hard "ignore@npm:^5.2.0, ignore@npm:^5.2.4": - version: 5.3.0 - resolution: "ignore@npm:5.3.0" - checksum: dc06bea5c23aae65d0725a957a0638b57e235ae4568dda51ca142053ed2c352de7e3bc93a69b2b32ac31966a1952e9a93c5ef2e2ab7c6b06aef9808f6b55b571 + version: 5.3.1 + resolution: "ignore@npm:5.3.1" + checksum: 703f7f45ffb2a27fb2c5a8db0c32e7dee66b33a225d28e8db4e1be6474795f606686a6e3bcc50e1aa12f2042db4c9d4a7d60af3250511de74620fbed052ea4cd languageName: node linkType: hard "immutable@npm:^4.0.0": - version: 4.3.4 - resolution: "immutable@npm:4.3.4" - checksum: c15b9f0fa7b3c9315725cb00704fddad59f0e668a7379c39b9a528a8386140ee9effb015ae51a5b423e05c59d15fc0b38c970db6964ad6b3e05d0761db68441f + version: 4.3.5 + resolution: "immutable@npm:4.3.5" + checksum: 63d2d7908241a955d18c7822fd2215b6e89ff5a1a33cc72cd475b013cbbdef7a705aa5170a51ce9f84a57f62fdddfaa34e7b5a14b33d8a43c65cc6a881d6e894 languageName: node linkType: hard @@ -5488,10 +5457,10 @@ __metadata: languageName: node linkType: hard -"lodash-es@npm:^4.17.21": - version: 4.17.21 - resolution: "lodash-es@npm:4.17.21" - checksum: fb407355f7e6cd523a9383e76e6b455321f0f153a6c9625e21a8827d10c54c2a2341bd2ae8d034358b60e07325e1330c14c224ff582d04612a46a4f0479ff2f2 +"lodash.debounce@npm:^4.0.8": + version: 4.0.8 + resolution: "lodash.debounce@npm:4.0.8" + checksum: 762998a63e095412b6099b8290903e0a8ddcb353ac6e2e0f2d7e7d03abd4275fe3c689d88960eb90b0dde4f177554d51a690f22a343932ecbc50a5d111849987 languageName: node linkType: hard @@ -5546,9 +5515,9 @@ __metadata: linkType: hard "lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.1.0 - resolution: "lru-cache@npm:10.1.0" - checksum: 778bc8b2626daccd75f24c4b4d10632496e21ba064b126f526c626fbdbc5b28c472013fccd45d7646b9e1ef052444824854aed617b59cd570d01a8b7d651fc1e + version: 10.2.0 + resolution: "lru-cache@npm:10.2.0" + checksum: c9847612aa2daaef102d30542a8d6d9b2c2bb36581c1bf0dc3ebf5e5f3352c772a749e604afae2e46873b930a9e9523743faac4e5b937c576ab29196774712ee languageName: node linkType: hard @@ -5571,11 +5540,11 @@ __metadata: linkType: hard "magic-string@npm:^0.30.5": - version: 0.30.5 - resolution: "magic-string@npm:0.30.5" + version: 0.30.6 + resolution: "magic-string@npm:0.30.6" dependencies: "@jridgewell/sourcemap-codec": "npm:^1.4.15" - checksum: 38ac220ca7539e96da7ea2f38d85796bdf5c69b6bcae728c4bc2565084e6dc326b9174ee9770bea345cf6c9b3a24041b767167874fab5beca874d2356a9d1520 + checksum: e8835de3427d28678667c4a82e3b54d82ac73d455aa508b8445867b70024eb7dabe4ef82393762baf296256981fa973fc1b8a3e7f837efbc44eec9a65b19efd3 languageName: node linkType: hard @@ -5848,13 +5817,13 @@ __metadata: linkType: hard "nock@npm:^13.4.0": - version: 13.5.0 - resolution: "nock@npm:13.5.0" + version: 13.5.1 + resolution: "nock@npm:13.5.1" dependencies: debug: "npm:^4.1.0" json-stringify-safe: "npm:^5.0.1" propagate: "npm:^2.0.0" - checksum: ba98390042a61b8687da9174fa07282d14f8b0cb5ac50c310eba9bb70a0beb5ea8257ba1f2a3e324db5570111689485a1f16746c2527f3050357e6917666bdef + checksum: 92d42145c184d51a1a44f88711d4286e5a887f8cf3aedf11402e02009b97b590713c8dcbd368be3522d454e6c06b5dd8df1be441d20136c00df69b4db424bbfa languageName: node linkType: hard @@ -6313,6 +6282,7 @@ __metadata: "@polkawatch/ddp-client": "npm:^2.0.10" "@substrate/connect": "npm:0.7.35" "@types/chroma-js": "npm:^2.4.3" + "@types/lodash.debounce": "npm:^4" "@types/lodash.throttle": "npm:^4.1.9" "@types/react": "npm:^18.2.48" "@types/react-dom": "npm:^18.2.17" @@ -6344,6 +6314,7 @@ __metadata: html5-qrcode: "npm:^2.3.8" i18next: "npm:^23.8.2" i18next-browser-languagedetector: "npm:^7.2.0" + lodash.debounce: "npm:^4.0.8" lodash.throttle: "npm:^4.1.1" prettier: "npm:^3.2.4" prettier-plugin-organize-imports: "npm:^3.2.4" @@ -8058,8 +8029,8 @@ __metadata: linkType: hard "vite-plugin-checker@npm:^0.6.3": - version: 0.6.3 - resolution: "vite-plugin-checker@npm:0.6.3" + version: 0.6.4 + resolution: "vite-plugin-checker@npm:0.6.4" dependencies: "@babel/code-frame": "npm:^7.12.13" ansi-escapes: "npm:^4.3.0" @@ -8068,7 +8039,6 @@ __metadata: commander: "npm:^8.0.0" fast-glob: "npm:^3.2.7" fs-extra: "npm:^11.1.0" - lodash-es: "npm:^4.17.21" npm-run-path: "npm:^4.0.1" semver: "npm:^7.5.0" strip-ansi: "npm:^6.0.0" @@ -8104,7 +8074,7 @@ __metadata: optional: true vue-tsc: optional: true - checksum: 3315b0a14d503ae2f563029f8bf98c5f7c587ae2463b443035a22f9f847a5a59001b43e689da20b51c0a36f3eaf7df46e3b8fd47405ac629ed5b2c4859f5f847 + checksum: ae61f01b620c458e355ad05ff632e3143312e75c67acdaaa1fe5160d679283364867a4a8d6c6a3f85838f0251033275af96a1aa9b52eed227151cdbca0c996cf languageName: node linkType: hard From 58b5856326947a24a229ec9a807ab568cd60a218 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 1 Feb 2024 22:34:44 +0800 Subject: [PATCH 17/51] chore: remove unused hook --- src/hooks/useBlockNumber/index.tsx | 49 ------------------------------ 1 file changed, 49 deletions(-) delete mode 100644 src/hooks/useBlockNumber/index.tsx diff --git a/src/hooks/useBlockNumber/index.tsx b/src/hooks/useBlockNumber/index.tsx deleted file mode 100644 index 895f2b5254..0000000000 --- a/src/hooks/useBlockNumber/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { rmCommas } from '@polkadot-cloud/utils'; -import BigNumber from 'bignumber.js'; -import { useEffect, useRef, useState } from 'react'; -import { useApi } from 'contexts/Api'; -import type { AnyApi } from 'types'; -import { useNetwork } from 'contexts/Network'; - -export const useBlockNumber = () => { - const { network } = useNetwork(); - const { isReady, api } = useApi(); - - // store the current block number. - const [block, setBlock] = useState(new BigNumber(0)); - - // store block unsub. - const unsub = useRef(); - - useEffect(() => { - if (isReady) { - subscribeBlockNumber(); - } - return () => { - if (unsub.current) { - unsub.current(); - } - }; - }, [network, isReady]); - - const subscribeBlockNumber = async () => { - if (!api) { - return; - } - - const subscribeBlock = async () => { - const u = await api.query.system.number((number: AnyApi) => { - setBlock(new BigNumber(rmCommas(number.toString()))); - }); - return u; - }; - Promise.all([subscribeBlock]).then(([u]) => { - unsub.current = u; - }); - }; - - return block; -}; From 654352ffb0d3a79e045423873a5b04931e64bd04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 22:02:41 +0000 Subject: [PATCH 18/51] chore(deps): bump usehooks-ts from 2.11.0 to 2.12.1 (#1922) --- package.json | 2 +- yarn.lock | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 83ae91023b..3985c2507f 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "react-router-dom": "^6.21.3", "react-scroll": "^1.9.0", "styled-components": "^6.1.8", - "usehooks-ts": "2.11.0" + "usehooks-ts": "2.12.1" }, "devDependencies": { "@ledgerhq/logs": "^6.12.0", diff --git a/yarn.lock b/yarn.lock index b9b81762ed..f4c5cdc10c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6331,7 +6331,7 @@ __metadata: sass: "npm:^1.70.0" styled-components: "npm:^6.1.8" typescript: "npm:^5.3.3" - usehooks-ts: "npm:2.11.0" + usehooks-ts: "npm:2.12.1" vite: "npm:^5.0.12" vite-bundle-visualizer: "npm:^1.0.1" vite-plugin-checker: "npm:^0.6.3" @@ -7950,16 +7950,14 @@ __metadata: languageName: node linkType: hard -"usehooks-ts@npm:2.11.0": - version: 2.11.0 - resolution: "usehooks-ts@npm:2.11.0" +"usehooks-ts@npm:2.12.1": + version: 2.12.1 + resolution: "usehooks-ts@npm:2.12.1" + dependencies: + lodash.debounce: "npm:^4.0.8" peerDependencies: - lodash.debounce: ^4 react: ^16.8.0 || ^17 || ^18 - peerDependenciesMeta: - lodash.debounce: - optional: true - checksum: ca748fb72eea1728d1ba8de8fc9cc3be7cd33a0b274293b775773193fc5074b4d650ced4bacbb408c32825354afdddfb06a50283d08c6783dbb1c4a31a4f39e6 + checksum: 996ff8afe1a222746af9bfb5dd752ceaf33120e56c2e660906ec30e53d99fce3b04a342776c64dba5db477dd12655fab5f3742fac8639c4a35fdc5be74f46074 languageName: node linkType: hard From e7684eb45706481feba9fc43b963ad1f1941bfa1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 22:06:16 +0000 Subject: [PATCH 19/51] chore(deps): bump react-router-dom from 6.21.3 to 6.22.0 (#1923) --- package.json | 2 +- yarn.lock | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 3985c2507f..c28efbcfa3 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "react-error-boundary": "^4.0.12", "react-helmet": "^6.1.0", "react-i18next": "^14.0.1", - "react-router-dom": "^6.21.3", + "react-router-dom": "^6.22.0", "react-scroll": "^1.9.0", "styled-components": "^6.1.8", "usehooks-ts": "2.12.1" diff --git a/yarn.lock b/yarn.lock index f4c5cdc10c..d5d847b6c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1622,10 +1622,10 @@ __metadata: languageName: node linkType: hard -"@remix-run/router@npm:1.14.2": - version: 1.14.2 - resolution: "@remix-run/router@npm:1.14.2" - checksum: 163d4a8ea3e25a7a7e3015f274e54b8043eddaaa92da220cad2893eb0fcbb649f617152c6d74680a4b55c0f319944ff1b362e87f814bb73be54f8d687ee730d6 +"@remix-run/router@npm:1.15.0": + version: 1.15.0 + resolution: "@remix-run/router@npm:1.15.0" + checksum: 4805b5761ccbce3a522d3313c74ece7d4a411f02fd6d495d20f4352dcc87d8899f1b79a4c172a130e0f7a73b5f63a29177d8860131c35e3388552b1bd38a97f2 languageName: node linkType: hard @@ -6326,7 +6326,7 @@ __metadata: react-error-boundary: "npm:^4.0.12" react-helmet: "npm:^6.1.0" react-i18next: "npm:^14.0.1" - react-router-dom: "npm:^6.21.3" + react-router-dom: "npm:^6.22.0" react-scroll: "npm:^1.9.0" sass: "npm:^1.70.0" styled-components: "npm:^6.1.8" @@ -6635,27 +6635,27 @@ __metadata: languageName: node linkType: hard -"react-router-dom@npm:^6.21.3": - version: 6.21.3 - resolution: "react-router-dom@npm:6.21.3" +"react-router-dom@npm:^6.22.0": + version: 6.22.0 + resolution: "react-router-dom@npm:6.22.0" dependencies: - "@remix-run/router": "npm:1.14.2" - react-router: "npm:6.21.3" + "@remix-run/router": "npm:1.15.0" + react-router: "npm:6.22.0" peerDependencies: react: ">=16.8" react-dom: ">=16.8" - checksum: 1bea7bf17eb148461a7a99c9314e5ccc662ae00f563e29c16038e0b1b40aefb7381685f21289df07ad2295087a783c897b4e841ed12b07cd9a0829274a224231 + checksum: f1c338d6a37ee331f141d683a9139bc397128f6c15ef796589894ba29de1101eeeab7c4bf26429632c86bbca7376a9d900a6bfbd351ac5c9e1e231ac1b05fe5d languageName: node linkType: hard -"react-router@npm:6.21.3": - version: 6.21.3 - resolution: "react-router@npm:6.21.3" +"react-router@npm:6.22.0": + version: 6.22.0 + resolution: "react-router@npm:6.22.0" dependencies: - "@remix-run/router": "npm:1.14.2" + "@remix-run/router": "npm:1.15.0" peerDependencies: react: ">=16.8" - checksum: 66a0377a74ef2d298b9d617c02ba7a10e7e8b8be34b303068b5b5986f05e965037c51c0d45ea3a7967438f2dfde76c2c0f638cf19d3417fb408608ee9aee1668 + checksum: aa3878321797e526e4f3a57f97e8dd06f7cf6d7b9f95db39ea5d74259a2058404a04af0f852296ba6f09f9cecd7ca5f67125b9853ceb7fe6a852ffa5e3369dca languageName: node linkType: hard From 8530103a444a05649c3dadea677081644720bf47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 21:51:46 +0000 Subject: [PATCH 20/51] chore(deps-dev): bump @typescript-eslint/eslint-plugin from 6.20.0 to 6.21.0 (#1924) --- package.json | 2 +- yarn.lock | 90 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index c28efbcfa3..c166c67893 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "@types/react-helmet": "^6.1.11", "@types/react-scroll": "^1.8.10", "@types/styled-components": "^5.1.34", - "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.20.0", "@vitejs/plugin-react-swc": "^3.6.0", "eslint": "^8.56.0", diff --git a/yarn.lock b/yarn.lock index d5d847b6c0..232ea9f3ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2259,15 +2259,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.20.0" +"@typescript-eslint/eslint-plugin@npm:^6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.20.0" - "@typescript-eslint/type-utils": "npm:6.20.0" - "@typescript-eslint/utils": "npm:6.20.0" - "@typescript-eslint/visitor-keys": "npm:6.20.0" + "@typescript-eslint/scope-manager": "npm:6.21.0" + "@typescript-eslint/type-utils": "npm:6.21.0" + "@typescript-eslint/utils": "npm:6.21.0" + "@typescript-eslint/visitor-keys": "npm:6.21.0" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -2280,7 +2280,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 5020faac39be476de056342f58f2bf68bb788f230e2fa4a2e27ceab8a5187dc450beba7333b0aa741a43aeaff45a117558132953f9390b5eca4c2cc004fde716 + checksum: f911a79ee64d642f814a3b6cdb0d324b5f45d9ef955c5033e78903f626b7239b4aa773e464a38c3e667519066169d983538f2bf8e5d00228af587c9d438fb344 languageName: node linkType: hard @@ -2312,12 +2312,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/type-utils@npm:6.20.0" +"@typescript-eslint/scope-manager@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/scope-manager@npm:6.21.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.20.0" - "@typescript-eslint/utils": "npm:6.20.0" + "@typescript-eslint/types": "npm:6.21.0" + "@typescript-eslint/visitor-keys": "npm:6.21.0" + checksum: eaf868938d811cbbea33e97e44ba7050d2b6892202cea6a9622c486b85ab1cf801979edf78036179a8ba4ac26f1dfdf7fcc83a68c1ff66be0b3a8e9a9989b526 + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/type-utils@npm:6.21.0" + dependencies: + "@typescript-eslint/typescript-estree": "npm:6.21.0" + "@typescript-eslint/utils": "npm:6.21.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -2325,7 +2335,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 8f622fbb14268f1d00b2948f995b570f0ef82be02c12be41d90385290a56ea0dbd34d855d6a5aff100b57f3bdd300ff0c300f16c78f12d6064f7ae6e34fd71bf + checksum: 7409c97d1c4a4386b488962739c4f1b5b04dc60cf51f8cd88e6b12541f84d84c6b8b67e491a147a2c95f9ec486539bf4519fb9d418411aef6537b9c156468117 languageName: node linkType: hard @@ -2336,6 +2346,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/types@npm:6.21.0" + checksum: 020631d3223bbcff8a0da3efbdf058220a8f48a3de221563996ad1dcc30d6c08dadc3f7608cc08830d21c0d565efd2db19b557b9528921c78aabb605eef2d74d + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:6.20.0": version: 6.20.0 resolution: "@typescript-eslint/typescript-estree@npm:6.20.0" @@ -2355,20 +2372,39 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/utils@npm:6.20.0" +"@typescript-eslint/typescript-estree@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/typescript-estree@npm:6.21.0" + dependencies: + "@typescript-eslint/types": "npm:6.21.0" + "@typescript-eslint/visitor-keys": "npm:6.21.0" + debug: "npm:^4.3.4" + globby: "npm:^11.1.0" + is-glob: "npm:^4.0.3" + minimatch: "npm:9.0.3" + semver: "npm:^7.5.4" + ts-api-utils: "npm:^1.0.1" + peerDependenciesMeta: + typescript: + optional: true + checksum: af1438c60f080045ebb330155a8c9bb90db345d5069cdd5d01b67de502abb7449d6c75500519df829f913a6b3f490ade3e8215279b6bdc63d0fb0ae61034df5f + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/utils@npm:6.21.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.20.0" - "@typescript-eslint/types": "npm:6.20.0" - "@typescript-eslint/typescript-estree": "npm:6.20.0" + "@typescript-eslint/scope-manager": "npm:6.21.0" + "@typescript-eslint/types": "npm:6.21.0" + "@typescript-eslint/typescript-estree": "npm:6.21.0" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 0a8ede3d80a365b52ae96d88e4a9f6e6abf3569c6b60ff9f42ff900cd843ae7c5493cd95f8f2029d90bb0acbf31030980206af98e581d760d6d41e0f80e9fb86 + checksum: ab2df3833b2582d4e5467a484d08942b4f2f7208f8e09d67de510008eb8001a9b7460f2f9ba11c12086fd3cdcac0c626761c7995c2c6b5657d5fa6b82030a32d languageName: node linkType: hard @@ -2382,6 +2418,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/visitor-keys@npm:6.21.0" + dependencies: + "@typescript-eslint/types": "npm:6.21.0" + eslint-visitor-keys: "npm:^3.4.1" + checksum: 7395f69739cfa1cb83c1fb2fad30afa2a814756367302fb4facd5893eff66abc807e8d8f63eba94ed3b0fe0c1c996ac9a1680bcbf0f83717acedc3f2bb724fbf + languageName: node + linkType: hard + "@ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" @@ -6289,7 +6335,7 @@ __metadata: "@types/react-helmet": "npm:^6.1.11" "@types/react-scroll": "npm:^1.8.10" "@types/styled-components": "npm:^5.1.34" - "@typescript-eslint/eslint-plugin": "npm:^6.20.0" + "@typescript-eslint/eslint-plugin": "npm:^6.21.0" "@typescript-eslint/parser": "npm:^6.20.0" "@vitejs/plugin-react-swc": "npm:^3.6.0" "@zondax/ledger-substrate": "npm:^0.41.3" From bcd1053b837bcb16df16710f261d6a8a4a8a8632 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 21:51:55 +0000 Subject: [PATCH 21/51] chore(deps): bump react-i18next from 14.0.1 to 14.0.3 (#1925) --- package.json | 2 +- yarn.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index c166c67893..f945f74d0d 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "react-dom": "^18.2.0", "react-error-boundary": "^4.0.12", "react-helmet": "^6.1.0", - "react-i18next": "^14.0.1", + "react-i18next": "^14.0.3", "react-router-dom": "^6.22.0", "react-scroll": "^1.9.0", "styled-components": "^6.1.8", diff --git a/yarn.lock b/yarn.lock index 232ea9f3ff..9d555a013b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -207,7 +207,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2": +"@babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9": version: 7.23.9 resolution: "@babel/runtime@npm:7.23.9" dependencies: @@ -6371,7 +6371,7 @@ __metadata: react-dom: "npm:^18.2.0" react-error-boundary: "npm:^4.0.12" react-helmet: "npm:^6.1.0" - react-i18next: "npm:^14.0.1" + react-i18next: "npm:^14.0.3" react-router-dom: "npm:^6.22.0" react-scroll: "npm:^1.9.0" sass: "npm:^1.70.0" @@ -6649,11 +6649,11 @@ __metadata: languageName: node linkType: hard -"react-i18next@npm:^14.0.1": - version: 14.0.1 - resolution: "react-i18next@npm:14.0.1" +"react-i18next@npm:^14.0.3": + version: 14.0.3 + resolution: "react-i18next@npm:14.0.3" dependencies: - "@babel/runtime": "npm:^7.22.5" + "@babel/runtime": "npm:^7.23.9" html-parse-stringify: "npm:^3.0.1" peerDependencies: i18next: ">= 23.2.3" @@ -6663,7 +6663,7 @@ __metadata: optional: true react-native: optional: true - checksum: 9104c51c5d185e6d1b8ad71714b9ef490286f87b8833c7f67949da34f46f65ebcc994f6f3d087ffe180b7f811ba367c64e5e500c8e442fedbdf54c193bad0257 + checksum: e5d5f2a14324a295094944b71139e60d473a48843e8ce6e68d0edd8b34826900c6dd2ac7bac9a8b23c70a54fa89cdc9da9556b2e96fca0027d3555d3075813ed languageName: node linkType: hard From 1a3b873c8b491f5355345c2ffb48558ffc4ca478 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 21:52:35 +0000 Subject: [PATCH 22/51] chore(deps-dev): bump prettier from 3.2.4 to 3.2.5 (#1926) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index f945f74d0d..5a7fc49871 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-unused-imports": "^3.0.0", "gh-pages": "^6.1.1", - "prettier": "^3.2.4", + "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "sass": "^1.70.0", "typescript": "^5.3.3", diff --git a/yarn.lock b/yarn.lock index 9d555a013b..3bf7d397a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6362,7 +6362,7 @@ __metadata: i18next-browser-languagedetector: "npm:^7.2.0" lodash.debounce: "npm:^4.0.8" lodash.throttle: "npm:^4.1.1" - prettier: "npm:^3.2.4" + prettier: "npm:^3.2.5" prettier-plugin-organize-imports: "npm:^3.2.4" qrcode-generator: "npm:1.4.4" rc-slider: "npm:^10.5.0" @@ -6485,12 +6485,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^3.2.4": - version: 3.2.4 - resolution: "prettier@npm:3.2.4" +"prettier@npm:^3.2.5": + version: 3.2.5 + resolution: "prettier@npm:3.2.5" bin: prettier: bin/prettier.cjs - checksum: 88dfeb78ac6096522c9a5b81f1413d875f568420d9bb6a5e5103527912519b993f2bcdcac311fcff5718d5869671d44e4f85827d3626f3a6ce32b9abc65d88e0 + checksum: ea327f37a7d46f2324a34ad35292af2ad4c4c3c3355da07313339d7e554320f66f65f91e856add8530157a733c6c4a897dc41b577056be5c24c40f739f5ee8c6 languageName: node linkType: hard From bfb8ae189ba2cec684f53f5933ec577ef0147ffd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 21:56:11 +0000 Subject: [PATCH 23/51] chore(deps-dev): bump @typescript-eslint/parser from 6.20.0 to 6.21.0 (#1928) --- package.json | 2 +- yarn.lock | 66 ++++++++-------------------------------------------- 2 files changed, 11 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 5a7fc49871..5207bc478c 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@types/react-scroll": "^1.8.10", "@types/styled-components": "^5.1.34", "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.20.0", + "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react-swc": "^3.6.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", diff --git a/yarn.lock b/yarn.lock index 3bf7d397a0..0735a1f20d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2284,31 +2284,21 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/parser@npm:6.20.0" - dependencies: - "@typescript-eslint/scope-manager": "npm:6.20.0" - "@typescript-eslint/types": "npm:6.20.0" - "@typescript-eslint/typescript-estree": "npm:6.20.0" - "@typescript-eslint/visitor-keys": "npm:6.20.0" +"@typescript-eslint/parser@npm:^6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/parser@npm:6.21.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:6.21.0" + "@typescript-eslint/types": "npm:6.21.0" + "@typescript-eslint/typescript-estree": "npm:6.21.0" + "@typescript-eslint/visitor-keys": "npm:6.21.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: d84ad5e2282b1096c80dedb903c83ecc31eaf7be1aafcb14c18d9ec2d4a319f2fd1e5a9038b944d9f42c36c1c57add5e4292d4026ca7d3d5441d41286700d402 - languageName: node - linkType: hard - -"@typescript-eslint/scope-manager@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/scope-manager@npm:6.20.0" - dependencies: - "@typescript-eslint/types": "npm:6.20.0" - "@typescript-eslint/visitor-keys": "npm:6.20.0" - checksum: f6768ed2dcd2d1771d55ed567ff392a6569ffd683a26500067509dd41769f8838c43686460fe7337144f324fd063df33f5d5646d44e5df4998ceffb3ad1fb790 + checksum: a8f99820679decd0d115c0af61903fb1de3b1b5bec412dc72b67670bf636de77ab07f2a68ee65d6da7976039bbf636907f9d5ca546db3f0b98a31ffbc225bc7d languageName: node linkType: hard @@ -2339,13 +2329,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/types@npm:6.20.0" - checksum: 37589003b0e06f83c1945e3748e91af85918cfd997766894642a08e6f355f611cfe11df4e7632dda96e3a9b3441406283fe834ab0906cf81ea97fd43ca2aebe3 - languageName: node - linkType: hard - "@typescript-eslint/types@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/types@npm:6.21.0" @@ -2353,25 +2336,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.20.0" - dependencies: - "@typescript-eslint/types": "npm:6.20.0" - "@typescript-eslint/visitor-keys": "npm:6.20.0" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - minimatch: "npm:9.0.3" - semver: "npm:^7.5.4" - ts-api-utils: "npm:^1.0.1" - peerDependenciesMeta: - typescript: - optional: true - checksum: 551f13445a303882d9fc0fbe14ef8507eb8414253fd87a5f13d2e324b5280b626421a238b8ec038e628bc80128dc06c057757f668738e82e64d5b39a9083c27d - languageName: node - linkType: hard - "@typescript-eslint/typescript-estree@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/typescript-estree@npm:6.21.0" @@ -2408,16 +2372,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.20.0" - dependencies: - "@typescript-eslint/types": "npm:6.20.0" - eslint-visitor-keys: "npm:^3.4.1" - checksum: 852d938f2e5d57200cf62733b42e73a369f797b097d17e8fd3fffd0f7315c3b9e1863eed60bb8d57d6535a3b7f1980f645f96ec6d513950f182bfa8107b33fab - languageName: node - linkType: hard - "@typescript-eslint/visitor-keys@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/visitor-keys@npm:6.21.0" @@ -6336,7 +6290,7 @@ __metadata: "@types/react-scroll": "npm:^1.8.10" "@types/styled-components": "npm:^5.1.34" "@typescript-eslint/eslint-plugin": "npm:^6.21.0" - "@typescript-eslint/parser": "npm:^6.20.0" + "@typescript-eslint/parser": "npm:^6.21.0" "@vitejs/plugin-react-swc": "npm:^3.6.0" "@zondax/ledger-substrate": "npm:^0.41.3" bignumber.js: "npm:^9.1.2" From f4ddbe4abe460e081a4c422bd31d95078ab97c8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:00:20 +0000 Subject: [PATCH 24/51] chore(deps-dev): bump @types/react from 18.2.51 to 18.2.54 (#1927) --- package.json | 2 +- yarn.lock | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5207bc478c..67bc152977 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@types/chroma-js": "^2.4.3", "@types/lodash.debounce": "^4", "@types/lodash.throttle": "^4.1.9", - "@types/react": "^18.2.48", + "@types/react": "^18.2.54", "@types/react-dom": "^18.2.17", "@types/react-helmet": "^6.1.11", "@types/react-scroll": "^1.8.10", diff --git a/yarn.lock b/yarn.lock index 0735a1f20d..b86de83ec3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2202,7 +2202,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:^18.2.48": +"@types/react@npm:*": version: 18.2.51 resolution: "@types/react@npm:18.2.51" dependencies: @@ -2213,6 +2213,17 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:^18.2.54": + version: 18.2.54 + resolution: "@types/react@npm:18.2.54" + dependencies: + "@types/prop-types": "npm:*" + "@types/scheduler": "npm:*" + csstype: "npm:^3.0.2" + checksum: ad38193c30a063a481aeec2460de6396c80d8de2f1c7a8cbb80a4e8bc594f74c308ce93e165d743b38507c3ac0a491c24ce0efbd84c9ab21fd5fd38d2963d329 + languageName: node + linkType: hard + "@types/scheduler@npm:*": version: 0.16.8 resolution: "@types/scheduler@npm:0.16.8" @@ -6284,7 +6295,7 @@ __metadata: "@types/chroma-js": "npm:^2.4.3" "@types/lodash.debounce": "npm:^4" "@types/lodash.throttle": "npm:^4.1.9" - "@types/react": "npm:^18.2.48" + "@types/react": "npm:^18.2.54" "@types/react-dom": "npm:^18.2.17" "@types/react-helmet": "npm:^6.1.11" "@types/react-scroll": "npm:^1.8.10" From d2446e1140fff47424984ec37e6233388b9f542b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 6 Feb 2024 13:35:34 +0700 Subject: [PATCH 25/51] feat(refactor): Active pools and syncing state redesign (#1929) --- .github/workflows/ci.yml | 2 +- README.md | 2 + check-markdown-links-config.json | 9 +- src/Providers.tsx | 6 +- src/canvas/ManageNominations/index.tsx | 28 +- src/canvas/PoolMembers/Lists/FetchPage.tsx | 8 +- src/canvas/PoolMembers/Lists/Member.tsx | 6 +- src/canvas/PoolMembers/Members.tsx | 15 +- src/contexts/Api/index.tsx | 5 + src/contexts/Balances/defaults.ts | 1 - src/contexts/Balances/index.tsx | 16 +- src/contexts/Balances/types.ts | 1 - src/contexts/Migrate/index.tsx | 8 +- src/contexts/Pools/ActivePool/defaults.ts | 37 ++ src/contexts/Pools/ActivePool/index.tsx | 339 ++++++++++ .../{ActivePools => ActivePool}/types.ts | 19 +- src/contexts/Pools/ActivePools/defaults.ts | 82 --- src/contexts/Pools/ActivePools/index.tsx | 595 ------------------ src/contexts/Pools/BondedPools/defaults.ts | 21 +- src/contexts/Pools/BondedPools/index.tsx | 91 +-- src/contexts/Pools/BondedPools/types.ts | 31 +- src/contexts/Pools/types.ts | 2 +- src/contexts/Setup/types.ts | 2 +- src/contexts/Staking/index.tsx | 4 + src/contexts/UI/defaults.ts | 3 - src/contexts/UI/index.tsx | 70 --- src/contexts/UI/types.ts | 3 - .../Validators/ValidatorEntries/index.tsx | 18 +- src/hooks/useActivePools/index.tsx | 100 +++ src/hooks/useActivePools/types.ts | 15 + src/hooks/useIdentities/index.tsx | 76 --- src/hooks/useNominationStatus/index.tsx | 12 +- src/hooks/useSyncing/index.tsx | 60 ++ src/library/Account/types.ts | 2 +- src/library/Form/Bond/BondFeedback.tsx | 6 +- .../Form/CreatePoolStatusBar/index.tsx | 12 +- src/library/Form/NominateStatusBar/index.tsx | 16 +- src/library/Form/Unbond/UnbondFeedback.tsx | 4 +- src/library/GenerateNominations/index.tsx | 2 +- src/library/Graphs/PayoutBar.tsx | 6 +- src/library/Graphs/PayoutLine.tsx | 10 +- src/library/Headers/Connected.tsx | 18 +- src/library/Headers/Sync.tsx | 69 ++ src/library/Headers/index.tsx | 93 +-- src/library/ListItem/Labels/EraStatus.tsx | 8 +- src/library/Nominations/index.tsx | 22 +- src/library/Pool/index.tsx | 6 +- src/library/Pool/types.ts | 2 +- src/library/PoolList/Default.tsx | 10 +- src/library/SideMenu/Main.tsx | 12 +- src/library/StatusLabel/index.tsx | 6 +- src/library/ValidatorList/index.tsx | 6 +- src/locale/en/modals.json | 2 +- src/modals/AccountPoolRoles/Wrappers.ts | 5 + src/modals/AccountPoolRoles/index.tsx | 50 +- src/modals/Bond/index.tsx | 16 +- src/modals/ClaimReward/index.tsx | 17 +- .../Forms/ClaimCommission/index.tsx | 11 +- .../ManagePool/Forms/LeavePool/index.tsx | 19 +- .../Forms/ManageCommission/index.tsx | 6 +- .../Forms/ManageCommission/provider/index.tsx | 6 +- .../ManagePool/Forms/RenamePool/index.tsx | 10 +- .../Forms/SetClaimPermission/index.tsx | 4 +- .../ManagePool/Forms/SetPoolState/index.tsx | 8 +- src/modals/ManagePool/Tasks.tsx | 11 +- src/modals/ManagePool/index.tsx | 6 +- src/modals/StopNominations/index.tsx | 12 +- src/modals/Unbond/index.tsx | 12 +- src/modals/UnlockChunks/Forms.tsx | 10 +- src/modals/UnlockChunks/index.tsx | 4 +- .../Nominate/Active/ControllerNotStash.tsx | 10 +- src/pages/Nominate/Active/ManageBond.tsx | 14 +- .../Active/Status/NominationStatus.tsx | 6 +- .../Active/Status/PayoutDestinationStatus.tsx | 6 +- src/pages/Nominate/Active/UnstakePrompts.tsx | 11 +- src/pages/Nominate/Active/index.tsx | 8 +- src/pages/Overview/BalanceChart.tsx | 10 +- src/pages/Overview/Payouts.tsx | 8 +- .../Overview/StakeStatus/Tips/PageToggle.tsx | 6 +- src/pages/Overview/StakeStatus/Tips/index.tsx | 20 +- src/pages/Payouts/index.tsx | 6 +- src/pages/Pools/Create/PoolRoles/index.tsx | 2 +- src/pages/Pools/Home/ClosurePrompts.tsx | 25 +- src/pages/Pools/Home/Favorites/index.tsx | 6 +- src/pages/Pools/Home/ManageBond.tsx | 16 +- src/pages/Pools/Home/ManagePool/index.tsx | 18 +- .../Pools/Home/PoolStats/Announcements.tsx | 8 +- src/pages/Pools/Home/PoolStats/index.tsx | 12 +- .../Pools/Home/Status/MembershipStatus.tsx | 33 +- src/pages/Pools/Home/Status/PoolStatus.tsx | 18 +- src/pages/Pools/Home/Status/RewardsStatus.tsx | 21 +- src/pages/Pools/Home/Status/index.tsx | 6 +- .../Pools/Home/Status/useStatusButtons.tsx | 4 +- src/pages/Pools/Home/index.tsx | 29 +- src/pages/Pools/Roles/index.tsx | 32 +- src/pages/Pools/Roles/types.ts | 2 +- src/pages/Pools/types.ts | 2 +- src/static/APIController/index.ts | 7 + src/static/ActivePoolsController/index.ts | 201 ++++++ src/static/ActivePoolsController/types.ts | 18 + src/static/BalancesController/index.ts | 15 +- src/static/IdentitiesController/index.ts | 62 ++ src/static/SyncController/index.ts | 56 ++ src/static/SyncController/types.ts | 19 + src/types/index.ts | 4 + 105 files changed, 1524 insertions(+), 1403 deletions(-) create mode 100644 src/contexts/Pools/ActivePool/defaults.ts create mode 100644 src/contexts/Pools/ActivePool/index.tsx rename src/contexts/Pools/{ActivePools => ActivePool}/types.ts (78%) delete mode 100644 src/contexts/Pools/ActivePools/defaults.ts delete mode 100644 src/contexts/Pools/ActivePools/index.tsx create mode 100644 src/hooks/useActivePools/index.tsx create mode 100644 src/hooks/useActivePools/types.ts delete mode 100644 src/hooks/useIdentities/index.tsx create mode 100644 src/hooks/useSyncing/index.tsx create mode 100644 src/library/Headers/Sync.tsx create mode 100644 src/static/ActivePoolsController/index.ts create mode 100644 src/static/ActivePoolsController/types.ts create mode 100644 src/static/IdentitiesController/index.ts create mode 100644 src/static/SyncController/index.ts create mode 100644 src/static/SyncController/types.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c00e2d747c..5ccb0865dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: use-quiet-mode: 'yes' check-modified-files-only: 'yes' config-file: 'check-markdown-links-config.json' - + build: runs-on: ubuntu-latest strategy: diff --git a/README.md b/README.md index c411631758..9f5a50ca3d 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,9 @@ podman run --d -p 8080:80 localhost/polkadot-staking-dashboard ``` + And access the **Staking Dashboard** at http://localhost:8080/. + ## Presentations diff --git a/check-markdown-links-config.json b/check-markdown-links-config.json index cfe4600290..6b075fe601 100644 --- a/check-markdown-links-config.json +++ b/check-markdown-links-config.json @@ -1,10 +1,15 @@ { "httpHeaders": [ { - "urls": ["https://github.com/", "https://guides.github.com/", "https://help.github.com/", "https://docs.github.com/"], + "urls": [ + "https://github.com/", + "https://guides.github.com/", + "https://help.github.com/", + "https://docs.github.com/" + ], "headers": { "Accept-Encoding": "zstd, br, gzip, deflate" } } ] -} \ No newline at end of file +} diff --git a/src/Providers.tsx b/src/Providers.tsx index 79d17bbf19..e3f7c0610b 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -18,7 +18,7 @@ import { MenuProvider } from 'contexts/Menu'; import { MigrateProvider } from 'contexts/Migrate'; import { PromptProvider } from 'contexts/Prompt'; import { PluginsProvider } from 'contexts/Plugins'; -import { ActivePoolsProvider } from 'contexts/Pools/ActivePools'; +import { ActivePoolProvider } from 'contexts/Pools/ActivePool'; import { BondedPoolsProvider } from 'contexts/Pools/BondedPools'; import { PoolMembersProvider } from 'contexts/Pools/PoolMembers'; import { FavoritePoolsProvider } from 'contexts/Pools/FavoritePools'; @@ -54,6 +54,7 @@ export const Providers = () => { // !! Provider order matters const providers: (FC | [FC, AnyJson])[] = [ + UIProvider, [APIProvider, { network }], VaultAccountsProvider, LedgerHardwareProvider, @@ -75,14 +76,13 @@ export const Providers = () => { FavoritePoolsProvider, BondedPoolsProvider, PoolMembersProvider, - ActivePoolsProvider, + ActivePoolProvider, TransferOptionsProvider, ValidatorsProvider, FavoriteValidatorsProvider, FastUnstakeProvider, PayoutsProvider, PoolPerformanceProvider, - UIProvider, SetupProvider, MenuProvider, TooltipProvider, diff --git a/src/canvas/ManageNominations/index.tsx b/src/canvas/ManageNominations/index.tsx index 27b50c348e..b4c8e95fb9 100644 --- a/src/canvas/ManageNominations/index.tsx +++ b/src/canvas/ManageNominations/index.tsx @@ -18,7 +18,7 @@ import { useHelp } from 'contexts/Help'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useBonded } from 'contexts/Bonded'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { SubmitTx } from 'library/SubmitTx'; import type { NominationSelection, @@ -40,11 +40,12 @@ export const ManageNominations = () => { const { consts, api } = useApi(); const { getBondedAccount } = useBonded(); const { activeAccount } = useActiveAccounts(); - const { selectedActivePool } = useActivePools(); - const { openPromptWith, closePrompt } = usePrompt(); + const { activePool } = useActivePool(); const { updatePoolNominations } = useBondedPools(); - const controller = getBondedAccount(activeAccount); + const { openPromptWith, closePrompt } = usePrompt(); + const { maxNominations } = consts; + const controller = getBondedAccount(activeAccount); const bondFor = options?.bondFor || 'nominator'; const isPool = bondFor === 'pool'; const signingAccount = isPool ? activeAccount : controller; @@ -108,10 +109,9 @@ export const ManageNominations = () => { ); if (isPool) { - tx = api.tx.nominationPools.nominate( - selectedActivePool?.id || 0, - targetsToSubmit - ); + if (activePool) { + tx = api.tx.nominationPools.nominate(activePool.id, targetsToSubmit); + } } else { tx = api.tx.staking.nominate(targetsToSubmit); } @@ -126,14 +126,12 @@ export const ManageNominations = () => { setCanvasStatus('closing'); }, callbackInBlock: () => { - if (isPool) { + if (isPool && activePool) { // Update bonded pool targets if updating pool nominations. - if (selectedActivePool?.id) { - updatePoolNominations( - selectedActivePool.id, - newNominations.nominations.map((n) => n.address) - ); - } + updatePoolNominations( + activePool.id, + newNominations.nominations.map((n) => n.address) + ); } }, }); diff --git a/src/canvas/PoolMembers/Lists/FetchPage.tsx b/src/canvas/PoolMembers/Lists/FetchPage.tsx index 24c712e593..ceba60992c 100644 --- a/src/canvas/PoolMembers/Lists/FetchPage.tsx +++ b/src/canvas/PoolMembers/Lists/FetchPage.tsx @@ -5,7 +5,7 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ListItemsPerBatch, ListItemsPerPage } from 'consts'; import { usePlugins } from 'contexts/Plugins'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { List, ListStatusHeader, Wrapper as ListWrapper } from 'library/List'; import { Pagination } from 'library/List/Pagination'; @@ -28,7 +28,7 @@ export const MembersListInner = ({ const { network } = useNetwork(); const { pluginEnabled } = usePlugins(); const { activeAccount } = useActiveAccounts(); - const { selectedActivePool } = useActivePools(); + const { activePool } = useActivePool(); const { poolMembersApi, setPoolMembersApi, @@ -65,7 +65,7 @@ export const MembersListInner = ({ const fetchingMemberList = useRef(false); const setupMembersList = async () => { - const poolId = selectedActivePool?.id || 0; + const poolId = activePool?.id || 0; if (poolId > 0 && !fetchingMemberList.current) { fetchingMemberList.current = true; @@ -105,7 +105,7 @@ export const MembersListInner = ({ if (fetchedPoolMembersApi === 'unsynced') { setupMembersList(); } - }, [fetchedPoolMembersApi, selectedActivePool]); + }, [fetchedPoolMembersApi, activePool]); // Render throttle. useEffect(() => { diff --git a/src/canvas/PoolMembers/Lists/Member.tsx b/src/canvas/PoolMembers/Lists/Member.tsx index dc9d8048d4..9c056c7f92 100644 --- a/src/canvas/PoolMembers/Lists/Member.tsx +++ b/src/canvas/PoolMembers/Lists/Member.tsx @@ -10,7 +10,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useMenu } from 'contexts/Menu'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useList } from 'library/List/context'; import { Identity } from 'library/ListItem/Labels/Identity'; @@ -44,12 +44,12 @@ export const Member = ({ const { selectActive } = useList(); const { openPromptWith } = usePrompt(); const { setMenuPosition, setMenuItems, open } = useMenu(); - const { selectedActivePool, isOwner, isBouncer } = useActivePools(); + const { activePool, isOwner, isBouncer } = useActivePool(); // Ref for the member container. const memberRef = useRef(null); - const { state, roles } = selectedActivePool?.bondedPool || {}; + const { state, roles } = activePool?.bondedPool || {}; const { bouncer, root, depositor } = roles || {}; const canUnbondBlocked = diff --git a/src/canvas/PoolMembers/Members.tsx b/src/canvas/PoolMembers/Members.tsx index 9583c9b8b8..09bd0a1774 100644 --- a/src/canvas/PoolMembers/Members.tsx +++ b/src/canvas/PoolMembers/Members.tsx @@ -5,7 +5,7 @@ import { faBars } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useTranslation } from 'react-i18next'; import { usePlugins } from 'contexts/Plugins'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useTheme } from 'contexts/Themes'; import { CardWrapper } from 'library/Card/Wrappers'; @@ -18,15 +18,14 @@ export const Members = () => { const { mode } = useTheme(); const { pluginEnabled } = usePlugins(); const { getMembersOfPoolFromNode } = usePoolMembers(); - const { selectedActivePool, isOwner, isBouncer, selectedPoolMemberCount } = - useActivePools(); + const { activePool, isOwner, isBouncer, activePoolMemberCount } = + useActivePool(); const { colors } = useNetwork().networkData; const annuncementBorderColor = colors.secondary[mode]; const showBlockedPrompt = - selectedActivePool?.bondedPool?.state === 'Blocked' && - (isOwner() || isBouncer()); + activePool?.bondedPool?.state === 'Blocked' && (isOwner() || isBouncer()); const membersListProps = { batchKey: 'active_pool_members', @@ -58,7 +57,7 @@ export const Members = () => { )} {/* Pool in Destroying state: allow anyone to unbond & withdraw members */} - {selectedActivePool?.bondedPool?.state === 'Destroying' && ( + {activePool?.bondedPool?.state === 'Destroying' && ( { {pluginEnabled('subscan') ? ( ) : ( )} diff --git a/src/contexts/Api/index.tsx b/src/contexts/Api/index.tsx index b74188156c..eee660efed 100644 --- a/src/contexts/Api/index.tsx +++ b/src/contexts/Api/index.tsx @@ -36,6 +36,7 @@ import { NotificationsController } from 'static/NotificationsController'; import { useTranslation } from 'react-i18next'; import { useEventListener } from 'usehooks-ts'; import BigNumber from 'bignumber.js'; +import { SyncController } from 'static/SyncController'; export const APIContext = createContext(defaultApiContext); @@ -165,6 +166,9 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { // API is now ready to be used. setApiStatus('ready'); + // Set `initialization` syncing to complete. + SyncController.dispatch('initialization', 'complete'); + // Initialise subscriptions. APIController.subscribeNetworkMetrics(); APIController.subscribePoolsConfig(); @@ -358,6 +362,7 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { stakingMetricsRef ); } + // Reconnect API instance. APIController.initialize(network, isLightClient ? 'sc' : 'ws', rpcEndpoint); }, [isLightClient, network]); diff --git a/src/contexts/Balances/defaults.ts b/src/contexts/Balances/defaults.ts index 1250663702..3628ebbf1b 100644 --- a/src/contexts/Balances/defaults.ts +++ b/src/contexts/Balances/defaults.ts @@ -18,5 +18,4 @@ export const defaultBalancesContext: BalancesContextInterface = { getLedger: (source) => defaultLedger, getPayee: (address) => defaultPayee, getPoolMembership: (address) => null, - balancesInitialSynced: false, }; diff --git a/src/contexts/Balances/index.tsx b/src/contexts/Balances/index.tsx index a22510601f..4c3b6c56bd 100644 --- a/src/contexts/Balances/index.tsx +++ b/src/contexts/Balances/index.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import type { ReactNode } from 'react'; -import { createContext, useContext, useEffect, useRef, useState } from 'react'; +import { createContext, useContext, useEffect, useRef } from 'react'; import type { MaybeAddress } from 'types'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import * as defaults from './defaults'; @@ -13,6 +13,7 @@ import { BalancesController } from 'static/BalancesController'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useActiveBalances } from 'hooks/useActiveBalances'; import { useBonded } from 'contexts/Bonded'; +import { SyncController } from 'static/SyncController'; export const BalancesContext = createContext( defaults.defaultBalancesContext @@ -38,10 +39,6 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { accounts: [activeAccount, activeProxy, controller], }); - // Store whether balances for all imported accounts have been synced on initial page load. - const [balancesInitialSynced, setBalancesInitialSynced] = - useState(false); - // Check all accounts have been synced. App-wide syncing state for all accounts. const newAccountBalancesCallback = (e: Event) => { if ( @@ -56,9 +53,9 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { // Check whether all accounts have been synced and update state accordingly. const checkBalancesSynced = () => { - setBalancesInitialSynced( - Object.keys(BalancesController.balances).length === accounts.length - ); + if (Object.keys(BalancesController.balances).length === accounts.length) { + SyncController.dispatch('balances', 'complete'); + } }; // Gets an account's nonce directly from `BalanceController`. Used at the time of building a @@ -85,7 +82,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { // If no accounts are imported, set balances synced to true. useEffect(() => { if (!accounts.length) { - setBalancesInitialSynced(true); + SyncController.dispatch('balances', 'complete'); } }, [accounts.length]); @@ -99,7 +96,6 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { getLedger, getPayee, getPoolMembership, - balancesInitialSynced, }} > {children} diff --git a/src/contexts/Balances/types.ts b/src/contexts/Balances/types.ts index 660e5d297c..9f5c47d81e 100644 --- a/src/contexts/Balances/types.ts +++ b/src/contexts/Balances/types.ts @@ -14,7 +14,6 @@ export interface BalancesContextInterface { getLedger: (source: ActiveLedgerSource) => Ledger; getPayee: (address: MaybeAddress) => PayeeConfig; getPoolMembership: (address: MaybeAddress) => PoolMembership | null; - balancesInitialSynced: boolean; } export type ActiveBalancesState = Record; diff --git a/src/contexts/Migrate/index.tsx b/src/contexts/Migrate/index.tsx index 5819751bb9..43fdfcd635 100644 --- a/src/contexts/Migrate/index.tsx +++ b/src/contexts/Migrate/index.tsx @@ -6,18 +6,18 @@ import { createContext, useState } from 'react'; import { NetworkList } from 'config/networks'; import { AppVersion } from 'consts'; import { useApi } from 'contexts/Api'; -import { useUi } from 'contexts/UI'; import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { localStorageOrDefault } from '@polkadot-cloud/utils'; import type { ExternalAccount } from '@polkadot-cloud/react/types'; +import { useSyncing } from 'hooks/useSyncing'; export const MigrateContext = createContext(null); export const MigrateProvider = ({ children }: { children: ReactNode }) => { const { isReady } = useApi(); - const { isNetworkSyncing } = useUi(); const { accounts } = useImportedAccounts(); + const { syncing } = useSyncing(['initialization']); // The local app version of the current user. const localAppVersion = localStorage.getItem('app_version'); @@ -71,7 +71,7 @@ export const MigrateProvider = ({ children }: { children: ReactNode }) => { }; useEffectIgnoreInitial(() => { - if (isReady && !isNetworkSyncing && !done) { + if (isReady && !syncing && !done) { // Carry out migrations if local version is different to current version. if (localAppVersion !== AppVersion) { // Added in 1.0.2. @@ -107,7 +107,7 @@ export const MigrateProvider = ({ children }: { children: ReactNode }) => { setDone(true); } } - }, [isReady, isNetworkSyncing]); + }, [isReady, syncing]); return ( {children} diff --git a/src/contexts/Pools/ActivePool/defaults.ts b/src/contexts/Pools/ActivePool/defaults.ts new file mode 100644 index 0000000000..6e27e90bf8 --- /dev/null +++ b/src/contexts/Pools/ActivePool/defaults.ts @@ -0,0 +1,37 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */ + +import BigNumber from 'bignumber.js'; +import type { ActivePoolContextState } from './types'; + +export const nominationStatus = {}; + +export const defaultPoolRoles = { + depositor: '', + nominator: '', + root: '', + bouncer: '', +}; + +export const defaultPoolNominations = { + targets: [], + submittedIn: 0, +}; + +export const defaultActivePoolContext: ActivePoolContextState = { + isBonding: () => false, + isNominator: () => false, + isOwner: () => false, + isMember: () => false, + isDepositor: () => false, + isBouncer: () => false, + getPoolUnlocking: () => [], + getPoolRoles: () => defaultPoolRoles, + getNominationsStatus: () => nominationStatus, + setActivePoolId: (p) => {}, + activePool: null, + activePoolNominations: null, + activePoolMemberCount: 0, + pendingPoolRewards: new BigNumber(0), +}; diff --git a/src/contexts/Pools/ActivePool/index.tsx b/src/contexts/Pools/ActivePool/index.tsx new file mode 100644 index 0000000000..a3348287cd --- /dev/null +++ b/src/contexts/Pools/ActivePool/index.tsx @@ -0,0 +1,339 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { setStateWithRef } from '@polkadot-cloud/utils'; +import type { ReactNode } from 'react'; +import { + createContext, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { useStaking } from 'contexts/Staking'; +import type { Sync } from 'types'; +import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; +import { usePlugins } from 'contexts/Plugins'; +import { useNetwork } from 'contexts/Network'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useApi } from '../../Api'; +import { useBondedPools } from '../BondedPools'; +import { usePoolMembers } from '../PoolMembers'; +import type { ActivePoolContextState } from './types'; +import { SubscanController } from 'static/SubscanController'; +import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; +import { useBalances } from 'contexts/Balances'; +import { ActivePoolsController } from 'static/ActivePoolsController'; +import { + defaultActivePoolContext, + defaultPoolNominations, + defaultPoolRoles, +} from './defaults'; +import { SyncController } from 'static/SyncController'; +import { useActivePools } from 'hooks/useActivePools'; +import BigNumber from 'bignumber.js'; +import { APIController } from 'static/APIController'; + +export const ActivePoolContext = createContext( + defaultActivePoolContext +); + +export const useActivePool = () => useContext(ActivePoolContext); + +export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); + const { network } = useNetwork(); + const { eraStakers } = useStaking(); + const { pluginEnabled } = usePlugins(); + const { getPoolMembership } = useBalances(); + const { activeAccount } = useActiveAccounts(); + const createPoolAccounts = useCreatePoolAccounts(); + const { getMembersOfPoolFromNode } = usePoolMembers(); + const { getAccountPoolRoles, bondedPools } = useBondedPools(); + const membership = getPoolMembership(activeAccount); + + // Determine active pools to subscribe to. Dependencies of `activeAccount`, and `membership` mean + // that this object is only recalculated when these values change. + const accountPoolIds = useMemo(() => { + const rollPoolIds: string[] = Object.keys( + getAccountPoolRoles(activeAccount) || {} + ); + + // If a membership subscription has resulted in an update that is inconsistent with + // `bondedPools`, add that role to the list of the account's pool roles. + if ( + membership?.poolId && + !rollPoolIds.includes(String(membership.poolId)) + ) { + rollPoolIds.push(String(membership.poolId)); + } + return rollPoolIds; + }, [activeAccount, bondedPools, membership]); + + // Store the currently selected active pool for the UI. Should default to the membership pool if + // present. Used in event callback, therefore needs an accompanying ref. + const [activePoolId, setActivePoolIdState] = useState(null); + const activePoolIdRef = useRef(activePoolId); + + const setActivePoolId = (id: string | null) => { + setStateWithRef(id, setActivePoolIdState, activePoolIdRef); + }; + + // Only listen to the currently selected active pool, otherwise return an empty array. + const poolIds = activePoolIdRef.current ? [activePoolIdRef.current] : []; + + // Listen for active pools. + const { activePools, poolNominations } = useActivePools({ + poolIds, + onCallback: async () => { + // Sync: active pools synced once all account pools have been reported. + if (accountPoolIds.length <= ActivePoolsController.pools.length) { + SyncController.dispatch('active-pools', 'complete'); + } + }, + }); + + // Store the currently active pool's pending rewards for the active account. + const [pendingPoolRewards, setPendingPoolRewards] = useState( + new BigNumber(0) + ); + + const activePool = + activePoolId && activePools[activePoolId] + ? activePools[activePoolId] + : null; + + const activePoolNominations = + activePoolId && poolNominations[activePoolId] + ? poolNominations[activePoolId] + : null; + + // Store the member count of the selected pool. + const [activePoolMemberCount, setactivePoolMemberCount] = useState(0); + + // Keep track of whether the pool member count is being fetched. + const fetchingMemberCount = useRef('unsynced'); + + const getActivePoolNominations = () => + activePoolNominations || defaultPoolNominations; + + // Sync active pool subscriptions. + const syncActivePoolSubscriptions = async () => { + if (accountPoolIds.length) { + const newActivePools = accountPoolIds.map((pool) => ({ + id: pool, + addresses: { ...createPoolAccounts(Number(pool)) }, + })); + ActivePoolsController.syncPools(newActivePools); + } + }; + + // Attempt to assign the default `activePoolId` if one is not currently active. + const assignActivePoolId = () => { + // Membership takes priority, followed by the first pool the account has a role in. Falls back + // to `null` if no active roles are found. + const initialActivePoolId = membership?.poolId || accountPoolIds[0] || null; + if (initialActivePoolId && !activePool) { + setActivePoolId(String(initialActivePoolId)); + } + }; + + // Reset `activePoolId`. + const resetActivePoolId = () => { + setStateWithRef(null, setActivePoolIdState, activePoolIdRef); + }; + + // Returns whether the active pool is being bonded to (essentially if there is indeed an + // activePool). + const isBonding = () => !!activePool; + + // Returns whether the active account is the nominator in the active pool. + const isNominator = () => { + const roles = activePool?.bondedPool?.roles; + if (!activeAccount || !roles) { + return false; + } + return activeAccount === roles?.nominator; + }; + + // Returns whether the active account is the owner of the active pool. + const isOwner = () => { + const roles = activePool?.bondedPool?.roles; + if (!activeAccount || !roles) { + return false; + } + return activeAccount === roles?.root; + }; + + // Returns whether the active account is a member of the active pool. + const isMember = () => { + const p = activePool ? String(activePool.id) : '-1'; + return String(membership?.poolId || '') === p; + }; + + // Returns whether the active account is the depositor of the active pool. + const isDepositor = () => { + const roles = activePool?.bondedPool?.roles; + if (!activeAccount || !roles) { + return false; + } + return activeAccount === roles?.depositor; + }; + + // Returns whether the active account is the depositor of the active pool. + const isBouncer = () => { + const roles = activePool?.bondedPool?.roles; + if (!activeAccount || !roles) { + return false; + } + return activeAccount === roles?.bouncer; + }; + + // Get the status of nominations. Possible statuses: waiting, inactive, active. + const getNominationsStatus = () => { + const statuses: Record = {}; + + for (const nomination of getActivePoolNominations().targets) { + const staker = eraStakers.stakers.find( + ({ address }) => address === nomination + ); + if (staker === undefined) { + statuses[nomination] = 'waiting'; + continue; + } + const exists = (staker.others || []).find( + ({ who }) => who === activeAccount + ); + if (exists === undefined) { + statuses[nomination] = 'inactive'; + continue; + } + statuses[nomination] = 'active'; + } + return statuses; + }; + + // Returns the active pool's roles or the default roles object. + const getPoolRoles = () => activePool?.bondedPool?.roles || defaultPoolRoles; + + // Returns the unlock chunks of the active pool if `activeAccount` is a member of the pool. + const getPoolUnlocking = () => { + // exit early if the active pool is not membership pool + if (activePoolId !== String(membership?.poolId || -1)) { + return []; + } + return membership?.unlocking || []; + }; + + // Fetch and update pending rewards of the active pool when membership changes. + const updatePendingRewards = async () => { + if ( + activePool && + membership?.poolId && + String(activePool.id) === String(membership.poolId) + ) { + setPendingPoolRewards(await fetchPendingRewards(membership?.address)); + } else { + setPendingPoolRewards(new BigNumber(0)); + } + }; + + // Fetch and update unclaimed pool rewards for an address from runtime call. + const fetchPendingRewards = async (address: string | undefined) => { + if (address) { + const pendingRewards = + await APIController.api.call.nominationPoolsApi.pendingRewards(address); + return new BigNumber(pendingRewards?.toString() || 0); + } + return new BigNumber(0); + }; + + // Gets the member count of the currently selected pool. If Subscan is enabled, it is used instead of the connected node. + const getMemberCount = async () => { + if (!activePool?.id) { + setactivePoolMemberCount(0); + return; + } + // If `Subscan` plugin is enabled, fetch member count directly from the API. + if ( + pluginEnabled('subscan') && + fetchingMemberCount.current === 'unsynced' + ) { + fetchingMemberCount.current = 'syncing'; + const poolDetails = await SubscanController.handleFetchPoolDetails( + activePool.id + ); + fetchingMemberCount.current = 'synced'; + setactivePoolMemberCount(poolDetails?.member_count || 0); + return; + } + // If no plugin available, fetch all pool members from RPC and filter them to determine current + // pool member count. NOTE: Expensive operation. + setactivePoolMemberCount( + getMembersOfPoolFromNode(activePool?.id || 0)?.length || 0 + ); + }; + + // Fetch pool member count. We use `membership` as a dependency as the member count could change + // in the UI when active account's membership changes. NOTE: Do not have `poolMembersNode` as a + // dependency - could trigger many re-renders if value is constantly changing - more suited as a + // custom event. + useEffect(() => { + fetchingMemberCount.current = 'unsynced'; + getMemberCount(); + }, [activeAccount, activePool, membership?.poolId]); + + // Re-calculate pending rewards when membership changes. + useEffectIgnoreInitial(() => { + if (isReady) { + updatePendingRewards(); + } + }, [network, isReady, membership, activePool]); + + // Initialise subscriptions to all active pools of imported accounts. + useEffectIgnoreInitial(() => { + if (isReady) { + syncActivePoolSubscriptions(); + assignActivePoolId(); + } + }, [network, isReady, accountPoolIds]); + + // Reset everything when `activeAccount` changes. + useEffectIgnoreInitial(() => { + ActivePoolsController.unsubscribe(); + resetActivePoolId(); + }, [activeAccount]); + + // Reset on network change and component unmount. NOTE: ActivePoolsController also unsubscribes on + // network change; this is handled by the APIController. + useEffect(() => { + resetActivePoolId(); + return () => { + resetActivePoolId(); + }; + }, [network]); + + return ( + + {children} + + ); +}; diff --git a/src/contexts/Pools/ActivePools/types.ts b/src/contexts/Pools/ActivePool/types.ts similarity index 78% rename from src/contexts/Pools/ActivePools/types.ts rename to src/contexts/Pools/ActivePool/types.ts index 04a82a89f2..6b4bfa5647 100644 --- a/src/contexts/Pools/ActivePools/types.ts +++ b/src/contexts/Pools/ActivePool/types.ts @@ -4,28 +4,24 @@ import type BigNumber from 'bignumber.js'; import type { NominationStatuses, PoolAddresses } from '../BondedPools/types'; import type { MaybeAddress } from '@polkadot-cloud/react/types'; -import type { AnyJson, Sync } from 'types'; import type { Nominations } from 'contexts/Bonded/types'; import type { Identity, SuperIdentity } from 'contexts/Validators/types'; -export interface ActivePoolsContextState { +export interface ActivePoolContextState { isBonding: () => boolean; isNominator: () => boolean; isOwner: () => boolean; isMember: () => boolean; isDepositor: () => boolean; isBouncer: () => boolean; - getPoolBondedAccount: () => MaybeAddress; getPoolUnlocking: () => PoolUnlocking[]; getPoolRoles: () => PoolRoles; - setTargets: (t: PoolTargets) => void; getNominationsStatus: () => NominationStatuses; - setSelectedPoolId: (p: string) => void; - selectedActivePool: ActivePool | null; - targets: PoolTargets; - poolNominations: Nominations; - synced: Sync; - selectedPoolMemberCount: number; + setActivePoolId: (p: string) => void; + activePool: ActivePool | null; + activePoolNominations: Nominations | null; + activePoolMemberCount: number; + pendingPoolRewards: BigNumber; } export interface ActivePool { @@ -34,7 +30,6 @@ export interface ActivePool { bondedPool: ActiveBondedPool; rewardPool: RewardPool; rewardAccountBalance: BigNumber; - pendingRewards: BigNumber; } export interface ActiveBondedPool { @@ -63,7 +58,7 @@ export interface PoolUnlocking { value: BigNumber; } -export type PoolTargets = Record; +export type PoolRole = 'depositor' | 'nominator' | 'root' | 'bouncer'; export interface PoolRoles { depositor?: MaybeAddress; diff --git a/src/contexts/Pools/ActivePools/defaults.ts b/src/contexts/Pools/ActivePools/defaults.ts deleted file mode 100644 index 32444c8da3..0000000000 --- a/src/contexts/Pools/ActivePools/defaults.ts +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only -/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */ - -import BigNumber from 'bignumber.js'; -import type { - ActiveBondedPool, - ActivePool, - ActivePoolsContextState, - RewardPool, -} from './types'; - -export const nominationStatus = {}; - -export const poolRoles = { - depositor: '', - nominator: '', - root: '', - bouncer: '', -}; - -export const bondedPool: ActiveBondedPool = { - points: '0', - state: 'Blocked', - memberCounter: '0', - roles: { - depositor: '', - nominator: '', - root: '', - bouncer: '', - }, - roleIdentities: { identities: {}, supers: {} }, -}; - -export const rewardPool: RewardPool = { - lastRecordedRewardCounter: '0', - lastRecordedTotalPayouts: '0', - totalCommissionClaimed: '0', - totalCommissionPending: '0', - totalRewardsClaimed: '0', -}; - -export const selectedActivePool: ActivePool = { - id: 0, - addresses: { - stash: '', - reward: '', - }, - bondedPool, - rewardPool, - rewardAccountBalance: new BigNumber(0), - pendingRewards: new BigNumber(0), -}; - -export const targets = { - nominations: [], -}; - -export const poolNominations = { - targets: [], - submittedIn: 0, -}; - -export const defaultActivePoolContext: ActivePoolsContextState = { - isBonding: () => false, - isNominator: () => false, - isOwner: () => false, - isMember: () => false, - isDepositor: () => false, - isBouncer: () => false, - getPoolBondedAccount: () => null, - getPoolUnlocking: () => [], - getPoolRoles: () => poolRoles, - setTargets: (t) => {}, - getNominationsStatus: () => nominationStatus, - setSelectedPoolId: (p) => {}, - selectedActivePool, - targets, - poolNominations, - synced: 'unsynced', - selectedPoolMemberCount: 0, -}; diff --git a/src/contexts/Pools/ActivePools/index.tsx b/src/contexts/Pools/ActivePools/index.tsx deleted file mode 100644 index 45c913ae10..0000000000 --- a/src/contexts/Pools/ActivePools/index.tsx +++ /dev/null @@ -1,595 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { localStorageOrDefault, setStateWithRef } from '@polkadot-cloud/utils'; -import BigNumber from 'bignumber.js'; -import type { ReactNode } from 'react'; -import { - createContext, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; -import { useStaking } from 'contexts/Staking'; -import type { AnyApi, AnyJson, Sync } from 'types'; -import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; -import { usePlugins } from 'contexts/Plugins'; -import { useNetwork } from 'contexts/Network'; -import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useApi } from '../../Api'; -import { useBondedPools } from '../BondedPools'; -import * as defaults from './defaults'; -import { usePoolMembers } from '../PoolMembers'; -import type { ActivePool, ActivePoolsContextState, PoolTargets } from './types'; -import type { PoolAddresses } from '../BondedPools/types'; -import { SubscanController } from 'static/SubscanController'; -import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; -import { useBalances } from 'contexts/Balances'; -import { useIdentities } from 'hooks/useIdentities'; - -export const ActivePoolsContext = createContext( - defaults.defaultActivePoolContext -); - -export const useActivePools = () => useContext(ActivePoolsContext); - -export const ActivePoolsProvider = ({ children }: { children: ReactNode }) => { - const { network } = useNetwork(); - const { api, isReady } = useApi(); - const { eraStakers } = useStaking(); - const { pluginEnabled } = usePlugins(); - const { fetchIdentities } = useIdentities(); - const { getPoolMembership } = useBalances(); - const { activeAccount } = useActiveAccounts(); - const createPoolAccounts = useCreatePoolAccounts(); - const { getMembersOfPoolFromNode } = usePoolMembers(); - const { getAccountPools, bondedPools } = useBondedPools(); - - const membership = getPoolMembership(activeAccount); - - // Determine active pools to subscribe to. - const accountPools = useMemo(() => { - const newAccountPools = Object.keys(getAccountPools(activeAccount) || {}); - const p = membership?.poolId ? String(membership.poolId) : '-1'; - - if (membership?.poolId && !newAccountPools.includes(p || '-1')) { - newAccountPools.push(String(membership.poolId)); - } - return newAccountPools; - }, [activeAccount, bondedPools, membership]); - - // Stores member's active pools. - const [activePools, setActivePools] = useState([]); - const activePoolsRef = useRef(activePools); - - // Store active pools unsubs. - const unsubActivePools = useRef([]); - - // Store active pools nominations. - const [poolNominations, setPoolNominations] = useState< - Record - >({}); - const poolNominationsRef = useRef(poolNominations); - - // Store pool nominations unsubs. - const unsubNominations = useRef([]); - - // Store account target validators. - const [targets, setTargetsState] = useState({}); - const targetsRef = useRef(targets); - - // Store the member count of the selected pool. - const [selectedPoolMemberCount, setSelectedPoolMemberCount] = - useState(0); - - const fetchingMemberCount = useRef('unsynced'); - - // Store whether active pool data has been synced. this will be true if no active pool exists for - // the active account. We just need confirmation this is the case. - const [synced, setSynced] = useState('unsynced'); - const syncedRef = useRef(synced); - - // Store the currently selected active pool for the UI. Should default to the membership pool (if - // present). - const [selectedPoolId, setSelectedPoolId] = useState(null); - - // Get the `activePool` of the active account. - const getActivePoolMembership = () => - activePoolsRef.current.find((a) => { - const p = membership?.poolId ? String(membership.poolId) : '0'; - return String(a.id) === p; - }) || null; - - const getSelectedActivePool = () => - activePoolsRef.current.find((a) => a.id === Number(selectedPoolId)) || null; - - const getSelectedPoolNominations = () => - poolNominationsRef.current[Number(selectedPoolId) ?? -1] || - defaults.poolNominations; - - const getSelectedPoolTargets = () => - targetsRef.current[Number(selectedPoolId) ?? -1] || defaults.targets; - - // handle active pool subscriptions - const handlePoolSubscriptions = async () => { - if (accountPools.length) { - Promise.all(accountPools.map((p) => subscribeToActivePool(Number(p)))); - } else { - setStateWithRef('synced', setSynced, syncedRef); - } - - // assign default pool immediately if active pool not currently selected - const defaultSelected = membership?.poolId || accountPools[0] || null; - const activePoolSelected = - activePoolsRef.current.find( - (a) => String(a.id) === String(selectedPoolId) - ) || null; - - if (defaultSelected && !activePoolSelected) { - setSelectedPoolId(String(defaultSelected)); - } - }; - - // Unsubscribe and reset poolNominations. - const unsubscribePoolNominations = () => { - if (unsubNominations.current.length) { - for (const unsub of unsubNominations.current) { - unsub(); - } - } - setStateWithRef({}, setPoolNominations, poolNominationsRef); - unsubNominations.current = []; - }; - - // Unsubscribe and reset activePool and poolNominations. - const unsubscribeActivePools = () => { - if (unsubActivePools.current.length) { - for (const unsub of unsubActivePools.current) { - unsub(); - } - setStateWithRef([], setActivePools, activePoolsRef); - unsubActivePools.current = []; - } - }; - - const subscribeToActivePool = async (poolId: number) => { - if (!api) { - return; - } - - const addresses: PoolAddresses = createPoolAccounts(poolId); - - // new active pool subscription - const subscribeActivePool = async (id: number) => { - const unsub = await api.queryMulti( - [ - [api.query.nominationPools.bondedPools, id], - [api.query.nominationPools.rewardPools, id], - [api.query.system.account, addresses.reward], - ], - async ([bondedPool, rewardPool, accountData]): Promise => { - const balance = accountData.data; - bondedPool = bondedPool?.unwrapOr(undefined)?.toHuman(); - rewardPool = rewardPool?.unwrapOr(undefined)?.toHuman(); - - if (rewardPool && bondedPool) { - // Fetch identities & super identities for roles and expand `bondedPool` state to store - // them. - const { roles } = bondedPool; - const roleAddresses: string[] = []; - if (roles.root) { - roleAddresses.push(roles.root); - } - if (roles.depositor) { - roleAddresses.push(roles.depositor); - } - if (roles.nominator) { - roleAddresses.push(roles.nominator); - } - if (roles.bouncer) { - roleAddresses.push(roles.bouncer); - } - - bondedPool.roleIdentities = await fetchIdentities(roleAddresses); - - const rewardAccountBalance = balance?.free; - const pendingRewards = await fetchPendingRewards(); - const pool = { - id, - addresses, - bondedPool, - rewardPool, - rewardAccountBalance, - pendingRewards, - }; - - // set active pool state, removing the pool if it already exists first. - setStateWithRef( - [...activePoolsRef.current.filter((a) => a.id !== pool.id), pool], - setActivePools, - activePoolsRef - ); - - // get pool target nominations and set in state - const newTargets = localStorageOrDefault( - `${addresses.stash}_pool_targets`, - defaults.targets, - true - ); - - // add or replace current pool targets in targetsRef - const newPoolTargets = { ...targetsRef.current }; - newPoolTargets[poolId] = newTargets; - - // set pool staking targets - setStateWithRef(newPoolTargets, setTargetsState, targetsRef); - - // subscribe to pool nominations - subscribeToPoolNominations(poolId, addresses.stash); - } else { - // set default targets for pool - const newPoolTargets = { ...targetsRef.current }; - newPoolTargets[poolId] = defaults.targets; - setStateWithRef(newPoolTargets, setTargetsState, targetsRef); - } - } - ); - return unsub; - }; - - // initiate subscription, add to unsubs. - await Promise.all([subscribeActivePool(poolId)]).then((unsubs) => { - unsubActivePools.current = unsubActivePools.current.concat(unsubs); - }); - }; - - const subscribeToPoolNominations = async ( - poolId: number, - poolBondAddress: string - ) => { - if (!api) { - return; - } - const subscribePoolNominations = async (bondedAddress: string) => { - const unsub = await api.query.staking.nominators( - bondedAddress, - (nominations: AnyApi) => { - // set pool nominations - let newNominations = nominations.unwrapOr(null); - if (newNominations === null) { - newNominations = defaults.poolNominations; - } else { - newNominations = { - targets: newNominations.targets.toHuman(), - submittedIn: newNominations.submittedIn.toHuman(), - }; - } - - // add or replace current pool nominations in poolNominations - const newPoolNominations = { ...poolNominationsRef.current }; - newPoolNominations[poolId] = newNominations; - - // set pool nominations state - setStateWithRef( - newPoolNominations, - setPoolNominations, - poolNominationsRef - ); - } - ); - return unsub; - }; - - // initiate subscription, add to unsubs. - await Promise.all([subscribePoolNominations(poolBondAddress)]).then( - (unsubs) => { - unsubNominations.current = unsubNominations.current.concat(unsubs); - } - ); - }; - - // Utility functions - /* - * updateActivePoolPendingRewards - * A helper function to set the unclaimed rewards of an active pool. - */ - const updateActivePoolPendingRewards = ( - pendingRewards: BigNumber, - poolId: number - ) => { - if (!poolId) { - return; - } - - // update the active pool the account is a member of. - setStateWithRef( - [...activePoolsRef.current].map((a) => - a.id === poolId - ? { - ...a, - pendingRewards, - } - : a - ), - setActivePools, - activePoolsRef - ); - }; - - /* - * setTargets - * Sets currently selected pool's target validators in storage. - */ - const setTargets = (newTargets: AnyJson) => { - if (!selectedPoolId) { - return; - } - - const stashAddress = getPoolBondedAccount(); - if (stashAddress) { - localStorage.setItem( - `${stashAddress}_pool_targets`, - JSON.stringify(newTargets) - ); - // inject targets into targets object - const newPoolTargets = { ...targetsRef.current }; - newPoolTargets[Number(selectedPoolId)] = newTargets; - - setStateWithRef(newPoolTargets, setTargetsState, targetsRef); - } - }; - - /* - * isBonding - * Returns whether active pool exists - */ - const isBonding = () => !!getSelectedActivePool(); - - /* - * isNominator - * Returns whether the active account is - * the nominator in the active pool. - */ - const isNominator = () => { - const roles = getSelectedActivePool()?.bondedPool?.roles; - if (!activeAccount || !roles) { - return false; - } - return activeAccount === roles?.nominator; - }; - - /* - * isOwner - * Returns whether the active account is - * the owner of the active pool. - */ - const isOwner = () => { - const roles = getSelectedActivePool()?.bondedPool?.roles; - if (!activeAccount || !roles) { - return false; - } - return activeAccount === roles?.root; - }; - - /* - * isMember - * Returns whether the active account is - * a member of the active pool. - */ - const isMember = () => { - const selectedPool = getSelectedActivePool(); - const p = selectedPool ? String(selectedPool.id) : '-1'; - return String(membership?.poolId || '') === p; - }; - - /* - * isDepositor - * Returns whether the active account is - * the depositor of the active pool. - */ - const isDepositor = () => { - const roles = getSelectedActivePool()?.bondedPool?.roles; - if (!activeAccount || !roles) { - return false; - } - return activeAccount === roles?.depositor; - }; - - /* - * isBouncer - * Returns whether the active account is - * the depositor of the active pool. - */ - const isBouncer = () => { - const roles = getSelectedActivePool()?.bondedPool?.roles; - if (!activeAccount || !roles) { - return false; - } - return activeAccount === roles?.bouncer; - }; - - /* - * getPoolBondedAccount - * get the stash address of the bonded pool - * that the member is participating in. - */ - const getPoolBondedAccount = () => - getSelectedActivePool()?.addresses?.stash || null; - - /* - * Get the status of nominations. - * Possible statuses: waiting, inactive, active. - */ - const getNominationsStatus = () => { - const nominations = getSelectedPoolNominations().nominations?.targets || []; - const statuses: Record = {}; - - for (const nomination of nominations) { - const s = eraStakers.stakers.find( - ({ address }) => address === nomination - ); - - if (s === undefined) { - statuses[nomination] = 'waiting'; - continue; - } - const exists = (s.others ?? []).find(({ who }) => who === activeAccount); - if (exists === undefined) { - statuses[nomination] = 'inactive'; - continue; - } - statuses[nomination] = 'active'; - } - return statuses; - }; - - /* - * getPoolRoles - * Returns the active pool's roles or a default roles object. - */ - const getPoolRoles = () => - getSelectedActivePool()?.bondedPool?.roles || defaults.poolRoles; - - const getPoolUnlocking = () => { - const membershipPoolId = membership?.poolId - ? String(membership.poolId) - : '-1'; - - // exit early if the currently selected pool is not membership pool - if (selectedPoolId !== membershipPoolId) { - return []; - } - return membership?.unlocking || []; - }; - - // Fetch and update unclaimed rewards from runtime call. - const fetchPendingRewards = async () => { - if (getActivePoolMembership() && membership && api && isReady) { - const pendingRewards = await api.call.nominationPoolsApi.pendingRewards( - membership?.address || '' - ); - return new BigNumber(pendingRewards?.toString() || 0); - } - return new BigNumber(0); - }; - - // Fetch and update pending rewards when membership changes. - const updatePendingRewards = async () => { - const pendingRewards = await fetchPendingRewards(); - - updateActivePoolPendingRewards( - pendingRewards, - getActivePoolMembership()?.id || 0 - ); - }; - - // subscribe to pool that the active account is a member of. - useEffectIgnoreInitial(() => { - if (isReady && syncedRef.current === 'unsynced') { - setStateWithRef('syncing', setSynced, syncedRef); - handlePoolSubscriptions(); - } - }, [network, isReady, synced]); - - // re-calculate pending rewards when membership changes - useEffectIgnoreInitial(() => { - if (isReady) { - updatePendingRewards(); - } - }, [ - network, - isReady, - getActivePoolMembership()?.bondedPool, - getActivePoolMembership()?.rewardPool, - membership, - ]); - - // Gets the member count of the currently selected pool. If Subscan is enabled, it is used instead of the connected node. - const getMemberCount = async () => { - const selectedActivePool = getSelectedActivePool(); - - if (!selectedActivePool?.id) { - setSelectedPoolMemberCount(0); - return; - } - // If `Subscan` plugin is enabled, fetch member count directly from the API. - if ( - pluginEnabled('subscan') && - fetchingMemberCount.current === 'unsynced' - ) { - fetchingMemberCount.current = 'syncing'; - const poolDetails = await SubscanController.handleFetchPoolDetails( - selectedActivePool.id - ); - fetchingMemberCount.current = 'synced'; - setSelectedPoolMemberCount(poolDetails?.member_count || 0); - return; - } - // If no plugin available, fetch all pool members from RPC and filter them to determine current - // pool member count. NOTE: Expensive operation. - setSelectedPoolMemberCount( - getMembersOfPoolFromNode(selectedActivePool?.id || 0)?.length || 0 - ); - }; - - // Re-sync when number of `accountRoles` change. This can happen when `bondedPools` sync, when - // roles are edited within the dashboard, or when pool membership changes. - useEffectIgnoreInitial(() => { - unsubscribeActivePools(); - unsubscribePoolNominations(); - setStateWithRef('unsynced', setSynced, syncedRef); - }, [activeAccount, accountPools.length]); - - // When we are subscribed to all active pools, syncing is considered completed. - useEffectIgnoreInitial(() => { - if (unsubNominations.current.length === accountPools.length) { - setStateWithRef('synced', setSynced, syncedRef); - } - }, [accountPools, unsubNominations.current]); - - // Fetch pool member count. We use `membership` as a dependency as the member count could change - // in the UI when active account's membership changes. NOTE: Do not have `poolMembersNode` as a - // dependency - could trigger many re-renders if value is constantly changing - more suited as a - // custom event. - useEffect(() => { - fetchingMemberCount.current = 'unsynced'; - getMemberCount(); - }, [activeAccount, getSelectedActivePool()?.id, membership?.poolId]); - - // unsubscribe all on component unmount. - useEffect( - () => () => { - unsubscribeActivePools(); - unsubscribePoolNominations(); - }, - [network] - ); - - return ( - - {children} - - ); -}; diff --git a/src/contexts/Pools/BondedPools/defaults.ts b/src/contexts/Pools/BondedPools/defaults.ts index ce71c86864..bedd522abc 100644 --- a/src/contexts/Pools/BondedPools/defaults.ts +++ b/src/contexts/Pools/BondedPools/defaults.ts @@ -5,17 +5,16 @@ import type { BondedPoolsContextState } from './types'; export const defaultBondedPoolsContext: BondedPoolsContextState = { - queryBondedPool: (p) => {}, - getBondedPool: (p) => null, - updateBondedPools: (p) => {}, - addToBondedPools: (p) => {}, - removeFromBondedPools: (p) => {}, - getPoolNominationStatus: (n, o) => {}, - getPoolNominationStatusCode: (t) => '', - getAccountRoles: (w) => null, - getAccountPools: (w) => null, - replacePoolRoles: (p, e) => {}, - poolSearchFilter: (l, v) => {}, + queryBondedPool: (poolId) => {}, + getBondedPool: (poolId) => null, + updateBondedPools: (bondedPools) => {}, + addToBondedPools: (bondedPool) => {}, + removeFromBondedPools: (poolId) => {}, + getPoolNominationStatus: (nominator, address) => {}, + getPoolNominationStatusCode: (statuses) => '', + getAccountPoolRoles: (address) => null, + replacePoolRoles: (poolId, roleEdits) => {}, + poolSearchFilter: (filteredPools, searchTerm) => {}, bondedPools: [], poolsMetaData: {}, poolsNominations: {}, diff --git a/src/contexts/Pools/BondedPools/index.tsx b/src/contexts/Pools/BondedPools/index.tsx index b6e5bcccc9..f19acbf070 100644 --- a/src/contexts/Pools/BondedPools/index.tsx +++ b/src/contexts/Pools/BondedPools/index.tsx @@ -2,10 +2,11 @@ // SPDX-License-Identifier: GPL-3.0-only import { u8aToString, u8aUnwrapBytes } from '@polkadot/util'; -import { rmCommas, shuffle } from '@polkadot-cloud/utils'; +import { rmCommas, setStateWithRef, shuffle } from '@polkadot-cloud/utils'; import type { ReactNode } from 'react'; import { createContext, useContext, useRef, useState } from 'react'; import type { + AccountPoolRoles, BondedPool, BondedPoolsContextState, MaybePool, @@ -38,8 +39,9 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { const createPoolAccounts = useCreatePoolAccounts(); const { getNominationsStatusFromTargets } = useStaking(); - // Store bonded pools. + // Store bonded pools. Used implicitly in callbacks, ref is also defined. const [bondedPools, setBondedPools] = useState([]); + const bondedPoolsRef = useRef(bondedPools); // Track the sync status of `bondedPools`. const bondedPoolsSynced = useRef('unsynced'); @@ -73,7 +75,7 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { }); exposures = shuffle(exposures); - setBondedPools(exposures); + setStateWithRef(exposures, setBondedPools, bondedPoolsRef); // Fetch pools metadata. const metadataMulti = await api.query.nominationPools.metadata.multi(ids); @@ -236,11 +238,14 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { if (!updatedPools) { return; } - setBondedPools( + + setStateWithRef( bondedPools.map( (original) => updatedPools.find((updated) => updated.id === original.id) || original - ) + ), + setBondedPools, + bondedPoolsRef ); }; @@ -261,7 +266,11 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { }; const removeFromBondedPools = (id: number) => { - setBondedPools(bondedPools.filter((b: BondedPool) => b.id !== id)); + setStateWithRef( + bondedPools.filter((b) => b.id !== id), + setBondedPools, + bondedPoolsRef + ); }; // adds a record to bondedPools. @@ -273,68 +282,73 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { const exists = bondedPools.find((b) => b.id === pool.id); if (!exists) { - setBondedPools(bondedPools.concat(pool)); + setStateWithRef(bondedPools.concat(pool), setBondedPools, bondedPoolsRef); } }; - // get all the roles belonging to one pool account - const getAccountRoles = (who: MaybeAddress) => { + // Gets all pools that the account has a role in. Returns an object with each pool role as keys, + // and and array of pool ids as their values. + const accumulateAccountPoolRoles = (who: MaybeAddress): AccountPoolRoles => { if (!who) { return { - depositor: [], root: [], + depositor: [], nominator: [], bouncer: [], }; } - const depositor = bondedPools + const depositor = bondedPoolsRef.current .filter((b) => b.roles.depositor === who) .map((b) => b.id); - const root = bondedPools + const root = bondedPoolsRef.current .filter((b: BondedPool) => b.roles.root === who) .map((b) => b.id); - const nominator = bondedPools + const nominator = bondedPoolsRef.current .filter((b) => b.roles.nominator === who) .map((b) => b.id); - const bouncer = bondedPools + const bouncer = bondedPoolsRef.current .filter((b) => b.roles.bouncer === who) .map((b) => b.id); - return { - depositor, + const result = { root, + depositor, nominator, bouncer, }; + + return result; }; - // accumulate account pool list - const getAccountPools = (who: MaybeAddress) => { - // first get the roles of the account - const roles = getAccountRoles(who); - // format new list has pool => roles + // Gets a list of roles for all the pools the provided account has one or more roles in. + const getAccountPoolRoles = (who: MaybeAddress) => { + const allAccountRoles = accumulateAccountPoolRoles(who); + + // Reformat all roles object, keyed by pool id. const pools: Record = {}; - Object.entries(roles).forEach(([key, poolIds]) => { - // now looping through a role - poolIds.forEach((poolId) => { - const exists = Object.keys(pools).find( - (k) => String(k) === String(poolId) - ); - if (!exists) { - pools[poolId] = [key]; - } else { - pools[poolId].push(key); - } + + if (allAccountRoles) { + Object.entries(allAccountRoles).forEach(([role, poolIds]) => { + poolIds.forEach((poolId) => { + const exists = Object.keys(pools).find( + (k) => String(k) === String(poolId) + ); + if (!exists) { + pools[poolId] = [role]; + } else { + pools[poolId].push(role); + } + }); }); - }); + } return pools; }; - // determine roles to replace from roleEdits + // Determine roles to replace from roleEdits const toReplace = (roleEdits: AnyJson) => { const root = roleEdits?.root?.newAddress ?? ''; const nominator = roleEdits?.nominator?.newAddress ?? ''; @@ -347,7 +361,7 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { }; }; - // replaces the pool roles from roleEdits + // Replaces the pool roles from roleEdits const replacePoolRoles = (poolId: number, roleEdits: AnyJson) => { let pool = bondedPools.find((b) => b.id === poolId) || null; @@ -367,13 +381,13 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { ...bondedPools.map((b) => (b.id === poolId && pool !== null ? pool : b)), ]; - setBondedPools(newBondedPools); + setStateWithRef(newBondedPools, setBondedPools, bondedPoolsRef); }; // Clear existing state for network refresh. useEffectIgnoreInitial(() => { bondedPoolsSynced.current = 'unsynced'; - setBondedPools([]); + setStateWithRef([], setBondedPools, bondedPoolsRef); setPoolsMetadata({}); setPoolsNominations({}); }, [network]); @@ -402,8 +416,7 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { removeFromBondedPools, getPoolNominationStatus, getPoolNominationStatusCode, - getAccountRoles, - getAccountPools, + getAccountPoolRoles, replacePoolRoles, poolSearchFilter, bondedPools, diff --git a/src/contexts/Pools/BondedPools/types.ts b/src/contexts/Pools/BondedPools/types.ts index 5193f427a7..23ec64c2f7 100644 --- a/src/contexts/Pools/BondedPools/types.ts +++ b/src/contexts/Pools/BondedPools/types.ts @@ -2,21 +2,23 @@ // SPDX-License-Identifier: GPL-3.0-only import type { AnyApi, AnyJson, MaybeAddress } from 'types'; -import type { ActiveBondedPool } from '../ActivePools/types'; +import type { ActiveBondedPool } from '../ActivePool/types'; import type { AnyFilter } from 'library/Filter/types'; export interface BondedPoolsContextState { - queryBondedPool: (p: number) => AnyApi; - getBondedPool: (p: number) => BondedPool | null; - updateBondedPools: (p: BondedPool[]) => void; - addToBondedPools: (p: BondedPool) => void; - removeFromBondedPools: (p: number) => void; - getPoolNominationStatus: (n: MaybeAddress, o: MaybeAddress) => AnyApi; - getPoolNominationStatusCode: (t: NominationStatuses | null) => string; - getAccountRoles: (w: MaybeAddress) => AnyApi; - getAccountPools: (w: MaybeAddress) => AnyApi; + queryBondedPool: (poolId: number) => AnyApi; + getBondedPool: (poolId: number) => BondedPool | null; + updateBondedPools: (bondedPools: BondedPool[]) => void; + addToBondedPools: (bondedPool: BondedPool) => void; + removeFromBondedPools: (poolId: number) => void; + getPoolNominationStatus: ( + nominator: MaybeAddress, + address: MaybeAddress + ) => AnyApi; + getPoolNominationStatusCode: (statuses: NominationStatuses | null) => string; + getAccountPoolRoles: (address: MaybeAddress) => AnyApi; replacePoolRoles: (poolId: number, roleEdits: AnyJson) => void; - poolSearchFilter: (l: AnyFilter, v: string) => void; + poolSearchFilter: (filteredPools: AnyFilter, searchTerm: string) => void; bondedPools: BondedPool[]; poolsMetaData: Record; poolsNominations: Record; @@ -51,3 +53,10 @@ export type PoolNominations = { } | null; export type NominationStatuses = Record; + +export type AccountPoolRoles = { + root: number[]; + depositor: number[]; + nominator: number[]; + bouncer: number[]; +} | null; diff --git a/src/contexts/Pools/types.ts b/src/contexts/Pools/types.ts index 7a3cd25b36..0d7038455f 100644 --- a/src/contexts/Pools/types.ts +++ b/src/contexts/Pools/types.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import type BigNumber from 'bignumber.js'; -import type { PoolUnlocking } from './ActivePools/types'; +import type { PoolUnlocking } from './ActivePool/types'; export type ClaimPermission = | 'Permissioned' diff --git a/src/contexts/Setup/types.ts b/src/contexts/Setup/types.ts index 2d1c6a76d2..5ece87a2d6 100644 --- a/src/contexts/Setup/types.ts +++ b/src/contexts/Setup/types.ts @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { PoolRoles } from 'contexts/Pools/ActivePools/types'; +import type { PoolRoles } from 'contexts/Pools/ActivePool/types'; import type { ValidatorPrefs } from 'contexts/Validators/types'; import type { AnyJson, BondFor, MaybeAddress, MaybeString } from 'types'; diff --git a/src/contexts/Staking/index.tsx b/src/contexts/Staking/index.tsx index 0beadad00b..ce9d2592dc 100644 --- a/src/contexts/Staking/index.tsx +++ b/src/contexts/Staking/index.tsx @@ -39,6 +39,7 @@ import { formatRawExposures, } from './Utils'; import type { NominationStatus } from 'library/ValidatorList/ValidatorItem/types'; +import { SyncController } from 'static/SyncController'; const worker = new Worker(); @@ -103,6 +104,9 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { // check if account hasn't changed since worker started if (getActiveAccount() === who) { + // Syncing current eraStakers is now complete. + SyncController.dispatch('era-stakers', 'complete'); + setStateWithRef( { ...eraStakersRef.current, diff --git a/src/contexts/UI/defaults.ts b/src/contexts/UI/defaults.ts index cb58b4c32a..9199cbb40e 100644 --- a/src/contexts/UI/defaults.ts +++ b/src/contexts/UI/defaults.ts @@ -12,8 +12,5 @@ export const defaultUIContext: UIContextInterface = { userSideMenuMinimised: false, sideMenuMinimised: false, containerRefs: {}, - isSyncing: false, - isNetworkSyncing: false, - isPoolSyncing: false, isBraveBrowser: false, }; diff --git a/src/contexts/UI/index.tsx b/src/contexts/UI/index.tsx index 415d89dede..90303c663d 100644 --- a/src/contexts/UI/index.tsx +++ b/src/contexts/UI/index.tsx @@ -2,16 +2,11 @@ // SPDX-License-Identifier: GPL-3.0-only import { localStorageOrDefault, setStateWithRef } from '@polkadot-cloud/utils'; -import BigNumber from 'bignumber.js'; import type { ReactNode, RefObject } from 'react'; import { createContext, useContext, useEffect, useRef, useState } from 'react'; import { SideMenuStickyThreshold } from 'consts'; -import { useBalances } from 'contexts/Balances'; -import { useActivePools } from 'contexts/Pools/ActivePools'; import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; import type { AnyJson } from 'types'; -import { useApi } from '../Api'; -import { useStaking } from '../Staking'; import * as defaults from './defaults'; import type { UIContextInterface } from './types'; @@ -22,20 +17,6 @@ export const UIContext = createContext( export const useUi = () => useContext(UIContext); export const UIProvider = ({ children }: { children: ReactNode }) => { - const { eraStakers } = useStaking(); - const { balancesInitialSynced } = useBalances(); - const { synced: activePoolsSynced } = useActivePools(); - const { isReady, networkMetrics, activeEra, stakingMetrics } = useApi(); - - // Set whether the network has been synced. - const [isNetworkSyncing, setIsNetworkSyncing] = useState(false); - - // Set whether pools are being synced. - const [isPoolSyncing, setIsPoolSyncing] = useState(false); - - // Set whether app is syncing. Includes workers (active nominations). - const [isSyncing, setIsSyncing] = useState(false); - // Side whether the side menu is open. const [sideMenuOpen, setSideMenu] = useState(false); @@ -96,54 +77,6 @@ export const UIProvider = ({ children }: { children: ReactNode }) => { resizeCallback(); }, [userSideMenuMinimised]); - // App syncing updates. - useEffect(() => { - let syncing = false; - let networkSyncing = false; - let poolSyncing = false; - - // staking metrics have synced - if (stakingMetrics.lastReward === new BigNumber(0)) { - syncing = true; - networkSyncing = true; - } - - // era has synced from Network - if (activeEra.index.isZero()) { - syncing = true; - networkSyncing = true; - } - - if (!balancesInitialSynced) { - syncing = true; - networkSyncing = true; - } - - setIsNetworkSyncing(networkSyncing); - - // active pools have been synced - if (activePoolsSynced !== 'synced') { - syncing = true; - poolSyncing = true; - } - - setIsPoolSyncing(poolSyncing); - - // eraStakers total active nominators has synced - if (!eraStakers.totalActiveNominators) { - syncing = true; - } - - setIsSyncing(syncing); - }, [ - isReady, - stakingMetrics, - networkMetrics, - eraStakers, - activePoolsSynced, - balancesInitialSynced, - ]); - return ( { setContainerRefs, sideMenuOpen, sideMenuMinimised, - isSyncing, - isNetworkSyncing, - isPoolSyncing, containerRefs, isBraveBrowser, userSideMenuMinimised, diff --git a/src/contexts/UI/types.ts b/src/contexts/UI/types.ts index cfae64a5af..3e156bb26a 100644 --- a/src/contexts/UI/types.ts +++ b/src/contexts/UI/types.ts @@ -11,8 +11,5 @@ export interface UIContextInterface { userSideMenuMinimised: boolean; sideMenuMinimised: boolean; containerRefs: Record>; - isSyncing: boolean; - isNetworkSyncing: boolean; - isPoolSyncing: boolean; isBraveBrowser: boolean; } diff --git a/src/contexts/Validators/ValidatorEntries/index.tsx b/src/contexts/Validators/ValidatorEntries/index.tsx index f01c5f7290..33c732b088 100644 --- a/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/src/contexts/Validators/ValidatorEntries/index.tsx @@ -9,7 +9,7 @@ import { ValidatorCommunity } from '@polkadot-cloud/assets/validators'; import type { AnyApi, AnyJson, BondFor, Fn, Sync } from 'types'; import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; import { useBonded } from 'contexts/Bonded'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useNetwork } from 'contexts/Network'; import { useApi } from 'contexts/Api'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -35,7 +35,7 @@ import { import { getLocalEraValidators, setLocalEraValidators } from '../Utils'; import type { ValidatorEntry } from '@polkadot-cloud/assets/types'; import { useErasPerDay } from 'hooks/useErasPerDay'; -import { useIdentities } from 'hooks/useIdentities'; +import { IdentitiesController } from 'static/IdentitiesController'; export const ValidatorsContext = createContext( defaultValidatorsContext @@ -53,9 +53,8 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { } = useApi(); const { activeEra } = useApi(); const { stakers } = useStaking().eraStakers; - const { fetchIdentities } = useIdentities(); - const { poolNominations } = useActivePools(); const { activeAccount } = useActiveAccounts(); + const { activePoolNominations } = useActivePool(); const { erasPerDay, maxSupportedDays } = useErasPerDay(); const { bondedAccounts, getAccountNominations } = useBonded(); @@ -272,7 +271,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Fetches the active pool's nominees. const fetchPoolNominatedList = async () => { // get raw nominations list - const n = poolNominations.targets; + const n = activePoolNominations?.targets || []; // fetch preferences const nominationsWithPrefs = await fetchValidatorPrefs( @@ -358,11 +357,12 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { avg ); setAvgCommission(avg); - // Validators are shuffled before committed to state. + // NOTE: validators are shuffled before committed to state. setValidators(shuffle(validatorEntries)); const addresses = validatorEntries.map(({ address }) => address); - const { identities, supers } = await fetchIdentities(addresses); + const { identities, supers } = await IdentitiesController.fetch(addresses); + setValidatorIdentities(identities); setValidatorSupers(supers); setValidatorsFetched('synced'); @@ -605,10 +605,10 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Fetch active account's pool nominations in validator list format. useEffectIgnoreInitial(() => { - if (isReady && poolNominations) { + if (isReady && activePoolNominations) { fetchPoolNominatedList(); } - }, [isReady, poolNominations]); + }, [isReady, activePoolNominations]); // Unsubscribe on network change and component unmount. useEffect(() => { diff --git a/src/hooks/useActivePools/index.tsx b/src/hooks/useActivePools/index.tsx new file mode 100644 index 0000000000..bbaeb5a27b --- /dev/null +++ b/src/hooks/useActivePools/index.tsx @@ -0,0 +1,100 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { setStateWithRef } from '@polkadot-cloud/utils'; +import { useEffect, useRef, useState } from 'react'; +import { ActivePoolsController } from 'static/ActivePoolsController'; +import { isCustomEvent } from 'static/utils'; +import { useEventListener } from 'usehooks-ts'; +import type { + ActiveNominationsState, + ActivePoolsProps, + ActivePoolsState, +} from './types'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useNetwork } from 'contexts/Network'; + +export const useActivePools = ({ onCallback, poolIds }: ActivePoolsProps) => { + const { network } = useNetwork(); + const { activeAccount } = useActiveAccounts(); + + // Stores active pools. + const [activePools, setActivePools] = useState({}); + const activePoolsRef = useRef(activePools); + + // Store nominations of active pools. + const [poolNominations, setPoolNominations] = + useState({}); + const poolNominationsRef = useRef(poolNominations); + + // Handle report of new active pool data. + const newActivePoolCallback = async (e: Event) => { + if (isCustomEvent(e) && ActivePoolsController.isValidNewActivePool(e)) { + const { pool, nominations } = e.detail; + const { id } = pool; + + // Call custom `onCallback` function if provided. + if (typeof onCallback === 'function') { + await onCallback(e.detail); + } + + // Persist to active pools state if this pool is specified in `poolIds`. + if ( + poolIds === '*' || + (Array.isArray(poolIds) && poolIds.includes(String(id))) + ) { + const newActivePools = { ...activePoolsRef.current }; + newActivePools[id] = pool; + setStateWithRef(newActivePools, setActivePools, activePoolsRef); + + const newPoolNominations = { ...poolNominationsRef.current }; + newPoolNominations[id] = nominations; + setStateWithRef( + newPoolNominations, + setPoolNominations, + poolNominationsRef + ); + } + } + }; + + const documentRef = useRef(document); + + // Bootstrap state on initial render. + useEffect(() => { + const initialActivePools = + poolIds === '*' + ? ActivePoolsController.activePools + : Object.fromEntries( + Object.entries(ActivePoolsController.activePools).filter(([key]) => + poolIds.includes(key) + ) + ); + setStateWithRef(initialActivePools || {}, setActivePools, activePoolsRef); + + const initialPoolNominations = + poolIds === '*' + ? ActivePoolsController.poolNominations + : Object.fromEntries( + Object.entries(ActivePoolsController.poolNominations).filter( + ([key]) => poolIds.includes(key) + ) + ); + setStateWithRef( + initialPoolNominations, + setPoolNominations, + poolNominationsRef + ); + }, [JSON.stringify(poolIds)]); + + // Reset state on active account or network change. + useEffect(() => { + setStateWithRef({}, setActivePools, activePoolsRef); + setStateWithRef({}, setPoolNominations, poolNominationsRef); + }, [network, activeAccount]); + + // Listen for new active pool events. + useEventListener('new-active-pool', newActivePoolCallback, documentRef); + + return { activePools, poolNominations }; +}; diff --git a/src/hooks/useActivePools/types.ts b/src/hooks/useActivePools/types.ts new file mode 100644 index 0000000000..a71b1b08b8 --- /dev/null +++ b/src/hooks/useActivePools/types.ts @@ -0,0 +1,15 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { Nominations } from 'contexts/Bonded/types'; +import type { ActivePool } from 'contexts/Pools/ActivePool/types'; +import type { DetailActivePool } from 'static/ActivePoolsController/types'; + +export interface ActivePoolsProps { + poolIds: string[] | '*'; + onCallback?: (detail: DetailActivePool) => Promise; +} + +export type ActivePoolsState = Record; + +export type ActiveNominationsState = Record; diff --git a/src/hooks/useIdentities/index.tsx b/src/hooks/useIdentities/index.tsx deleted file mode 100644 index b1cefcb98d..0000000000 --- a/src/hooks/useIdentities/index.tsx +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import type { AnyApi } from '@polkadot-cloud/react/types'; -import { useApi } from 'contexts/Api'; - -export const useIdentities = () => { - const { api } = useApi(); - - // Fetches validator identities. - const fetchValidatorIdentities = async (addresses: string[]) => { - if (!api) { - return {}; - } - - const result = (await api.query.identity.identityOf.multi(addresses)).map( - (identity) => identity.toHuman() - ); - return Object.fromEntries( - result.map((k, i) => [addresses[i], k]).filter(([, v]) => v !== null) - ); - }; - - // Fetch an array of super accounts and their identities. - const fetchValidatorSupers = async (addresses: string[]) => { - if (!api) { - return {}; - } - - const supersRaw = (await api.query.identity.superOf.multi(addresses)).map( - (superOf) => superOf.toHuman() - ); - - const supers = Object.fromEntries( - supersRaw - .map((k, i) => [ - addresses[i], - { - superOf: k, - }, - ]) - .filter(([, { superOf }]: AnyApi) => superOf !== null) - ); - - const superIdentities = ( - await api.query.identity.identityOf.multi( - Object.values(supers).map(({ superOf }: AnyApi) => superOf[0]) - ) - ).map((superIdentity) => superIdentity.toHuman()); - - const supersWithIdentity = Object.fromEntries( - Object.entries(supers).map(([k, v]: AnyApi, i) => [ - k, - { - ...v, - identity: superIdentities[i], - }, - ]) - ); - return supersWithIdentity; - }; - - // Fetches both identities and super identities from an array of addresses. - const fetchIdentities = async (addresses: string[]) => { - const [identities, supers] = await Promise.all([ - fetchValidatorIdentities(addresses), - fetchValidatorSupers(addresses), - ]); - - return { identities, supers }; - }; - - return { - fetchIdentities, - }; -}; diff --git a/src/hooks/useNominationStatus/index.tsx b/src/hooks/useNominationStatus/index.tsx index df6a71a792..7349ec6dd8 100644 --- a/src/hooks/useNominationStatus/index.tsx +++ b/src/hooks/useNominationStatus/index.tsx @@ -5,16 +5,16 @@ import { planckToUnit } from '@polkadot-cloud/utils'; import BigNumber from 'bignumber.js'; import { useTranslation } from 'react-i18next'; import { useBonded } from 'contexts/Bonded'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; import type { AnyJson, BondFor, MaybeAddress } from 'types'; import { useNetwork } from 'contexts/Network'; +import { useSyncing } from 'hooks/useSyncing'; export const useNominationStatus = () => { const { t } = useTranslation(); - const { isSyncing } = useUi(); + const { syncing } = useSyncing(['era-stakers']); const { networkData: { units }, } = useNetwork(); @@ -26,7 +26,7 @@ export const useNominationStatus = () => { getLowestRewardFromStaker, } = useStaking(); const { validators } = useValidators(); - const { poolNominations } = useActivePools(); + const { activePoolNominations } = useActivePool(); const { getAccountNominations } = useBonded(); // Utility to get an account's nominees alongside their status. @@ -34,7 +34,7 @@ export const useNominationStatus = () => { const nominations = type === 'nominator' ? getAccountNominations(who) - : poolNominations?.targets ?? []; + : activePoolNominations?.targets ?? []; return getNominationsStatusFromTargets(who, nominations); }; @@ -90,7 +90,7 @@ export const useNominationStatus = () => { // Determine the localised message to display based on the nomination status. let str; - if (inSetup() || isSyncing) { + if (inSetup() || syncing) { str = t('nominate.notNominating', { ns: 'pages' }); } else if (!nominees.length) { str = t('nominate.noNominationsSet', { ns: 'pages' }); diff --git a/src/hooks/useSyncing/index.tsx b/src/hooks/useSyncing/index.tsx new file mode 100644 index 0000000000..14d6bb6e11 --- /dev/null +++ b/src/hooks/useSyncing/index.tsx @@ -0,0 +1,60 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { setStateWithRef } from '@polkadot-cloud/utils'; +import { useEffect, useRef, useState } from 'react'; +import { SyncController } from 'static/SyncController'; +import type { SyncID, SyncIDConfig } from 'static/SyncController/types'; +import { isCustomEvent } from 'static/utils'; +import { useEventListener } from 'usehooks-ts'; + +export const useSyncing = (config: SyncIDConfig) => { + // Retrieve the ids from the config provided. + const ids = SyncController.getIdsFromSyncConfig(config); + + // Keep a record of active sync statuses. + const [syncIds, setSyncIds] = useState([]); + const syncIdsRef = useRef(syncIds); + + // Handle new syncing status events. + const newSyncStatusCallback = async (e: Event) => { + if (isCustomEvent(e) && SyncController.isValidSyncStatus(e)) { + const { id, status } = e.detail; + const ignoreEvent = ids !== '*' && !ids.includes(id); + + if (!ignoreEvent) { + // An item is reported as syncing. Add its `id` to state if not already. + if (status === 'syncing') { + setStateWithRef([...syncIdsRef.current, id], setSyncIds, syncIdsRef); + } + + // An item is reported to have completed syncing. Remove its `id` from state if present. + if (status === 'complete' && syncIdsRef.current.includes(id)) { + setStateWithRef( + syncIdsRef.current.filter((syncStatus) => syncStatus !== id), + setSyncIds, + syncIdsRef + ); + } + } + } + }; + + const documentRef = useRef(document); + + // Bootstrap existing sync statuses of interest when hook is mounted. + useEffect(() => { + setStateWithRef( + SyncController.syncIds.filter( + (syncId) => ids === '*' || ids.includes(syncId) + ), + setSyncIds, + syncIdsRef + ); + }, []); + + // Listen for new sync events. + useEventListener('new-sync-status', newSyncStatusCallback, documentRef); + + return { syncing: syncIds.length > 0 }; +}; diff --git a/src/library/Account/types.ts b/src/library/Account/types.ts index c3a7452f9d..a77e2ac40b 100644 --- a/src/library/Account/types.ts +++ b/src/library/Account/types.ts @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ActivePool } from 'contexts/Pools/ActivePools/types'; +import type { ActivePool } from 'contexts/Pools/ActivePool/types'; import type { MaybeAddress } from 'types'; export interface AccountProps { diff --git a/src/library/Form/Bond/BondFeedback.tsx b/src/library/Form/Bond/BondFeedback.tsx index abeee1fd20..907157ed82 100644 --- a/src/library/Form/Bond/BondFeedback.tsx +++ b/src/library/Form/Bond/BondFeedback.tsx @@ -5,7 +5,7 @@ import { planckToUnit, unitToPlanck } from '@polkadot-cloud/utils'; import BigNumber from 'bignumber.js'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -32,13 +32,13 @@ export const BondFeedback = ({ const { networkData: { units, unit }, } = useNetwork(); + const { isDepositor } = useActivePool(); const { activeAccount } = useActiveAccounts(); - const { isDepositor } = useActivePools(); - const { getTransferOptions } = useTransferOptions(); const { poolsConfig: { minJoinBond, minCreateBond }, stakingMetrics: { minNominatorBond }, } = useApi(); + const { getTransferOptions } = useTransferOptions(); const allTransferOptions = getTransferOptions(activeAccount); const defaultBondStr = defaultBond ? String(defaultBond) : ''; diff --git a/src/library/Form/CreatePoolStatusBar/index.tsx b/src/library/Form/CreatePoolStatusBar/index.tsx index 28996b1f95..174c29192f 100644 --- a/src/library/Form/CreatePoolStatusBar/index.tsx +++ b/src/library/Form/CreatePoolStatusBar/index.tsx @@ -5,23 +5,21 @@ import { faFlag } from '@fortawesome/free-regular-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { planckToUnit } from '@polkadot-cloud/utils'; import { useTranslation } from 'react-i18next'; -import { useUi } from 'contexts/UI'; import { useNetwork } from 'contexts/Network'; import type { NominateStatusBarProps } from '../types'; import { Wrapper } from './Wrapper'; import { useApi } from 'contexts/Api'; +import { useSyncing } from 'hooks/useSyncing'; export const CreatePoolStatusBar = ({ value }: NominateStatusBarProps) => { const { t } = useTranslation('library'); - const { isSyncing } = useUi(); - const { unit, units } = useNetwork().networkData; + const { syncing } = useSyncing('*'); const { minCreateBond } = useApi().poolsConfig; + const { unit, units } = useNetwork().networkData; const minCreateBondUnit = planckToUnit(minCreateBond, units); const sectionClassName = - value.isGreaterThanOrEqualTo(minCreateBondUnit) && !isSyncing - ? 'invert' - : ''; + value.isGreaterThanOrEqualTo(minCreateBondUnit) && !syncing ? 'invert' : ''; return ( @@ -39,7 +37,7 @@ export const CreatePoolStatusBar = ({ value }: NominateStatusBarProps) => {
- {isSyncing + {syncing ? '...' : `${minCreateBondUnit.decimalPlaces(3).toFormat()} ${unit}`}
diff --git a/src/library/Form/NominateStatusBar/index.tsx b/src/library/Form/NominateStatusBar/index.tsx index 4b68889bb6..1521b73dcb 100644 --- a/src/library/Form/NominateStatusBar/index.tsx +++ b/src/library/Form/NominateStatusBar/index.tsx @@ -7,21 +7,21 @@ import { ButtonHelp } from '@polkadot-cloud/react'; import { planckToUnit } from '@polkadot-cloud/utils'; import { useTranslation } from 'react-i18next'; import { useHelp } from 'contexts/Help'; -import { useUi } from 'contexts/UI'; import { useNetwork } from 'contexts/Network'; import type { NominateStatusBarProps } from '../types'; import { Wrapper } from './Wrapper'; import { useApi } from 'contexts/Api'; +import { useSyncing } from 'hooks/useSyncing'; export const NominateStatusBar = ({ value }: NominateStatusBarProps) => { const { t } = useTranslation('library'); - const { isSyncing } = useUi(); - const { unit, units } = useNetwork().networkData; + const { openHelp } = useHelp(); + const { syncing } = useSyncing('*'); const { networkMetrics: { minimumActiveStake }, stakingMetrics: { minNominatorBond }, } = useApi(); - const { openHelp } = useHelp(); + const { unit, units } = useNetwork().networkData; const minNominatorBondUnit = planckToUnit(minNominatorBond, units); const minimumActiveStakeUnit = planckToUnit(minimumActiveStake, units); @@ -31,13 +31,13 @@ export const NominateStatusBar = ({ value }: NominateStatusBarProps) => { return (
-
+

 

{t('nominateInactive')}
-
+

  {t('nominate')} @@ -49,7 +49,7 @@ export const NominateStatusBar = ({ value }: NominateStatusBarProps) => {

-
+

 {t('nominateActive')} @@ -60,7 +60,7 @@ export const NominateStatusBar = ({ value }: NominateStatusBarProps) => {

- {isSyncing + {syncing ? '...' : `${(minimumActiveStakeUnit.isLessThan(minNominatorBondUnit) ? minNominatorBondUnit diff --git a/src/library/Form/Unbond/UnbondFeedback.tsx b/src/library/Form/Unbond/UnbondFeedback.tsx index f9e923c741..d4ea7ac9a7 100644 --- a/src/library/Form/Unbond/UnbondFeedback.tsx +++ b/src/library/Form/Unbond/UnbondFeedback.tsx @@ -5,7 +5,7 @@ import { isNotZero, planckToUnit, unitToPlanck } from '@polkadot-cloud/utils'; import BigNumber from 'bignumber.js'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -29,7 +29,7 @@ export const UnbondFeedback = ({ const { networkData: { units, unit }, } = useNetwork(); - const { isDepositor } = useActivePools(); + const { isDepositor } = useActivePool(); const { activeAccount } = useActiveAccounts(); const { getTransferOptions } = useTransferOptions(); const { diff --git a/src/library/GenerateNominations/index.tsx b/src/library/GenerateNominations/index.tsx index fe14ed1c20..3b8dc320cb 100644 --- a/src/library/GenerateNominations/index.tsx +++ b/src/library/GenerateNominations/index.tsx @@ -85,7 +85,7 @@ export const GenerateNominations = ({ setMethod('manual'); } } - }, [activeAccount, defaultNominations]); + }, [activeAccount]); // refetch if fetching is triggered useEffect(() => { diff --git a/src/library/Graphs/PayoutBar.tsx b/src/library/Graphs/PayoutBar.tsx index a7f2a91828..ba670817e6 100644 --- a/src/library/Graphs/PayoutBar.tsx +++ b/src/library/Graphs/PayoutBar.tsx @@ -19,7 +19,6 @@ import { useTranslation } from 'react-i18next'; import { DefaultLocale } from 'consts'; import { useStaking } from 'contexts/Staking'; import { useTheme } from 'contexts/Themes'; -import { useUi } from 'contexts/UI'; import { locales } from 'locale'; import { graphColors } from 'styles/graphs'; import type { AnyJson, AnySubscan } from 'types'; @@ -28,6 +27,7 @@ import type { PayoutBarProps } from './types'; import { formatRewardsForGraphs } from './Utils'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useBalances } from 'contexts/Balances'; +import { useSyncing } from 'hooks/useSyncing'; ChartJS.register( CategoryScale, @@ -47,14 +47,14 @@ export const PayoutBar = ({ }: PayoutBarProps) => { const { i18n, t } = useTranslation('library'); const { mode } = useTheme(); - const { isSyncing } = useUi(); const { inSetup } = useStaking(); + const { syncing } = useSyncing('*'); const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const membership = getPoolMembership(activeAccount); const { unit, units, colors } = useNetwork().networkData; - const notStaking = !isSyncing && inSetup() && !membership; + const notStaking = !syncing && inSetup() && !membership; // remove slashes from payouts (graph does not support negative values). const payoutsNoSlash = payouts?.filter((p) => p.event_id !== 'Slashed') || []; diff --git a/src/library/Graphs/PayoutLine.tsx b/src/library/Graphs/PayoutLine.tsx index 3244a5a5a0..38ebc4dc0a 100644 --- a/src/library/Graphs/PayoutLine.tsx +++ b/src/library/Graphs/PayoutLine.tsx @@ -16,7 +16,6 @@ import { Line } from 'react-chartjs-2'; import { useTranslation } from 'react-i18next'; import { useStaking } from 'contexts/Staking'; import { useTheme } from 'contexts/Themes'; -import { useUi } from 'contexts/UI'; import { graphColors } from 'styles/graphs'; import type { AnyJson, AnySubscan } from 'types'; import { useNetwork } from 'contexts/Network'; @@ -28,6 +27,7 @@ import { } from './Utils'; import { useBalances } from 'contexts/Balances'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useSyncing } from 'hooks/useSyncing'; ChartJS.register( CategoryScale, @@ -48,15 +48,15 @@ export const PayoutLine = ({ }: PayoutLineProps) => { const { t } = useTranslation('library'); const { mode } = useTheme(); - const { isSyncing } = useUi(); const { inSetup } = useStaking(); + const { syncing } = useSyncing('*'); const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const { unit, units, colors } = useNetwork().networkData; const poolMembership = getPoolMembership(activeAccount); - const notStaking = !isSyncing && inSetup() && !poolMembership; - const poolingOnly = !isSyncing && inSetup() && poolMembership !== null; + const notStaking = !syncing && inSetup() && !poolMembership; + const inPoolOnly = !syncing && inSetup() && !!poolMembership; // remove slashes from payouts (graph does not support negative values). const payoutsNoSlash = payouts?.filter((p) => p.event_id !== 'Slashed') || []; @@ -90,7 +90,7 @@ export const PayoutLine = ({ // determine color for payouts const color = notStaking ? colors.primary[mode] - : !poolingOnly + : !inPoolOnly ? colors.primary[mode] : colors.secondary[mode]; diff --git a/src/library/Headers/Connected.tsx b/src/library/Headers/Connected.tsx index b6d992696d..83999f1f5e 100644 --- a/src/library/Headers/Connected.tsx +++ b/src/library/Headers/Connected.tsx @@ -2,22 +2,22 @@ // SPDX-License-Identifier: GPL-3.0-only import { useTranslation } from 'react-i18next'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import DefaultAccount from '../Account/DefaultAccount'; import PoolAccount from '../Account/PoolAccount'; import { HeadingWrapper } from './Wrappers'; +import { useSyncing } from 'hooks/useSyncing'; export const Connected = () => { const { t } = useTranslation('library'); - const { isNetworkSyncing } = useUi(); const { isNominating } = useStaking(); + const { activePool } = useActivePool(); const { poolsMetaData } = useBondedPools(); - const { selectedActivePool } = useActivePools(); + const { syncing } = useSyncing(['initialization']); const { accountHasSigner } = useImportedAccounts(); const { activeAccount, activeProxy } = useActiveAccounts(); @@ -29,22 +29,18 @@ export const Connected = () => { {/* Pool account display / hide if not in pool or if syncing. */} - {selectedActivePool !== null && !isNetworkSyncing && ( + {activePool !== null && !syncing && ( diff --git a/src/library/Headers/Sync.tsx b/src/library/Headers/Sync.tsx new file mode 100644 index 0000000000..6163612895 --- /dev/null +++ b/src/library/Headers/Sync.tsx @@ -0,0 +1,69 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { pageFromUri } from '@polkadot-cloud/utils'; +import { useLocation } from 'react-router-dom'; +import { usePlugins } from 'contexts/Plugins'; +import { useBondedPools } from 'contexts/Pools/BondedPools'; +import { usePoolMembers } from 'contexts/Pools/PoolMembers'; +import { useValidators } from 'contexts/Validators/ValidatorEntries'; +import { usePayouts } from 'contexts/Payouts'; +import { Spinner } from './Spinner'; +import { useTxMeta } from 'contexts/TxMeta'; +import { useSyncing } from 'hooks/useSyncing'; + +export const Sync = () => { + const { pathname } = useLocation(); + const { syncing } = useSyncing('*'); + const { pendingNonces } = useTxMeta(); + const { payoutsSynced } = usePayouts(); + const { pluginEnabled } = usePlugins(); + const { validators } = useValidators(); + const { bondedPools } = useBondedPools(); + const { poolMembersNode } = usePoolMembers(); + + // Keep syncing if on nominate page and still fetching payouts. + const onNominateSyncing = () => { + if ( + pageFromUri(pathname, 'overview') === 'nominate' && + payoutsSynced !== 'synced' + ) { + return true; + } + return false; + }; + + // Keep syncing if on pools page and still fetching bonded pools or pool members. Ignore pool + // member sync if Subscan is enabled. + const onPoolsSyncing = () => { + if (pageFromUri(pathname, 'overview') === 'pools') { + if ( + !bondedPools.length || + (!poolMembersNode.length && !pluginEnabled('subscan')) + ) { + return true; + } + } + return false; + }; + + // Keep syncing if on validators page and still fetching. + const onValidatorsSyncing = () => { + if ( + pageFromUri(pathname, 'overview') === 'validators' && + !validators.length + ) { + return true; + } + return false; + }; + + const isSyncing = + syncing || + onPoolsSyncing() || + onNominateSyncing() || + onValidatorsSyncing() || + pendingNonces.length > 0; + + return isSyncing ? : null; +}; diff --git a/src/library/Headers/index.tsx b/src/library/Headers/index.tsx index e984c0eeb2..22b5dc4ff4 100644 --- a/src/library/Headers/index.tsx +++ b/src/library/Headers/index.tsx @@ -1,89 +1,26 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { pageFromUri } from '@polkadot-cloud/utils'; -import { useLocation } from 'react-router-dom'; -import { usePlugins } from 'contexts/Plugins'; -import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; -import { useUi } from 'contexts/UI'; -import { useValidators } from 'contexts/Validators/ValidatorEntries'; -import { usePayouts } from 'contexts/Payouts'; import { Connect } from './Connect'; import { Connected } from './Connected'; import { SideMenuToggle } from './SideMenuToggle'; -import { Spinner } from './Spinner'; import { LargeScreensOnly, Wrapper } from './Wrappers'; -import { useTxMeta } from 'contexts/TxMeta'; +import { Sync } from './Sync'; -export const Headers = () => { - const { isSyncing } = useUi(); - const { pathname } = useLocation(); - const { pendingNonces } = useTxMeta(); - const { payoutsSynced } = usePayouts(); - const { pluginEnabled } = usePlugins(); - const { validators } = useValidators(); - const { bondedPools } = useBondedPools(); - const { poolMembersNode } = usePoolMembers(); +export const Headers = () => ( + + {/* Side menu toggle: shows on small screens. */} + - // Keep syncing if on nominate page and still fetching payouts. - const onNominateSyncing = () => { - if (pageFromUri(pathname, 'overview') === 'nominate') { - if (payoutsSynced !== 'synced') { - return true; - } - } + {/* Spinner to show app syncing. */} + - return false; - }; + {/* Connected accounts. */} + + + - // Keep syncing if on pools page and still fetching bonded pools or pool members. Ignore pool - // member sync if Subscan is enabled. - const onPoolsSyncing = () => { - if (pageFromUri(pathname, 'overview') === 'pools') { - if ( - !bondedPools.length || - (!poolMembersNode.length && !pluginEnabled('subscan')) - ) { - return true; - } - } - - return false; - }; - - // Keep syncing if on validators page and still fetching. - const onValidatorsSyncing = () => { - if (pageFromUri(pathname, 'overview') === 'validators') { - if (!validators.length) { - return true; - } - } - - return false; - }; - - const syncing = - isSyncing || - onNominateSyncing() || - onValidatorsSyncing() || - onPoolsSyncing(); - - return ( - - {/* side menu toggle: shows on small screens */} - - - {/* spinner to show app syncing */} - {syncing || pendingNonces.length > 0 ? : null} - - {/* connected accounts */} - - - - - {/* connect button */} - - - ); -}; + {/* Connect button. */} + + +); diff --git a/src/library/ListItem/Labels/EraStatus.tsx b/src/library/ListItem/Labels/EraStatus.tsx index 75ac70ad86..6711e9d6b6 100644 --- a/src/library/ListItem/Labels/EraStatus.tsx +++ b/src/library/ListItem/Labels/EraStatus.tsx @@ -4,24 +4,24 @@ import { capitalizeFirstLetter, planckToUnit } from '@polkadot-cloud/utils'; import { useTranslation } from 'react-i18next'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { ValidatorStatusWrapper } from 'library/ListItem/Wrappers'; import { useNetwork } from 'contexts/Network'; import type { EraStatusProps } from '../types'; +import { useSyncing } from 'hooks/useSyncing'; export const EraStatus = ({ noMargin, status, totalStake }: EraStatusProps) => { const { t } = useTranslation('library'); - const { isSyncing } = useUi(); + const { syncing } = useSyncing('*'); const { erasStakersSyncing } = useStaking(); const { unit, units } = useNetwork().networkData; // Fallback to `waiting` status if still syncing. - const validatorStatus = isSyncing ? 'waiting' : status; + const validatorStatus = syncing ? 'waiting' : status; return (
- {isSyncing || erasStakersSyncing + {syncing || erasStakersSyncing ? t('syncing') : validatorStatus !== 'waiting' ? `${t('listItemActive')} / ${planckToUnit(totalStake, units) diff --git a/src/library/Nominations/index.tsx b/src/library/Nominations/index.tsx index 61d381c96e..fc655e969c 100644 --- a/src/library/Nominations/index.tsx +++ b/src/library/Nominations/index.tsx @@ -6,9 +6,8 @@ import { ButtonHelp, ButtonPrimary } from '@polkadot-cloud/react'; import { useTranslation } from 'react-i18next'; import { useBonded } from 'contexts/Bonded'; import { useHelp } from 'contexts/Help'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; import { useUnstaking } from 'hooks/useUnstaking'; @@ -19,6 +18,7 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { ListStatusHeader } from 'library/List'; import { Wrapper } from './Wrapper'; +import { useSyncing } from 'hooks/useSyncing'; export const Nominations = ({ bondFor, @@ -29,18 +29,18 @@ export const Nominations = ({ }) => { const { t } = useTranslation('pages'); const { - poolNominations, - selectedActivePool, + activePool, + activePoolNominations, isOwner: isPoolOwner, isNominator: isPoolNominator, - } = useActivePools(); - const { isSyncing } = useUi(); + } = useActivePool(); const { openHelp } = useHelp(); const { inSetup } = useStaking(); const { modal: { openModal }, canvas: { openCanvas }, } = useOverlay(); + const { syncing } = useSyncing('*'); const { getNominated } = useValidators(); const { isFastUnstaking } = useUnstaking(); const { activeAccount } = useActiveAccounts(); @@ -52,7 +52,7 @@ export const Nominations = ({ // Derive nominations from `bondFor` type. const nominations = isPool - ? poolNominations.targets + ? activePoolNominations?.targets || [] : getAccountNominations(nominator); const nominated = getNominated(bondFor); @@ -61,9 +61,7 @@ export const Nominations = ({ // Determine whether this is a pool that is in Destroying state & not nominating. const poolDestroying = - isPool && - selectedActivePool?.bondedPool?.state === 'Destroying' && - !isNominating; + isPool && activePool?.bondedPool?.state === 'Destroying' && !isNominating; // Determine whether to display buttons. // @@ -76,7 +74,7 @@ export const Nominations = ({ // Determine whether buttons are disabled. const btnsDisabled = (!isPool && inSetup()) || - isSyncing || + syncing || isReadOnlyAccount(activeAccount) || poolDestroying || isFastUnstaking; @@ -130,7 +128,7 @@ export const Nominations = ({ )}
- {isSyncing ? ( + {syncing ? ( {`${t('nominate.syncing')}...`} ) : !nominator ? ( {t('nominate.notNominating')}. diff --git a/src/library/Pool/index.tsx b/src/library/Pool/index.tsx index 863d182d46..258e3b8fa7 100644 --- a/src/library/Pool/index.tsx +++ b/src/library/Pool/index.tsx @@ -9,7 +9,6 @@ import { useTranslation } from 'react-i18next'; import { useMenu } from 'contexts/Menu'; import type { NotificationText } from 'static/NotificationsController/types'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { useUi } from 'contexts/UI'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; import { usePoolCommission } from 'hooks/usePoolCommission'; import { FavoritePool } from 'library/ListItem/Labels/FavoritePool'; @@ -34,17 +33,18 @@ import { Rewards } from './Rewards'; import { NotificationsController } from 'static/NotificationsController'; import type { MenuItem } from 'contexts/Menu/types'; import { useBalances } from 'contexts/Balances'; +import { useSyncing } from 'hooks/useSyncing'; export const Pool = ({ pool }: PoolProps) => { const { t } = useTranslation('library'); const { memberCounter, addresses, id, state } = pool; - const { isPoolSyncing } = useUi(); const { validators } = useValidators(); const { setActiveTab } = usePoolsTabs(); const { openModal } = useOverlay().modal; const { getPoolMembership } = useBalances(); const { poolsNominations } = useBondedPools(); const { activeAccount } = useActiveAccounts(); + const { syncing } = useSyncing(['active-pools']); const { isReadOnlyAccount } = useImportedAccounts(); const { getCurrentCommission } = usePoolCommission(); const { setMenuPosition, setMenuItems, open } = useMenu(); @@ -114,7 +114,7 @@ export const Pool = ({ pool }: PoolProps) => { }; const displayJoin = - !isPoolSyncing && + !syncing && state === 'Open' && !membership && !isReadOnlyAccount(activeAccount) && diff --git a/src/library/Pool/types.ts b/src/library/Pool/types.ts index 2a18b9f90f..fcd89f7814 100644 --- a/src/library/Pool/types.ts +++ b/src/library/Pool/types.ts @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { PoolRoles, PoolState } from 'contexts/Pools/ActivePools/types'; +import type { PoolRoles, PoolState } from 'contexts/Pools/ActivePool/types'; import type { PoolAddresses } from 'contexts/Pools/BondedPools/types'; import type { Identity, SuperIdentity } from 'contexts/Validators/types'; import type { DisplayFor } from 'types'; diff --git a/src/library/PoolList/Default.tsx b/src/library/PoolList/Default.tsx index 861d296de2..3cf3009d55 100644 --- a/src/library/PoolList/Default.tsx +++ b/src/library/PoolList/Default.tsx @@ -13,7 +13,6 @@ import { useApi } from 'contexts/Api'; import { useFilters } from 'contexts/Filters'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { useTheme } from 'contexts/Themes'; -import { useUi } from 'contexts/UI'; import { Tabs } from 'library/Filter/Tabs'; import { usePoolFilters } from 'hooks/usePoolFilters'; import { @@ -30,6 +29,7 @@ import { useNetwork } from 'contexts/Network'; import { usePoolList } from './context'; import type { PoolListProps } from './types'; import type { BondedPool } from 'contexts/Pools/BondedPools/types'; +import { useSyncing } from 'hooks/useSyncing'; export const PoolList = ({ allowMoreCols, @@ -46,7 +46,7 @@ export const PoolList = ({ const { networkData: { colors }, } = useNetwork(); - const { isSyncing } = useUi(); + const { syncing } = useSyncing('*'); const { applyFilter } = usePoolFilters(); const { listFormat, setListFormat } = usePoolList(); const { getFilters, setMultiFilters, getSearchTerm, setSearchTerm } = @@ -158,10 +158,10 @@ export const PoolList = ({ // List ui changes / validator changes trigger re-render of list. useEffect(() => { // only filter when pool nominations have been synced. - if (!isSyncing && Object.keys(poolsNominations).length) { + if (!syncing && Object.keys(poolsNominations).length) { handlePoolsFilterUpdate(); } - }, [isSyncing, includes, excludes, Object.keys(poolsNominations).length]); + }, [syncing, includes, excludes, Object.keys(poolsNominations).length]); // Scroll to top of the window on every filter. useEffect(() => { @@ -266,7 +266,7 @@ export const PoolList = ({ ) : ( - {isSyncing ? `${t('syncingPoolList')}...` : t('noMatch')} + {syncing ? `${t('syncingPoolList')}...` : t('noMatch')} )} diff --git a/src/library/SideMenu/Main.tsx b/src/library/SideMenu/Main.tsx index 7b927557a9..6195d7f441 100644 --- a/src/library/SideMenu/Main.tsx +++ b/src/library/SideMenu/Main.tsx @@ -21,11 +21,13 @@ import { Primary } from './Primary'; import { LogoWrapper } from './Wrapper'; import type { AnyJson } from '@polkadot-cloud/react/types'; import { useBalances } from 'contexts/Balances'; +import { useSyncing } from 'hooks/useSyncing'; export const Main = () => { const { t, i18n } = useTranslation('base'); - const { networkData } = useNetwork(); const { pathname } = useLocation(); + const { syncing } = useSyncing('*'); + const { networkData } = useNetwork(); const { getBondedAccount } = useBonded(); const { accounts } = useImportedAccounts(); const { getPoolMembership } = useBalances(); @@ -37,7 +39,7 @@ export const Main = () => { getPoolSetupPercent, getNominatorSetupPercent, }: SetupContextInterface = useSetup(); - const { isSyncing, sideMenuMinimised }: UIContextInterface = useUi(); + const { sideMenuMinimised }: UIContextInterface = useUi(); const membership = getPoolMembership(activeAccount); const controller = getBondedAccount(activeAccount); @@ -61,7 +63,7 @@ export const Main = () => { // set undefined action as default pages[i].action = undefined; if (uri === `${import.meta.env.BASE_URL}`) { - const warning = !isSyncing && controllerDifferentToStash; + const warning = !syncing && controllerDifferentToStash; if (warning) { pages[i].action = { type: 'bullet', @@ -73,7 +75,7 @@ export const Main = () => { if (uri === `${import.meta.env.BASE_URL}nominate`) { // configure Stake action const staking = !inNominatorSetup(); - const warning = !isSyncing && controllerDifferentToStash; + const warning = !syncing && controllerDifferentToStash; const setupPercent = getNominatorSetupPercent(activeAccount); if (staking) { @@ -130,7 +132,7 @@ export const Main = () => { activeAccount, accounts, controllerDifferentToStash, - isSyncing, + syncing, membership, inNominatorSetup(), getNominatorSetupPercent(activeAccount), diff --git a/src/library/StatusLabel/index.tsx b/src/library/StatusLabel/index.tsx index 5013ede23d..11792a0e7d 100644 --- a/src/library/StatusLabel/index.tsx +++ b/src/library/StatusLabel/index.tsx @@ -7,11 +7,11 @@ import { ButtonHelp } from '@polkadot-cloud/react'; import { useHelp } from 'contexts/Help'; import { usePlugins } from 'contexts/Plugins'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { Wrapper } from './Wrapper'; import type { StatusLabelProps } from './types'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useBalances } from 'contexts/Balances'; +import { useSyncing } from 'hooks/useSyncing'; export const StatusLabel = ({ title, @@ -21,10 +21,10 @@ export const StatusLabel = ({ topOffset = '40%', status = 'sync_or_setup', }: StatusLabelProps) => { - const { isSyncing } = useUi(); const { openHelp } = useHelp(); const { plugins } = usePlugins(); const { inSetup } = useStaking(); + const { syncing } = useSyncing('*'); const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); @@ -32,7 +32,7 @@ export const StatusLabel = ({ // syncing or not staking if (status === 'sync_or_setup') { - if (isSyncing || !inSetup() || membership !== null) { + if (syncing || !inSetup() || membership !== null) { return null; } } diff --git a/src/library/ValidatorList/index.tsx b/src/library/ValidatorList/index.tsx index 3cb24d4ccc..7a4aedb7e4 100644 --- a/src/library/ValidatorList/index.tsx +++ b/src/library/ValidatorList/index.tsx @@ -12,7 +12,6 @@ import { ListItemsPerBatch, ListItemsPerPage } from 'consts'; import { useApi } from 'contexts/Api'; import { useFilters } from 'contexts/Filters'; import { useTheme } from 'contexts/Themes'; -import { useUi } from 'contexts/UI'; import { FilterHeaderWrapper, List, @@ -36,6 +35,7 @@ import type { ValidatorListProps } from './types'; import { FilterHeaders } from './Filters/FilterHeaders'; import { FilterBadges } from './Filters/FilterBadges'; import type { NominationStatus } from './ValidatorItem/types'; +import { useSyncing } from 'hooks/useSyncing'; export const ValidatorListInner = ({ nominator: initialNominator, @@ -74,8 +74,8 @@ export const ValidatorListInner = ({ clearSearchTerm, } = useFilters(); const { mode } = useTheme(); - const { isSyncing } = useUi(); const listProvider = useList(); + const { syncing } = useSyncing('*'); const { isReady, activeEra } = useApi(); const { activeAccount } = useActiveAccounts(); const { setModalResize } = useOverlay().modal; @@ -314,7 +314,7 @@ export const ValidatorListInner = ({ if (allowFilters && fetched) { handleValidatorsFilterUpdate(); } - }, [order, isSyncing, includes, excludes]); + }, [order, syncing, includes, excludes]); // Handle modal resize on list format change. useEffect(() => { diff --git a/src/locale/en/modals.json b/src/locale/en/modals.json index f529ff67de..8e14b244fc 100644 --- a/src/locale/en/modals.json +++ b/src/locale/en/modals.json @@ -8,7 +8,7 @@ "accounts": "Accounts", "activeRoles_one": "Active Roles in {{count}} Pool", "activeRoles_other": "Active Roles in {{count}} Pools", - "activeRoles_zero": "Active Roles In No Pool", + "activeRoles_zero": "No Other Active Pool Roles", "add": "Add", "addToBond": "Add to Bond", "addToNominations": "Add to Nominations", diff --git a/src/modals/AccountPoolRoles/Wrappers.ts b/src/modals/AccountPoolRoles/Wrappers.ts index a974d6ad65..3ac6339dae 100644 --- a/src/modals/AccountPoolRoles/Wrappers.ts +++ b/src/modals/AccountPoolRoles/Wrappers.ts @@ -31,5 +31,10 @@ export const ContentWrapper = styled.div` h2 { margin: 0.75rem 0; } + .item { + &:disabled { + opacity: 1; + } + } } `; diff --git a/src/modals/AccountPoolRoles/index.tsx b/src/modals/AccountPoolRoles/index.tsx index beb49bd4be..be07d113b3 100644 --- a/src/modals/AccountPoolRoles/index.tsx +++ b/src/modals/AccountPoolRoles/index.tsx @@ -4,28 +4,28 @@ import { faBars } from '@fortawesome/free-solid-svg-icons'; import { ButtonOption, ModalPadding, Polkicon } from '@polkadot-cloud/react'; import { useTranslation } from 'react-i18next'; -import { useActivePools } from 'contexts/Pools/ActivePools'; -import { useBondedPools } from 'contexts/Pools/BondedPools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { Title } from 'library/Modal/Title'; import { useStatusButtons } from 'pages/Pools/Home/Status/useStatusButtons'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { ContentWrapper } from './Wrappers'; import { useBalances } from 'contexts/Balances'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import type { ActivePool } from 'contexts/Pools/ActivePool/types'; export const AccountPoolRoles = () => { const { t } = useTranslation('modals'); const { getPoolMembership } = useBalances(); - const { getAccountPools } = useBondedPools(); const { options } = useOverlay().modal.config; const { activeAccount } = useActiveAccounts(); - const { who } = options; - const accountPools = getAccountPools(who); + const { who, activePools } = options; const membership = getPoolMembership(activeAccount); - const totalAccountPools = Object.entries(accountPools).length; const { label } = useStatusButtons(); + // Delete membership from activePools if it exists + delete activePools[membership?.poolId || -1]; + return ( <> @@ -35,19 +35,20 @@ export const AccountPoolRoles = () => { <> <h4>{label}</h4> <div className="items"> - <Button item={['member']} poolId={String(membership.poolId)} /> + <Button who={who} poolId={String(membership.poolId)} /> </div> </> )} <h4> {t('activeRoles', { - count: totalAccountPools, + count: activePools?.length || 0, })} </h4> <div className="items"> - {Object.entries(accountPools).map(([key, item], i: number) => ( + {Object.entries(activePools).map(([key, item], i: number) => ( <Button - item={item as string[]} + who={who} + activePool={item as ActivePool} poolId={key} key={`all_roles_root_${i}`} /> @@ -59,22 +60,31 @@ export const AccountPoolRoles = () => { ); }; -const Button = ({ item, poolId }: { item: string[]; poolId: string }) => { +const Button = ({ + who, + activePool, + poolId, +}: { + who: string; + activePool?: ActivePool; + poolId: string; +}) => { const { t } = useTranslation('modals'); + const { setActivePoolId } = useActivePool(); const { setModalStatus } = useOverlay().modal; - const { bondedPools } = useBondedPools(); - const { setSelectedPoolId } = useActivePools(); - const pool = bondedPools.find((b) => String(b.id) === poolId); - const stash = pool?.addresses?.stash || ''; + + const { roles } = activePool?.bondedPool || {}; + const stash = activePool?.addresses?.stash || ''; return ( <ButtonOption content - disabled={false} + disabled onClick={() => { - setSelectedPoolId(poolId); + setActivePoolId(poolId); setModalStatus('closing'); }} + className="item" > <div className="icon"> <Polkicon address={stash} size={30} /> @@ -85,9 +95,9 @@ const Button = ({ item, poolId }: { item: string[]; poolId: string }) => { {t('pool')} {poolId} </h3> <h4> - {item.includes('root') ? <span>{t('root')}</span> : null} - {item.includes('nominator') ? <span>{t('nominator')}</span> : null} - {item.includes('bouncer') ? <span>{t('bouncer')}</span> : null} + {roles?.root === who ? <span>{t('root')}</span> : null} + {roles?.nominator === who ? <span>{t('nominator')}</span> : null} + {roles?.bouncer === who ? <span>{t('bouncer')}</span> : null} </h4> </div> </ButtonOption> diff --git a/src/modals/Bond/index.tsx b/src/modals/Bond/index.tsx index 662e9f13fd..98b13903a0 100644 --- a/src/modals/Bond/index.tsx +++ b/src/modals/Bond/index.tsx @@ -7,7 +7,7 @@ import BigNumber from 'bignumber.js'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; import { BondFeedback } from 'library/Form/Bond/BondFeedback'; import { Warning } from 'library/Form/Warning'; @@ -27,9 +27,9 @@ export const Bond = () => { const { networkData: { units, unit }, } = useNetwork(); - const { activeAccount } = useActiveAccounts(); const { notEnoughFunds } = useTxMeta(); - const { selectedActivePool } = useActivePools(); + const { activeAccount } = useActiveAccounts(); + const { pendingPoolRewards } = useActivePool(); const { getSignerWarnings } = useSignerWarnings(); const { feeReserve, getTransferOptions } = useTransferOptions(); const { @@ -53,10 +53,8 @@ export const Bond = () => { const largestTxFee = useBondGreatestFee({ bondFor }); - // calculate any unclaimed pool rewards. - let { pendingRewards } = selectedActivePool || {}; - pendingRewards = pendingRewards ?? new BigNumber(0); - pendingRewards = planckToUnit(pendingRewards, units); + // Format unclaimed pool rewards. + const pendingRewardsUnit = planckToUnit(pendingPoolRewards, units); // local bond value. const [bond, setBond] = useState<{ bond: string }>({ @@ -153,10 +151,10 @@ export const Bond = () => { <Close /> <ModalPadding> <h2 className="title unbounded">{t('addToBond')}</h2> - {pendingRewards.isGreaterThan(0) && bondFor === 'pool' ? ( + {pendingRewardsUnit.isGreaterThan(0) && bondFor === 'pool' ? ( <ModalWarnings withMargin> <Warning - text={`${t('bondingWithdraw')} ${pendingRewards} ${unit}.`} + text={`${t('bondingWithdraw')} ${pendingRewardsUnit} ${unit}.`} /> </ModalWarnings> ) : null} diff --git a/src/modals/ClaimReward/index.tsx b/src/modals/ClaimReward/index.tsx index 9402eb43ae..22b114fb3b 100644 --- a/src/modals/ClaimReward/index.tsx +++ b/src/modals/ClaimReward/index.tsx @@ -3,11 +3,10 @@ import { ActionItem, ModalPadding, ModalWarnings } from '@polkadot-cloud/react'; import { greaterThanZero, planckToUnit } from '@polkadot-cloud/utils'; -import BigNumber from 'bignumber.js'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; @@ -24,28 +23,26 @@ export const ClaimReward = () => { const { networkData: { units, unit }, } = useNetwork(); - const { activeAccount } = useActiveAccounts(); const { notEnoughFunds } = useTxMeta(); - const { selectedActivePool } = useActivePools(); + const { activeAccount } = useActiveAccounts(); const { getSignerWarnings } = useSignerWarnings(); + const { activePool, pendingPoolRewards } = useActivePool(); const { setModalStatus, config: { options }, setModalResize, } = useOverlay().modal; - let { pendingRewards } = selectedActivePool || {}; - pendingRewards = pendingRewards ?? new BigNumber(0); const { claimType } = options; // ensure selected payout is valid useEffect(() => { - if (pendingRewards?.isGreaterThan(0)) { + if (pendingPoolRewards?.isGreaterThan(0)) { setValid(true); } else { setValid(false); } - }, [selectedActivePool]); + }, [activePool]); // valid to submit transaction const [valid, setValid] = useState<boolean>(false); @@ -80,7 +77,7 @@ export const ClaimReward = () => { submitExtrinsic.proxySupported ); - if (!greaterThanZero(pendingRewards)) { + if (!greaterThanZero(pendingPoolRewards)) { warnings.push(`${t('noRewards')}`); } @@ -102,7 +99,7 @@ export const ClaimReward = () => { ) : null} <ActionItem text={`${t('claim')} ${`${planckToUnit( - pendingRewards, + pendingPoolRewards, units )} ${unit}`}`} /> diff --git a/src/modals/ManagePool/Forms/ClaimCommission/index.tsx b/src/modals/ManagePool/Forms/ClaimCommission/index.tsx index f609d7e884..1f6084e3c1 100644 --- a/src/modals/ManagePool/Forms/ClaimCommission/index.tsx +++ b/src/modals/ManagePool/Forms/ClaimCommission/index.tsx @@ -15,7 +15,7 @@ import type { Dispatch, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; @@ -36,11 +36,12 @@ export const ClaimCommission = ({ } = useNetwork(); const { setModalStatus } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); - const { isOwner, selectedActivePool } = useActivePools(); + const { isOwner, activePool } = useActivePool(); const { getSignerWarnings } = useSignerWarnings(); - const poolId = selectedActivePool?.id; + + const poolId = activePool?.id; const pendingCommission = new BigNumber( - rmCommas(selectedActivePool?.rewardPool?.totalCommissionPending || '0') + rmCommas(activePool?.rewardPool?.totalCommissionPending || '0') ); // valid to submit transaction @@ -48,7 +49,7 @@ export const ClaimCommission = ({ useEffect(() => { setValid(isOwner() && greaterThanZero(pendingCommission)); - }, [selectedActivePool, pendingCommission]); + }, [activePool, pendingCommission]); // tx to submit const getTx = () => { diff --git a/src/modals/ManagePool/Forms/LeavePool/index.tsx b/src/modals/ManagePool/Forms/LeavePool/index.tsx index 5d83a4505e..6aebcaefa1 100644 --- a/src/modals/ManagePool/Forms/LeavePool/index.tsx +++ b/src/modals/ManagePool/Forms/LeavePool/index.tsx @@ -13,13 +13,12 @@ import { planckToUnit, unitToPlanck, } from '@polkadot-cloud/utils'; -import BigNumber from 'bignumber.js'; import { getUnixTime } from 'date-fns'; import type { Dispatch, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; import { Warning } from 'library/Form/Warning'; import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; @@ -42,12 +41,12 @@ export const LeavePool = ({ const { networkData: { units, unit }, } = useNetwork(); - const { activeAccount } = useActiveAccounts(); - const { setModalStatus, setModalResize } = useOverlay().modal; - const { getTransferOptions } = useTransferOptions(); - const { selectedActivePool } = useActivePools(); const { erasToSeconds } = useErasToTimeLeft(); + const { activeAccount } = useActiveAccounts(); + const { pendingPoolRewards } = useActivePool(); const { getSignerWarnings } = useSignerWarnings(); + const { getTransferOptions } = useTransferOptions(); + const { setModalStatus, setModalResize } = useOverlay().modal; const allTransferOptions = getTransferOptions(activeAccount); const { active: activeBn } = allTransferOptions.pool; @@ -60,9 +59,7 @@ export const LeavePool = ({ true ); - let { pendingRewards } = selectedActivePool || {}; - pendingRewards = pendingRewards ?? new BigNumber(0); - pendingRewards = planckToUnit(pendingRewards, units); + const pendingRewardsUnit = planckToUnit(pendingPoolRewards, units); // convert BigNumber values to number const freeToUnbond = planckToUnit(activeBn, units); @@ -115,9 +112,9 @@ export const LeavePool = ({ submitExtrinsic.proxySupported ); - if (greaterThanZero(pendingRewards)) { + if (greaterThanZero(pendingRewardsUnit)) { warnings.push( - `${t('unbondingWithdraw')} ${pendingRewards.toString()} ${unit}.` + `${t('unbondingWithdraw')} ${pendingRewardsUnit.toString()} ${unit}.` ); } diff --git a/src/modals/ManagePool/Forms/ManageCommission/index.tsx b/src/modals/ManagePool/Forms/ManageCommission/index.tsx index a9d5a8b154..90f359ee7a 100644 --- a/src/modals/ManagePool/Forms/ManageCommission/index.tsx +++ b/src/modals/ManagePool/Forms/ManageCommission/index.tsx @@ -15,7 +15,7 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useHelp } from 'contexts/Help'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { Warning } from 'library/Form/Warning'; import { useBatchCall } from 'hooks/useBatchCall'; @@ -46,8 +46,8 @@ export const ManageCommission = ({ const { newBatchCall } = useBatchCall(); const { activeAccount } = useActiveAccounts(); const { setModalStatus } = useOverlay().modal; + const { isOwner, activePool } = useActivePool(); const { getSignerWarnings } = useSignerWarnings(); - const { isOwner, selectedActivePool } = useActivePools(); const { getBondedPool, updateBondedPools } = useBondedPools(); const { getInitial, @@ -59,7 +59,7 @@ export const ManageCommission = ({ isUpdated, } = usePoolCommission(); - const poolId = selectedActivePool?.id || 0; + const poolId = activePool?.id || 0; const bondedPool = getBondedPool(poolId); // Get currently set commission values. diff --git a/src/modals/ManagePool/Forms/ManageCommission/provider/index.tsx b/src/modals/ManagePool/Forms/ManageCommission/provider/index.tsx index 6be6670abb..7da0ede7e5 100644 --- a/src/modals/ManagePool/Forms/ManageCommission/provider/index.tsx +++ b/src/modals/ManagePool/Forms/ManageCommission/provider/index.tsx @@ -3,7 +3,7 @@ import { createContext, useContext, useEffect, useState } from 'react'; import type { MaybeAddress } from 'types'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { rmCommas } from '@polkadot-cloud/utils'; import type { @@ -23,9 +23,9 @@ export const usePoolCommission = () => useContext(PoolCommissionContext); export const PoolCommissionProvider = ({ children, }: PoolCommissionProviderProps) => { + const { activePool } = useActivePool(); const { getBondedPool } = useBondedPools(); - const { selectedActivePool } = useActivePools(); - const poolId = selectedActivePool?.id || 0; + const poolId = activePool?.id || 0; const bondedPool = getBondedPool(poolId); // Get initial commission value from the bonded pool commission config. diff --git a/src/modals/ManagePool/Forms/RenamePool/index.tsx b/src/modals/ManagePool/Forms/RenamePool/index.tsx index a856d249be..c1c31a35da 100644 --- a/src/modals/ManagePool/Forms/RenamePool/index.tsx +++ b/src/modals/ManagePool/Forms/RenamePool/index.tsx @@ -12,7 +12,7 @@ import type { Dispatch, FormEvent, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; @@ -32,11 +32,11 @@ export const RenamePool = ({ const { api } = useApi(); const { setModalStatus } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); - const { isOwner, selectedActivePool } = useActivePools(); - const { bondedPools, poolsMetaData } = useBondedPools(); + const { isOwner, activePool } = useActivePool(); const { getSignerWarnings } = useSignerWarnings(); + const { bondedPools, poolsMetaData } = useBondedPools(); - const poolId = selectedActivePool?.id; + const poolId = activePool?.id; // Valid to submit transaction const [valid, setValid] = useState<boolean>(false); @@ -47,7 +47,7 @@ export const RenamePool = ({ // Determine current pool metadata and set in state. useEffect(() => { const pool = bondedPools.find( - ({ addresses }) => addresses.stash === selectedActivePool?.addresses.stash + ({ addresses }) => addresses.stash === activePool?.addresses.stash ); if (pool) { setMetadata(u8aToString(u8aUnwrapBytes(poolsMetaData[Number(pool.id)]))); diff --git a/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx b/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx index f390b3c703..5aa29142da 100644 --- a/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx +++ b/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx @@ -11,7 +11,7 @@ import type { Dispatch, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { ClaimPermissionInput } from 'library/Form/ClaimPermissionInput'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; @@ -34,7 +34,7 @@ export const SetClaimPermission = ({ const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const { setModalStatus } = useOverlay().modal; - const { isOwner, isMember } = useActivePools(); + const { isOwner, isMember } = useActivePool(); const { getSignerWarnings } = useSignerWarnings(); const membership = getPoolMembership(activeAccount); diff --git a/src/modals/ManagePool/Forms/SetPoolState/index.tsx b/src/modals/ManagePool/Forms/SetPoolState/index.tsx index 06f3f65a2a..888ce2ee2c 100644 --- a/src/modals/ManagePool/Forms/SetPoolState/index.tsx +++ b/src/modals/ManagePool/Forms/SetPoolState/index.tsx @@ -12,7 +12,7 @@ import type { Dispatch, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; @@ -32,11 +32,11 @@ export const SetPoolState = ({ const { api } = useApi(); const { setModalStatus } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); - const { isOwner, isBouncer, selectedActivePool } = useActivePools(); - const { updateBondedPools, getBondedPool } = useBondedPools(); const { getSignerWarnings } = useSignerWarnings(); + const { isOwner, isBouncer, activePool } = useActivePool(); + const { updateBondedPools, getBondedPool } = useBondedPools(); - const poolId = selectedActivePool?.id; + const poolId = activePool?.id; // valid to submit transaction const [valid, setValid] = useState<boolean>(false); diff --git a/src/modals/ManagePool/Tasks.tsx b/src/modals/ManagePool/Tasks.tsx index 5072a8d84f..36bf848a8a 100644 --- a/src/modals/ManagePool/Tasks.tsx +++ b/src/modals/ManagePool/Tasks.tsx @@ -5,7 +5,7 @@ import { ButtonOption } from '@polkadot-cloud/react'; import type { ForwardedRef } from 'react'; import { forwardRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; import { Warning } from 'library/Form/Warning'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -19,14 +19,13 @@ export const Tasks = forwardRef( const { activeAccount } = useActiveAccounts(); const { getTransferOptions } = useTransferOptions(); const { globalMaxCommission } = useApi().poolsConfig; - const { selectedActivePool, isOwner, isBouncer, isMember, isDepositor } = - useActivePools(); + const { activePool, isOwner, isBouncer, isMember, isDepositor } = + useActivePool(); const { active } = getTransferOptions(activeAccount).pool; - const poolLocked = selectedActivePool?.bondedPool?.state === 'Blocked'; - const poolDestroying = - selectedActivePool?.bondedPool?.state === 'Destroying'; + const poolLocked = activePool?.bondedPool?.state === 'Blocked'; + const poolDestroying = activePool?.bondedPool?.state === 'Destroying'; return ( <ContentWrapper> diff --git a/src/modals/ManagePool/index.tsx b/src/modals/ManagePool/index.tsx index 4964bbc4ea..b36d9a1742 100644 --- a/src/modals/ManagePool/index.tsx +++ b/src/modals/ManagePool/index.tsx @@ -9,7 +9,7 @@ import { } from '@polkadot-cloud/react'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { Title } from 'library/Modal/Title'; import { useTxMeta } from 'contexts/TxMeta'; import { useOverlay } from '@polkadot-cloud/react/hooks'; @@ -21,7 +21,7 @@ export const ManagePool = () => { const { t } = useTranslation('modals'); const { notEnoughFunds } = useTxMeta(); const { integrityChecked } = useLedgerHardware(); - const { isOwner, selectedActivePool } = useActivePools(); + const { isOwner, activePool } = useActivePool(); const { setModalHeight, modalMaxHeight } = useOverlay().modal; // modal task @@ -59,7 +59,7 @@ export const ManagePool = () => { task, notEnoughFunds, calculateHeight, - selectedActivePool?.bondedPool?.state, + activePool?.bondedPool?.state, ]); useEffect(() => { diff --git a/src/modals/StopNominations/index.tsx b/src/modals/StopNominations/index.tsx index 1574c822eb..4596ed1fb1 100644 --- a/src/modals/StopNominations/index.tsx +++ b/src/modals/StopNominations/index.tsx @@ -10,7 +10,7 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; @@ -32,10 +32,10 @@ export const StopNominations = () => { config: { options }, setModalResize, } = useOverlay().modal; - const { poolNominations, isNominator, isOwner, selectedActivePool } = - useActivePools(); - const { bondFor } = options; + const { activePoolNominations, isNominator, isOwner, activePool } = + useActivePool(); + const { bondFor } = options; const isPool = bondFor === 'pool'; const isStaking = bondFor === 'nominator'; const controller = getBondedAccount(activeAccount); @@ -43,7 +43,7 @@ export const StopNominations = () => { const nominations = isPool === true - ? poolNominations?.targets || [] + ? activePoolNominations?.targets || [] : getAccountNominations(activeAccount); // valid to submit transaction @@ -69,7 +69,7 @@ export const StopNominations = () => { if (isPool) { // wishing to stop all nominations, call chill - tx = api.tx.nominationPools.chill(selectedActivePool?.id || 0); + tx = api.tx.nominationPools.chill(activePool?.id || 0); } else if (isStaking) { tx = api.tx.staking.chill(); } diff --git a/src/modals/Unbond/index.tsx b/src/modals/Unbond/index.tsx index ab8054391a..0bb7d2ec29 100644 --- a/src/modals/Unbond/index.tsx +++ b/src/modals/Unbond/index.tsx @@ -9,7 +9,7 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; import { useTxMeta } from 'contexts/TxMeta'; import { UnbondFeedback } from 'library/Form/Unbond/UnbondFeedback'; @@ -37,7 +37,7 @@ export const Unbond = () => { const { erasToSeconds } = useErasToTimeLeft(); const { getSignerWarnings } = useSignerWarnings(); const { getTransferOptions } = useTransferOptions(); - const { isDepositor, selectedActivePool } = useActivePools(); + const { isDepositor, pendingPoolRewards } = useActivePool(); const { minNominatorBond: minNominatorBondBn } = useApi().stakingMetrics; const { setModalStatus, @@ -61,9 +61,7 @@ export const Unbond = () => { true ); - let { pendingRewards } = selectedActivePool || {}; - pendingRewards = pendingRewards ?? new BigNumber(0); - pendingRewards = planckToUnit(pendingRewards, units); + const pendingRewardsUnit = planckToUnit(pendingPoolRewards, units); const isStaking = bondFor === 'nominator'; const isPooling = bondFor === 'pool'; @@ -148,8 +146,8 @@ export const Unbond = () => { submitExtrinsic.proxySupported ); - if (pendingRewards.isGreaterThan(0) && bondFor === 'pool') { - warnings.push(`${t('unbondingWithdraw')} ${pendingRewards} ${unit}.`); + if (pendingRewardsUnit.isGreaterThan(0) && bondFor === 'pool') { + warnings.push(`${t('unbondingWithdraw')} ${pendingRewardsUnit} ${unit}.`); } if (nominatorActiveBelowMin) { warnings.push( diff --git a/src/modals/UnlockChunks/Forms.tsx b/src/modals/UnlockChunks/Forms.tsx index 3accd990bc..3a849e3769 100644 --- a/src/modals/UnlockChunks/Forms.tsx +++ b/src/modals/UnlockChunks/Forms.tsx @@ -15,7 +15,7 @@ import { forwardRef, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useFavoritePools } from 'contexts/Pools/FavoritePools'; @@ -40,9 +40,9 @@ export const Forms = forwardRef( const { networkData: { units, unit }, } = useNetwork(); + const { activePool } = useActivePool(); const { activeAccount } = useActiveAccounts(); const { removePoolMember } = usePoolMembers(); - const { selectedActivePool } = useActivePools(); const { removeFromBondedPools } = useBondedPools(); const { setModalStatus, @@ -77,7 +77,7 @@ export const Forms = forwardRef( tx = api.tx.staking.rebond(unlock.value.toNumber() || 0); } else if (task === 'withdraw' && isStaking) { tx = api.tx.staking.withdrawUnbonded(historyDepth.toString()); - } else if (task === 'withdraw' && isPooling && selectedActivePool) { + } else if (task === 'withdraw' && isPooling && activePool) { tx = api.tx.nominationPools.withdrawUnbonded( activeAccount, historyDepth.toString() @@ -96,8 +96,8 @@ export const Forms = forwardRef( callbackInBlock: () => { // if pool is being closed, remove from static lists if (poolClosure) { - removeFavoritePool(selectedActivePool?.addresses?.stash ?? ''); - removeFromBondedPools(selectedActivePool?.id ?? 0); + removeFavoritePool(activePool?.addresses?.stash ?? ''); + removeFromBondedPools(activePool?.id ?? 0); } // if no more bonded funds from pool, remove from poolMembers list diff --git a/src/modals/UnlockChunks/index.tsx b/src/modals/UnlockChunks/index.tsx index 352d3f432c..ec64be23a9 100644 --- a/src/modals/UnlockChunks/index.tsx +++ b/src/modals/UnlockChunks/index.tsx @@ -10,7 +10,7 @@ import { setStateWithRef } from '@polkadot-cloud/utils'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useBalances } from 'contexts/Balances'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { Title } from 'library/Modal/Title'; import { useTxMeta } from 'contexts/TxMeta'; import { useOverlay } from '@polkadot-cloud/react/hooks'; @@ -29,8 +29,8 @@ export const UnlockChunks = () => { } = useOverlay().modal; const { getLedger } = useBalances(); const { notEnoughFunds } = useTxMeta(); + const { getPoolUnlocking } = useActivePool(); const { activeAccount } = useActiveAccounts(); - const { getPoolUnlocking } = useActivePools(); const { integrityChecked } = useLedgerHardware(); const { bondFor } = options || {}; diff --git a/src/pages/Nominate/Active/ControllerNotStash.tsx b/src/pages/Nominate/Active/ControllerNotStash.tsx index 33b1da6d21..43a443992a 100644 --- a/src/pages/Nominate/Active/ControllerNotStash.tsx +++ b/src/pages/Nominate/Active/ControllerNotStash.tsx @@ -12,23 +12,23 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useBonded } from 'contexts/Bonded'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; +import { useSyncing } from 'hooks/useSyncing'; export const ControllerNotStash = () => { const { t } = useTranslation('pages'); const { network } = useNetwork(); - const { activeAccount } = useActiveAccounts(); - const { addressDifferentToStash } = useStaking(); const { getBondedAccount } = useBonded(); const { openModal } = useOverlay().modal; - const { isSyncing } = useUi(); + const { activeAccount } = useActiveAccounts(); + const { addressDifferentToStash } = useStaking(); const { isReadOnlyAccount } = useImportedAccounts(); const controller = getBondedAccount(activeAccount); + const { syncing } = useSyncing(['initialization', 'balances']); const [showPrompt, setShowPrompt] = useState<boolean>( addressDifferentToStash(controller) @@ -39,7 +39,7 @@ export const ControllerNotStash = () => { }, [controller]); return showPrompt - ? !isSyncing && !isReadOnlyAccount(activeAccount) && ( + ? !syncing && !isReadOnlyAccount(activeAccount) && ( <PageRow> <CardWrapper className="warning"> <CardHeaderWrapper> diff --git a/src/pages/Nominate/Active/ManageBond.tsx b/src/pages/Nominate/Active/ManageBond.tsx index 616d055f9b..b8fe418415 100644 --- a/src/pages/Nominate/Active/ManageBond.tsx +++ b/src/pages/Nominate/Active/ManageBond.tsx @@ -15,7 +15,6 @@ import { useBalances } from 'contexts/Balances'; import { useHelp } from 'contexts/Help'; import { useStaking } from 'contexts/Staking'; import { useTransferOptions } from 'contexts/TransferOptions'; -import { useUi } from 'contexts/UI'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; import { useUnstaking } from 'hooks/useUnstaking'; import { useOverlay } from '@polkadot-cloud/react/hooks'; @@ -23,6 +22,7 @@ import { BondedChart } from 'library/BarChart/BondedChart'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; +import { useSyncing } from 'hooks/useSyncing'; export const ManageBond = () => { const { t } = useTranslation('pages'); @@ -32,12 +32,12 @@ export const ManageBond = () => { brand: { token: Token }, }, } = useNetwork(); - const { isSyncing } = useUi(); const { openHelp } = useHelp(); const { inSetup } = useStaking(); + const { syncing } = useSyncing('*'); + const { getLedger } = useBalances(); const { openModal } = useOverlay().modal; const { isFastUnstaking } = useUnstaking(); - const { getLedger } = useBalances(); const { isReadOnlyAccount } = useImportedAccounts(); const { getTransferOptions, feeReserve } = useTransferOptions(); const { activeAccount } = useActiveAccounts(); @@ -71,7 +71,7 @@ export const ManageBond = () => { <ButtonPrimary disabled={ inSetup() || - isSyncing || + syncing || isReadOnlyAccount(activeAccount) || isFastUnstaking } @@ -88,7 +88,7 @@ export const ManageBond = () => { <ButtonPrimary disabled={ inSetup() || - isSyncing || + syncing || isReadOnlyAccount(activeAccount) || isFastUnstaking } @@ -103,9 +103,7 @@ export const ManageBond = () => { text="-" /> <ButtonPrimary - disabled={ - isSyncing || inSetup() || isReadOnlyAccount(activeAccount) - } + disabled={syncing || inSetup() || isReadOnlyAccount(activeAccount)} iconLeft={faLockOpen} marginRight onClick={() => diff --git a/src/pages/Nominate/Active/Status/NominationStatus.tsx b/src/pages/Nominate/Active/Status/NominationStatus.tsx index c1ec31d9d4..df4e9ed2fa 100644 --- a/src/pages/Nominate/Active/Status/NominationStatus.tsx +++ b/src/pages/Nominate/Active/Status/NominationStatus.tsx @@ -12,13 +12,13 @@ import { useBonded } from 'contexts/Bonded'; import { useFastUnstake } from 'contexts/FastUnstake'; import { useSetup } from 'contexts/Setup'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { useNominationStatus } from 'hooks/useNominationStatus'; import { useUnstaking } from 'hooks/useUnstaking'; import { Stat } from 'library/Stat'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; +import { useSyncing } from 'hooks/useSyncing'; export const NominationStatus = ({ showButtons = true, @@ -29,9 +29,9 @@ export const NominationStatus = ({ }) => { const { t } = useTranslation('pages'); const { inSetup } = useStaking(); - const { isNetworkSyncing } = useUi(); const { openModal } = useOverlay().modal; const { getBondedAccount } = useBonded(); + const { syncing } = useSyncing(['initialization']); const { isReady, networkMetrics: { fastUnstakeErasToCheckPerBlock }, @@ -88,7 +88,7 @@ export const NominationStatus = ({ ? !isUnstaking ? [unstakeButton] : [] - : isNetworkSyncing + : syncing ? [] : [ { diff --git a/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx b/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx index c55b4e4343..6ac3f467c5 100644 --- a/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx +++ b/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx @@ -4,7 +4,6 @@ import { faGear, faWallet } from '@fortawesome/free-solid-svg-icons'; import { useTranslation } from 'react-i18next'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { usePayeeConfig } from 'hooks/usePayeeConfig'; import { useUnstaking } from 'hooks/useUnstaking'; import { Stat } from 'library/Stat'; @@ -12,12 +11,13 @@ import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useBalances } from 'contexts/Balances'; +import { useSyncing } from 'hooks/useSyncing'; export const PayoutDestinationStatus = () => { const { t } = useTranslation('pages'); - const { isSyncing } = useUi(); const { getPayee } = useBalances(); const { inSetup } = useStaking(); + const { syncing } = useSyncing('*'); const { openModal } = useOverlay().modal; const { isFastUnstaking } = useUnstaking(); const { getPayeeItems } = usePayeeConfig(); @@ -61,8 +61,8 @@ export const PayoutDestinationStatus = () => { icon: faGear, small: true, disabled: + syncing || inSetup() || - isSyncing || isReadOnlyAccount(activeAccount) || isFastUnstaking, onClick: () => openModal({ key: 'UpdatePayee', size: 'sm' }), diff --git a/src/pages/Nominate/Active/UnstakePrompts.tsx b/src/pages/Nominate/Active/UnstakePrompts.tsx index abcfc26ae2..794c8ea0ad 100644 --- a/src/pages/Nominate/Active/UnstakePrompts.tsx +++ b/src/pages/Nominate/Active/UnstakePrompts.tsx @@ -7,21 +7,22 @@ import { isNotZero } from '@polkadot-cloud/utils'; import { useTranslation } from 'react-i18next'; import { useTheme } from 'contexts/Themes'; import { useTransferOptions } from 'contexts/TransferOptions'; -import { useUi } from 'contexts/UI'; import { CardWrapper } from 'library/Card/Wrappers'; import { useUnstaking } from 'hooks/useUnstaking'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useSyncing } from 'hooks/useSyncing'; export const UnstakePrompts = () => { const { t } = useTranslation('pages'); - const { unit, colors } = useNetwork().networkData; - const { activeAccount } = useActiveAccounts(); const { mode } = useTheme(); + const { syncing } = useSyncing('*'); const { openModal } = useOverlay().modal; - const { isNetworkSyncing } = useUi(); + const { activeAccount } = useActiveAccounts(); + const { unit, colors } = useNetwork().networkData; const { isFastUnstaking, isUnstaking, getFastUnstakeText } = useUnstaking(); + const { getTransferOptions } = useTransferOptions(); const { active, totalUnlockChunks, totalUnlocked, totalUnlocking } = getTransferOptions(activeAccount).nominate; @@ -36,7 +37,7 @@ export const UnstakePrompts = () => { return ( (isUnstaking || isFastUnstaking) && - !isNetworkSyncing && ( + !syncing && ( <PageRow> <CardWrapper style={{ border: `1px solid ${annuncementBorderColor}` }}> <div className="content"> diff --git a/src/pages/Nominate/Active/index.tsx b/src/pages/Nominate/Active/index.tsx index 13e6d234c4..bb3fefbbd6 100644 --- a/src/pages/Nominate/Active/index.tsx +++ b/src/pages/Nominate/Active/index.tsx @@ -12,7 +12,6 @@ import { import { useTranslation } from 'react-i18next'; import { useHelp } from 'contexts/Help'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; import { useUnstaking } from 'hooks/useUnstaking'; import { StatBoxList } from 'library/StatBoxList'; @@ -28,12 +27,13 @@ import { MinimumActiveStakeStat } from './Stats/MinimumActiveStake'; import { MinimumNominatorBondStat } from './Stats/MinimumNominatorBond'; import { Status } from './Status'; import { UnstakePrompts } from './UnstakePrompts'; +import { useSyncing } from 'hooks/useSyncing'; export const Active = () => { const { t } = useTranslation(); - const { isSyncing } = useUi(); const { openHelp } = useHelp(); const { inSetup } = useStaking(); + const { syncing } = useSyncing('*'); const { nominated } = useValidators(); const { isFastUnstaking } = useUnstaking(); const { openCanvas } = useOverlay().canvas; @@ -63,7 +63,7 @@ export const Active = () => { </PageRow> <PageRow> <CardWrapper> - {nominated?.length || inSetup() || isSyncing ? ( + {nominated?.length || inSetup() || syncing ? ( <Nominations bondFor="nominator" nominator={activeAccount} /> ) : ( <> @@ -80,7 +80,7 @@ export const Active = () => { iconLeft={faChevronCircleRight} iconTransform="grow-1" text={t('nominate.nominate', { ns: 'pages' })} - disabled={inSetup() || isSyncing || isFastUnstaking} + disabled={inSetup() || syncing || isFastUnstaking} onClick={() => openCanvas({ key: 'ManageNominations', diff --git a/src/pages/Overview/BalanceChart.tsx b/src/pages/Overview/BalanceChart.tsx index b6390e61e7..e58a219fd3 100644 --- a/src/pages/Overview/BalanceChart.tsx +++ b/src/pages/Overview/BalanceChart.tsx @@ -13,7 +13,6 @@ import { useTranslation } from 'react-i18next'; import { useBalances } from 'contexts/Balances'; import { usePlugins } from 'contexts/Plugins'; import { useTransferOptions } from 'contexts/TransferOptions'; -import { useUi } from 'contexts/UI'; import { BarSegment } from 'library/BarChart/BarSegment'; import { LegendItem } from 'library/BarChart/LegendItem'; import { Bar, BarChartWrapper, Legend } from 'library/BarChart/Wrappers'; @@ -23,6 +22,7 @@ import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; +import { useSyncing } from 'hooks/useSyncing'; export const BalanceChart = () => { const { t } = useTranslation('pages'); @@ -35,11 +35,11 @@ export const BalanceChart = () => { } = useNetwork(); const prices = usePrices(); const { plugins } = usePlugins(); - const { isNetworkSyncing } = useUi(); const { openModal } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); - const { accountHasSigner } = useImportedAccounts(); const { getBalance, getLocks } = useBalances(); + const { syncing } = useSyncing(['initialization']); + const { accountHasSigner } = useImportedAccounts(); const { feeReserve, getTransferOptions } = useTransferOptions(); const balance = getBalance(activeAccount); @@ -266,7 +266,7 @@ export const BalanceChart = () => { openModal({ key: 'UpdateReserve', size: 'sm' }) } iconRight={ - isNetworkSyncing + syncing ? undefined : !feeReserve.isZero() && !edReserved.isZero() ? faCheckDouble @@ -277,7 +277,7 @@ export const BalanceChart = () => { iconTransform="shrink-1" disabled={ !activeAccount || - isNetworkSyncing || + syncing || !accountHasSigner(activeAccount) } /> diff --git a/src/pages/Overview/Payouts.tsx b/src/pages/Overview/Payouts.tsx index 5edbf0e000..e8ee5f84ae 100644 --- a/src/pages/Overview/Payouts.tsx +++ b/src/pages/Overview/Payouts.tsx @@ -5,7 +5,6 @@ import { useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { usePlugins } from 'contexts/Plugins'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { PayoutBar } from 'library/Graphs/PayoutBar'; import { PayoutLine } from 'library/Graphs/PayoutLine'; import { formatRewardsForGraphs, formatSize } from 'library/Graphs/Utils'; @@ -21,24 +20,25 @@ import { formatDistance, fromUnixTime, getUnixTime } from 'date-fns'; import { minDecimalPlaces, planckToUnit } from '@polkadot-cloud/utils'; import { useNetwork } from 'contexts/Network'; import { DefaultLocale } from 'consts'; +import { useSyncing } from 'hooks/useSyncing'; export const Payouts = () => { const { i18n, t } = useTranslation('pages'); - const { isSyncing } = useUi(); - const { inSetup } = useStaking(); const { networkData: { units, brand: { token: Token }, }, } = useNetwork(); + const { inSetup } = useStaking(); + const { syncing } = useSyncing('*'); const { plugins } = usePlugins(); const { getData, injectBlockTimestamp } = useSubscanData([ 'payouts', 'unclaimedPayouts', 'poolClaims', ]); - const notStaking = !isSyncing && inSetup(); + const notStaking = !syncing && inSetup(); // Get data safely from subscan hook. const data = getData(['payouts', 'unclaimedPayouts', 'poolClaims']); diff --git a/src/pages/Overview/StakeStatus/Tips/PageToggle.tsx b/src/pages/Overview/StakeStatus/Tips/PageToggle.tsx index d77048f9e6..c3bf5543ef 100644 --- a/src/pages/Overview/StakeStatus/Tips/PageToggle.tsx +++ b/src/pages/Overview/StakeStatus/Tips/PageToggle.tsx @@ -7,9 +7,9 @@ import { } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useTranslation } from 'react-i18next'; -import { useUi } from 'contexts/UI'; import { PageToggleWrapper } from './Wrappers'; import type { PageToggleProps } from './types'; +import { useSyncing } from 'hooks/useSyncing'; export const PageToggle = ({ start, @@ -20,9 +20,9 @@ export const PageToggle = ({ setPageHandler, }: PageToggleProps) => { const { t } = useTranslation(); - const { isNetworkSyncing } = useUi(); + const { syncing } = useSyncing(['initialization']); - totalItems = isNetworkSyncing ? 1 : totalItems; + totalItems = syncing ? 1 : totalItems; const totalPages = Math.ceil(totalItems / itemsPerPage); return ( diff --git a/src/pages/Overview/StakeStatus/Tips/index.tsx b/src/pages/Overview/StakeStatus/Tips/index.tsx index e4c9eeb142..e62088b842 100644 --- a/src/pages/Overview/StakeStatus/Tips/index.tsx +++ b/src/pages/Overview/StakeStatus/Tips/index.tsx @@ -7,10 +7,9 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { TipsConfig } from 'config/tips'; import { DefaultLocale, TipsThresholdMedium, TipsThresholdSmall } from 'consts'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useStaking } from 'contexts/Staking'; import { useTransferOptions } from 'contexts/TransferOptions'; -import { useUi } from 'contexts/UI'; import { useFillVariables } from 'hooks/useFillVariables'; import type { AnyJson } from 'types'; import { useNetwork } from 'contexts/Network'; @@ -22,19 +21,20 @@ import { TipsWrapper } from './Wrappers'; import type { TipDisplay } from './types'; import { useApi } from 'contexts/Api'; import { useBalances } from 'contexts/Balances'; +import { useSyncing } from 'hooks/useSyncing'; export const Tips = () => { const { i18n, t } = useTranslation(); const { network } = useNetwork(); - const { isNetworkSyncing } = useUi(); - const { activeAccount } = useActiveAccounts(); - const { fillVariables } = useFillVariables(); const { stakingMetrics: { minNominatorBond }, } = useApi(); - const { isOwner } = useActivePools(); + const { isOwner } = useActivePool(); const { isNominating } = useStaking(); const { getPoolMembership } = useBalances(); + const { activeAccount } = useActiveAccounts(); + const { fillVariables } = useFillVariables(); + const { syncing } = useSyncing(['initialization']); const { feeReserve, getTransferOptions } = useTransferOptions(); const membership = getPoolMembership(activeAccount); @@ -65,7 +65,7 @@ export const Tips = () => { // This function ensures totalPages is never surpassed, but does not guarantee // that the start item will maintain across resizes. const getPage = () => { - const totalItmes = isNetworkSyncing ? 1 : items.length; + const totalItmes = syncing ? 1 : items.length; const itemsPerPage = getItemsPerPage(); const totalPages = Math.ceil(totalItmes / itemsPerPage); if (pageRef.current > totalPages) { @@ -161,10 +161,10 @@ export const Tips = () => { }); // determine items to be displayed - const end = isNetworkSyncing + const end = syncing ? 1 : Math.min(pageRef.current * itemsPerPageRef.current, items.length); - const start = isNetworkSyncing + const start = syncing ? 1 : pageRef.current * itemsPerPageRef.current - (itemsPerPageRef.current - 1); @@ -176,7 +176,7 @@ export const Tips = () => { return ( <TipsWrapper> <div style={{ flexGrow: 1 }}> - {isNetworkSyncing ? ( + {syncing ? ( <Syncing /> ) : ( <Items diff --git a/src/pages/Payouts/index.tsx b/src/pages/Payouts/index.tsx index d84f9aaa9e..0c622ee384 100644 --- a/src/pages/Payouts/index.tsx +++ b/src/pages/Payouts/index.tsx @@ -8,7 +8,6 @@ import { DefaultLocale, MaxPayoutDays } from 'consts'; import { useHelp } from 'contexts/Help'; import { usePlugins } from 'contexts/Plugins'; import { useStaking } from 'contexts/Staking'; -import { useUi } from 'contexts/UI'; import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; import { PayoutBar } from 'library/Graphs/PayoutBar'; import { PayoutLine } from 'library/Graphs/PayoutLine'; @@ -24,19 +23,20 @@ import { LastEraPayoutStat } from './Stats/LastEraPayout'; import { useSubscanData } from 'hooks/useSubscanData'; import { SubscanController } from 'static/SubscanController'; import { locales } from 'locale'; +import { useSyncing } from 'hooks/useSyncing'; export const Payouts = ({ page: { key } }: PageProps) => { const { i18n, t } = useTranslation(); - const { isSyncing } = useUi(); const { openHelp } = useHelp(); const { plugins } = usePlugins(); const { inSetup } = useStaking(); + const { syncing } = useSyncing('*'); const { getData, injectBlockTimestamp } = useSubscanData([ 'payouts', 'unclaimedPayouts', 'poolClaims', ]); - const notStaking = !isSyncing && inSetup(); + const notStaking = !syncing && inSetup(); const [payoutsList, setPayoutLists] = useState<AnySubscan>([]); diff --git a/src/pages/Pools/Create/PoolRoles/index.tsx b/src/pages/Pools/Create/PoolRoles/index.tsx index 75d6b42b04..a0222c3f3a 100644 --- a/src/pages/Pools/Create/PoolRoles/index.tsx +++ b/src/pages/Pools/Create/PoolRoles/index.tsx @@ -11,7 +11,7 @@ import type { SetupStepProps } from 'library/SetupSteps/types'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { Roles } from '../../Roles'; import type { PoolProgress } from 'contexts/Setup/types'; -import type { PoolRoles as PoolRolesInterface } from 'contexts/Pools/ActivePools/types'; +import type { PoolRoles as PoolRolesInterface } from 'contexts/Pools/ActivePool/types'; export const PoolRoles = ({ section }: SetupStepProps) => { const { t } = useTranslation('pages'); diff --git a/src/pages/Pools/Home/ClosurePrompts.tsx b/src/pages/Pools/Home/ClosurePrompts.tsx index 335bd0db77..0c13b4aa4a 100644 --- a/src/pages/Pools/Home/ClosurePrompts.tsx +++ b/src/pages/Pools/Home/ClosurePrompts.tsx @@ -4,34 +4,34 @@ import { faLockOpen } from '@fortawesome/free-solid-svg-icons'; import { ButtonPrimary, ButtonRow, PageRow } from '@polkadot-cloud/react'; import { useTranslation } from 'react-i18next'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTheme } from 'contexts/Themes'; import { useTransferOptions } from 'contexts/TransferOptions'; -import { useUi } from 'contexts/UI'; import { CardWrapper } from 'library/Card/Wrappers'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useSyncing } from 'hooks/useSyncing'; export const ClosurePrompts = () => { const { t } = useTranslation('pages'); - const { colors } = useNetwork().networkData; - const { activeAccount } = useActiveAccounts(); const { mode } = useTheme(); const { openModal } = useOverlay().modal; - const { isPoolSyncing } = useUi(); - const { isBonding, selectedActivePool, isDepositor, poolNominations } = - useActivePools(); + const { colors } = useNetwork().networkData; + const { activeAccount } = useActiveAccounts(); + const { syncing } = useSyncing(['active-pools']); const { getTransferOptions } = useTransferOptions(); + const { isBonding, activePool, isDepositor, activePoolNominations } = + useActivePool(); - const { state, memberCounter } = selectedActivePool?.bondedPool || {}; + const { state, memberCounter } = activePool?.bondedPool || {}; const { active, totalUnlockChunks } = getTransferOptions(activeAccount).pool; - const targets = poolNominations?.targets ?? []; + const targets = activePoolNominations?.targets ?? []; const annuncementBorderColor = colors.secondary[mode]; // is the pool in a state for the depositor to close const depositorCanClose = - !isPoolSyncing && + !syncing && isDepositor() && state === 'Destroying' && memberCounter === '1'; @@ -64,8 +64,7 @@ export const ClosurePrompts = () => { marginRight text={t('pools.unbond')} disabled={ - isPoolSyncing || - (!depositorCanWithdraw && !depositorCanUnbond) + syncing || (!depositorCanWithdraw && !depositorCanUnbond) } onClick={() => openModal({ @@ -82,7 +81,7 @@ export const ClosurePrompts = () => { ? t('pools.unlocked') : String(totalUnlockChunks ?? 0) } - disabled={isPoolSyncing || !isBonding()} + disabled={syncing || !isBonding()} onClick={() => openModal({ key: 'UnlockChunks', diff --git a/src/pages/Pools/Home/Favorites/index.tsx b/src/pages/Pools/Home/Favorites/index.tsx index 09be7c4c67..70716bd1f2 100644 --- a/src/pages/Pools/Home/Favorites/index.tsx +++ b/src/pages/Pools/Home/Favorites/index.tsx @@ -7,18 +7,18 @@ import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { useFavoritePools } from 'contexts/Pools/FavoritePools'; -import { useUi } from 'contexts/UI'; import { CardWrapper } from 'library/Card/Wrappers'; import { PoolList } from 'library/PoolList/Default'; import { ListStatusHeader } from 'library/List'; import { PoolListProvider } from 'library/PoolList/context'; import type { BondedPool } from 'contexts/Pools/BondedPools/types'; +import { useSyncing } from 'hooks/useSyncing'; export const PoolFavorites = () => { const { t } = useTranslation('pages'); const { isReady } = useApi(); - const { isPoolSyncing } = useUi(); const { bondedPools } = useBondedPools(); + const { syncing } = useSyncing(['active-pools']); const { favorites, removeFavorite } = useFavoritePools(); // Store local favorite list and update when favorites list is mutated. @@ -43,7 +43,7 @@ export const PoolFavorites = () => { return ( <PageRow> <CardWrapper> - {favoritesList === null || isPoolSyncing ? ( + {favoritesList === null || syncing ? ( <ListStatusHeader> {t('pools.fetchingFavoritePools')}... </ListStatusHeader> diff --git a/src/pages/Pools/Home/ManageBond.tsx b/src/pages/Pools/Home/ManageBond.tsx index 40c0ec5b21..50a755cf59 100644 --- a/src/pages/Pools/Home/ManageBond.tsx +++ b/src/pages/Pools/Home/ManageBond.tsx @@ -11,15 +11,15 @@ import { import { minDecimalPlaces, planckToUnit } from '@polkadot-cloud/utils'; import { useTranslation } from 'react-i18next'; import { useHelp } from 'contexts/Help'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; -import { useUi } from 'contexts/UI'; import { BondedChart } from 'library/BarChart/BondedChart'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; +import { useSyncing } from 'hooks/useSyncing'; export const ManageBond = () => { const { t } = useTranslation('pages'); @@ -31,12 +31,12 @@ export const ManageBond = () => { }, } = useNetwork(); const { openHelp } = useHelp(); - const { isPoolSyncing } = useUi(); const { openModal } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); + const { syncing } = useSyncing(['active-pools']); const { isReadOnlyAccount } = useImportedAccounts(); const { getTransferOptions } = useTransferOptions(); - const { isBonding, isMember, selectedActivePool } = useActivePools(); + const { isBonding, isMember, activePool } = useActivePool(); const allTransferOptions = getTransferOptions(activeAccount); const { @@ -44,7 +44,7 @@ export const ManageBond = () => { transferrableBalance, } = allTransferOptions; - const { state } = selectedActivePool?.bondedPool || {}; + const { state } = activePool?.bondedPool || {}; return ( <> @@ -63,7 +63,7 @@ export const ManageBond = () => { <ButtonRow> <ButtonPrimary disabled={ - isPoolSyncing || + syncing || !isBonding() || !isMember() || isReadOnlyAccount(activeAccount) || @@ -81,7 +81,7 @@ export const ManageBond = () => { /> <ButtonPrimary disabled={ - isPoolSyncing || + syncing || !isBonding() || !isMember() || isReadOnlyAccount(activeAccount) || @@ -99,7 +99,7 @@ export const ManageBond = () => { /> <ButtonPrimary disabled={ - isPoolSyncing || !isMember() || isReadOnlyAccount(activeAccount) + syncing || !isMember() || isReadOnlyAccount(activeAccount) } iconLeft={faLockOpen} onClick={() => diff --git a/src/pages/Pools/Home/ManagePool/index.tsx b/src/pages/Pools/Home/ManagePool/index.tsx index 9f895b4f2a..6a692c7ff7 100644 --- a/src/pages/Pools/Home/ManagePool/index.tsx +++ b/src/pages/Pools/Home/ManagePool/index.tsx @@ -5,26 +5,26 @@ import { faChevronCircleRight } from '@fortawesome/free-solid-svg-icons'; import { ButtonHelp, ButtonPrimary, PageRow } from '@polkadot-cloud/react'; import { useTranslation } from 'react-i18next'; import { useHelp } from 'contexts/Help'; -import { useActivePools } from 'contexts/Pools/ActivePools'; -import { useUi } from 'contexts/UI'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; import { Nominations } from 'library/Nominations'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; +import { useSyncing } from 'hooks/useSyncing'; export const ManagePool = () => { const { t } = useTranslation(); - const { isSyncing } = useUi(); + const { syncing } = useSyncing(['active-pools']); const { poolNominated } = useValidators(); const { openCanvas } = useOverlay().canvas; const { activeAccount } = useActiveAccounts(); - const { isOwner, isNominator, poolNominations, selectedActivePool } = - useActivePools(); + const { isOwner, isNominator, activePoolNominations, activePool } = + useActivePool(); - const isNominating = !!poolNominations?.targets?.length; - const nominator = selectedActivePool?.addresses?.stash ?? null; - const { state } = selectedActivePool?.bondedPool || {}; + const isNominating = !!activePoolNominations?.targets?.length; + const nominator = activePool?.addresses?.stash ?? null; + const { state } = activePool?.bondedPool || {}; const { openHelp } = useHelp(); const canNominate = isOwner() || isNominator(); @@ -32,7 +32,7 @@ export const ManagePool = () => { return ( <PageRow> <CardWrapper> - {isSyncing ? ( + {syncing ? ( <Nominations bondFor="pool" nominator={activeAccount} /> ) : canNominate && !isNominating && state !== 'Destroying' ? ( <> diff --git a/src/pages/Pools/Home/PoolStats/Announcements.tsx b/src/pages/Pools/Home/PoolStats/Announcements.tsx index 0d8a66ea84..993473ccb3 100644 --- a/src/pages/Pools/Home/PoolStats/Announcements.tsx +++ b/src/pages/Pools/Home/PoolStats/Announcements.tsx @@ -8,7 +8,7 @@ import BigNumber from 'bignumber.js'; import { motion } from 'framer-motion'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { Announcement as AnnouncementLoader } from 'library/Loader/Announcement'; import { useNetwork } from 'contexts/Network'; import { Item } from './Wrappers'; @@ -19,9 +19,9 @@ export const Announcements = () => { const { networkData: { units, unit }, } = useNetwork(); - const { selectedActivePool } = useActivePools(); - const { rewardAccountBalance } = selectedActivePool || {}; - const { totalRewardsClaimed } = selectedActivePool?.rewardPool || {}; + const { activePool } = useActivePool(); + const { rewardAccountBalance } = activePool || {}; + const { totalRewardsClaimed } = activePool?.rewardPool || {}; const { existentialDeposit } = consts; // calculate the latest reward account balance diff --git a/src/pages/Pools/Home/PoolStats/index.tsx b/src/pages/Pools/Home/PoolStats/index.tsx index 41a08d5f7a..760711a442 100644 --- a/src/pages/Pools/Home/PoolStats/index.tsx +++ b/src/pages/Pools/Home/PoolStats/index.tsx @@ -4,7 +4,7 @@ import { planckToUnit, rmCommas } from '@polkadot-cloud/utils'; import BigNumber from 'bignumber.js'; import { useTranslation } from 'react-i18next'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; import { usePoolCommission } from 'hooks/usePoolCommission'; import { StatsHead } from 'library/StatsHead'; @@ -21,11 +21,11 @@ export const PoolStats = () => { networkData: { units, unit }, } = useNetwork(); const { getCurrentCommission } = usePoolCommission(); - const { selectedActivePool, selectedPoolMemberCount } = useActivePools(); + const { activePool, activePoolMemberCount } = useActivePool(); - const poolId = selectedActivePool?.id || 0; + const poolId = activePool?.id || 0; - const { state, points } = selectedActivePool?.bondedPool || {}; + const { state, points } = activePool?.bondedPool || {}; const currentCommission = getCurrentCommission(poolId); const bonded = planckToUnit( @@ -65,13 +65,13 @@ export const PoolStats = () => { items.push( { label: t('pools.poolMembers'), - value: `${selectedPoolMemberCount}`, + value: `${activePoolMemberCount}`, button: { text: t('pools.browseMembers'), onClick: () => { openCanvas({ key: 'PoolMembers', size: 'xl' }); }, - disabled: selectedPoolMemberCount === 0, + disabled: activePoolMemberCount === 0, }, }, { diff --git a/src/pages/Pools/Home/Status/MembershipStatus.tsx b/src/pages/Pools/Home/Status/MembershipStatus.tsx index 81b0c35fda..0fe63a4f80 100644 --- a/src/pages/Pools/Home/Status/MembershipStatus.tsx +++ b/src/pages/Pools/Home/Status/MembershipStatus.tsx @@ -5,15 +5,15 @@ import { faCog } from '@fortawesome/free-solid-svg-icons'; import { determinePoolDisplay } from '@polkadot-cloud/utils'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { useTransferOptions } from 'contexts/TransferOptions'; -import { useUi } from 'contexts/UI'; import { Stat } from 'library/Stat'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useStatusButtons } from './useStatusButtons'; +import { useSyncing } from 'hooks/useSyncing'; export const MembershipStatus = ({ showButtons = true, @@ -24,32 +24,27 @@ export const MembershipStatus = ({ }) => { const { t } = useTranslation('pages'); const { isReady } = useApi(); - const { isPoolSyncing } = useUi(); const { openModal } = useOverlay().modal; + const { poolsMetaData } = useBondedPools(); const { activeAccount } = useActiveAccounts(); const { label, buttons } = useStatusButtons(); + const { syncing } = useSyncing(['active-pools']); const { isReadOnlyAccount } = useImportedAccounts(); const { getTransferOptions } = useTransferOptions(); - const { bondedPools, poolsMetaData } = useBondedPools(); - const { selectedActivePool, isOwner, isBouncer, isMember } = useActivePools(); + const { activePool, isOwner, isBouncer, isMember } = useActivePool(); const { active } = getTransferOptions(activeAccount).pool; - const poolState = selectedActivePool?.bondedPool?.state ?? null; + const poolState = activePool?.bondedPool?.state ?? null; const membershipButtons = []; let membershipDisplay = t('pools.notInPool'); - if (selectedActivePool) { - const pool = bondedPools.find( - (p) => p.addresses.stash === selectedActivePool.addresses.stash + if (activePool) { + // Determine pool membership display. + membershipDisplay = determinePoolDisplay( + activePool.addresses.stash, + poolsMetaData[Number(activePool.id)] ); - if (pool) { - // Determine pool membership display. - membershipDisplay = determinePoolDisplay( - selectedActivePool.addresses.stash, - poolsMetaData[Number(pool.id)] - ); - } // Display manage button if active account is pool owner or bouncer. // Or display manage button if active account is a pool member. @@ -72,13 +67,13 @@ export const MembershipStatus = ({ } } - return selectedActivePool ? ( + return activePool ? ( <Stat label={label} helpKey="Pool Membership" type="address" stat={{ - address: selectedActivePool?.addresses?.stash ?? '', + address: activePool?.addresses?.stash ?? '', display: membershipDisplay, }} buttons={showButtons ? membershipButtons : []} @@ -88,7 +83,7 @@ export const MembershipStatus = ({ label={t('pools.poolMembership')} helpKey="Pool Membership" stat={t('pools.notInPool')} - buttons={!showButtons || isPoolSyncing ? [] : buttons} + buttons={!showButtons || syncing ? [] : buttons} buttonType={buttonType} /> ); diff --git a/src/pages/Pools/Home/Status/PoolStatus.tsx b/src/pages/Pools/Home/Status/PoolStatus.tsx index d1e530a6e3..c9d59acfe9 100644 --- a/src/pages/Pools/Home/Status/PoolStatus.tsx +++ b/src/pages/Pools/Home/Status/PoolStatus.tsx @@ -6,21 +6,21 @@ import { faLock, } from '@fortawesome/free-solid-svg-icons'; import { useTranslation } from 'react-i18next'; -import { useActivePools } from 'contexts/Pools/ActivePools'; -import { useUi } from 'contexts/UI'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useNominationStatus } from 'hooks/useNominationStatus'; import { Stat } from 'library/Stat'; +import { useSyncing } from 'hooks/useSyncing'; export const PoolStatus = () => { const { t } = useTranslation('pages'); - const { isPoolSyncing } = useUi(); + const { syncing } = useSyncing(['active-pools']); const { getNominationStatus } = useNominationStatus(); - const { selectedActivePool, poolNominations } = useActivePools(); + const { activePool, activePoolNominations } = useActivePool(); - const poolStash = selectedActivePool?.addresses?.stash || ''; + const poolStash = activePool?.addresses?.stash || ''; const { earningRewards, nominees } = getNominationStatus(poolStash, 'pool'); - const poolState = selectedActivePool?.bondedPool?.state ?? null; - const poolNominating = !!poolNominations?.targets?.length; + const poolState = activePool?.bondedPool?.state ?? null; + const poolNominating = !!activePoolNominations?.targets?.length; // Determine pool state icon. let poolStateIcon; @@ -44,7 +44,7 @@ export const PoolStatus = () => { : ''; // Determine pool status - right side. - const poolStatusRight = isPoolSyncing + const poolStatusRight = syncing ? t('pools.inactivePoolNotNominating') : !poolNominating ? t('pools.inactivePoolNotNominating') @@ -58,7 +58,7 @@ export const PoolStatus = () => { return ( <Stat - icon={isPoolSyncing ? undefined : poolStateIcon} + icon={syncing ? undefined : poolStateIcon} label={t('pools.poolStatus')} helpKey="Nomination Status" stat={`${poolStatusLeft}${poolStatusRight}`} diff --git a/src/pages/Pools/Home/Status/RewardsStatus.tsx b/src/pages/Pools/Home/Status/RewardsStatus.tsx index d700c47b18..744494ae18 100644 --- a/src/pages/Pools/Home/Status/RewardsStatus.tsx +++ b/src/pages/Pools/Home/Status/RewardsStatus.tsx @@ -6,13 +6,13 @@ import { planckToUnit } from '@polkadot-cloud/utils'; import BigNumber from 'bignumber.js'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; -import { useUi } from 'contexts/UI'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { Stat } from 'library/Stat'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; +import { useSyncing } from 'hooks/useSyncing'; export const RewardsStatus = () => { const { t } = useTranslation('pages'); @@ -20,24 +20,21 @@ export const RewardsStatus = () => { networkData: { units }, } = useNetwork(); const { isReady } = useApi(); - const { isPoolSyncing } = useUi(); const { openModal } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); - const { selectedActivePool } = useActivePools(); + const { syncing } = useSyncing(['active-pools']); const { isReadOnlyAccount } = useImportedAccounts(); - - let { pendingRewards } = selectedActivePool || {}; - pendingRewards = pendingRewards ?? new BigNumber(0); + const { activePool, pendingPoolRewards } = useActivePool(); // Set the minimum unclaimed planck value to prevent e numbers. const minUnclaimedDisplay = new BigNumber(1_000_000); - const labelRewards = pendingRewards.isGreaterThan(minUnclaimedDisplay) - ? planckToUnit(pendingRewards, units).toString() + const labelRewards = pendingPoolRewards.isGreaterThan(minUnclaimedDisplay) + ? planckToUnit(pendingPoolRewards, units).toString() : '0'; // Display Reward buttons if unclaimed rewards is a non-zero value. - const buttonsRewards = pendingRewards.isGreaterThan(minUnclaimedDisplay) + const buttonsRewards = pendingPoolRewards.isGreaterThan(minUnclaimedDisplay) ? [ { title: t('pools.withdraw'), @@ -57,7 +54,7 @@ export const RewardsStatus = () => { disabled: !isReady || isReadOnlyAccount(activeAccount) || - selectedActivePool?.bondedPool?.state === 'Destroying', + activePool?.bondedPool?.state === 'Destroying', small: true, onClick: () => openModal({ @@ -75,7 +72,7 @@ export const RewardsStatus = () => { helpKey="Pool Rewards" type="odometer" stat={{ value: labelRewards }} - buttons={isPoolSyncing ? [] : buttonsRewards} + buttons={syncing ? [] : buttonsRewards} /> ); }; diff --git a/src/pages/Pools/Home/Status/index.tsx b/src/pages/Pools/Home/Status/index.tsx index 0a2036aeeb..5d0dfd4962 100644 --- a/src/pages/Pools/Home/Status/index.tsx +++ b/src/pages/Pools/Home/Status/index.tsx @@ -2,21 +2,21 @@ // SPDX-License-Identifier: GPL-3.0-only import { Separator } from '@polkadot-cloud/react'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { CardWrapper } from 'library/Card/Wrappers'; import { MembershipStatus } from './MembershipStatus'; import { PoolStatus } from './PoolStatus'; import { RewardsStatus } from './RewardsStatus'; export const Status = ({ height }: { height: number }) => { - const { selectedActivePool } = useActivePools(); + const { activePool } = useActivePool(); return ( <CardWrapper height={height}> <MembershipStatus /> <Separator /> <RewardsStatus /> - {selectedActivePool && ( + {activePool && ( <> <Separator /> <PoolStatus /> diff --git a/src/pages/Pools/Home/Status/useStatusButtons.tsx b/src/pages/Pools/Home/Status/useStatusButtons.tsx index fcc63e51be..aa2d209f1e 100644 --- a/src/pages/Pools/Home/Status/useStatusButtons.tsx +++ b/src/pages/Pools/Home/Status/useStatusButtons.tsx @@ -4,7 +4,7 @@ import { faPlusCircle, faUserPlus } from '@fortawesome/free-solid-svg-icons'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { useSetup } from 'contexts/Setup'; import { useTransferOptions } from 'contexts/TransferOptions'; @@ -19,7 +19,7 @@ export const useStatusButtons = () => { isReady, poolsConfig: { maxPools }, } = useApi(); - const { isOwner } = useActivePools(); + const { isOwner } = useActivePool(); const { setActiveTab } = usePoolsTabs(); const { bondedPools } = useBondedPools(); const { getPoolMembership } = useBalances(); diff --git a/src/pages/Pools/Home/index.tsx b/src/pages/Pools/Home/index.tsx index 85fae0fb27..be05ca7819 100644 --- a/src/pages/Pools/Home/index.tsx +++ b/src/pages/Pools/Home/index.tsx @@ -5,7 +5,7 @@ import { PageRow, PageTitle, RowSection } from '@polkadot-cloud/react'; import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import type { PageTitleTabProps } from '@polkadot-cloud/react/types'; -import { useActivePools } from 'contexts/Pools/ActivePools'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { CardWrapper } from 'library/Card/Wrappers'; import { PoolList } from 'library/PoolList/Default'; @@ -26,18 +26,27 @@ import { MinJoinBondStat } from './Stats/MinJoinBond'; import { Status } from './Status'; import { PoolsTabsProvider, usePoolsTabs } from './context'; import { useApi } from 'contexts/Api'; +import { useActivePools } from 'hooks/useActivePools'; +import { useBalances } from 'contexts/Balances'; export const HomeInner = () => { const { t } = useTranslation('pages'); const { favorites } = useFavoritePools(); const { openModal } = useOverlay().modal; + const { bondedPools } = useBondedPools(); + const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const { activeTab, setActiveTab } = usePoolsTabs(); + const { getPoolRoles, activePool } = useActivePool(); const { counterForBondedPools } = useApi().poolsConfig; - const { bondedPools, getAccountPools } = useBondedPools(); - const { getPoolRoles, selectedActivePool } = useActivePools(); - const accountPools = getAccountPools(activeAccount); - const totalAccountPools = Object.entries(accountPools).length; + const membership = getPoolMembership(activeAccount); + + const { activePools } = useActivePools({ + poolIds: '*', + }); + + const activePoolsNoMembership = { ...activePools }; + delete activePoolsNoMembership[membership?.poolId || -1]; let tabs: PageTitleTabProps[] = [ { @@ -64,10 +73,10 @@ export const HomeInner = () => { // Back to tab 0 if not in a pool & on members tab. useEffect(() => { - if (!selectedActivePool) { + if (!activePool) { setActiveTab(0); } - }, [selectedActivePool]); + }, [activePool]); const ROW_HEIGHT = 220; @@ -77,13 +86,13 @@ export const HomeInner = () => { title={t('pools.pools')} tabs={tabs} button={ - totalAccountPools + Object.keys(activePoolsNoMembership).length > 0 ? { title: t('pools.allRoles'), onClick: () => openModal({ key: 'AccountPoolRoles', - options: { who: activeAccount }, + options: { who: activeAccount, activePools }, }), } : undefined @@ -109,7 +118,7 @@ export const HomeInner = () => { </CardWrapper> </RowSection> </PageRow> - {selectedActivePool !== null && ( + {activePool !== null && ( <> <ManagePool /> <PageRow> diff --git a/src/pages/Pools/Roles/index.tsx b/src/pages/Pools/Roles/index.tsx index efc31c950b..9551b5c2c6 100644 --- a/src/pages/Pools/Roles/index.tsx +++ b/src/pages/Pools/Roles/index.tsx @@ -15,8 +15,7 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useApi } from 'contexts/Api'; import { useHelp } from 'contexts/Help'; -import { useActivePools } from 'contexts/Pools/ActivePools'; -import { useUi } from 'contexts/UI'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; @@ -26,6 +25,7 @@ import { RolesWrapper } from '../Home/ManagePool/Wrappers'; import { PoolAccount } from '../PoolAccount'; import { RoleEditInput } from './RoleEditInput'; import type { RoleEditEntry, RolesProps } from './types'; +import { useSyncing } from 'hooks/useSyncing'; export const Roles = ({ defaultRoles, @@ -37,12 +37,13 @@ export const Roles = ({ const { isReady } = useApi(); const { openHelp } = useHelp(); const { network } = useNetwork(); - const { isPoolSyncing } = useUi(); const { openModal } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); + const { isOwner, activePool } = useActivePool(); + const { syncing } = useSyncing(['active-pools']); const { isReadOnlyAccount } = useImportedAccounts(); - const { isOwner, selectedActivePool } = useActivePools(); - const { id } = selectedActivePool || { id: 0 }; + + const { id } = activePool || { id: 0 }; const roles = defaultRoles; const initialiseEdits = (() => { @@ -162,7 +163,7 @@ export const Roles = ({ iconLeft={faTimesCircle} iconTransform="grow-1" text={t('pools.cancel')} - disabled={isPoolSyncing || isReadOnlyAccount(activeAccount)} + disabled={syncing || isReadOnlyAccount(activeAccount)} onClick={() => cancelHandler()} /> </div> @@ -174,7 +175,7 @@ export const Roles = ({ iconTransform="grow-1" text={isEditing ? t('pools.save') : t('pools.edit')} disabled={ - isPoolSyncing || + syncing || isReadOnlyAccount(activeAccount) || !isRoleEditsValid() } @@ -188,10 +189,7 @@ export const Roles = ({ <section> <div className="inner"> <h4>{t('pools.depositor')}</h4> - <PoolAccount - address={roles.depositor ?? null} - pool={selectedActivePool} - /> + <PoolAccount address={roles.depositor ?? null} pool={activePool} /> </div> </section> <section> @@ -204,10 +202,7 @@ export const Roles = ({ setRoleEdit={setRoleEditHandler} /> ) : ( - <PoolAccount - address={roles.root ?? null} - pool={selectedActivePool} - /> + <PoolAccount address={roles.root ?? null} pool={activePool} /> )} </div> </section> @@ -223,7 +218,7 @@ export const Roles = ({ ) : ( <PoolAccount address={roles.nominator ?? null} - pool={selectedActivePool} + pool={activePool} /> )} </div> @@ -238,10 +233,7 @@ export const Roles = ({ setRoleEdit={setRoleEditHandler} /> ) : ( - <PoolAccount - address={roles.bouncer ?? null} - pool={selectedActivePool} - /> + <PoolAccount address={roles.bouncer ?? null} pool={activePool} /> )} </div> </section> diff --git a/src/pages/Pools/Roles/types.ts b/src/pages/Pools/Roles/types.ts index 17d960bb3a..c98fe04db9 100644 --- a/src/pages/Pools/Roles/types.ts +++ b/src/pages/Pools/Roles/types.ts @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { PoolRoles } from 'contexts/Pools/ActivePools/types'; +import type { PoolRoles } from 'contexts/Pools/ActivePool/types'; import type { AnyFunction } from 'types'; export interface RolesProps { diff --git a/src/pages/Pools/types.ts b/src/pages/Pools/types.ts index b558aecc3d..3cf1cab391 100644 --- a/src/pages/Pools/types.ts +++ b/src/pages/Pools/types.ts @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ActivePool } from 'contexts/Pools/ActivePools/types'; +import type { ActivePool } from 'contexts/Pools/ActivePool/types'; import type { ListFormat } from 'library/PoolList/types'; export interface PoolAccountProps { diff --git a/src/static/APIController/index.ts b/src/static/APIController/index.ts index 2e45a9d9c0..708667c2de 100644 --- a/src/static/APIController/index.ts +++ b/src/static/APIController/index.ts @@ -27,6 +27,8 @@ import type { } from 'contexts/Api/types'; import { WellKnownChain } from '@substrate/connect'; import { defaultActiveEra } from 'contexts/Api/defaults'; +import { ActivePoolsController } from 'static/ActivePoolsController'; +import { SyncController } from 'static/SyncController'; export class APIController { // ------------------------------------------------------ @@ -130,6 +132,10 @@ export class APIController { await this.disconnect(); } + // Add initial syncing items. + SyncController.dispatch('initialization', 'syncing'); + SyncController.dispatch('era-stakers', 'syncing'); + const config: APIConfig = { type, network, @@ -758,6 +764,7 @@ export class APIController { // Unsubscribe from all subscriptions. this.unsubscribe(); BalancesController.unsubscribe(); + ActivePoolsController.unsubscribe(); // Disconnect from provider and api. this.unsubscribeProvider(); diff --git a/src/static/ActivePoolsController/index.ts b/src/static/ActivePoolsController/index.ts new file mode 100644 index 0000000000..32357f98bc --- /dev/null +++ b/src/static/ActivePoolsController/index.ts @@ -0,0 +1,201 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { VoidFn } from '@polkadot/api/types'; +import type { Nominations } from 'contexts/Bonded/types'; +import { defaultPoolNominations } from 'contexts/Pools/ActivePool/defaults'; +import type { ActivePool, PoolRoles } from 'contexts/Pools/ActivePool/types'; +import { APIController } from 'static/APIController'; +import { IdentitiesController } from 'static/IdentitiesController'; +import type { AnyApi } from 'types'; +import type { ActivePoolItem, DetailActivePool } from './types'; +import { SyncController } from 'static/SyncController'; + +export class ActivePoolsController { + // ------------------------------------------------------ + // Class members. + // ------------------------------------------------------ + + // Pool ids that are being subscribed to. + static pools: ActivePoolItem[] = []; + + // Active pools that are being returned from subscriptions, keyed by pool id. + static activePools: Record<string, ActivePool | null> = {}; + + // Active pool nominations, keyed by pool id. + static poolNominations: Record<string, Nominations> = {}; + + // Unsubscribe objects. + static _unsubs: Record<string, VoidFn> = {}; + + // ------------------------------------------------------ + // Pool membership syncing. + // ------------------------------------------------------ + + // Subscribes to pools and unsubscribes from removed pools. + static syncPools = async (newPools: ActivePoolItem[]): Promise<void> => { + const { api } = APIController; + + // Sync: Checking active pools. + SyncController.dispatch('active-pools', 'syncing'); + + // Handle pools that have been removed. + this.handleRemovedPools(newPools); + + // Determine new pools that need to be subscribed to. + const poolsAdded = newPools.filter( + (newPool) => !this.pools.find((pool) => pool.id === newPool.id) + ); + + if (poolsAdded.length) { + // Subscribe to and add new pool data. + poolsAdded.forEach(async (pool) => { + this.pools.push(pool); + + const unsub = await api.queryMulti<AnyApi>( + [ + [api.query.nominationPools.bondedPools, pool.id], + [api.query.nominationPools.rewardPools, pool.id], + [api.query.system.account, pool.addresses.reward], + [api.query.staking.nominators, pool.addresses.stash], + ], + async ([ + bondedPool, + rewardPool, + accountData, + nominators, + ]): Promise<void> => { + // NOTE: async: fetches identity data for roles. + await this.handleActivePoolCallback( + pool, + bondedPool, + rewardPool, + accountData + ); + this.handleNominatorsCallback(pool, nominators); + + if (this.activePools[pool.id] && this.poolNominations[pool.id]) { + document.dispatchEvent( + new CustomEvent('new-active-pool', { + detail: { + pool: this.activePools[pool.id], + nominations: this.poolNominations[pool.id], + }, + }) + ); + } + } + ); + this._unsubs[pool.id] = unsub; + }); + } else { + // Status: Pools Synced Completed. + SyncController.dispatch('active-pools', 'complete'); + } + }; + + // Handle active pool callback. + static handleActivePoolCallback = async ( + pool: ActivePoolItem, + bondedPoolResult: AnyApi, + rewardPoolResult: AnyApi, + accountDataResult: AnyApi + ): Promise<void> => { + const bondedPool = bondedPoolResult?.unwrapOr(undefined)?.toHuman(); + const rewardPool = rewardPoolResult?.unwrapOr(undefined)?.toHuman(); + const balance = accountDataResult.data; + const rewardAccountBalance = balance?.free.toString(); + + // Fetch identities for roles and expand `bondedPool` state to store them. + bondedPool.roleIdentities = await IdentitiesController.fetch( + this.getUniqueRoleAddresses(bondedPool.roles) + ); + + // Only persist the active pool to class state (and therefore dispatch an event) if both the + // bonded pool and reward pool are returned. + if (bondedPool && rewardPool) { + const newPool = { + id: Number(pool.id), + addresses: pool.addresses, + bondedPool, + rewardPool, + rewardAccountBalance, + }; + + this.activePools[pool.id] = newPool; + } else { + // Invalid pools were returned. To signal pool was synced, set active pool to `null`. + this.activePools[pool.id] = null; + } + }; + + // Handle nominators callback. + static handleNominatorsCallback = ( + pool: ActivePoolItem, + nominatorsResult: AnyApi + ): void => { + const maybeNewNominations = nominatorsResult.unwrapOr(null); + + const newNominations: Nominations = + maybeNewNominations === null + ? defaultPoolNominations + : { + targets: maybeNewNominations.targets.toHuman(), + submittedIn: maybeNewNominations.submittedIn.toHuman(), + }; + + this.poolNominations[pool.id] = newNominations; + }; + + // Remove pools that no longer exist. + static handleRemovedPools = (newPools: ActivePoolItem[]): void => { + // Determine removed pools - current ones that no longer exist in `newPools`. + const poolsRemoved = this.pools.filter( + (pool) => !newPools.find((newPool) => newPool.id === pool.id) + ); + + // Unsubscribe from removed pool subscriptions. + poolsRemoved.forEach((pool) => { + this._unsubs[pool.id](); + delete this._unsubs[pool.id]; + delete this.activePools[pool.id]; + delete this.poolNominations[pool.id]; + }); + + // Remove removed pools from class. + this.pools = this.pools.filter((pool) => !poolsRemoved.includes(pool)); + }; + + // ------------------------------------------------------ + // Subscription handling. + // ------------------------------------------------------ + + // Unsubscribe from all subscriptions and reset class members. + static unsubscribe = (): void => { + Object.values(this._unsubs).forEach((unsub) => { + unsub(); + }); + this.pools = []; + this.activePools = {}; + this.poolNominations = {}; + this._unsubs = {}; + }; + + // ------------------------------------------------------ + // Class helpers. + // ------------------------------------------------------ + + // Gets unique role addresses from a bonded pool's `roles` record. + static getUniqueRoleAddresses = (roles: PoolRoles): string[] => { + const roleAddresses: string[] = [ + ...new Set(Object.values(roles).filter((role) => role !== undefined)), + ]; + return roleAddresses; + }; + + // Checks if event detailis a valid `new-active-pool` event. + static isValidNewActivePool = ( + event: CustomEvent + ): event is CustomEvent<DetailActivePool> => + event.detail && event.detail.pool && event.detail.nominations; +} diff --git a/src/static/ActivePoolsController/types.ts b/src/static/ActivePoolsController/types.ts new file mode 100644 index 0000000000..100c85b98d --- /dev/null +++ b/src/static/ActivePoolsController/types.ts @@ -0,0 +1,18 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { Nominations } from 'contexts/Bonded/types'; +import type { ActivePool } from 'contexts/Pools/ActivePool/types'; + +export interface DetailActivePool { + pool: ActivePool; + nominations: Nominations; +} + +export interface ActivePoolItem { + id: string; + addresses: { + stash: string; + reward: string; + }; +} diff --git a/src/static/BalancesController/index.ts b/src/static/BalancesController/index.ts index f188270c12..52800e1d59 100644 --- a/src/static/BalancesController/index.ts +++ b/src/static/BalancesController/index.ts @@ -14,6 +14,7 @@ import type { } from 'contexts/Balances/types'; import type { PayeeConfig, PayeeOptions } from 'contexts/Setup/types'; import type { PoolMembership } from 'contexts/Pools/types'; +import { SyncController } from 'static/SyncController'; export class BalancesController { // ------------------------------------------------------ @@ -54,6 +55,14 @@ export class BalancesController { (account) => !this.accounts.includes(account) ); + // Exit early if there are no new accounts to subscribe to. + if (!accountsAdded.length) { + return; + } + + // Strart syncing if new accounts added. + SyncController.dispatch('balances', 'syncing'); + // Subscribe to and add new accounts data. accountsAdded.forEach(async (address) => { this.accounts.push(address); @@ -78,7 +87,7 @@ export class BalancesController { this.handleAccountCallback(address, accountResult, locksResult); this.handlePayeeCallback(address, payeeResult); - // NOTE: async; contains runtime call for pending rewards. + // NOTE: async: contains runtime call for pending rewards. await this.handlePoolMembershipCallback( address, poolMembersResult, @@ -113,6 +122,10 @@ export class BalancesController { accountsRemoved.forEach((account) => { this._unsubs[account](); delete this._unsubs[account]; + delete this.ledgers[account]; + delete this.balances[account]; + delete this.payees[account]; + delete this.poolMemberships[account]; }); // Remove removed accounts from class. this.accounts = this.accounts.filter( diff --git a/src/static/IdentitiesController/index.ts b/src/static/IdentitiesController/index.ts new file mode 100644 index 0000000000..1c5d4408d3 --- /dev/null +++ b/src/static/IdentitiesController/index.ts @@ -0,0 +1,62 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { AnyApi } from '@polkadot-cloud/react/types'; +import { APIController } from '../APIController'; + +export class IdentitiesController { + static fetch = async (addresses: string[]) => { + const { api } = APIController; + + // Fetches identities for addresses. + const fetchBase = async () => { + const result = (await api.query.identity.identityOf.multi(addresses)).map( + (identity) => identity.toHuman() + ); + return Object.fromEntries( + result.map((k, i) => [addresses[i], k]).filter(([, v]) => v !== null) + ); + }; + + // Fetch an array of super accounts and their identities. + const fetchSupers = async () => { + const supersRaw = (await api.query.identity.superOf.multi(addresses)).map( + (superOf) => superOf.toHuman() + ); + const supers = Object.fromEntries( + supersRaw + .map((k, i) => [ + addresses[i], + { + superOf: k, + }, + ]) + .filter(([, { superOf }]: AnyApi) => superOf !== null) + ); + + const superIdentities = ( + await api.query.identity.identityOf.multi( + Object.values(supers).map(({ superOf }: AnyApi) => superOf[0]) + ) + ).map((superIdentity) => superIdentity.toHuman()); + + const supersWithIdentity = Object.fromEntries( + Object.entries(supers).map(([k, v]: AnyApi, i) => [ + k, + { + ...v, + identity: superIdentities[i], + }, + ]) + ); + return supersWithIdentity; + }; + + const [identities, supers] = await Promise.all([ + fetchBase(), + fetchSupers(), + ]); + + return { identities, supers }; + }; +} diff --git a/src/static/SyncController/index.ts b/src/static/SyncController/index.ts new file mode 100644 index 0000000000..434fc40304 --- /dev/null +++ b/src/static/SyncController/index.ts @@ -0,0 +1,56 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { SyncEvent, SyncID, SyncIDConfig, SyncStatus } from './types'; + +export class SyncController { + // ------------------------------------------------------ + // Class members + // ------------------------------------------------------ + static syncIds: SyncID[] = []; + + // ------------------------------------------------------ + // Dispatch sync events + // ------------------------------------------------------ + + // Dispatches a new sync event to the document. + static dispatch = (id: SyncID, status: SyncStatus) => { + const detail: SyncEvent = { + id, + status, + }; + + // Keep class syncIds up to date. + if (status === 'syncing' && !this.syncIds.includes(id)) { + this.syncIds.push(id); + } + if (status === 'complete' && this.syncIds.includes(id)) { + this.syncIds = this.syncIds.filter((syncId) => syncId !== id); + } + + // Dispatch event to UI. + document.dispatchEvent( + new CustomEvent('new-sync-status', { + detail, + }) + ); + }; + + // Checks if event detailis a valid `new-sync-status` event. + static isValidSyncStatus = ( + event: CustomEvent + ): event is CustomEvent<SyncEvent> => + event.detail && event.detail.id && event.detail.status; + + // Gets SyncIDs from a given config. + static getIdsFromSyncConfig = (config: SyncIDConfig): SyncID[] | '*' => { + if (config === '*' || !this.isSyncIdArray(config)) { + return '*'; + } + return config; + }; + + // Checks if a sync config is just an array of syncIds. + static isSyncIdArray = (config: SyncIDConfig): config is SyncID[] => + Array.isArray(config) && config.every((item) => typeof item === 'string'); +} diff --git a/src/static/SyncController/types.ts b/src/static/SyncController/types.ts new file mode 100644 index 0000000000..915aa8b624 --- /dev/null +++ b/src/static/SyncController/types.ts @@ -0,0 +1,19 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +export type SyncID = + | 'initialization' + | 'balances' + | 'era-stakers' + | 'active-pools'; + +export interface SyncEvent { + id: SyncID; + status: SyncStatus; +} + +export type SyncStatus = 'syncing' | 'complete'; + +export type SyncIDConfig = SyncIDWildcard | SyncID[]; + +export type SyncIDWildcard = '*'; diff --git a/src/types/index.ts b/src/types/index.ts index 8696bc79da..19e0e60016 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -14,6 +14,8 @@ import type { APIPoolsConfig, APIStakingMetrics, } from 'contexts/Api/types'; +import type { SyncEvent } from 'static/SyncController/types'; +import type { DetailActivePool } from 'static/ActivePoolsController/types'; declare global { interface Window { @@ -30,6 +32,8 @@ declare global { 'new-staking-metrics': CustomEvent<{ stakingMetrics: APIStakingMetrics; }>; + 'new-active-pool': CustomEvent<DetailActivePool>; + 'new-sync-status': CustomEvent<SyncEvent>; 'new-external-account': CustomEvent<{ address: string }>; 'new-account-balance': CustomEvent<ActiveBalance & { address: string }>; 'subscan-data-updated': CustomEvent<{ keys: PayoutType[] }>; From 9145cb87ca01371f37630ac8d341d7129175037e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 21:08:17 +0000 Subject: [PATCH 26/51] chore(deps): bump react-i18next from 14.0.3 to 14.0.5 (#1930) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 67bc152977..b91af115f2 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "react-dom": "^18.2.0", "react-error-boundary": "^4.0.12", "react-helmet": "^6.1.0", - "react-i18next": "^14.0.3", + "react-i18next": "^14.0.5", "react-router-dom": "^6.22.0", "react-scroll": "^1.9.0", "styled-components": "^6.1.8", diff --git a/yarn.lock b/yarn.lock index b86de83ec3..d94092bfc3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6336,7 +6336,7 @@ __metadata: react-dom: "npm:^18.2.0" react-error-boundary: "npm:^4.0.12" react-helmet: "npm:^6.1.0" - react-i18next: "npm:^14.0.3" + react-i18next: "npm:^14.0.5" react-router-dom: "npm:^6.22.0" react-scroll: "npm:^1.9.0" sass: "npm:^1.70.0" @@ -6614,9 +6614,9 @@ __metadata: languageName: node linkType: hard -"react-i18next@npm:^14.0.3": - version: 14.0.3 - resolution: "react-i18next@npm:14.0.3" +"react-i18next@npm:^14.0.5": + version: 14.0.5 + resolution: "react-i18next@npm:14.0.5" dependencies: "@babel/runtime": "npm:^7.23.9" html-parse-stringify: "npm:^3.0.1" @@ -6628,7 +6628,7 @@ __metadata: optional: true react-native: optional: true - checksum: e5d5f2a14324a295094944b71139e60d473a48843e8ce6e68d0edd8b34826900c6dd2ac7bac9a8b23c70a54fa89cdc9da9556b2e96fca0027d3555d3075813ed + checksum: 60e3bedc6889689cfb96005b134da79d50097d3ff088284b35f213820aacedbdad1ff32e97b8447db70834c16d472ded829675e05c2a099baea9fd96b3a5b72f languageName: node linkType: hard From 75384eb65dcf2dfd2b7f56c94704899e7c0aaf82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 21:09:04 +0000 Subject: [PATCH 27/51] chore(deps): bump usehooks-ts from 2.12.1 to 2.13.0 (#1931) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b91af115f2..8ca3d36ee7 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "react-router-dom": "^6.22.0", "react-scroll": "^1.9.0", "styled-components": "^6.1.8", - "usehooks-ts": "2.12.1" + "usehooks-ts": "2.13.0" }, "devDependencies": { "@ledgerhq/logs": "^6.12.0", diff --git a/yarn.lock b/yarn.lock index d94092bfc3..be40775f2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6342,7 +6342,7 @@ __metadata: sass: "npm:^1.70.0" styled-components: "npm:^6.1.8" typescript: "npm:^5.3.3" - usehooks-ts: "npm:2.12.1" + usehooks-ts: "npm:2.13.0" vite: "npm:^5.0.12" vite-bundle-visualizer: "npm:^1.0.1" vite-plugin-checker: "npm:^0.6.3" @@ -7961,14 +7961,14 @@ __metadata: languageName: node linkType: hard -"usehooks-ts@npm:2.12.1": - version: 2.12.1 - resolution: "usehooks-ts@npm:2.12.1" +"usehooks-ts@npm:2.13.0": + version: 2.13.0 + resolution: "usehooks-ts@npm:2.13.0" dependencies: lodash.debounce: "npm:^4.0.8" peerDependencies: react: ^16.8.0 || ^17 || ^18 - checksum: 996ff8afe1a222746af9bfb5dd752ceaf33120e56c2e660906ec30e53d99fce3b04a342776c64dba5db477dd12655fab5f3742fac8639c4a35fdc5be74f46074 + checksum: 3924bb1f9f4baee08e84b4bc311ea14492ed19db2c38d1b54c542fc45e5da6a48b4469da9281339ad7a0de76184ebf5d8de9d654dc25aceb2ccc88a4e6db115a languageName: node linkType: hard From 3ee2589b629f263b3a79fd0df137625332b1ba2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 21:09:42 +0000 Subject: [PATCH 28/51] chore(deps-dev): bump @types/react from 18.2.54 to 18.2.55 (#1932) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 8ca3d36ee7..4d7ea01900 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@types/chroma-js": "^2.4.3", "@types/lodash.debounce": "^4", "@types/lodash.throttle": "^4.1.9", - "@types/react": "^18.2.54", + "@types/react": "^18.2.55", "@types/react-dom": "^18.2.17", "@types/react-helmet": "^6.1.11", "@types/react-scroll": "^1.8.10", diff --git a/yarn.lock b/yarn.lock index be40775f2c..09241bd905 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2213,14 +2213,14 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^18.2.54": - version: 18.2.54 - resolution: "@types/react@npm:18.2.54" +"@types/react@npm:^18.2.55": + version: 18.2.55 + resolution: "@types/react@npm:18.2.55" dependencies: "@types/prop-types": "npm:*" "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: ad38193c30a063a481aeec2460de6396c80d8de2f1c7a8cbb80a4e8bc594f74c308ce93e165d743b38507c3ac0a491c24ce0efbd84c9ab21fd5fd38d2963d329 + checksum: 6b1c73beafbbc582dc54ffd92b3779f6d850e8f6b5ce5d04b31e9498a3d77bfc416bb08f0d8d63ab4f4649ccd6639996472416871c01c74a528b16a55faeaf38 languageName: node linkType: hard @@ -6295,7 +6295,7 @@ __metadata: "@types/chroma-js": "npm:^2.4.3" "@types/lodash.debounce": "npm:^4" "@types/lodash.throttle": "npm:^4.1.9" - "@types/react": "npm:^18.2.54" + "@types/react": "npm:^18.2.55" "@types/react-dom": "npm:^18.2.17" "@types/react-helmet": "npm:^6.1.11" "@types/react-scroll": "npm:^1.8.10" From c905258ddc5587b41febfa4266fd388bf00ee75e Mon Sep 17 00:00:00 2001 From: Ross Bulat <ross@parity.io> Date: Wed, 7 Feb 2024 11:28:36 +0700 Subject: [PATCH 29/51] feat(refactor): Misc project tidy ups (#1933) --- src/App.tsx | 32 +-- src/Page.tsx | 26 +++ src/Router.tsx | 79 +++----- src/Themes.tsx | 10 + src/consts.ts | 8 +- src/contexts/Migrate/index.tsx | 41 ---- src/contexts/Network/index.tsx | 9 +- .../index.tsx => Announcements/Header.tsx} | 10 +- .../Wrapper.ts => Announcements/Wrappers.ts} | 55 ++++- .../{StatsHead => Announcements}/types.ts | 2 +- src/library/GenerateNominations/Wrapper.ts | 10 + src/library/GenerateNominations/index.tsx | 2 +- src/library/Graphs/PayoutBar.tsx | 3 +- src/library/Help/index.tsx | 2 +- src/library/SideMenu/index.tsx | 188 +++++++++--------- src/locale/index.tsx | 28 +-- src/locale/utils.ts | 8 +- .../Overview/NetworkSats/Announcements.tsx | 2 +- src/pages/Overview/NetworkSats/Wrappers.ts | 53 ----- src/pages/Overview/NetworkSats/index.tsx | 6 +- src/pages/Overview/Payouts.tsx | 3 +- src/pages/Overview/StakeStatus/Tips/index.tsx | 3 +- src/pages/Payouts/PayoutList/index.tsx | 4 +- src/pages/Payouts/index.tsx | 4 +- .../Pools/Home/PoolStats/Announcements.tsx | 2 +- src/pages/Pools/Home/PoolStats/Wrappers.ts | 54 ----- src/pages/Pools/Home/PoolStats/index.tsx | 8 +- src/{types/index.ts => types.ts} | 0 28 files changed, 286 insertions(+), 366 deletions(-) create mode 100644 src/Page.tsx rename src/library/{StatsHead/index.tsx => Announcements/Header.tsx} (83%) rename src/library/{StatsHead/Wrapper.ts => Announcements/Wrappers.ts} (63%) rename src/library/{StatsHead => Announcements}/types.ts (90%) create mode 100644 src/library/GenerateNominations/Wrapper.ts delete mode 100644 src/pages/Overview/NetworkSats/Wrappers.ts delete mode 100644 src/pages/Pools/Home/PoolStats/Wrappers.ts rename src/{types/index.ts => types.ts} (100%) diff --git a/src/App.tsx b/src/App.tsx index 67259d4158..cb42545132 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,30 +3,20 @@ import type { FC } from 'react'; import { I18nextProvider } from 'react-i18next'; -import { DefaultNetwork } from 'consts'; import { ThemesProvider } from 'contexts/Themes'; import { i18next } from 'locale'; import { Providers } from 'Providers'; import { NetworkProvider } from 'contexts/Network'; import { ActiveAccountsProvider } from 'contexts/ActiveAccounts'; -export const App: FC = () => { - let network = localStorage.getItem('network'); - - if (network === null) { - network = DefaultNetwork; - localStorage.setItem('network', network); - } - - return ( - <I18nextProvider i18n={i18next}> - <ThemesProvider> - <NetworkProvider> - <ActiveAccountsProvider> - <Providers /> - </ActiveAccountsProvider> - </NetworkProvider> - </ThemesProvider> - </I18nextProvider> - ); -}; +export const App: FC = () => ( + <I18nextProvider i18n={i18next}> + <ThemesProvider> + <NetworkProvider> + <ActiveAccountsProvider> + <Providers /> + </ActiveAccountsProvider> + </NetworkProvider> + </ThemesProvider> + </I18nextProvider> +); diff --git a/src/Page.tsx b/src/Page.tsx new file mode 100644 index 0000000000..9a2880e224 --- /dev/null +++ b/src/Page.tsx @@ -0,0 +1,26 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { useNetwork } from 'contexts/Network'; +import { Helmet } from 'react-helmet'; +import type { PageItem } from 'types'; +import { Page as PageWrapper } from '@polkadot-cloud/react'; +import { useTranslation } from 'react-i18next'; + +export const Page = ({ page }: { page: PageItem }) => { + const { t } = useTranslation(); + const { network } = useNetwork(); + const { Entry, key } = page; + + return ( + <PageWrapper> + <Helmet> + <title>{`${t(key, { ns: 'base' })} : ${t('title', { + context: `${network}`, + ns: 'base', + })}`} + + + + ); +}; diff --git a/src/Router.tsx b/src/Router.tsx index 2a6dd65b18..8b0c788caf 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -1,12 +1,10 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { Body, Main, Page, Side } from '@polkadot-cloud/react'; +import { Body, Main } from '@polkadot-cloud/react'; import { extractUrlValue } from '@polkadot-cloud/utils'; -import { AnimatePresence } from 'framer-motion'; import { useEffect, useRef } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; -import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { HashRouter, @@ -30,42 +28,34 @@ import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useOtherAccounts } from 'contexts/Connect/OtherAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; -import { SideMenuMaximisedWidth } from 'consts'; -import { useTheme } from 'styled-components'; import { Notifications } from 'library/Notifications'; import { NotificationsController } from 'static/NotificationsController'; +import { Page } from 'Page'; export const RouterInner = () => { const { t } = useTranslation(); - const mode = useTheme(); const { network } = useNetwork(); const { pathname } = useLocation(); + const { setContainerRefs } = useUi(); const { accounts } = useImportedAccounts(); const { accountsInitialised } = useOtherAccounts(); const { activeAccount, setActiveAccount } = useActiveAccounts(); - const { sideMenuOpen, sideMenuMinimised, setContainerRefs } = useUi(); + + // References to outer container. + const mainInterfaceRef = useRef(null); // Scroll to top of the window on every page change or network change. useEffect(() => { window.scrollTo(0, 0); }, [pathname, network]); - // Set references to UI context and make available throughout app. + // Set container references to UI context and make available throughout app. useEffect(() => { setContainerRefs({ mainInterface: mainInterfaceRef, }); }, []); - // Update body background to `--background-default` upon theme change. - useEffect(() => { - const elem = document.querySelector('.core-entry'); - if (elem) { - document.getElementsByTagName('body')[0].style.backgroundColor = - getComputedStyle(elem).getPropertyValue('--background-default'); - } - }, [mode]); - // Open default account modal if url var present and accounts initialised. useEffect(() => { if (accountsInitialised) { @@ -86,9 +76,6 @@ export const RouterInner = () => { } }, [accountsInitialised]); - // References to outer containers - const mainInterfaceRef = useRef(null); - return ( {/* Notification popups */} @@ -111,50 +98,32 @@ export const RouterInner = () => { {/* Left side menu */} - - - + {/* Main content window */}
{/* Fixed headers */} + {/* Isolate route errors to `Main` container */} - - - {PagesConfig.map((page, i) => { - const { Entry, hash, key } = page; - - return ( - - - {`${t(key, { ns: 'base' })} : ${t('title', { - context: `${network}`, - ns: 'base', - })}`} - - - - } - /> - ); - })} + + {/* App page routes */} + {PagesConfig.map((page, i) => ( } + key={`main_interface_page_${i}`} + path={page.hash} + element={} /> - - + ))} + + {/* Default route to overview */} + } + /> +
diff --git a/src/Themes.tsx b/src/Themes.tsx index 1aa9738b58..2ac063baf0 100644 --- a/src/Themes.tsx +++ b/src/Themes.tsx @@ -6,6 +6,7 @@ import { Entry } from '@polkadot-cloud/react'; import { Router } from 'Router'; import { useTheme } from 'contexts/Themes'; import { useNetwork } from 'contexts/Network'; +import { useEffect } from 'react'; // light / dark `mode` added to styled-components provider // `@polkadot-cloud/react` themes are added to `Entry`. @@ -13,6 +14,15 @@ export const ThemedRouter = () => { const { mode } = useTheme(); const { network } = useNetwork(); + // Update body background to `--background-default` color upon theme change. + useEffect(() => { + const elem = document.querySelector('.core-entry'); + if (elem) { + document.getElementsByTagName('body')[0].style.backgroundColor = + getComputedStyle(elem).getPropertyValue('--background-default'); + } + }, [mode]); + return ( diff --git a/src/consts.ts b/src/consts.ts index ae91090065..bb796c0608 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -7,11 +7,12 @@ import type { Plugin } from 'types'; /* * Global Constants */ -export const AppVersion = '1.1.3'; +export const AppVersion = '1.1.5'; export const DappName = 'Polkadot Staking Dashboard'; export const PolkadotUrl = 'https://polkadot.network/features/staking/'; export const DefaultNetwork = 'polkadot'; export const ManualSigners = ['ledger', 'vault']; + /* * Data Structure Helpers */ @@ -67,8 +68,3 @@ export const DefaultParams = { minInflation: 0.025, stakeTarget: 0.5, }; - -/* - * locale - */ -export const DefaultLocale = 'en'; diff --git a/src/contexts/Migrate/index.tsx b/src/contexts/Migrate/index.tsx index 43fdfcd635..85024fe1a2 100644 --- a/src/contexts/Migrate/index.tsx +++ b/src/contexts/Migrate/index.tsx @@ -3,11 +3,9 @@ import type { ReactNode } from 'react'; import { createContext, useState } from 'react'; -import { NetworkList } from 'config/networks'; import { AppVersion } from 'consts'; import { useApi } from 'contexts/Api'; import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; -import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { localStorageOrDefault } from '@polkadot-cloud/utils'; import type { ExternalAccount } from '@polkadot-cloud/react/types'; import { useSyncing } from 'hooks/useSyncing'; @@ -16,7 +14,6 @@ export const MigrateContext = createContext(null); export const MigrateProvider = ({ children }: { children: ReactNode }) => { const { isReady } = useApi(); - const { accounts } = useImportedAccounts(); const { syncing } = useSyncing(['initialization']); // The local app version of the current user. @@ -25,28 +22,6 @@ export const MigrateProvider = ({ children }: { children: ReactNode }) => { // Store whether the migration check has taken place. const [done, setDone] = useState(localAppVersion === AppVersion); - // Removes the previous nominator setup objects from local storage. - const removeDeprecatedNominatorSetups = () => - Object.values(NetworkList).forEach((n) => { - for (const a of accounts) { - localStorage.removeItem(`${n.name}_stake_setup_${a.address}`); - } - }); - - // Removes the previous pool setup objects from local storage. - const removeDeprecatedPoolSetups = () => - Object.values(NetworkList).forEach((n) => { - for (const a of accounts) { - localStorage.removeItem(`${n.name}_pool_setup_${a.address}`); - } - }); - - // Removes the previous active proxies from local storage. - const removeDeprecatedActiveProxies = () => - Object.values(NetworkList).forEach((n) => { - localStorage.removeItem(`${n.name}_active_proxy`); - }); - // Removes `system` added external accounts from local storage. const removeSystemExternalAccounts = () => { const current = localStorageOrDefault('external_accounts', [], true); @@ -74,22 +49,6 @@ export const MigrateProvider = ({ children }: { children: ReactNode }) => { if (isReady && !syncing && !done) { // Carry out migrations if local version is different to current version. if (localAppVersion !== AppVersion) { - // Added in 1.0.2. - // - // Remove local language resources. No expiry. - localStorage.removeItem('lng_resources'); - - // Added in 1.0.4. - // - // Remove legacy local nominator setup and pool setup items. - removeDeprecatedNominatorSetups(); - removeDeprecatedPoolSetups(); - - // Added in 1.0.8. - // - // Remove legacy local active proxy records. - removeDeprecatedActiveProxies(); - // Added in 1.1.2 // // Remove local `system` external accounts. diff --git a/src/contexts/Network/index.tsx b/src/contexts/Network/index.tsx index f36f65b6e6..c5b446f1c5 100644 --- a/src/contexts/Network/index.tsx +++ b/src/contexts/Network/index.tsx @@ -37,10 +37,17 @@ export const NetworkProvider = ({ children }: { children: ReactNode }) => { const localNetwork: NetworkName = localStorage.getItem( 'network' ) as NetworkName; + const localNetworkValid = !!Object.values(NetworkList).find( (n) => n.name === localNetwork ); - return localNetworkValid ? localNetwork : DefaultNetwork; + + const initialNetwork = localNetworkValid ? localNetwork : DefaultNetwork; + + // Commit initial to local storage. + localStorage.setItem('network', initialNetwork); + + return initialNetwork; }; // handle network switching diff --git a/src/library/StatsHead/index.tsx b/src/library/Announcements/Header.tsx similarity index 83% rename from src/library/StatsHead/index.tsx rename to src/library/Announcements/Header.tsx index 85f7b86a30..1d176ebc7d 100644 --- a/src/library/StatsHead/index.tsx +++ b/src/library/Announcements/Header.tsx @@ -3,14 +3,14 @@ import { ButtonHelp, ButtonTertiary } from '@polkadot-cloud/react'; import { useHelp } from 'contexts/Help'; -import { Wrapper } from './Wrapper'; -import type { StatsHeadProps } from './types'; +import { HeaderWrapper } from './Wrappers'; +import type { HeaderProps } from './types'; -export const StatsHead = ({ items }: StatsHeadProps) => { +export const Header = ({ items }: HeaderProps) => { const { openHelp } = useHelp(); return ( - + {items.map(({ label, value, button, helpKey }, i) => (
@@ -33,6 +33,6 @@ export const StatsHead = ({ items }: StatsHeadProps) => {
))} -
+ ); }; diff --git a/src/library/StatsHead/Wrapper.ts b/src/library/Announcements/Wrappers.ts similarity index 63% rename from src/library/StatsHead/Wrapper.ts rename to src/library/Announcements/Wrappers.ts index 134e880277..ba44c7d9de 100644 --- a/src/library/StatsHead/Wrapper.ts +++ b/src/library/Announcements/Wrappers.ts @@ -1,10 +1,15 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import styled from 'styled-components'; import { SmallFontSizeMaxWidth } from 'consts'; +import { motion } from 'framer-motion'; +import styled from 'styled-components'; export const Wrapper = styled.div` + flex: 1; + display: flex; + flex-flow: column wrap; + width: 100%; +`; + +export const HeaderWrapper = styled.div` flex-grow: 1; display: flex; flex-flow: row wrap; @@ -77,3 +82,45 @@ export const Wrapper = styled.div` } } `; + +export const Item = styled(motion.div)` + border-bottom: 1px solid var(--border-primary-color); + list-style: none; + flex: 1; + margin-bottom: 1rem; + padding: 0.75rem; + padding-bottom: 1.5rem; + + &:last-child { + border-bottom: 0; + margin-bottom: 0; + } + + h4 { + font-family: Inter, sans-serif; + display: flex; + flex-flow: row wrap; + align-items: center; + margin: 0 0 0.5rem; + padding-bottom: 0.2rem; + + &.neutral { + color: var(--accent-color-primary); + } + &.danger { + color: #d2545d; + } + &.warning { + color: #b5a200; + } + &.pools { + color: var(--accent-color-secondary); + } + } + + p { + color: var(--text-color-secondary); + margin: 0; + line-height: 1.2rem; + } +`; diff --git a/src/library/StatsHead/types.ts b/src/library/Announcements/types.ts similarity index 90% rename from src/library/StatsHead/types.ts rename to src/library/Announcements/types.ts index 8d6fd4a4c4..e220e00db0 100644 --- a/src/library/StatsHead/types.ts +++ b/src/library/Announcements/types.ts @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -export interface StatsHeadProps { +export interface HeaderProps { items: PoolStatLabel[]; } diff --git a/src/library/GenerateNominations/Wrapper.ts b/src/library/GenerateNominations/Wrapper.ts new file mode 100644 index 0000000000..5b92d73eb1 --- /dev/null +++ b/src/library/GenerateNominations/Wrapper.ts @@ -0,0 +1,10 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import styled from 'styled-components'; + +export const Wrapper = styled.div` + display: flex; + flex-flow: column wrap; + width: 100%; +`; diff --git a/src/library/GenerateNominations/index.tsx b/src/library/GenerateNominations/index.tsx index 3b8dc320cb..dec926d037 100644 --- a/src/library/GenerateNominations/index.tsx +++ b/src/library/GenerateNominations/index.tsx @@ -18,7 +18,7 @@ import { SelectableWrapper } from 'library/List'; import { SelectItems } from 'library/SelectItems'; import { SelectItem } from 'library/SelectItems/Item'; import { ValidatorList } from 'library/ValidatorList'; -import { Wrapper } from 'pages/Overview/NetworkSats/Wrappers'; +import { Wrapper } from './Wrapper'; import { useStaking } from 'contexts/Staking'; import { useFavoriteValidators } from 'contexts/Validators/FavoriteValidators'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; diff --git a/src/library/Graphs/PayoutBar.tsx b/src/library/Graphs/PayoutBar.tsx index ba670817e6..f9a921debc 100644 --- a/src/library/Graphs/PayoutBar.tsx +++ b/src/library/Graphs/PayoutBar.tsx @@ -16,10 +16,9 @@ import { import { format, fromUnixTime } from 'date-fns'; import { Bar } from 'react-chartjs-2'; import { useTranslation } from 'react-i18next'; -import { DefaultLocale } from 'consts'; import { useStaking } from 'contexts/Staking'; import { useTheme } from 'contexts/Themes'; -import { locales } from 'locale'; +import { DefaultLocale, locales } from 'locale'; import { graphColors } from 'styles/graphs'; import type { AnyJson, AnySubscan } from 'types'; import { useNetwork } from 'contexts/Network'; diff --git a/src/library/Help/index.tsx b/src/library/Help/index.tsx index c143afdc5f..73613019b1 100644 --- a/src/library/Help/index.tsx +++ b/src/library/Help/index.tsx @@ -13,7 +13,7 @@ import { useAnimation } from 'framer-motion'; import { useCallback, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { HelpConfig } from 'config/help'; -import { DefaultLocale } from 'consts'; +import { DefaultLocale } from 'locale'; import { useHelp } from 'contexts/Help'; import type { DefinitionWithKeys, diff --git a/src/library/SideMenu/index.tsx b/src/library/SideMenu/index.tsx index 8da42525df..336fcae5db 100644 --- a/src/library/SideMenu/index.tsx +++ b/src/library/SideMenu/index.tsx @@ -7,7 +7,7 @@ import { capitalizeFirstLetter } from '@polkadot-cloud/utils'; import throttle from 'lodash.throttle'; import { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { SideMenuStickyThreshold } from 'consts'; +import { SideMenuMaximisedWidth, SideMenuStickyThreshold } from 'consts'; import { useApi } from 'contexts/Api'; import { useHelp } from 'contexts/Help'; import { useTheme } from 'contexts/Themes'; @@ -27,6 +27,7 @@ import { Main } from './Main'; import { Secondary } from './Secondary'; import { ConnectionSymbol, Separator, Wrapper } from './Wrapper'; import { useOutsideAlerter } from 'hooks/useOutsideAlerter'; +import { Side } from '@polkadot-cloud/react'; export const SideMenu = () => { const { t } = useTranslation('base'); @@ -36,6 +37,7 @@ export const SideMenu = () => { const { openModal } = useOverlay().modal; const { setSideMenu, + sideMenuOpen, sideMenuMinimised, userSideMenuMinimised, setUserSideMenuMinimised, @@ -73,103 +75,109 @@ export const SideMenu = () => { : 'danger'; return ( - -
-
- - { - openHelp(null); - }} - name={t('resources')} - minimised={sideMenuMinimised} - icon={{ - Svg: InfoSVG, - size: sideMenuMinimised ? '1.4em' : '1.2em', - }} - /> - openModal({ key: 'GoToFeedback' })} - name={t('feedback')} - minimised={sideMenuMinimised} - icon={{ - Svg: ForumSVG, - size: sideMenuMinimised ? '1.4em' : '1.2em', - }} - /> - - - openModal({ key: 'Networks' })} - icon={{ - Svg: networkData.brand.inline.svg, - size: networkData.brand.inline.size, - }} - minimised={sideMenuMinimised} - action={ - - } - /> -
- -
- - - - - {mode === 'dark' ? ( +
+ +
+ + - ) : ( - )} -
-
+ {mode === 'dark' ? ( + + ) : ( + + )} +
+ + ); }; diff --git a/src/locale/index.tsx b/src/locale/index.tsx index 1d6db77700..e54273c33e 100644 --- a/src/locale/index.tsx +++ b/src/locale/index.tsx @@ -4,7 +4,7 @@ import { enGB, zhCN } from 'date-fns/locale'; import i18next from 'i18next'; import { initReactI18next } from 'react-i18next'; -import { AppVersion, DefaultLocale } from 'consts'; +import { AppVersion } from 'consts'; import type { AnyJson } from 'types'; import baseEn from './en/base.json'; import helpEn from './en/help.json'; @@ -14,19 +14,22 @@ import pagesEn from './en/pages.json'; import tipsEn from './en/tips.json'; import { doDynamicImport, getInitialLanguage, getResources } from './utils'; -// available locales as key value pairs +// The default locale. +export const DefaultLocale = 'en'; + +// Available locales as key value pairs export const locales: Record = { en: enGB, cn: zhCN, }; -// available languages as an array of strings. +// Available languages as an array of strings. export const availableLanguages: string[][] = [ ['en', 'English'], ['cn', '中文'], ]; -// the supported namespaces. +// Supported namespaces. export const lngNamespaces = [ 'base', 'help', @@ -36,7 +39,7 @@ export const lngNamespaces = [ 'tips', ]; -// default structure of language resources. +// Default structure of language resources. export const fallbackResources = { ...baseEn, ...helpEn, @@ -54,17 +57,17 @@ if ( localStorage.removeItem('lng_resources'); } -// get initial language. +// Get initial language. const lng: string = getInitialLanguage(); -// get default resources and whether a dynamic load is required for +// Get default resources and whether a dynamic load is required for // the active language. const { resources, dynamicLoad } = getResources(lng); -// default language to show before any dynamic load +// Default language to show before any dynamic load const defaultLng = dynamicLoad ? DefaultLocale : lng; -// configure i18n object. +// Configure i18n object. i18next.use(initReactI18next).init({ debug: import.meta.env.VITE_DEBUG_I18N === '1', fallbackLng: DefaultLocale, @@ -72,20 +75,19 @@ i18next.use(initReactI18next).init({ resources, }); -// dynamically load default language resources if needed. +// Dynamically load default language resources if needed. if (dynamicLoad) { doDynamicImport(lng, i18next); } -// map i18n to BCP 47 keys, with any custom amendments. +// Map i18n to BCP 47 keys, with any custom amendments. const i18ToLocaleMap: Record = { ...Object.fromEntries(availableLanguages.map((a) => [a[0], a[0]])), en: 'en-gb', cn: 'zh-cn', }; -// convert i18n locale key to BCP 47 key if needed. +// Convert i18n locale key to BCP 47 key if needed. export const i18ToLocale = (l: string) => i18ToLocaleMap[l] || DefaultLocale; -// export i18next for context. export { i18next }; diff --git a/src/locale/utils.ts b/src/locale/utils.ts index 3aa0c1687c..250c916a24 100644 --- a/src/locale/utils.ts +++ b/src/locale/utils.ts @@ -2,9 +2,13 @@ // SPDX-License-Identifier: GPL-3.0-only import { extractUrlValue, varToUrlHash } from '@polkadot-cloud/utils'; -import { DefaultLocale } from 'consts'; +import { + DefaultLocale, + availableLanguages, + fallbackResources, + lngNamespaces, +} from 'locale'; import type { AnyApi, AnyJson } from 'types'; -import { availableLanguages, fallbackResources, lngNamespaces } from '.'; // Gets the active language // diff --git a/src/pages/Overview/NetworkSats/Announcements.tsx b/src/pages/Overview/NetworkSats/Announcements.tsx index e689c1fab5..246564c3f5 100644 --- a/src/pages/Overview/NetworkSats/Announcements.tsx +++ b/src/pages/Overview/NetworkSats/Announcements.tsx @@ -15,9 +15,9 @@ import { useTranslation } from 'react-i18next'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { Announcement as AnnouncementLoader } from 'library/Loader/Announcement'; import { useNetwork } from 'contexts/Network'; -import { Item } from './Wrappers'; import type { BondedPool } from 'contexts/Pools/BondedPools/types'; import { useApi } from 'contexts/Api'; +import { Item } from 'library/Announcements/Wrappers'; export const Announcements = () => { const { t } = useTranslation('pages'); diff --git a/src/pages/Overview/NetworkSats/Wrappers.ts b/src/pages/Overview/NetworkSats/Wrappers.ts deleted file mode 100644 index bf2362717f..0000000000 --- a/src/pages/Overview/NetworkSats/Wrappers.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { motion } from 'framer-motion'; -import styled from 'styled-components'; - -export const Wrapper = styled.div` - display: flex; - flex-flow: column wrap; - width: 100%; -`; - -export const Item = styled(motion.div)` - border-bottom: 1px solid var(--border-primary-color); - list-style: none; - flex: 1; - margin-bottom: 1rem; - padding: 0.75rem; - padding-bottom: 1.5rem; - - &:last-child { - border-bottom: 0; - margin-bottom: 0; - } - - h4 { - font-family: Inter, sans-serif; - display: flex; - flex-flow: row wrap; - align-items: center; - margin: 0 0 0.5rem; - padding-bottom: 0.2rem; - - &.neutral { - color: var(--accent-color-primary); - } - &.danger { - color: #d2545d; - } - &.warning { - color: #b5a200; - } - &.pools { - color: var(--accent-color-secondary); - } - } - - p { - color: var(--text-color-secondary); - margin: 0; - line-height: 1.2rem; - } -`; diff --git a/src/pages/Overview/NetworkSats/index.tsx b/src/pages/Overview/NetworkSats/index.tsx index 90b53fb3bd..25d1fa7a4f 100644 --- a/src/pages/Overview/NetworkSats/index.tsx +++ b/src/pages/Overview/NetworkSats/index.tsx @@ -5,9 +5,9 @@ import BigNumber from 'bignumber.js'; import { useTranslation } from 'react-i18next'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; -import { StatsHead } from 'library/StatsHead'; +import { Header } from 'library/Announcements/Header'; +import { Wrapper } from 'library/Announcements/Wrappers'; import { Announcements } from './Announcements'; -import { Wrapper } from './Wrappers'; import { useAverageRewardRate } from 'hooks/useAverageRewardRate'; import { useApi } from 'contexts/Api'; @@ -52,7 +52,7 @@ export const NetworkStats = () => {

{t('overview.networkStats')}

- +
diff --git a/src/pages/Overview/Payouts.tsx b/src/pages/Overview/Payouts.tsx index e8ee5f84ae..f3a7864efd 100644 --- a/src/pages/Overview/Payouts.tsx +++ b/src/pages/Overview/Payouts.tsx @@ -14,12 +14,11 @@ import { StatusLabel } from 'library/StatusLabel'; import { useSubscanData } from 'hooks/useSubscanData'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; import { Odometer } from '@polkadot-cloud/react'; -import { locales } from 'locale'; +import { locales, DefaultLocale } from 'locale'; import BigNumber from 'bignumber.js'; import { formatDistance, fromUnixTime, getUnixTime } from 'date-fns'; import { minDecimalPlaces, planckToUnit } from '@polkadot-cloud/utils'; import { useNetwork } from 'contexts/Network'; -import { DefaultLocale } from 'consts'; import { useSyncing } from 'hooks/useSyncing'; export const Payouts = () => { diff --git a/src/pages/Overview/StakeStatus/Tips/index.tsx b/src/pages/Overview/StakeStatus/Tips/index.tsx index e62088b842..8503f1c54a 100644 --- a/src/pages/Overview/StakeStatus/Tips/index.tsx +++ b/src/pages/Overview/StakeStatus/Tips/index.tsx @@ -6,7 +6,7 @@ import throttle from 'lodash.throttle'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { TipsConfig } from 'config/tips'; -import { DefaultLocale, TipsThresholdMedium, TipsThresholdSmall } from 'consts'; +import { TipsThresholdMedium, TipsThresholdSmall } from 'consts'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useStaking } from 'contexts/Staking'; import { useTransferOptions } from 'contexts/TransferOptions'; @@ -22,6 +22,7 @@ import type { TipDisplay } from './types'; import { useApi } from 'contexts/Api'; import { useBalances } from 'contexts/Balances'; import { useSyncing } from 'hooks/useSyncing'; +import { DefaultLocale } from 'locale'; export const Tips = () => { const { i18n, t } = useTranslation(); diff --git a/src/pages/Payouts/PayoutList/index.tsx b/src/pages/Payouts/PayoutList/index.tsx index e0a569e44f..6ffe787c32 100644 --- a/src/pages/Payouts/PayoutList/index.tsx +++ b/src/pages/Payouts/PayoutList/index.tsx @@ -9,7 +9,7 @@ import { formatDistance, fromUnixTime } from 'date-fns'; import { motion } from 'framer-motion'; import { Component, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { DefaultLocale, ListItemsPerBatch, ListItemsPerPage } from 'consts'; +import { ListItemsPerBatch, ListItemsPerPage } from 'consts'; import { useApi } from 'contexts/Api'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { StakingContext } from 'contexts/Staking'; @@ -20,7 +20,7 @@ import { MotionContainer } from 'library/List/MotionContainer'; import { Pagination } from 'library/List/Pagination'; import { Identity } from 'library/ListItem/Labels/Identity'; import { PoolIdentity } from 'library/ListItem/Labels/PoolIdentity'; -import { locales } from 'locale'; +import { DefaultLocale, locales } from 'locale'; import type { AnySubscan } from 'types'; import { useNetwork } from 'contexts/Network'; import { ItemWrapper } from '../Wrappers'; diff --git a/src/pages/Payouts/index.tsx b/src/pages/Payouts/index.tsx index 0c622ee384..2ed708e6d7 100644 --- a/src/pages/Payouts/index.tsx +++ b/src/pages/Payouts/index.tsx @@ -4,7 +4,7 @@ import { ButtonHelp, PageRow, PageTitle } from '@polkadot-cloud/react'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { DefaultLocale, MaxPayoutDays } from 'consts'; +import { MaxPayoutDays } from 'consts'; import { useHelp } from 'contexts/Help'; import { usePlugins } from 'contexts/Plugins'; import { useStaking } from 'contexts/Staking'; @@ -22,7 +22,7 @@ import { PayoutList } from './PayoutList'; import { LastEraPayoutStat } from './Stats/LastEraPayout'; import { useSubscanData } from 'hooks/useSubscanData'; import { SubscanController } from 'static/SubscanController'; -import { locales } from 'locale'; +import { DefaultLocale, locales } from 'locale'; import { useSyncing } from 'hooks/useSyncing'; export const Payouts = ({ page: { key } }: PageProps) => { diff --git a/src/pages/Pools/Home/PoolStats/Announcements.tsx b/src/pages/Pools/Home/PoolStats/Announcements.tsx index 993473ccb3..31a570f1bd 100644 --- a/src/pages/Pools/Home/PoolStats/Announcements.tsx +++ b/src/pages/Pools/Home/PoolStats/Announcements.tsx @@ -11,7 +11,7 @@ import { useApi } from 'contexts/Api'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { Announcement as AnnouncementLoader } from 'library/Loader/Announcement'; import { useNetwork } from 'contexts/Network'; -import { Item } from './Wrappers'; +import { Item } from 'library/Announcements/Wrappers'; export const Announcements = () => { const { t } = useTranslation('pages'); diff --git a/src/pages/Pools/Home/PoolStats/Wrappers.ts b/src/pages/Pools/Home/PoolStats/Wrappers.ts deleted file mode 100644 index 67b934b0de..0000000000 --- a/src/pages/Pools/Home/PoolStats/Wrappers.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { motion } from 'framer-motion'; -import styled from 'styled-components'; - -export const Wrapper = styled.div` - flex: 1; - display: flex; - flex-flow: column wrap; - width: 100%; -`; - -export const Item = styled(motion.div)` - border-bottom: 1px solid var(--border-primary-color); - list-style: none; - flex: 1; - margin-bottom: 1rem; - padding: 0.75rem; - padding-bottom: 1.5rem; - - &:last-child { - border-bottom: 0; - margin-bottom: 0; - } - - h4 { - font-family: Inter, sans-serif; - display: flex; - flex-flow: row wrap; - align-items: center; - margin: 0 0 0.5rem; - padding-bottom: 0.2rem; - - &.neutral { - color: var(--accent-color-primary); - } - &.danger { - color: #d2545d; - } - &.warning { - color: #b5a200; - } - &.pools { - color: var(--accent-color-secondary); - } - } - - p { - color: var(--text-color-secondary); - line-height: 1.2rem; - margin: 0; - } -`; diff --git a/src/pages/Pools/Home/PoolStats/index.tsx b/src/pages/Pools/Home/PoolStats/index.tsx index 760711a442..9d82748f67 100644 --- a/src/pages/Pools/Home/PoolStats/index.tsx +++ b/src/pages/Pools/Home/PoolStats/index.tsx @@ -7,12 +7,12 @@ import { useTranslation } from 'react-i18next'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; import { usePoolCommission } from 'hooks/usePoolCommission'; -import { StatsHead } from 'library/StatsHead'; +import { Header } from 'library/Announcements/Header'; import { useNetwork } from 'contexts/Network'; import { Announcements } from './Announcements'; -import { Wrapper } from './Wrappers'; -import type { PoolStatLabel } from 'library/StatsHead/types'; +import type { PoolStatLabel } from 'library/Announcements/types'; import { useOverlay } from '@polkadot-cloud/react/hooks'; +import { Wrapper } from 'library/Announcements/Wrappers'; export const PoolStats = () => { const { t } = useTranslation('pages'); @@ -86,7 +86,7 @@ export const PoolStats = () => {

{t('pools.poolStats')}

- +
diff --git a/src/types/index.ts b/src/types.ts similarity index 100% rename from src/types/index.ts rename to src/types.ts From bb17e88ffa80b0965f55065b8ce9441267e83719 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 7 Feb 2024 12:23:38 +0700 Subject: [PATCH 30/51] chore: fix onClick --- src/library/ErrorBoundary/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/library/ErrorBoundary/index.tsx b/src/library/ErrorBoundary/index.tsx index 957d895a75..07bbc91bb3 100644 --- a/src/library/ErrorBoundary/index.tsx +++ b/src/library/ErrorBoundary/index.tsx @@ -21,7 +21,10 @@ export const ErrorFallbackApp = ({

{t('errorUnknown')}

-

@@ -43,7 +46,10 @@ export const ErrorFallbackRoutes = ({

{t('errorUnknown')}

-

From 070dca405bbb1be9474306398543b2dc2b4466ac Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 7 Feb 2024 13:58:35 +0700 Subject: [PATCH 31/51] chore: amend tsconfig --- src/contexts/Hardware/Vault/VaultAccounts.tsx | 8 +----- tsconfig.json | 27 ++++++++++--------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/contexts/Hardware/Vault/VaultAccounts.tsx b/src/contexts/Hardware/Vault/VaultAccounts.tsx index fdd8b5bf37..4e29dbc224 100644 --- a/src/contexts/Hardware/Vault/VaultAccounts.tsx +++ b/src/contexts/Hardware/Vault/VaultAccounts.tsx @@ -3,13 +3,7 @@ import { ellipsisFn, setStateWithRef } from '@polkadot-cloud/utils'; import type { ReactNode } from 'react'; -import React, { - createContext, - useContext, - useEffect, - useRef, - useState, -} from 'react'; +import { createContext, useContext, useEffect, useRef, useState } from 'react'; import type { VaultAccount } from '@polkadot-cloud/react/types'; import { useNetwork } from 'contexts/Network'; import { getLocalVaultAccounts, isLocalNetworkAddress } from '../Utils'; diff --git a/tsconfig.json b/tsconfig.json index 9a4e47a622..d6f507c4e1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,29 @@ { "compilerOptions": { "baseUrl": "src", + "target": "ESNext", "useDefineForClassFields": true, "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": false, - "skipLibCheck": true, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ "moduleResolution": "Node", + "allowSyntheticDefaultImports": true, + "allowImportingTsExtensions": true, + "allowJs": false, + "esModuleInterop": false, "resolveJsonModule": true, "isolatedModules": true, - "noEmit": true, "jsx": "react-jsx", - "types": [ - "react", - "react-dom", - ], + "noEmit": true, + + /* Linting */ + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, }, "include": [ "src", From 3bbde209f6f14335d3407ddd6f3b3045efad2f44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 21:37:27 +0000 Subject: [PATCH 32/51] chore(deps-dev): bump @types/react-dom from 18.2.18 to 18.2.19 (#1934) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 4d7ea01900..df4a3eb4e4 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "@types/lodash.debounce": "^4", "@types/lodash.throttle": "^4.1.9", "@types/react": "^18.2.55", - "@types/react-dom": "^18.2.17", + "@types/react-dom": "^18.2.19", "@types/react-helmet": "^6.1.11", "@types/react-scroll": "^1.8.10", "@types/styled-components": "^5.1.34", diff --git a/yarn.lock b/yarn.lock index 09241bd905..0d13bc2de8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2175,12 +2175,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^18.2.17": - version: 18.2.18 - resolution: "@types/react-dom@npm:18.2.18" +"@types/react-dom@npm:^18.2.19": + version: 18.2.19 + resolution: "@types/react-dom@npm:18.2.19" dependencies: "@types/react": "npm:*" - checksum: 74dba11a1b8156f3a763f3fca1fb4ec1dcd349153279b8bf79210024a69f994bf2cf0728198c047f8130c5318420ea56281b0a4ef84c8ae943cd9a0cac705220 + checksum: 88d7c6daa4659f661d0c97985d9fca492f24b421a34bb614dcd94c343aed7bea121463149e97fb01ecaa693be17b7d1542cf71ddb1705f3889a81eb2639a88aa languageName: node linkType: hard @@ -6296,7 +6296,7 @@ __metadata: "@types/lodash.debounce": "npm:^4" "@types/lodash.throttle": "npm:^4.1.9" "@types/react": "npm:^18.2.55" - "@types/react-dom": "npm:^18.2.17" + "@types/react-dom": "npm:^18.2.19" "@types/react-helmet": "npm:^6.1.11" "@types/react-scroll": "npm:^1.8.10" "@types/styled-components": "npm:^5.1.34" From 61645b17cb8efb0a1a72532930583ab2c671c1af Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 8 Feb 2024 16:03:22 +0700 Subject: [PATCH 33/51] feat(refactor): Syncing improvements (#1935) --- src/Providers.tsx | 2 + src/canvas/ManageNominations/index.tsx | 2 +- src/contexts/Bonded/index.tsx | 27 +++++- src/contexts/Community/defaults.ts | 9 ++ src/contexts/Community/index.tsx | 29 ++++++ src/contexts/Community/types.ts | 8 ++ src/contexts/Pools/ActivePool/defaults.ts | 1 - src/contexts/Pools/ActivePool/index.tsx | 36 +------- src/contexts/Pools/ActivePool/types.ts | 3 +- src/contexts/Staking/defaults.ts | 1 - src/contexts/Staking/index.tsx | 17 +--- src/contexts/Staking/types.ts | 1 - .../Validators/ValidatorEntries/defaults.ts | 5 +- .../Validators/ValidatorEntries/index.tsx | 89 ++++--------------- src/contexts/Validators/types.ts | 12 ++- src/hooks/useNominationStatus/index.tsx | 13 ++- src/hooks/useValidatorFilters/index.tsx | 6 +- .../Form/CreatePoolStatusBar/index.tsx | 2 +- src/library/Form/NominateStatusBar/index.tsx | 2 +- src/library/Graphs/PayoutBar.tsx | 2 +- src/library/Graphs/PayoutLine.tsx | 2 +- src/library/ListItem/Labels/EraStatus.tsx | 4 +- .../ListItem/Labels/NominationStatus.tsx | 7 +- .../ListItem/Labels/Oversubscribed.tsx | 6 +- src/library/ListItem/types.ts | 7 +- src/library/Nominations/index.tsx | 14 +-- .../ValidatorList/ValidatorItem/index.tsx | 13 +-- src/library/ValidatorList/index.tsx | 23 +++-- src/pages/Community/List.tsx | 4 +- src/pages/Nominate/Active/index.tsx | 7 +- src/pages/Pools/Home/ManagePool/index.tsx | 8 +- src/static/APIController/index.ts | 1 - src/static/SyncController/types.ts | 1 + 33 files changed, 168 insertions(+), 196 deletions(-) create mode 100644 src/contexts/Community/defaults.ts create mode 100644 src/contexts/Community/index.tsx create mode 100644 src/contexts/Community/types.ts diff --git a/src/Providers.tsx b/src/Providers.tsx index e3f7c0610b..9bfccb9f11 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -44,6 +44,7 @@ import { ImportedAccountsProvider } from 'contexts/Connect/ImportedAccounts'; import { PoolPerformanceProvider } from 'contexts/Pools/PoolPerformance'; import { ExternalAccountsProvider } from 'contexts/Connect/ExternalAccounts'; import { withProviders } from 'hooks/withProviders'; +import { CommunityProvider } from 'contexts/Community'; export const Providers = () => { const { @@ -91,6 +92,7 @@ export const Providers = () => { PromptProvider, MigrateProvider, FiltersProvider, + CommunityProvider, ]; return withProviders(providers, ThemedRouter); diff --git a/src/canvas/ManageNominations/index.tsx b/src/canvas/ManageNominations/index.tsx index b4c8e95fb9..f18539b2c7 100644 --- a/src/canvas/ManageNominations/index.tsx +++ b/src/canvas/ManageNominations/index.tsx @@ -38,9 +38,9 @@ export const ManageNominations = () => { } = useOverlay().canvas; const { openHelp } = useHelp(); const { consts, api } = useApi(); + const { activePool } = useActivePool(); const { getBondedAccount } = useBonded(); const { activeAccount } = useActiveAccounts(); - const { activePool } = useActivePool(); const { updatePoolNominations } = useBondedPools(); const { openPromptWith, closePrompt } = usePrompt(); diff --git a/src/contexts/Bonded/index.tsx b/src/contexts/Bonded/index.tsx index 2d8baa5651..4f613574a0 100644 --- a/src/contexts/Bonded/index.tsx +++ b/src/contexts/Bonded/index.tsx @@ -19,6 +19,8 @@ import { useOtherAccounts } from 'contexts/Connect/OtherAccounts'; import { useExternalAccounts } from 'contexts/Connect/ExternalAccounts'; import * as defaults from './defaults'; import type { BondedAccount, BondedContextInterface } from './types'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { SyncController } from 'static/SyncController'; export const BondedContext = createContext( defaults.defaultBondedContext @@ -30,6 +32,7 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); const { api, isReady } = useApi(); const { accounts } = useImportedAccounts(); + const { activeAccount } = useActiveAccounts(); const { addExternalAccount } = useExternalAccounts(); const { addOrReplaceOtherAccount } = useOtherAccounts(); @@ -60,10 +63,23 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { }; // Sync added accounts. const handleAddedAccounts = () => { - addedTo(accounts, bondedAccountsRef.current, ['address'])?.map( - ({ address }) => subscribeToBondedAccount(address) - ); + const added = addedTo(accounts, bondedAccountsRef.current, ['address']); + + if (added.length) { + // If the current active account is being subscribed to, dispatch the `nominator` syncing + // event. + const activeAccountInAdded = added.find( + ({ address }) => address === activeAccount + ); + if (activeAccountInAdded) { + SyncController.dispatch('nominator', 'syncing'); + } + + // Subscribe to all newly added accounts bonded and nominator status. + added.map(({ address }) => subscribeToBondedAccount(address)); + } }; + // Sync existing accounts. const handleExistingAccounts = () => { setStateWithRef( @@ -127,6 +143,11 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { .concat(newAccount); setStateWithRef(newBonded, setBondedAccounts, bondedAccountsRef); + + // If this callback was syncing the active account, mark `nominator` syncing as complete. + if (address === activeAccount) { + SyncController.dispatch('nominator', 'complete'); + } } ); diff --git a/src/contexts/Community/defaults.ts b/src/contexts/Community/defaults.ts new file mode 100644 index 0000000000..5ae2551f54 --- /dev/null +++ b/src/contexts/Community/defaults.ts @@ -0,0 +1,9 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import type { CommunityContextInterface } from './types'; + +export const defaultCommunityContext: CommunityContextInterface = { + validatorCommunity: [], +}; diff --git a/src/contexts/Community/index.tsx b/src/contexts/Community/index.tsx new file mode 100644 index 0000000000..4a89aa37ea --- /dev/null +++ b/src/contexts/Community/index.tsx @@ -0,0 +1,29 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { ValidatorEntry } from '@polkadot-cloud/assets/types'; +import { ValidatorCommunity } from '@polkadot-cloud/assets/validators'; +import { shuffle } from '@polkadot-cloud/utils'; +import type { ReactNode } from 'react'; +import { createContext, useContext, useState } from 'react'; +import { defaultCommunityContext } from './defaults'; +import type { CommunityContextInterface } from './types'; + +export const CommunityContext = createContext( + defaultCommunityContext +); + +export const useCommunity = () => useContext(CommunityContext); + +export const CommunityProvider = ({ children }: { children: ReactNode }) => { + // Stores a randomised validator community dataset. + const [validatorCommunity] = useState([ + ...shuffle(ValidatorCommunity), + ]); + + return ( + + {children} + + ); +}; diff --git a/src/contexts/Community/types.ts b/src/contexts/Community/types.ts new file mode 100644 index 0000000000..5236dd4f4f --- /dev/null +++ b/src/contexts/Community/types.ts @@ -0,0 +1,8 @@ +// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { ValidatorEntry } from '@polkadot-cloud/assets/types'; + +export interface CommunityContextInterface { + validatorCommunity: ValidatorEntry[]; +} diff --git a/src/contexts/Pools/ActivePool/defaults.ts b/src/contexts/Pools/ActivePool/defaults.ts index 6e27e90bf8..c877aa8384 100644 --- a/src/contexts/Pools/ActivePool/defaults.ts +++ b/src/contexts/Pools/ActivePool/defaults.ts @@ -28,7 +28,6 @@ export const defaultActivePoolContext: ActivePoolContextState = { isBouncer: () => false, getPoolUnlocking: () => [], getPoolRoles: () => defaultPoolRoles, - getNominationsStatus: () => nominationStatus, setActivePoolId: (p) => {}, activePool: null, activePoolNominations: null, diff --git a/src/contexts/Pools/ActivePool/index.tsx b/src/contexts/Pools/ActivePool/index.tsx index a3348287cd..c617b1272b 100644 --- a/src/contexts/Pools/ActivePool/index.tsx +++ b/src/contexts/Pools/ActivePool/index.tsx @@ -11,7 +11,6 @@ import { useRef, useState, } from 'react'; -import { useStaking } from 'contexts/Staking'; import type { Sync } from 'types'; import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; import { usePlugins } from 'contexts/Plugins'; @@ -25,11 +24,7 @@ import { SubscanController } from 'static/SubscanController'; import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; import { useBalances } from 'contexts/Balances'; import { ActivePoolsController } from 'static/ActivePoolsController'; -import { - defaultActivePoolContext, - defaultPoolNominations, - defaultPoolRoles, -} from './defaults'; +import { defaultActivePoolContext, defaultPoolRoles } from './defaults'; import { SyncController } from 'static/SyncController'; import { useActivePools } from 'hooks/useActivePools'; import BigNumber from 'bignumber.js'; @@ -44,7 +39,6 @@ export const useActivePool = () => useContext(ActivePoolContext); export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { const { isReady } = useApi(); const { network } = useNetwork(); - const { eraStakers } = useStaking(); const { pluginEnabled } = usePlugins(); const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); @@ -115,9 +109,6 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { // Keep track of whether the pool member count is being fetched. const fetchingMemberCount = useRef('unsynced'); - const getActivePoolNominations = () => - activePoolNominations || defaultPoolNominations; - // Sync active pool subscriptions. const syncActivePoolSubscriptions = async () => { if (accountPoolIds.length) { @@ -190,30 +181,6 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { return activeAccount === roles?.bouncer; }; - // Get the status of nominations. Possible statuses: waiting, inactive, active. - const getNominationsStatus = () => { - const statuses: Record = {}; - - for (const nomination of getActivePoolNominations().targets) { - const staker = eraStakers.stakers.find( - ({ address }) => address === nomination - ); - if (staker === undefined) { - statuses[nomination] = 'waiting'; - continue; - } - const exists = (staker.others || []).find( - ({ who }) => who === activeAccount - ); - if (exists === undefined) { - statuses[nomination] = 'inactive'; - continue; - } - statuses[nomination] = 'active'; - } - return statuses; - }; - // Returns the active pool's roles or the default roles object. const getPoolRoles = () => activePool?.bondedPool?.roles || defaultPoolRoles; @@ -325,7 +292,6 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { isBonding, getPoolUnlocking, getPoolRoles, - getNominationsStatus, setActivePoolId, activePool, activePoolMemberCount, diff --git a/src/contexts/Pools/ActivePool/types.ts b/src/contexts/Pools/ActivePool/types.ts index 6b4bfa5647..9090e7651c 100644 --- a/src/contexts/Pools/ActivePool/types.ts +++ b/src/contexts/Pools/ActivePool/types.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import type BigNumber from 'bignumber.js'; -import type { NominationStatuses, PoolAddresses } from '../BondedPools/types'; +import type { PoolAddresses } from '../BondedPools/types'; import type { MaybeAddress } from '@polkadot-cloud/react/types'; import type { Nominations } from 'contexts/Bonded/types'; import type { Identity, SuperIdentity } from 'contexts/Validators/types'; @@ -16,7 +16,6 @@ export interface ActivePoolContextState { isBouncer: () => boolean; getPoolUnlocking: () => PoolUnlocking[]; getPoolRoles: () => PoolRoles; - getNominationsStatus: () => NominationStatuses; setActivePoolId: (p: string) => void; activePool: ActivePool | null; activePoolNominations: Nominations | null; diff --git a/src/contexts/Staking/defaults.ts b/src/contexts/Staking/defaults.ts index 2889400f83..315dad3f2a 100644 --- a/src/contexts/Staking/defaults.ts +++ b/src/contexts/Staking/defaults.ts @@ -41,6 +41,5 @@ export const defaultStakingContext: StakingContextInterface = { getLowestRewardFromStaker: (address) => defaultLowestReward, eraStakers: defaultEraStakers, targets: defaultTargets, - erasStakersSyncing: true, getPagedErasStakers: (e) => new Promise((resolve) => resolve([])), }; diff --git a/src/contexts/Staking/index.tsx b/src/contexts/Staking/index.tsx index ce9d2592dc..9f041ea77a 100644 --- a/src/contexts/Staking/index.tsx +++ b/src/contexts/Staking/index.tsx @@ -51,23 +51,19 @@ export const useStaking = () => useContext(StakingContext); export const StakingProvider = ({ children }: { children: ReactNode }) => { const { getLedger } = useBalances(); - const { isReady, api, apiStatus, consts, activeEra, isPagedRewardsActive } = - useApi(); const { networkData, network } = useNetwork(); const { accounts: connectAccounts } = useImportedAccounts(); const { activeAccount, getActiveAccount } = useActiveAccounts(); const { bondedAccounts, getBondedAccount, getAccountNominations } = useBonded(); + const { isReady, api, apiStatus, consts, activeEra, isPagedRewardsActive } = + useApi(); const { maxExposurePageSize } = consts; // Store eras stakers in state. const [eraStakers, setEraStakers] = useState(defaultEraStakers); const eraStakersRef = useRef(eraStakers); - // Flags whether `eraStakers` is resyncing. - const [erasStakersSyncing, setErasStakersSyncing] = useState(false); - const erasStakersSyncingRef = useRef(erasStakersSyncing); - // Store target validators for the active account. const [targets, setTargetsState] = useState( localStorageOrDefault( @@ -99,9 +95,6 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { who, } = data; - // finish sync - setStateWithRef(false, setErasStakersSyncing, erasStakersSyncingRef); - // check if account hasn't changed since worker started if (getActiveAccount() === who) { // Syncing current eraStakers is now complete. @@ -155,8 +148,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { return; } - // flag eraStakers is recyncing - setStateWithRef(true, setErasStakersSyncing, erasStakersSyncingRef); + SyncController.dispatch('era-stakers', 'syncing'); const exposures = await fetchEraStakers(activeEra.index.toString()); @@ -380,8 +372,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { isNominating, inSetup, getLowestRewardFromStaker, - eraStakers: eraStakersRef.current, - erasStakersSyncing: erasStakersSyncingRef.current, + eraStakers, targets, getPagedErasStakers, }} diff --git a/src/contexts/Staking/types.ts b/src/contexts/Staking/types.ts index cd995087cd..95e4c09131 100644 --- a/src/contexts/Staking/types.ts +++ b/src/contexts/Staking/types.ts @@ -70,7 +70,6 @@ export interface StakingContextInterface { getLowestRewardFromStaker: (a: MaybeAddress) => LowestReward; eraStakers: EraStakers; targets: StakingTargets; - erasStakersSyncing: boolean; getPagedErasStakers: (e: string) => Promise; } diff --git a/src/contexts/Validators/ValidatorEntries/defaults.ts b/src/contexts/Validators/ValidatorEntries/defaults.ts index a1bcbcb79f..bbb76689ec 100644 --- a/src/contexts/Validators/ValidatorEntries/defaults.ts +++ b/src/contexts/Validators/ValidatorEntries/defaults.ts @@ -13,7 +13,6 @@ export const defaultAverageEraValidatorReward = { export const defaultValidatorsContext: ValidatorsContextInterface = { fetchValidatorPrefs: async (a) => new Promise((resolve) => resolve(null)), getValidatorPointsFromEras: (startEra, address) => ({}), - getNominated: (bondFor) => [], injectValidatorListData: (entries) => [], validators: [], validatorIdentities: {}, @@ -21,15 +20,13 @@ export const defaultValidatorsContext: ValidatorsContextInterface = { avgCommission: 0, sessionValidators: [], sessionParaValidators: [], - nominated: null, - poolNominated: null, - validatorCommunity: [], erasRewardPoints: {}, validatorsFetched: 'unsynced', eraPointsBoundaries: null, validatorEraPointsHistory: {}, erasRewardPointsFetched: 'unsynced', averageEraValidatorReward: defaultAverageEraValidatorReward, + formatWithPrefs: (addresses) => [], }; export const defaultValidatorsData = { diff --git a/src/contexts/Validators/ValidatorEntries/index.tsx b/src/contexts/Validators/ValidatorEntries/index.tsx index 33c732b088..98c4f99a5e 100644 --- a/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/src/contexts/Validators/ValidatorEntries/index.tsx @@ -5,14 +5,10 @@ import { greaterThanZero, rmCommas, shuffle } from '@polkadot-cloud/utils'; import BigNumber from 'bignumber.js'; import type { ReactNode } from 'react'; import { createContext, useContext, useEffect, useRef, useState } from 'react'; -import { ValidatorCommunity } from '@polkadot-cloud/assets/validators'; -import type { AnyApi, AnyJson, BondFor, Fn, Sync } from 'types'; +import type { AnyApi, AnyJson, Fn, Sync } from 'types'; import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks'; -import { useBonded } from 'contexts/Bonded'; -import { useActivePool } from 'contexts/Pools/ActivePool'; import { useNetwork } from 'contexts/Network'; import { useApi } from 'contexts/Api'; -import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { MaxEraRewardPointsEras } from 'consts'; import { useStaking } from 'contexts/Staking'; import type { @@ -25,6 +21,7 @@ import type { ValidatorListEntry, ValidatorsContextInterface, ValidatorEraPointHistory, + ValidatorStatus, } from '../types'; import { defaultAverageEraValidatorReward, @@ -33,7 +30,6 @@ import { defaultEraPointsBoundaries, } from './defaults'; import { getLocalEraValidators, setLocalEraValidators } from '../Utils'; -import type { ValidatorEntry } from '@polkadot-cloud/assets/types'; import { useErasPerDay } from 'hooks/useErasPerDay'; import { IdentitiesController } from 'static/IdentitiesController'; @@ -53,10 +49,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { } = useApi(); const { activeEra } = useApi(); const { stakers } = useStaking().eraStakers; - const { activeAccount } = useActiveAccounts(); - const { activePoolNominations } = useActivePool(); const { erasPerDay, maxSupportedDays } = useErasPerDay(); - const { bondedAccounts, getAccountNominations } = useBonded(); // Stores all validator entries. const [validators, setValidators] = useState([]); @@ -88,17 +81,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Stores the average network commission rate. const [avgCommission, setAvgCommission] = useState(0); - // Stores the user's nominated validators as list - const [nominated, setNominated] = useState(null); - - // Stores the nominated validators by the members pool's as list - const [poolNominated, setPoolNominated] = useState(null); - - // Stores a randomised validator community dataset. - const [validatorCommunity] = useState([ - ...shuffle(ValidatorCommunity), - ]); - // Track whether the validator list has been fetched. const [erasRewardPointsFetched, setErasRewawrdPointsFetched] = useState('unsynced'); @@ -253,33 +235,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { setValidatorEraPointsHistory(newEraPointsHistory); }; - // Fetches the active account's nominees. - const fetchNominatedList = async () => { - if (!activeAccount) { - return; - } - - // format to list format - const targetsFormatted = getAccountNominations(activeAccount).map( - (item) => ({ address: item }) - ); - // fetch preferences - const nominationsWithPrefs = await fetchValidatorPrefs(targetsFormatted); - setNominated(nominationsWithPrefs || []); - }; - - // Fetches the active pool's nominees. - const fetchPoolNominatedList = async () => { - // get raw nominations list - const n = activePoolNominations?.targets || []; - - // fetch preferences - const nominationsWithPrefs = await fetchValidatorPrefs( - n.map((item: string) => ({ address: item })) - ); - setPoolNominated(nominationsWithPrefs || []); - }; - // Fetch validator entries and format the returning data. const getValidatorEntries = async () => { if (!isReady || !api) { @@ -417,6 +372,16 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { return formatted; }; + // Formats a list of addresses with validator preferences. + const formatWithPrefs = (addresses: string[]) => + addresses.map((address) => ({ + address, + prefs: validators.find((v) => v.address === address)?.prefs || { + blocked: false, + commission: 0, + }, + })); + // Gets era points for a validator const getValidatorPointsFromEras = (startEra: BigNumber, address: string) => { startEra = BigNumber.max(startEra, 1); @@ -467,11 +432,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { setErasRewawrdPointsFetched('synced'); }; - // Gets either `nominated` or `poolNominated` depending on bondFor, and injects the validator - // status into the entries. - const getNominated = (bondFor: BondFor) => - bondFor === 'nominator' ? nominated : poolNominated; - // Inject status into validator entries. const injectValidatorListData = ( entries: Validator[] @@ -480,8 +440,11 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { entries.map((entry) => { const inEra = stakers.find(({ address }) => address === entry.address) || false; + let totalStake = new BigNumber(0); + let validatorStatus: ValidatorStatus = 'waiting'; if (inEra) { + validatorStatus = 'active'; const { others, own } = inEra; if (own) { totalStake = totalStake.plus(own); @@ -493,9 +456,10 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { return { ...entry, totalStake, - validatorStatus: inEra ? 'active' : 'waiting', + validatorStatus, }; }) || []; + return injected; }; @@ -596,20 +560,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { } }, [isReady, earliestStoredSession]); - // Fetch active account's nominations in validator list format. - useEffectIgnoreInitial(() => { - if (isReady && activeAccount) { - fetchNominatedList(); - } - }, [isReady, activeAccount, bondedAccounts]); - - // Fetch active account's pool nominations in validator list format. - useEffectIgnoreInitial(() => { - if (isReady && activePoolNominations) { - fetchPoolNominatedList(); - } - }, [isReady, activePoolNominations]); - // Unsubscribe on network change and component unmount. useEffect(() => { if (sessionParaValidators.length) { @@ -626,7 +576,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { value={{ fetchValidatorPrefs, getValidatorPointsFromEras, - getNominated, injectValidatorListData, validators, validatorIdentities, @@ -634,15 +583,13 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { avgCommission, sessionValidators, sessionParaValidators, - nominated, - poolNominated, - validatorCommunity, erasRewardPoints, validatorsFetched, eraPointsBoundaries, validatorEraPointsHistory, erasRewardPointsFetched, averageEraValidatorReward, + formatWithPrefs, }} > {children} diff --git a/src/contexts/Validators/types.ts b/src/contexts/Validators/types.ts index 26d9b6ec85..59e0637f4c 100644 --- a/src/contexts/Validators/types.ts +++ b/src/contexts/Validators/types.ts @@ -1,9 +1,8 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ValidatorEntry } from '@polkadot-cloud/assets/types'; import type BigNumber from 'bignumber.js'; -import type { AnyJson, BondFor, Sync } from 'types'; +import type { AnyJson, Sync } from 'types'; export interface ValidatorsContextInterface { fetchValidatorPrefs: (a: ValidatorAddresses) => Promise; @@ -11,7 +10,6 @@ export interface ValidatorsContextInterface { startEra: BigNumber, address: string ) => Record; - getNominated: (bondFor: BondFor) => Validator[] | null; injectValidatorListData: (entries: Validator[]) => ValidatorListEntry[]; validators: Validator[]; validatorIdentities: Record; @@ -19,17 +17,17 @@ export interface ValidatorsContextInterface { avgCommission: number; sessionValidators: string[]; sessionParaValidators: string[]; - nominated: Validator[] | null; - poolNominated: Validator[] | null; - validatorCommunity: ValidatorEntry[]; erasRewardPoints: ErasRewardPoints; validatorsFetched: Sync; eraPointsBoundaries: EraPointsBoundaries; validatorEraPointsHistory: Record; erasRewardPointsFetched: Sync; averageEraValidatorReward: AverageEraValidatorReward; + formatWithPrefs: (addresses: string[]) => Validator[]; } +export type ValidatorStatus = 'waiting' | 'active'; + export interface AverageEraValidatorReward { days: number; reward: BigNumber; @@ -86,7 +84,7 @@ export type EraPointsBoundaries = { } | null; export type ValidatorListEntry = Validator & { - validatorStatus: 'waiting' | 'active'; + validatorStatus: ValidatorStatus; totalStake: BigNumber; }; diff --git a/src/hooks/useNominationStatus/index.tsx b/src/hooks/useNominationStatus/index.tsx index 7349ec6dd8..e30dce588b 100644 --- a/src/hooks/useNominationStatus/index.tsx +++ b/src/hooks/useNominationStatus/index.tsx @@ -14,23 +14,22 @@ import { useSyncing } from 'hooks/useSyncing'; export const useNominationStatus = () => { const { t } = useTranslation(); - const { syncing } = useSyncing(['era-stakers']); const { networkData: { units }, } = useNetwork(); const { inSetup, eraStakers, - erasStakersSyncing, getNominationsStatusFromTargets, getLowestRewardFromStaker, } = useStaking(); const { validators } = useValidators(); - const { activePoolNominations } = useActivePool(); const { getAccountNominations } = useBonded(); + const { activePoolNominations } = useActivePool(); + const { syncing } = useSyncing(['era-stakers']); // Utility to get an account's nominees alongside their status. - const getNomineesStatus = (who: MaybeAddress, type: BondFor) => { + const getNominationSetStatus = (who: MaybeAddress, type: BondFor) => { const nominations = type === 'nominator' ? getAccountNominations(who) @@ -49,13 +48,13 @@ export const useNominationStatus = () => { // reards. const getNominationStatus = (who: MaybeAddress, type: BondFor) => { // Get the sets nominees from the provided account's targets. - const nominees = Object.entries(getNomineesStatus(who, type)); + const nominees = Object.entries(getNominationSetStatus(who, type)); const activeNominees = getNomineesByStatus(nominees, 'active'); // Determine whether active nominees are earning rewards. This function exists once the // eras stakers has synced. let earningRewards = false; - if (!erasStakersSyncing) { + if (!syncing) { getNomineesByStatus(nominees, 'active').every((nominee) => { const validator = validators.find(({ address }) => address === nominee); @@ -116,5 +115,5 @@ export const useNominationStatus = () => { }; }; - return { getNominationStatus, getNomineesStatus }; + return { getNominationStatus, getNominationSetStatus }; }; diff --git a/src/hooks/useValidatorFilters/index.tsx b/src/hooks/useValidatorFilters/index.tsx index eaf50c03b8..d3b73864ec 100644 --- a/src/hooks/useValidatorFilters/index.tsx +++ b/src/hooks/useValidatorFilters/index.tsx @@ -8,6 +8,7 @@ import type { AnyFunction, AnyJson } from 'types'; import { useStaking } from 'contexts/Staking'; import { MaxEraRewardPointsEras } from 'consts'; import type { AnyFilter } from 'library/Filter/types'; +import { useSyncing } from 'hooks/useSyncing'; export const useValidatorFilters = () => { const { t } = useTranslation('library'); @@ -18,7 +19,8 @@ export const useValidatorFilters = () => { validatorSupers, validatorEraPointsHistory, } = useValidators(); - const { erasStakersSyncing, getLowestRewardFromStaker } = useStaking(); + const { syncing } = useSyncing(['era-stakers']); + const { getLowestRewardFromStaker } = useStaking(); /* * filterMissingIdentity: Iterates through the supplied list and filters those with missing @@ -52,7 +54,7 @@ export const useValidatorFilters = () => { */ const filterOverSubscribed = (list: AnyFilter) => { // Return list early if eraStakers is still syncing. - if (erasStakersSyncing) { + if (syncing) { return list; } diff --git a/src/library/Form/CreatePoolStatusBar/index.tsx b/src/library/Form/CreatePoolStatusBar/index.tsx index 174c29192f..c1ad9c6790 100644 --- a/src/library/Form/CreatePoolStatusBar/index.tsx +++ b/src/library/Form/CreatePoolStatusBar/index.tsx @@ -13,9 +13,9 @@ import { useSyncing } from 'hooks/useSyncing'; export const CreatePoolStatusBar = ({ value }: NominateStatusBarProps) => { const { t } = useTranslation('library'); - const { syncing } = useSyncing('*'); const { minCreateBond } = useApi().poolsConfig; const { unit, units } = useNetwork().networkData; + const { syncing } = useSyncing(['initialization']); const minCreateBondUnit = planckToUnit(minCreateBond, units); const sectionClassName = diff --git a/src/library/Form/NominateStatusBar/index.tsx b/src/library/Form/NominateStatusBar/index.tsx index 1521b73dcb..8fa84f8cb6 100644 --- a/src/library/Form/NominateStatusBar/index.tsx +++ b/src/library/Form/NominateStatusBar/index.tsx @@ -16,12 +16,12 @@ import { useSyncing } from 'hooks/useSyncing'; export const NominateStatusBar = ({ value }: NominateStatusBarProps) => { const { t } = useTranslation('library'); const { openHelp } = useHelp(); - const { syncing } = useSyncing('*'); const { networkMetrics: { minimumActiveStake }, stakingMetrics: { minNominatorBond }, } = useApi(); const { unit, units } = useNetwork().networkData; + const { syncing } = useSyncing(['initialization']); const minNominatorBondUnit = planckToUnit(minNominatorBond, units); const minimumActiveStakeUnit = planckToUnit(minimumActiveStake, units); diff --git a/src/library/Graphs/PayoutBar.tsx b/src/library/Graphs/PayoutBar.tsx index f9a921debc..89b0d77697 100644 --- a/src/library/Graphs/PayoutBar.tsx +++ b/src/library/Graphs/PayoutBar.tsx @@ -47,8 +47,8 @@ export const PayoutBar = ({ const { i18n, t } = useTranslation('library'); const { mode } = useTheme(); const { inSetup } = useStaking(); - const { syncing } = useSyncing('*'); const { getPoolMembership } = useBalances(); + const { syncing } = useSyncing(['nominator']); const { activeAccount } = useActiveAccounts(); const membership = getPoolMembership(activeAccount); diff --git a/src/library/Graphs/PayoutLine.tsx b/src/library/Graphs/PayoutLine.tsx index 38ebc4dc0a..9676ea7deb 100644 --- a/src/library/Graphs/PayoutLine.tsx +++ b/src/library/Graphs/PayoutLine.tsx @@ -49,7 +49,7 @@ export const PayoutLine = ({ const { t } = useTranslation('library'); const { mode } = useTheme(); const { inSetup } = useStaking(); - const { syncing } = useSyncing('*'); + const { syncing } = useSyncing(['nominator']); const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); diff --git a/src/library/ListItem/Labels/EraStatus.tsx b/src/library/ListItem/Labels/EraStatus.tsx index 6711e9d6b6..6bc4d594be 100644 --- a/src/library/ListItem/Labels/EraStatus.tsx +++ b/src/library/ListItem/Labels/EraStatus.tsx @@ -3,7 +3,6 @@ import { capitalizeFirstLetter, planckToUnit } from '@polkadot-cloud/utils'; import { useTranslation } from 'react-i18next'; -import { useStaking } from 'contexts/Staking'; import { ValidatorStatusWrapper } from 'library/ListItem/Wrappers'; import { useNetwork } from 'contexts/Network'; import type { EraStatusProps } from '../types'; @@ -12,7 +11,6 @@ import { useSyncing } from 'hooks/useSyncing'; export const EraStatus = ({ noMargin, status, totalStake }: EraStatusProps) => { const { t } = useTranslation('library'); const { syncing } = useSyncing('*'); - const { erasStakersSyncing } = useStaking(); const { unit, units } = useNetwork().networkData; // Fallback to `waiting` status if still syncing. @@ -21,7 +19,7 @@ export const EraStatus = ({ noMargin, status, totalStake }: EraStatusProps) => { return (
- {syncing || erasStakersSyncing + {syncing ? t('syncing') : validatorStatus !== 'waiting' ? `${t('listItemActive')} / ${planckToUnit(totalStake, units) diff --git a/src/library/ListItem/Labels/NominationStatus.tsx b/src/library/ListItem/Labels/NominationStatus.tsx index 09e3a50adf..62c13740c9 100644 --- a/src/library/ListItem/Labels/NominationStatus.tsx +++ b/src/library/ListItem/Labels/NominationStatus.tsx @@ -8,6 +8,7 @@ import { useStaking } from 'contexts/Staking'; import { ValidatorStatusWrapper } from 'library/ListItem/Wrappers'; import { useNetwork } from 'contexts/Network'; import type { NominationStatusProps } from '../types'; +import { useSyncing } from 'hooks/useSyncing'; export const NominationStatus = ({ address, @@ -22,8 +23,8 @@ export const NominationStatus = ({ } = useNetwork(); const { eraStakers: { activeAccountOwnStake, stakers }, - erasStakersSyncing, } = useStaking(); + const { syncing } = useSyncing(['era-stakers']); // determine staked amount let stakedAmount = new BigNumber(0); @@ -48,9 +49,7 @@ export const NominationStatus = ({
{t(`${status || 'waiting'}`)} {greaterThanZero(stakedAmount) - ? ` / ${ - erasStakersSyncing ? '...' : `${stakedAmount.toFormat()} ${unit}` - }` + ? ` / ${syncing ? '...' : `${stakedAmount.toFormat()} ${unit}`}` : null}
diff --git a/src/library/ListItem/Labels/Oversubscribed.tsx b/src/library/ListItem/Labels/Oversubscribed.tsx index 0b6147f610..d440c3cb09 100644 --- a/src/library/ListItem/Labels/Oversubscribed.tsx +++ b/src/library/ListItem/Labels/Oversubscribed.tsx @@ -14,6 +14,7 @@ import { import { useStaking } from 'contexts/Staking'; import { useNetwork } from 'contexts/Network'; import type { OversubscribedProps } from '../types'; +import { useSyncing } from 'hooks/useSyncing'; export const Oversubscribed = ({ address }: OversubscribedProps) => { const { t } = useTranslation('library'); @@ -21,11 +22,12 @@ export const Oversubscribed = ({ address }: OversubscribedProps) => { networkData: { unit }, } = useNetwork(); const { setTooltipTextAndOpen } = useTooltip(); - const { erasStakersSyncing, getLowestRewardFromStaker } = useStaking(); + const { syncing } = useSyncing(['era-stakers']); + const { getLowestRewardFromStaker } = useStaking(); const { lowest, oversubscribed } = getLowestRewardFromStaker(address); - const displayOversubscribed = !erasStakersSyncing && oversubscribed; + const displayOversubscribed = !syncing && oversubscribed; const lowestRewardFormatted = lowest .decimalPlaces(MinBondPrecision) diff --git a/src/library/ListItem/types.ts b/src/library/ListItem/types.ts index e1ea39a967..f86e8b1a18 100644 --- a/src/library/ListItem/types.ts +++ b/src/library/ListItem/types.ts @@ -2,7 +2,10 @@ // SPDX-License-Identifier: GPL-3.0-only import type { BondFor, MaybeAddress } from 'types'; -import type { ValidatorPrefs } from 'contexts/Validators/types'; +import type { + ValidatorPrefs, + ValidatorStatus, +} from 'contexts/Validators/types'; import type BigNumber from 'bignumber.js'; import type { NominationStatus } from 'library/ValidatorList/ValidatorItem/types'; import type { BondedPool } from 'contexts/Pools/BondedPools/types'; @@ -59,5 +62,5 @@ export interface EraStatusProps { address: MaybeAddress; noMargin: boolean; totalStake: BigNumber; - status: 'waiting' | 'active'; + status: ValidatorStatus; } diff --git a/src/library/Nominations/index.tsx b/src/library/Nominations/index.tsx index fc655e969c..e9ba205ca7 100644 --- a/src/library/Nominations/index.tsx +++ b/src/library/Nominations/index.tsx @@ -41,8 +41,8 @@ export const Nominations = ({ canvas: { openCanvas }, } = useOverlay(); const { syncing } = useSyncing('*'); - const { getNominated } = useValidators(); const { isFastUnstaking } = useUnstaking(); + const { formatWithPrefs } = useValidators(); const { activeAccount } = useActiveAccounts(); const { getAccountNominations } = useBonded(); const { isReadOnlyAccount } = useImportedAccounts(); @@ -51,10 +51,12 @@ export const Nominations = ({ const isPool = bondFor === 'pool'; // Derive nominations from `bondFor` type. - const nominations = isPool - ? activePoolNominations?.targets || [] - : getAccountNominations(nominator); - const nominated = getNominated(bondFor); + const nominated = + bondFor === 'nominator' + ? formatWithPrefs(getAccountNominations(activeAccount)) + : activePoolNominations + ? formatWithPrefs(activePoolNominations.targets) + : []; // Determine if this nominator is actually nominating. const isNominating = nominated?.length ?? false; @@ -68,7 +70,7 @@ export const Nominations = ({ // If regular staking and nominating, or if pool and account is nominator or root, display stop // button. const displayBtns = - (!isPool && nominations.length) || + (!isPool && nominated.length) || (isPool && (isPoolNominator() || isPoolOwner())); // Determine whether buttons are disabled. diff --git a/src/library/ValidatorList/ValidatorItem/index.tsx b/src/library/ValidatorList/ValidatorItem/index.tsx index b070c314d4..4cd4741d04 100644 --- a/src/library/ValidatorList/ValidatorItem/index.tsx +++ b/src/library/ValidatorList/ValidatorItem/index.tsx @@ -1,12 +1,11 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { Component } from 'react'; import { Default } from './Default'; import { Nomination } from './Nomination'; import type { ValidatorItemProps } from './types'; -export const ValidatorItemInner = (props: ValidatorItemProps) => { +export const ValidatorItem = (props: ValidatorItemProps) => { const { format } = props; return format === 'nomination' ? ( @@ -15,13 +14,3 @@ export const ValidatorItemInner = (props: ValidatorItemProps) => { ); }; - -export class ValidatorItem extends Component { - shouldComponentUpdate(nextProps: ValidatorItemProps) { - return this.props.validator.address !== nextProps.validator.address; - } - - render() { - return ; - } -} diff --git a/src/library/ValidatorList/index.tsx b/src/library/ValidatorList/index.tsx index 7a4aedb7e4..ccab7612f5 100644 --- a/src/library/ValidatorList/index.tsx +++ b/src/library/ValidatorList/index.tsx @@ -38,24 +38,27 @@ import type { NominationStatus } from './ValidatorItem/types'; import { useSyncing } from 'hooks/useSyncing'; export const ValidatorListInner = ({ + // Default list values. nominator: initialNominator, validators: initialValidators, + // Validator list config options. + bondFor, allowMoreCols, allowFilters, toggleFavorites, pagination, format, selectable, - bondFor, onSelected, actions = [], showMenu = true, displayFor = 'default', allowSearch = false, allowListFormat = true, - alwaysRefetchValidators = false, defaultOrder = undefined, defaultFilters = undefined, + // Throttling and re-fetching. + alwaysRefetchValidators = false, disableThrottle = false, }: ValidatorListProps) => { const { t } = useTranslation('library'); @@ -80,8 +83,8 @@ export const ValidatorListInner = ({ const { activeAccount } = useActiveAccounts(); const { setModalResize } = useOverlay().modal; const { injectValidatorListData } = useValidators(); - const { getNomineesStatus } = useNominationStatus(); const { getPoolNominationStatus } = useBondedPools(); + const { getNominationSetStatus } = useNominationStatus(); const { applyFilter, applyOrder, applySearch } = useValidatorFilters(); const { selected, listFormat, setListFormat } = listProvider; @@ -110,7 +113,10 @@ export const ValidatorListInner = ({ ); } else { // get all active account's nominations. - const nominationStatuses = getNomineesStatus(nominator, 'nominator'); + const nominationStatuses = getNominationSetStatus( + nominator, + 'nominator' + ); // find the nominator status within the returned nominations. nominationStatus.current = Object.fromEntries( @@ -245,7 +251,8 @@ export const ValidatorListInner = ({ setSearchTerm('validators', newValue); }; - // Set default filters. + // Set default filters. Should re-render if era stakers re-syncs as era points effect the + // performance order. useEffect(() => { if (allowFilters) { if (defaultFilters?.includes?.length) { @@ -277,7 +284,7 @@ export const ValidatorListInner = ({ clearSearchTerm('validators'); } }; - }, []); + }, [syncing]); // Handle validator list bootstrapping. const setupValidatorList = () => { @@ -288,10 +295,10 @@ export const ValidatorListInner = ({ // Configure validator list when network is ready to fetch. useEffect(() => { - if (isReady && isNotZero(activeEra.index) && !fetched) { + if (isReady && isNotZero(activeEra.index)) { setupValidatorList(); } - }, [isReady, activeEra.index, fetched]); + }, [isReady, activeEra.index, syncing]); // Control render throttle. useEffect(() => { diff --git a/src/pages/Community/List.tsx b/src/pages/Community/List.tsx index fa0fd9233e..d31a0c92b7 100644 --- a/src/pages/Community/List.tsx +++ b/src/pages/Community/List.tsx @@ -3,17 +3,17 @@ import { PageRow } from '@polkadot-cloud/react'; import { useEffect, useState } from 'react'; -import { useValidators } from 'contexts/Validators/ValidatorEntries'; import { useNetwork } from 'contexts/Network'; import { Item } from './Item'; import { ItemsWrapper } from './Wrappers'; import { useCommunitySections } from './context'; import type { ValidatorEntry } from '@polkadot-cloud/assets/types'; +import { useCommunity } from 'contexts/Community'; export const List = () => { const { network } = useNetwork(); - const { validatorCommunity } = useValidators(); const { scrollPos } = useCommunitySections(); + const { validatorCommunity } = useCommunity(); const [entityItems, setEntityItems] = useState( validatorCommunity.filter((v) => v.validators[network] !== undefined) diff --git a/src/pages/Nominate/Active/index.tsx b/src/pages/Nominate/Active/index.tsx index bb3fefbbd6..5dd1d67001 100644 --- a/src/pages/Nominate/Active/index.tsx +++ b/src/pages/Nominate/Active/index.tsx @@ -28,17 +28,20 @@ import { MinimumNominatorBondStat } from './Stats/MinimumNominatorBond'; import { Status } from './Status'; import { UnstakePrompts } from './UnstakePrompts'; import { useSyncing } from 'hooks/useSyncing'; +import { useBonded } from 'contexts/Bonded'; export const Active = () => { const { t } = useTranslation(); const { openHelp } = useHelp(); const { inSetup } = useStaking(); const { syncing } = useSyncing('*'); - const { nominated } = useValidators(); - const { isFastUnstaking } = useUnstaking(); const { openCanvas } = useOverlay().canvas; + const { isFastUnstaking } = useUnstaking(); + const { formatWithPrefs } = useValidators(); + const { getAccountNominations } = useBonded(); const { activeAccount } = useActiveAccounts(); + const nominated = formatWithPrefs(getAccountNominations(activeAccount)); const ROW_HEIGHT = 220; return ( diff --git a/src/pages/Pools/Home/ManagePool/index.tsx b/src/pages/Pools/Home/ManagePool/index.tsx index 6a692c7ff7..35d9511109 100644 --- a/src/pages/Pools/Home/ManagePool/index.tsx +++ b/src/pages/Pools/Home/ManagePool/index.tsx @@ -10,18 +10,22 @@ import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; import { Nominations } from 'library/Nominations'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useValidators } from 'contexts/Validators/ValidatorEntries'; import { useSyncing } from 'hooks/useSyncing'; +import { useValidators } from 'contexts/Validators/ValidatorEntries'; export const ManagePool = () => { const { t } = useTranslation(); const { syncing } = useSyncing(['active-pools']); - const { poolNominated } = useValidators(); const { openCanvas } = useOverlay().canvas; + const { formatWithPrefs } = useValidators(); const { activeAccount } = useActiveAccounts(); const { isOwner, isNominator, activePoolNominations, activePool } = useActivePool(); + const poolNominated = activePoolNominations + ? formatWithPrefs(activePoolNominations.targets) + : []; + const isNominating = !!activePoolNominations?.targets?.length; const nominator = activePool?.addresses?.stash ?? null; const { state } = activePool?.bondedPool || {}; diff --git a/src/static/APIController/index.ts b/src/static/APIController/index.ts index 708667c2de..3ae0132539 100644 --- a/src/static/APIController/index.ts +++ b/src/static/APIController/index.ts @@ -134,7 +134,6 @@ export class APIController { // Add initial syncing items. SyncController.dispatch('initialization', 'syncing'); - SyncController.dispatch('era-stakers', 'syncing'); const config: APIConfig = { type, diff --git a/src/static/SyncController/types.ts b/src/static/SyncController/types.ts index 915aa8b624..4fc13b07e7 100644 --- a/src/static/SyncController/types.ts +++ b/src/static/SyncController/types.ts @@ -5,6 +5,7 @@ export type SyncID = | 'initialization' | 'balances' | 'era-stakers' + | 'nominator' | 'active-pools'; export interface SyncEvent { From 191061606147a7499b496fadf9a1a04db2017ab3 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 8 Feb 2024 16:28:24 +0700 Subject: [PATCH 34/51] chore: add Provider type --- src/Providers.tsx | 5 ++--- src/hooks/withProviders/index.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Providers.tsx b/src/Providers.tsx index 9bfccb9f11..aef0a4ed8d 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -35,14 +35,13 @@ import { PayoutsProvider } from 'contexts/Payouts'; import { useNetwork } from 'contexts/Network'; import { APIProvider } from 'contexts/Api'; import { ThemedRouter } from 'Themes'; -import type { AnyJson } from 'types'; -import type { FC } from 'react'; import { OtherAccountsProvider } from 'contexts/Connect/OtherAccounts'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { DappName } from 'consts'; import { ImportedAccountsProvider } from 'contexts/Connect/ImportedAccounts'; import { PoolPerformanceProvider } from 'contexts/Pools/PoolPerformance'; import { ExternalAccountsProvider } from 'contexts/Connect/ExternalAccounts'; +import type { Provider } from 'hooks/withProviders'; import { withProviders } from 'hooks/withProviders'; import { CommunityProvider } from 'contexts/Community'; @@ -54,7 +53,7 @@ export const Providers = () => { const { activeAccount, setActiveAccount } = useActiveAccounts(); // !! Provider order matters - const providers: (FC | [FC, AnyJson])[] = [ + const providers: Provider[] = [ UIProvider, [APIProvider, { network }], VaultAccountsProvider, diff --git a/src/hooks/withProviders/index.tsx b/src/hooks/withProviders/index.tsx index 881b852d37..ed7a321646 100644 --- a/src/hooks/withProviders/index.tsx +++ b/src/hooks/withProviders/index.tsx @@ -4,11 +4,11 @@ import type { FC } from 'react'; import type { AnyJson } from 'types'; +// `providers` accepts standalone functional components or an array of a functional component and its props. +export type Provider = FC | [FC, AnyJson]; + // A pure function that applies an arbitrary amount of context providers to a wrapped component. -export const withProviders = ( - providers: (FC | [FC, AnyJson])[], - Wrapped: FC -) => +export const withProviders = (providers: Provider[], Wrapped: FC) => providers.reduceRight( (acc, prov) => { if (Array.isArray(prov)) { From 6745186c23eb5e48e7df3e99c9d42f1065fdd501 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 8 Feb 2024 16:29:11 +0700 Subject: [PATCH 35/51] chore: comment --- src/Providers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Providers.tsx b/src/Providers.tsx index aef0a4ed8d..5cd53382a2 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -52,7 +52,7 @@ export const Providers = () => { } = useNetwork(); const { activeAccount, setActiveAccount } = useActiveAccounts(); - // !! Provider order matters + // !! Provider order matters. const providers: Provider[] = [ UIProvider, [APIProvider, { network }], From 00b5d6c3a82d1b5bad4e326d4badf1b295b8c75a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 21:28:25 +0000 Subject: [PATCH 36/51] chore(deps-dev): bump vite from 5.0.12 to 5.1.0 (#1936) --- package.json | 2 +- yarn.lock | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index df4a3eb4e4..9cb37b8ad9 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "prettier-plugin-organize-imports": "^3.2.4", "sass": "^1.70.0", "typescript": "^5.3.3", - "vite": "^5.0.12", + "vite": "^5.1.0", "vite-bundle-visualizer": "^1.0.1", "vite-plugin-checker": "^0.6.3", "vite-plugin-eslint": "^1.8.1", diff --git a/yarn.lock b/yarn.lock index 0d13bc2de8..ad982cfdc7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6343,7 +6343,7 @@ __metadata: styled-components: "npm:^6.1.8" typescript: "npm:^5.3.3" usehooks-ts: "npm:2.13.0" - vite: "npm:^5.0.12" + vite: "npm:^5.1.0" vite-bundle-visualizer: "npm:^1.0.1" vite-plugin-checker: "npm:^0.6.3" vite-plugin-eslint: "npm:^1.8.1" @@ -6417,6 +6417,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.35": + version: 8.4.35 + resolution: "postcss@npm:8.4.35" + dependencies: + nanoid: "npm:^3.3.7" + picocolors: "npm:^1.0.0" + source-map-js: "npm:^1.0.2" + checksum: e8dd04e48001eb5857abc9475365bf08f4e508ddf9bc0b8525449a95d190f10d025acebc5b56ac2e94b3c7146790e4ae78989bb9633cb7ee20d1cc9b7dc909b2 + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -8130,7 +8141,7 @@ __metadata: languageName: node linkType: hard -"vite@npm:^5.0.0, vite@npm:^5.0.12": +"vite@npm:^5.0.0": version: 5.0.12 resolution: "vite@npm:5.0.12" dependencies: @@ -8170,6 +8181,46 @@ __metadata: languageName: node linkType: hard +"vite@npm:^5.1.0": + version: 5.1.0 + resolution: "vite@npm:5.1.0" + dependencies: + esbuild: "npm:^0.19.3" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.35" + rollup: "npm:^4.2.0" + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 62aa632b6f30cfca39db534b5b461b1e73dc4f4a3093088c1140a1e1b0c4c8f4eacc0e472a0d96d765ad6976b00e202da20a647865886df692240a2b06b62c6f + languageName: node + linkType: hard + "vitest@npm:^1.2.2": version: 1.2.2 resolution: "vitest@npm:1.2.2" From 05923ab54ebc631d39d95d3f397c712fb4e399ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 21:29:15 +0000 Subject: [PATCH 37/51] chore(deps-dev): bump @types/chroma-js from 2.4.3 to 2.4.4 (#1937) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9cb37b8ad9..87a7a21e52 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ }, "devDependencies": { "@ledgerhq/logs": "^6.12.0", - "@types/chroma-js": "^2.4.3", + "@types/chroma-js": "^2.4.4", "@types/lodash.debounce": "^4", "@types/lodash.throttle": "^4.1.9", "@types/react": "^18.2.55", diff --git a/yarn.lock b/yarn.lock index ad982cfdc7..929f22651b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2086,10 +2086,10 @@ __metadata: languageName: node linkType: hard -"@types/chroma-js@npm:^2.4.3": - version: 2.4.3 - resolution: "@types/chroma-js@npm:2.4.3" - checksum: b804865f9ed5cbf23039112564aae0308721a085bb9ac5c95a969eb561858f1253acaa2970ee1d359f6da995c72d7b18665fddcee0a52278104b18b62d8704f9 +"@types/chroma-js@npm:^2.4.4": + version: 2.4.4 + resolution: "@types/chroma-js@npm:2.4.4" + checksum: 8acd341c523fc960686ed9e60f23ae643da35bddf012b4b18cb0d941ee3bf82087262245ffa8c886ea31571cd5dc6eb553e849fab3c0509ea2c3a045a246178c languageName: node linkType: hard @@ -6292,7 +6292,7 @@ __metadata: "@polkadot/util-crypto": "npm:^12.6.2" "@polkawatch/ddp-client": "npm:^2.0.10" "@substrate/connect": "npm:0.7.35" - "@types/chroma-js": "npm:^2.4.3" + "@types/chroma-js": "npm:^2.4.4" "@types/lodash.debounce": "npm:^4" "@types/lodash.throttle": "npm:^4.1.9" "@types/react": "npm:^18.2.55" From af53aff830cdff835d3512dceb212cffb57b8435 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 21:38:56 +0000 Subject: [PATCH 38/51] chore(deps-dev): bump vite from 5.1.0 to 5.1.1 (#1939) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 87a7a21e52..6708c4433a 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "prettier-plugin-organize-imports": "^3.2.4", "sass": "^1.70.0", "typescript": "^5.3.3", - "vite": "^5.1.0", + "vite": "^5.1.1", "vite-bundle-visualizer": "^1.0.1", "vite-plugin-checker": "^0.6.3", "vite-plugin-eslint": "^1.8.1", diff --git a/yarn.lock b/yarn.lock index 929f22651b..bc903923c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6343,7 +6343,7 @@ __metadata: styled-components: "npm:^6.1.8" typescript: "npm:^5.3.3" usehooks-ts: "npm:2.13.0" - vite: "npm:^5.1.0" + vite: "npm:^5.1.1" vite-bundle-visualizer: "npm:^1.0.1" vite-plugin-checker: "npm:^0.6.3" vite-plugin-eslint: "npm:^1.8.1" @@ -8181,9 +8181,9 @@ __metadata: languageName: node linkType: hard -"vite@npm:^5.1.0": - version: 5.1.0 - resolution: "vite@npm:5.1.0" +"vite@npm:^5.1.1": + version: 5.1.1 + resolution: "vite@npm:5.1.1" dependencies: esbuild: "npm:^0.19.3" fsevents: "npm:~2.3.3" @@ -8217,7 +8217,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 62aa632b6f30cfca39db534b5b461b1e73dc4f4a3093088c1140a1e1b0c4c8f4eacc0e472a0d96d765ad6976b00e202da20a647865886df692240a2b06b62c6f + checksum: d7c23284aeb3a8333e0ea208fab7da4dd4f69134f22ff959d865f56d48c3afd04fbc548368c15a5fbc0c6453ee950db42bd3532aa0dd50b85464eabefaa51396 languageName: node linkType: hard From b8ed79273868f43c18f8fb85081b9a9830ff720b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 21:42:36 +0000 Subject: [PATCH 39/51] chore(deps): bump usehooks-ts from 2.13.0 to 2.14.0 (#1940) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 6708c4433a..93edba6567 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "react-router-dom": "^6.22.0", "react-scroll": "^1.9.0", "styled-components": "^6.1.8", - "usehooks-ts": "2.13.0" + "usehooks-ts": "2.14.0" }, "devDependencies": { "@ledgerhq/logs": "^6.12.0", diff --git a/yarn.lock b/yarn.lock index bc903923c7..5c7e392d52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6342,7 +6342,7 @@ __metadata: sass: "npm:^1.70.0" styled-components: "npm:^6.1.8" typescript: "npm:^5.3.3" - usehooks-ts: "npm:2.13.0" + usehooks-ts: "npm:2.14.0" vite: "npm:^5.1.1" vite-bundle-visualizer: "npm:^1.0.1" vite-plugin-checker: "npm:^0.6.3" @@ -7972,14 +7972,14 @@ __metadata: languageName: node linkType: hard -"usehooks-ts@npm:2.13.0": - version: 2.13.0 - resolution: "usehooks-ts@npm:2.13.0" +"usehooks-ts@npm:2.14.0": + version: 2.14.0 + resolution: "usehooks-ts@npm:2.14.0" dependencies: lodash.debounce: "npm:^4.0.8" peerDependencies: react: ^16.8.0 || ^17 || ^18 - checksum: 3924bb1f9f4baee08e84b4bc311ea14492ed19db2c38d1b54c542fc45e5da6a48b4469da9281339ad7a0de76184ebf5d8de9d654dc25aceb2ccc88a4e6db115a + checksum: 032d8e39687592cad9e00fa853a32c0edf286dd1c5843cfa06167b080f9da9e24479b28096e11531912c9c47b219fb969041a4dc831dfe3a3ca007fe11a2b454 languageName: node linkType: hard From 37d23770b822a38dbb393bdf1798a00e682f0948 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 10 Feb 2024 22:25:48 +0700 Subject: [PATCH 40/51] chore: rm deprecated StakingProvider values --- src/contexts/Staking/defaults.ts | 8 ------ src/contexts/Staking/index.tsx | 42 ++------------------------------ src/contexts/Staking/types.ts | 7 ------ 3 files changed, 2 insertions(+), 55 deletions(-) diff --git a/src/contexts/Staking/defaults.ts b/src/contexts/Staking/defaults.ts index 315dad3f2a..f2685c56bc 100644 --- a/src/contexts/Staking/defaults.ts +++ b/src/contexts/Staking/defaults.ts @@ -7,7 +7,6 @@ import type { EraStakers, NominationStatuses, StakingContextInterface, - StakingTargets, } from 'contexts/Staking/types'; export const defaultEraStakers: EraStakers = { @@ -17,10 +16,6 @@ export const defaultEraStakers: EraStakers = { totalActiveNominators: 0, }; -export const defaultTargets: StakingTargets = { - nominations: [], -}; - const defaultLowestReward = { lowest: new BigNumber(0), oversubscribed: false, @@ -31,8 +26,6 @@ export const defaultNominationStatus: NominationStatuses = {}; export const defaultStakingContext: StakingContextInterface = { fetchEraStakers: async (e) => new Promise((resolve) => resolve([])), getNominationsStatusFromTargets: (w, t) => defaultNominationStatus, - setTargets: (t) => {}, - hasController: () => false, getControllerNotImported: (a) => false, addressDifferentToStash: (a) => false, isBonding: () => false, @@ -40,6 +33,5 @@ export const defaultStakingContext: StakingContextInterface = { inSetup: () => true, getLowestRewardFromStaker: (address) => defaultLowestReward, eraStakers: defaultEraStakers, - targets: defaultTargets, getPagedErasStakers: (e) => new Promise((resolve) => resolve([])), }; diff --git a/src/contexts/Staking/index.tsx b/src/contexts/Staking/index.tsx index 9f041ea77a..a73b8cbc5f 100644 --- a/src/contexts/Staking/index.tsx +++ b/src/contexts/Staking/index.tsx @@ -3,7 +3,6 @@ import { greaterThanZero, - localStorageOrDefault, rmCommas, setStateWithRef, } from '@polkadot-cloud/utils'; @@ -17,7 +16,6 @@ import type { Exposure, ExposureOther, StakingContextInterface, - StakingTargets, } from 'contexts/Staking/types'; import type { AnyApi, MaybeAddress } from 'types'; import Worker from 'workers/stakers?worker'; @@ -28,11 +26,7 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useApi } from '../Api'; import { useBonded } from '../Bonded'; -import { - defaultEraStakers, - defaultStakingContext, - defaultTargets, -} from './defaults'; +import { defaultEraStakers, defaultStakingContext } from './defaults'; import { setLocalEraExposures, getLocalEraExposures, @@ -54,8 +48,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { const { networkData, network } = useNetwork(); const { accounts: connectAccounts } = useImportedAccounts(); const { activeAccount, getActiveAccount } = useActiveAccounts(); - const { bondedAccounts, getBondedAccount, getAccountNominations } = - useBonded(); + const { getBondedAccount, getAccountNominations } = useBonded(); const { isReady, api, apiStatus, consts, activeEra, isPagedRewardsActive } = useApi(); const { maxExposurePageSize } = consts; @@ -64,15 +57,6 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { const [eraStakers, setEraStakers] = useState(defaultEraStakers); const eraStakersRef = useRef(eraStakers); - // Store target validators for the active account. - const [targets, setTargetsState] = useState( - localStorageOrDefault( - `${activeAccount ?? ''}_targets`, - defaultTargets, - true - ) as StakingTargets - ); - worker.onmessage = (message: MessageEvent) => { if (message) { const { data }: { data: ProcessExposuresResponse } = message; @@ -164,12 +148,6 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { }); }; - // Sets an account's stored target validators. - const setTargets = (value: StakingTargets): void => { - localStorage.setItem(`${activeAccount}_targets`, JSON.stringify(value)); - setTargetsState(value); - }; - // Gets the nomination statuses of passed in nominations. const getNominationsStatusFromTargets = ( who: MaybeAddress, @@ -346,26 +324,11 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { } }, [isReady, activeEra.index, activeAccount]); - useEffectIgnoreInitial(() => { - if (activeAccount) { - // set account's targets - setTargetsState( - localStorageOrDefault( - `${activeAccount}_targets`, - defaultTargets, - true - ) as StakingTargets - ); - } - }, [isReady, bondedAccounts, activeAccount, eraStakersRef.current?.stakers]); - return ( { inSetup, getLowestRewardFromStaker, eraStakers, - targets, getPagedErasStakers, }} > diff --git a/src/contexts/Staking/types.ts b/src/contexts/Staking/types.ts index 95e4c09131..a5199fc12c 100644 --- a/src/contexts/Staking/types.ts +++ b/src/contexts/Staking/types.ts @@ -18,10 +18,6 @@ export interface EraStakers { export type NominationStatuses = Record; -export interface StakingTargets { - nominations: string[]; -} - export interface Exposure { keys: string[]; val: ExposureValue; @@ -60,8 +56,6 @@ export interface StakingContextInterface { w: MaybeAddress, t: string[] ) => Record; - setTargets: (t: StakingTargets) => void; - hasController: () => boolean; getControllerNotImported: (a: MaybeAddress) => boolean; addressDifferentToStash: (a: MaybeAddress) => boolean; isBonding: () => boolean; @@ -69,7 +63,6 @@ export interface StakingContextInterface { inSetup: () => boolean; getLowestRewardFromStaker: (a: MaybeAddress) => LowestReward; eraStakers: EraStakers; - targets: StakingTargets; getPagedErasStakers: (e: string) => Promise; } From 9c49bf53a62959d23d8d34bd25d3a2dcb1df3f23 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 10 Feb 2024 22:27:20 +0700 Subject: [PATCH 41/51] chore: remove unused SetupProvider value --- src/contexts/Setup/defaults.ts | 4 ---- src/contexts/Setup/index.tsx | 1 - src/contexts/Setup/types.ts | 1 - 3 files changed, 6 deletions(-) diff --git a/src/contexts/Setup/defaults.ts b/src/contexts/Setup/defaults.ts index 69c1336947..15c5e5b7df 100644 --- a/src/contexts/Setup/defaults.ts +++ b/src/contexts/Setup/defaults.ts @@ -25,10 +25,6 @@ export const defaultPoolProgress: PoolProgress = { }; export const defaultSetupContext: SetupContextInterface = { - getSetupProgress: (a, b) => ({ - section: 1, - progress: defaultNominatorProgress, - }), removeSetupProgress: (a, b) => {}, getNominatorSetupPercent: (a) => 0, getPoolSetupPercent: (a) => 0, diff --git a/src/contexts/Setup/index.tsx b/src/contexts/Setup/index.tsx index fea2df4bee..9cf1cdd111 100644 --- a/src/contexts/Setup/index.tsx +++ b/src/contexts/Setup/index.tsx @@ -291,7 +291,6 @@ export const SetupProvider = ({ children }: { children: ReactNode }) => { return ( PoolSetup | NominatorSetup; removeSetupProgress: (t: BondFor, a: MaybeAddress) => void; getNominatorSetupPercent: (a: MaybeAddress) => number; getPoolSetupPercent: (a: MaybeAddress) => number; From 850c0d9d6c8d8f481a4a5ca59616b7b0ce34b14f Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 11 Feb 2024 11:11:10 +0700 Subject: [PATCH 42/51] feat(ux): View & copy pool addresses (#1941) --- src/library/Pool/index.tsx | 34 +++-- src/locale/cn/library.json | 2 +- src/locale/cn/modals.json | 1 + src/locale/en/library.json | 2 +- src/locale/en/modals.json | 1 + src/modals/ManagePool/Tasks.tsx | 213 ++++++++++++++++++------------ src/modals/ManagePool/Wrappers.ts | 47 +++++++ src/modals/ManagePool/index.tsx | 7 +- 8 files changed, 205 insertions(+), 102 deletions(-) diff --git a/src/library/Pool/index.tsx b/src/library/Pool/index.tsx index 258e3b8fa7..95340588fd 100644 --- a/src/library/Pool/index.tsx +++ b/src/library/Pool/index.tsx @@ -67,18 +67,20 @@ export const Pool = ({ pool }: PoolProps) => { const posRef = useRef(null); // copy address notification - const notificationCopyAddress: NotificationText | null = - addresses.stash == null + const notificationCopyAddress = ( + key: 'stash' | 'reward' + ): NotificationText | null => + addresses[key] == null ? null : { title: t('addressCopiedToClipboard'), - subtitle: addresses.stash, + subtitle: addresses[key], }; // Consruct pool menu items. const menuItems: MenuItem[] = []; - // add view pool nominations button to menu + // Add view pool nominations button to menu menuItems.push({ icon: , title: `${t('viewPoolNominations')}`, @@ -93,14 +95,28 @@ export const Pool = ({ pool }: PoolProps) => { }, }); - // add copy pool address button to menu + // add copy pool stash address button to menu menuItems.push({ icon: , - title: t('copyPoolAddress'), + title: t('copyPoolAddress', { type: 'Stash' }), cb: () => { - navigator.clipboard.writeText(addresses.stash); - if (notificationCopyAddress) { - NotificationsController.emit(notificationCopyAddress); + const notification = notificationCopyAddress('stash'); + if (notification) { + navigator.clipboard.writeText(addresses.stash); + NotificationsController.emit(notification); + } + }, + }); + + // add copy pool reward address button to menu + menuItems.push({ + icon: , + title: t('copyPoolAddress', { type: 'Reward' }), + cb: () => { + const notification = notificationCopyAddress('reward'); + if (notification) { + navigator.clipboard.writeText(addresses.reward); + NotificationsController.emit(notification); } }, }); diff --git a/src/locale/cn/library.json b/src/locale/cn/library.json index f712e8e461..894afb3979 100644 --- a/src/locale/cn/library.json +++ b/src/locale/cn/library.json @@ -50,7 +50,7 @@ "connectionLost": "连接已丢失", "continue": "继续", "copyAddress": "复制地址", - "copyPoolAddress": "复制池地址", + "copyPoolAddress": "复制池{{type}}地址", "createPool": "创建提名池", "dayAverage": "日平均值", "dayPerformance": "天内表现", diff --git a/src/locale/cn/modals.json b/src/locale/cn/modals.json index 467ed8069b..b701b0e709 100644 --- a/src/locale/cn/modals.json +++ b/src/locale/cn/modals.json @@ -181,6 +181,7 @@ "pendingPayout": "{{count}} 个待申领收益", "polkawatchDisabled": "Polkawatch己断开", "pool": "池", + "poolAddress": "池{{type}}地址", "poolIsNotNominating": "该提名池未提名任何验证人", "poolMembers": "提名池成员", "poolName": "提名池名称", diff --git a/src/locale/en/library.json b/src/locale/en/library.json index 286b88be1c..c77b0a37c7 100644 --- a/src/locale/en/library.json +++ b/src/locale/en/library.json @@ -50,7 +50,7 @@ "connectionLost": "Connection has been lost", "continue": "Continue", "copyAddress": "Copy Address", - "copyPoolAddress": "Copy Pool Address", + "copyPoolAddress": "Copy Pool {{type}} Address", "createPool": "Create Pool", "dayAverage": "Day Average", "dayPerformance": "Day Performance", diff --git a/src/locale/en/modals.json b/src/locale/en/modals.json index 8e14b244fc..301a7abb0d 100644 --- a/src/locale/en/modals.json +++ b/src/locale/en/modals.json @@ -193,6 +193,7 @@ "pendingPayout_other": "{{count}} Pending Payouts", "polkawatchDisabled": "Polkawatch Disabled", "pool": "Pool", + "poolAddress": " Pool {{type}} Address", "poolIsNotNominating": "Pool is Not Nominating.", "poolMembers": "Pool Members", "poolName": "Pool Name", diff --git a/src/modals/ManagePool/Tasks.tsx b/src/modals/ManagePool/Tasks.tsx index 36bf848a8a..2236a65141 100644 --- a/src/modals/ManagePool/Tasks.tsx +++ b/src/modals/ManagePool/Tasks.tsx @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { ButtonOption } from '@polkadot-cloud/react'; +import { ButtonOption, Polkicon } from '@polkadot-cloud/react'; import type { ForwardedRef } from 'react'; import { forwardRef } from 'react'; import { useTranslation } from 'react-i18next'; @@ -9,9 +9,11 @@ import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; import { Warning } from 'library/Form/Warning'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { ContentWrapper } from './Wrappers'; +import { ButtonRowWrapper, ContentWrapper } from './Wrappers'; import type { TasksProps } from './types'; import { useApi } from 'contexts/Api'; +import { ellipsisFn, remToUnit } from '@polkadot-cloud/utils'; +import { CopyAddress } from 'library/ListItem/Labels/CopyAddress'; export const Tasks = forwardRef( ({ setSection, setTask }: TasksProps, ref: ForwardedRef) => { @@ -27,120 +29,159 @@ export const Tasks = forwardRef( const poolLocked = activePool?.bondedPool?.state === 'Blocked'; const poolDestroying = activePool?.bondedPool?.state === 'Destroying'; + const stash = activePool?.addresses.stash || ''; + const reward = activePool?.addresses.reward || ''; + return ( -
-
- {poolDestroying && } -
- {isOwner() && globalMaxCommission > 0 && ( - <> - { - setSection(1); - setTask('claim_commission'); - }} - > -
-

{t('claimCommission')}

-

{t('claimOutstandingCommission')}

+
+
+ {poolDestroying && ( +
+ +
+ )} + + +
+
+ + + +
+

+ {t('poolAddress', { type: 'Stash' })}{' '} + +

+

{ellipsisFn(stash, 5)}

+
- - { - setSection(1); - setTask('manage_commission'); - }} - > -
-

{t('manageCommission')}

-

{t('updatePoolCommission')}

+
+
+
+ + + +
+

+ {t('poolAddress', { type: 'Reward' })}{' '} + +

+

{ellipsisFn(reward, 5)}

+
- - - )} - { - setSection(1); - setTask('set_claim_permission'); - }} - > -
-

{t('updateClaimPermission')}

-

{t('updateWhoClaimRewards')}

-
-
+
+
- {isOwner() && ( - { - setSection(1); - setTask('set_pool_metadata'); - }} - > -
-

{t('renamePool')}

-

{t('updateName')}

-
-
- )} - {(isOwner() || isBouncer()) && ( - <> - {poolLocked ? ( + {isOwner() && globalMaxCommission > 0 && ( + <> { setSection(1); - setTask('unlock_pool'); + setTask('claim_commission'); }} >
-

{t('unlockPool')}

-

{t('allowToJoin')}

+

{t('claimCommission')}

+

{t('claimOutstandingCommission')}

- ) : ( { setSection(1); - setTask('lock_pool'); + setTask('manage_commission'); }} >
-

{t('lockPool')}

-

{t('stopJoiningPool')}

+

{t('manageCommission')}

+

{t('updatePoolCommission')}

- )} + + )} + { + setSection(1); + setTask('set_claim_permission'); + }} + > +
+

{t('updateClaimPermission')}

+

{t('updateWhoClaimRewards')}

+
+
+ + {isOwner() && ( { setSection(1); - setTask('destroy_pool'); + setTask('set_pool_metadata'); }} >
-

{t('destroyPool')}

-

{t('changeToDestroy')}

+

{t('renamePool')}

+

{t('updateName')}

- - )} - {isMember() && !isDepositor() && active?.isGreaterThan(0) && ( - { - setSection(1); - setTask('leave_pool'); - }} - > -
-

{t('leavePool')}

-

{t('unbondFundsLeavePool')}

-
-
- )} + )} + {(isOwner() || isBouncer()) && ( + <> + {poolLocked ? ( + { + setSection(1); + setTask('unlock_pool'); + }} + > +
+

{t('unlockPool')}

+

{t('allowToJoin')}

+
+
+ ) : ( + { + setSection(1); + setTask('lock_pool'); + }} + > +
+

{t('lockPool')}

+

{t('stopJoiningPool')}

+
+
+ )} + { + setSection(1); + setTask('destroy_pool'); + }} + > +
+

{t('destroyPool')}

+

{t('changeToDestroy')}

+
+
+ + )} + {isMember() && !isDepositor() && active?.isGreaterThan(0) && ( + { + setSection(1); + setTask('leave_pool'); + }} + > +
+

{t('leavePool')}

+

{t('unbondFundsLeavePool')}

+
+
+ )} +
); diff --git a/src/modals/ManagePool/Wrappers.ts b/src/modals/ManagePool/Wrappers.ts index 8feb9748f8..c2e5a26c88 100644 --- a/src/modals/ManagePool/Wrappers.ts +++ b/src/modals/ManagePool/Wrappers.ts @@ -116,3 +116,50 @@ export const SliderWrapper = styled.div` margin-top: 2.5rem; } `; + +export const ButtonRowWrapper = styled.div` + width: 100%; + display: flex; + flex-flow: row wrap; + margin: 0.5rem 0 1.25rem 0; + + > section { + width: 50%; + flex-grow: 0; + + &:first-child { + padding-right: 0.25rem; + } + &:last-child { + padding-left: 0.25rem; + } + + > .inner { + padding: 0.5rem; + display: flex; + align-items: center; + + > .icon { + margin-right: 0.75rem; + } + + > div > h3 { + display: flex; + align-items: center; + + button { + color: var(--text-color-primary); + margin-left: 0.75rem; + } + } + } + + @media (max-width: 800px) { + border-bottom: 1px solid var(--border-primary-color); + padding-bottom: 0.5rem; + margin-bottom: 0.75rem; + padding: 0; + width: 100%; + } + } +`; diff --git a/src/modals/ManagePool/index.tsx b/src/modals/ManagePool/index.tsx index b36d9a1742..7b9e2c6a31 100644 --- a/src/modals/ManagePool/index.tsx +++ b/src/modals/ManagePool/index.tsx @@ -20,8 +20,8 @@ import { Tasks } from './Tasks'; export const ManagePool = () => { const { t } = useTranslation('modals'); const { notEnoughFunds } = useTxMeta(); + const { activePool } = useActivePool(); const { integrityChecked } = useLedgerHardware(); - const { isOwner, activePool } = useActivePool(); const { setModalHeight, modalMaxHeight } = useOverlay().modal; // modal task @@ -72,10 +72,7 @@ export const ManagePool = () => { return ( - + <Title title={`${t('managePool')}`} fixed /> </ModalFixedTitle> <ModalMotionTwoSection style={{ From d40c148560cd2fad34e35c643c5544eea8e15d80 Mon Sep 17 00:00:00 2001 From: Ross Bulat <ross@parity.io> Date: Sun, 11 Feb 2024 12:10:15 +0700 Subject: [PATCH 43/51] chore: sync tidy up --- src/contexts/FastUnstake/index.tsx | 19 ++++++---- .../Active/Status/NominationStatus.tsx | 37 ++++++++++--------- .../Pools/Home/Status/MembershipStatus.tsx | 2 +- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/contexts/FastUnstake/index.tsx b/src/contexts/FastUnstake/index.tsx index e86e8598e2..633b41aa95 100644 --- a/src/contexts/FastUnstake/index.tsx +++ b/src/contexts/FastUnstake/index.tsx @@ -38,8 +38,8 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { const { activeEra } = useApi(); const { network } = useNetwork(); const { activeAccount } = useActiveAccounts(); - const { inSetup, fetchEraStakers } = useStaking(); const { getNominationStatus } = useNominationStatus(); + const { inSetup, fetchEraStakers, isNominating } = useStaking(); const { api, isReady, @@ -87,7 +87,8 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { isReady && activeAccount && isNotZero(activeEra.index) && - fastUnstakeErasToCheckPerBlock > 0 + fastUnstakeErasToCheckPerBlock > 0 && + isNominating() ) { // cancel fast unstake check on network change or account change. for (const unsub of unsubs.current) { @@ -119,7 +120,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { } } - // checkpoint: initial local meta: localMeta + // Initial local meta: localMeta setStateWithRef(initialMeta, setMeta, metaRef); setStateWithRef(initialIsExposed, setIsExposed, isExposedRef); @@ -138,7 +139,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { ? new BigNumber(nextEra - 1) : activeEra.index; - // checkpoint: check from the possible next era `maybeNextEra`. + // Check from the possible next era `maybeNextEra`. processEligibility(activeAccount, maybeNextEra); } @@ -161,6 +162,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { activeAccount, activeEra.index, fastUnstakeErasToCheckPerBlock, + isNominating(), ]); // handle worker message on completed exposure check. @@ -207,7 +209,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { } if (exposed) { - // checkpoint: 'exposed! Stop checking. + // Account is exposed - stop checking. // cancel checking and update exposed state. setStateWithRef(false, setChecking, checkingRef); @@ -217,7 +219,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { setStateWithRef(false, setChecking, checkingRef); setStateWithRef(false, setIsExposed, isExposedRef); - // checkpoint: check finished, not exposed. + // Finished, not exposed. // subscribe to fast unstake queue for user and queue counter. subscribeToFastUnstakeQueue(); @@ -237,7 +239,8 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { !api || !a || checkingRef.current || - !activeAccount + !activeAccount || + !isNominating() ) { return; } @@ -296,7 +299,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { return u; }; - // checkpoint: subscribing to queue + head. + // Subscribe to queue + head. // initiate subscription, add to unsubs. await Promise.all([ diff --git a/src/pages/Nominate/Active/Status/NominationStatus.tsx b/src/pages/Nominate/Active/Status/NominationStatus.tsx index df4e9ed2fa..2bf626e221 100644 --- a/src/pages/Nominate/Active/Status/NominationStatus.tsx +++ b/src/pages/Nominate/Active/Status/NominationStatus.tsx @@ -31,15 +31,20 @@ export const NominationStatus = ({ const { inSetup } = useStaking(); const { openModal } = useOverlay().modal; const { getBondedAccount } = useBonded(); - const { syncing } = useSyncing(['initialization']); + const { syncing } = useSyncing([ + 'initialization', + 'era-stakers', + 'balances', + 'nominator', + ]); const { isReady, networkMetrics: { fastUnstakeErasToCheckPerBlock }, } = useApi(); + const { activeAccount } = useActiveAccounts(); const { checking, isExposed } = useFastUnstake(); const { isReadOnlyAccount } = useImportedAccounts(); const { getNominationStatus } = useNominationStatus(); - const { activeAccount } = useActiveAccounts(); const { getFastUnstakeText, isUnstaking } = useUnstaking(); const { setOnNominatorSetup, getNominatorSetupPercent } = useSetup(); @@ -82,26 +87,24 @@ export const NominationStatus = ({ helpKey="Nomination Status" stat={nominationStatus.message} buttons={ - !showButtons + !showButtons || syncing ? [] : !inSetup() ? !isUnstaking ? [unstakeButton] : [] - : syncing - ? [] - : [ - { - title: startTitle, - icon: faChevronCircleRight, - transform: 'grow-1', - disabled: - !isReady || - isReadOnlyAccount(activeAccount) || - !activeAccount, - onClick: () => setOnNominatorSetup(true), - }, - ] + : [ + { + title: startTitle, + icon: faChevronCircleRight, + transform: 'grow-1', + disabled: + !isReady || + isReadOnlyAccount(activeAccount) || + !activeAccount, + onClick: () => setOnNominatorSetup(true), + }, + ] } buttonType={buttonType} /> diff --git a/src/pages/Pools/Home/Status/MembershipStatus.tsx b/src/pages/Pools/Home/Status/MembershipStatus.tsx index 0fe63a4f80..2b7e0e64c6 100644 --- a/src/pages/Pools/Home/Status/MembershipStatus.tsx +++ b/src/pages/Pools/Home/Status/MembershipStatus.tsx @@ -24,11 +24,11 @@ export const MembershipStatus = ({ }) => { const { t } = useTranslation('pages'); const { isReady } = useApi(); + const { syncing } = useSyncing('*'); const { openModal } = useOverlay().modal; const { poolsMetaData } = useBondedPools(); const { activeAccount } = useActiveAccounts(); const { label, buttons } = useStatusButtons(); - const { syncing } = useSyncing(['active-pools']); const { isReadOnlyAccount } = useImportedAccounts(); const { getTransferOptions } = useTransferOptions(); const { activePool, isOwner, isBouncer, isMember } = useActivePool(); From 78c8300dd1046a261dfda888f9d5ac4d0c916e20 Mon Sep 17 00:00:00 2001 From: Ross Bulat <ross@parity.io> Date: Sun, 11 Feb 2024 12:51:53 +0700 Subject: [PATCH 44/51] feat(refactor): nominatons to BalancesController --- src/contexts/Balances/defaults.ts | 1 + src/contexts/Balances/index.tsx | 2 + src/contexts/Balances/types.ts | 9 +++++ src/contexts/Bonded/defaults.ts | 7 +--- src/contexts/Bonded/index.tsx | 39 +------------------ src/contexts/Bonded/types.ts | 9 ----- src/contexts/Pools/ActivePool/types.ts | 2 +- src/contexts/Staking/index.tsx | 6 +-- src/hooks/useActiveBalances/index.tsx | 14 +++++++ src/hooks/useActivePools/types.ts | 2 +- src/hooks/useNominationStatus/index.tsx | 10 ++--- src/library/Graphs/PayoutBar.tsx | 2 +- src/library/Graphs/PayoutLine.tsx | 2 +- src/library/Nominations/index.tsx | 6 +-- src/modals/StopNominations/index.tsx | 8 ++-- src/modals/Unstake/index.tsx | 14 ++++--- .../Active/Status/NominationStatus.tsx | 7 +--- src/pages/Nominate/Active/index.tsx | 6 +-- src/static/ActivePoolsController/index.ts | 2 +- src/static/ActivePoolsController/types.ts | 2 +- src/static/BalancesController/defaults.ts | 7 +++- src/static/BalancesController/index.ts | 30 ++++++++++++++ src/static/SyncController/types.ts | 1 - 23 files changed, 100 insertions(+), 88 deletions(-) diff --git a/src/contexts/Balances/defaults.ts b/src/contexts/Balances/defaults.ts index 3628ebbf1b..792a24a3a9 100644 --- a/src/contexts/Balances/defaults.ts +++ b/src/contexts/Balances/defaults.ts @@ -18,4 +18,5 @@ export const defaultBalancesContext: BalancesContextInterface = { getLedger: (source) => defaultLedger, getPayee: (address) => defaultPayee, getPoolMembership: (address) => null, + getNominations: (address) => [], }; diff --git a/src/contexts/Balances/index.tsx b/src/contexts/Balances/index.tsx index 4c3b6c56bd..e5705435d1 100644 --- a/src/contexts/Balances/index.tsx +++ b/src/contexts/Balances/index.tsx @@ -35,6 +35,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { getLedger, getPayee, getPoolMembership, + getNominations, } = useActiveBalances({ accounts: [activeAccount, activeProxy, controller], }); @@ -96,6 +97,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { getLedger, getPayee, getPoolMembership, + getNominations, }} > {children} diff --git a/src/contexts/Balances/types.ts b/src/contexts/Balances/types.ts index 9f5c47d81e..793de5b90e 100644 --- a/src/contexts/Balances/types.ts +++ b/src/contexts/Balances/types.ts @@ -14,6 +14,7 @@ export interface BalancesContextInterface { getLedger: (source: ActiveLedgerSource) => Ledger; getPayee: (address: MaybeAddress) => PayeeConfig; getPoolMembership: (address: MaybeAddress) => PoolMembership | null; + getNominations: (address: MaybeAddress) => Targets; } export type ActiveBalancesState = Record<string, ActiveBalance>; @@ -23,6 +24,7 @@ export interface ActiveBalance { balances: Balances; payee: PayeeConfig; poolMembership: PoolMembership; + nominations: Nominations; } export interface Balances { @@ -67,3 +69,10 @@ export interface Ledger { export type ActiveLedgerSource = { [key in 'stash' | 'key']?: MaybeAddress; }; + +export interface Nominations { + targets: Targets; + submittedIn: string | number; +} + +export type Targets = string[]; diff --git a/src/contexts/Bonded/defaults.ts b/src/contexts/Bonded/defaults.ts index 73aa456aa7..87f4069e9f 100644 --- a/src/contexts/Bonded/defaults.ts +++ b/src/contexts/Bonded/defaults.ts @@ -2,10 +2,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* eslint-disable @typescript-eslint/no-unused-vars */ -import type { - BondedContextInterface, - Nominations, -} from 'contexts/Bonded/types'; +import type { Nominations } from 'contexts/Balances/types'; +import type { BondedContextInterface } from 'contexts/Bonded/types'; export const nominations: Nominations = { targets: [], @@ -14,6 +12,5 @@ export const nominations: Nominations = { export const defaultBondedContext: BondedContextInterface = { getBondedAccount: (address) => null, - getAccountNominations: (address) => [], bondedAccounts: [], }; diff --git a/src/contexts/Bonded/index.tsx b/src/contexts/Bonded/index.tsx index 4f613574a0..5bb9090d2f 100644 --- a/src/contexts/Bonded/index.tsx +++ b/src/contexts/Bonded/index.tsx @@ -19,8 +19,6 @@ import { useOtherAccounts } from 'contexts/Connect/OtherAccounts'; import { useExternalAccounts } from 'contexts/Connect/ExternalAccounts'; import * as defaults from './defaults'; import type { BondedAccount, BondedContextInterface } from './types'; -import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { SyncController } from 'static/SyncController'; export const BondedContext = createContext<BondedContextInterface>( defaults.defaultBondedContext @@ -32,7 +30,6 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); const { api, isReady } = useApi(); const { accounts } = useImportedAccounts(); - const { activeAccount } = useActiveAccounts(); const { addExternalAccount } = useExternalAccounts(); const { addOrReplaceOtherAccount } = useOtherAccounts(); @@ -66,15 +63,6 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { const added = addedTo(accounts, bondedAccountsRef.current, ['address']); if (added.length) { - // If the current active account is being subscribed to, dispatch the `nominator` syncing - // event. - const activeAccountInAdded = added.find( - ({ address }) => address === activeAccount - ); - if (activeAccountInAdded) { - SyncController.dispatch('nominator', 'syncing'); - } - // Subscribe to all newly added accounts bonded and nominator status. added.map(({ address }) => subscribeToBondedAccount(address)); } @@ -100,11 +88,8 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { } const unsub = await api.queryMulti<AnyApi>( - [ - [api.query.staking.bonded, address], - [api.query.staking.nominators, address], - ], - async ([controller, nominations]) => { + [[api.query.staking.bonded, address]], + async ([controller]) => { const newAccount: BondedAccount = { address, }; @@ -127,27 +112,12 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { } } - // set account nominations. - const newNominations = nominations.unwrapOr(null); - newAccount.nominations = - newNominations === null - ? defaults.nominations - : { - targets: newNominations.targets.toHuman(), - submittedIn: newNominations.submittedIn.toHuman(), - }; - // remove stale account if it's already in list. const newBonded = Object.values(bondedAccountsRef.current) .filter((a) => a.address !== address) .concat(newAccount); setStateWithRef(newBonded, setBondedAccounts, bondedAccountsRef); - - // If this callback was syncing the active account, mark `nominator` syncing as complete. - if (address === activeAccount) { - SyncController.dispatch('nominator', 'complete'); - } } ); @@ -159,10 +129,6 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { bondedAccountsRef.current.find((a) => a.address === address)?.bonded || null; - const getAccountNominations = (address: MaybeAddress) => - bondedAccountsRef.current.find((a) => a.address === address)?.nominations - ?.targets || []; - // Handle accounts sync on connected accounts change. useEffectIgnoreInitial(() => { if (isReady) { @@ -182,7 +148,6 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { <BondedContext.Provider value={{ getBondedAccount, - getAccountNominations, bondedAccounts, }} > diff --git a/src/contexts/Bonded/types.ts b/src/contexts/Bonded/types.ts index 4b4fc417fe..a7fe6aef8b 100644 --- a/src/contexts/Bonded/types.ts +++ b/src/contexts/Bonded/types.ts @@ -6,18 +6,9 @@ import type { MaybeAddress } from 'types'; export interface BondedAccount { address?: string; bonded?: string; - nominations?: Nominations; } -export interface Nominations { - targets: Targets; - submittedIn: string | number; -} - -export type Targets = string[]; - export interface BondedContextInterface { getBondedAccount: (address: MaybeAddress) => string | null; - getAccountNominations: (address: MaybeAddress) => Targets; bondedAccounts: BondedAccount[]; } diff --git a/src/contexts/Pools/ActivePool/types.ts b/src/contexts/Pools/ActivePool/types.ts index 9090e7651c..5f9be92bc2 100644 --- a/src/contexts/Pools/ActivePool/types.ts +++ b/src/contexts/Pools/ActivePool/types.ts @@ -4,8 +4,8 @@ import type BigNumber from 'bignumber.js'; import type { PoolAddresses } from '../BondedPools/types'; import type { MaybeAddress } from '@polkadot-cloud/react/types'; -import type { Nominations } from 'contexts/Bonded/types'; import type { Identity, SuperIdentity } from 'contexts/Validators/types'; +import type { Nominations } from 'contexts/Balances/types'; export interface ActivePoolContextState { isBonding: () => boolean; diff --git a/src/contexts/Staking/index.tsx b/src/contexts/Staking/index.tsx index a73b8cbc5f..6844dc84e7 100644 --- a/src/contexts/Staking/index.tsx +++ b/src/contexts/Staking/index.tsx @@ -44,11 +44,11 @@ export const StakingContext = createContext<StakingContextInterface>( export const useStaking = () => useContext(StakingContext); export const StakingProvider = ({ children }: { children: ReactNode }) => { - const { getLedger } = useBalances(); + const { getBondedAccount } = useBonded(); const { networkData, network } = useNetwork(); + const { getLedger, getNominations } = useBalances(); const { accounts: connectAccounts } = useImportedAccounts(); const { activeAccount, getActiveAccount } = useActiveAccounts(); - const { getBondedAccount, getAccountNominations } = useBonded(); const { isReady, api, apiStatus, consts, activeEra, isPagedRewardsActive } = useApi(); const { maxExposurePageSize } = consts; @@ -224,7 +224,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { hasController() && getLedger({ stash: activeAccount }).unlocking.length; // Helper function to determine whether the active account is nominating, or is yet to start. - const isNominating = () => getAccountNominations(activeAccount).length > 0; + const isNominating = () => getNominations(activeAccount).length > 0; // Helper function to determine whether the active account is nominating, or is yet to start. const inSetup = () => diff --git a/src/hooks/useActiveBalances/index.tsx b/src/hooks/useActiveBalances/index.tsx index 219fc8bd77..2e187ef5b2 100644 --- a/src/hooks/useActiveBalances/index.tsx +++ b/src/hooks/useActiveBalances/index.tsx @@ -10,6 +10,7 @@ import type { BalanceLock, BalanceLocks, Ledger, + Targets, } from 'contexts/Balances/types'; import { useNetwork } from 'contexts/Network'; import { useEffect, useRef, useState } from 'react'; @@ -126,6 +127,18 @@ export const useActiveBalances = ({ return new BigNumber(0); }; + // Gets an active balance's nominations. + const getNominations = (address: MaybeAddress): Targets => { + if (address) { + const maybeNominations = + activeBalances[address]?.nominations?.targets || []; + if (maybeNominations) { + return maybeNominations; + } + } + return []; + }; + // Handle new account balance event being reported from `BalancesController`. const newAccountBalancesCallback = (e: Event) => { if ( @@ -191,5 +204,6 @@ export const useActiveBalances = ({ getPayee, getPoolMembership, getEdReserved, + getNominations, }; }; diff --git a/src/hooks/useActivePools/types.ts b/src/hooks/useActivePools/types.ts index a71b1b08b8..8a705426ce 100644 --- a/src/hooks/useActivePools/types.ts +++ b/src/hooks/useActivePools/types.ts @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { Nominations } from 'contexts/Bonded/types'; +import type { Nominations } from 'contexts/Balances/types'; import type { ActivePool } from 'contexts/Pools/ActivePool/types'; import type { DetailActivePool } from 'static/ActivePoolsController/types'; diff --git a/src/hooks/useNominationStatus/index.tsx b/src/hooks/useNominationStatus/index.tsx index e30dce588b..12b0584871 100644 --- a/src/hooks/useNominationStatus/index.tsx +++ b/src/hooks/useNominationStatus/index.tsx @@ -4,13 +4,13 @@ import { planckToUnit } from '@polkadot-cloud/utils'; import BigNumber from 'bignumber.js'; import { useTranslation } from 'react-i18next'; -import { useBonded } from 'contexts/Bonded'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useStaking } from 'contexts/Staking'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; import type { AnyJson, BondFor, MaybeAddress } from 'types'; import { useNetwork } from 'contexts/Network'; import { useSyncing } from 'hooks/useSyncing'; +import { useBalances } from 'contexts/Balances'; export const useNominationStatus = () => { const { t } = useTranslation(); @@ -20,19 +20,19 @@ export const useNominationStatus = () => { const { inSetup, eraStakers, - getNominationsStatusFromTargets, getLowestRewardFromStaker, + getNominationsStatusFromTargets, } = useStaking(); const { validators } = useValidators(); - const { getAccountNominations } = useBonded(); - const { activePoolNominations } = useActivePool(); + const { getNominations } = useBalances(); const { syncing } = useSyncing(['era-stakers']); + const { activePoolNominations } = useActivePool(); // Utility to get an account's nominees alongside their status. const getNominationSetStatus = (who: MaybeAddress, type: BondFor) => { const nominations = type === 'nominator' - ? getAccountNominations(who) + ? getNominations(who) : activePoolNominations?.targets ?? []; return getNominationsStatusFromTargets(who, nominations); diff --git a/src/library/Graphs/PayoutBar.tsx b/src/library/Graphs/PayoutBar.tsx index 89b0d77697..4e11a1aa53 100644 --- a/src/library/Graphs/PayoutBar.tsx +++ b/src/library/Graphs/PayoutBar.tsx @@ -48,7 +48,7 @@ export const PayoutBar = ({ const { mode } = useTheme(); const { inSetup } = useStaking(); const { getPoolMembership } = useBalances(); - const { syncing } = useSyncing(['nominator']); + const { syncing } = useSyncing(['balances']); const { activeAccount } = useActiveAccounts(); const membership = getPoolMembership(activeAccount); diff --git a/src/library/Graphs/PayoutLine.tsx b/src/library/Graphs/PayoutLine.tsx index 9676ea7deb..e3ac3b725f 100644 --- a/src/library/Graphs/PayoutLine.tsx +++ b/src/library/Graphs/PayoutLine.tsx @@ -49,7 +49,7 @@ export const PayoutLine = ({ const { t } = useTranslation('library'); const { mode } = useTheme(); const { inSetup } = useStaking(); - const { syncing } = useSyncing(['nominator']); + const { syncing } = useSyncing(['balances']); const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); diff --git a/src/library/Nominations/index.tsx b/src/library/Nominations/index.tsx index e9ba205ca7..00a840558f 100644 --- a/src/library/Nominations/index.tsx +++ b/src/library/Nominations/index.tsx @@ -4,7 +4,6 @@ import { faCog, faStopCircle } from '@fortawesome/free-solid-svg-icons'; import { ButtonHelp, ButtonPrimary } from '@polkadot-cloud/react'; import { useTranslation } from 'react-i18next'; -import { useBonded } from 'contexts/Bonded'; import { useHelp } from 'contexts/Help'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useStaking } from 'contexts/Staking'; @@ -19,6 +18,7 @@ import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { ListStatusHeader } from 'library/List'; import { Wrapper } from './Wrapper'; import { useSyncing } from 'hooks/useSyncing'; +import { useBalances } from 'contexts/Balances'; export const Nominations = ({ bondFor, @@ -41,10 +41,10 @@ export const Nominations = ({ canvas: { openCanvas }, } = useOverlay(); const { syncing } = useSyncing('*'); + const { getNominations } = useBalances(); const { isFastUnstaking } = useUnstaking(); const { formatWithPrefs } = useValidators(); const { activeAccount } = useActiveAccounts(); - const { getAccountNominations } = useBonded(); const { isReadOnlyAccount } = useImportedAccounts(); // Determine if pool or nominator. @@ -53,7 +53,7 @@ export const Nominations = ({ // Derive nominations from `bondFor` type. const nominated = bondFor === 'nominator' - ? formatWithPrefs(getAccountNominations(activeAccount)) + ? formatWithPrefs(getNominations(activeAccount)) : activePoolNominations ? formatWithPrefs(activePoolNominations.targets) : []; diff --git a/src/modals/StopNominations/index.tsx b/src/modals/StopNominations/index.tsx index 4596ed1fb1..a620e00e4b 100644 --- a/src/modals/StopNominations/index.tsx +++ b/src/modals/StopNominations/index.tsx @@ -19,14 +19,16 @@ import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useBalances } from 'contexts/Balances'; export const StopNominations = () => { const { t } = useTranslation('modals'); const { api } = useApi(); - const { activeAccount } = useActiveAccounts(); const { notEnoughFunds } = useTxMeta(); + const { getBondedAccount } = useBonded(); + const { getNominations } = useBalances(); + const { activeAccount } = useActiveAccounts(); const { getSignerWarnings } = useSignerWarnings(); - const { getBondedAccount, getAccountNominations } = useBonded(); const { setModalStatus, config: { options }, @@ -44,7 +46,7 @@ export const StopNominations = () => { const nominations = isPool === true ? activePoolNominations?.targets || [] - : getAccountNominations(activeAccount); + : getNominations(activeAccount); // valid to submit transaction const [valid, setValid] = useState<boolean>(false); diff --git a/src/modals/Unstake/index.tsx b/src/modals/Unstake/index.tsx index 167c59fb89..00ad3e78de 100644 --- a/src/modals/Unstake/index.tsx +++ b/src/modals/Unstake/index.tsx @@ -26,24 +26,26 @@ import { useTxMeta } from 'contexts/TxMeta'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useBalances } from 'contexts/Balances'; export const Unstake = () => { const { t } = useTranslation('modals'); - const { newBatchCall } = useBatchCall(); - const { notEnoughFunds } = useTxMeta(); - const { activeAccount } = useActiveAccounts(); - const { api, consts } = useApi(); const { networkData: { units, unit }, } = useNetwork(); + const { api, consts } = useApi(); + const { notEnoughFunds } = useTxMeta(); + const { newBatchCall } = useBatchCall(); + const { getBondedAccount } = useBonded(); + const { getNominations } = useBalances(); + const { activeAccount } = useActiveAccounts(); const { erasToSeconds } = useErasToTimeLeft(); const { getSignerWarnings } = useSignerWarnings(); const { getTransferOptions } = useTransferOptions(); const { setModalStatus, setModalResize } = useOverlay().modal; - const { getBondedAccount, getAccountNominations } = useBonded(); const controller = getBondedAccount(activeAccount); - const nominations = getAccountNominations(activeAccount); + const nominations = getNominations(activeAccount); const { bondDuration } = consts; const allTransferOptions = getTransferOptions(activeAccount); const { active } = allTransferOptions.nominate; diff --git a/src/pages/Nominate/Active/Status/NominationStatus.tsx b/src/pages/Nominate/Active/Status/NominationStatus.tsx index 2bf626e221..3886f9c723 100644 --- a/src/pages/Nominate/Active/Status/NominationStatus.tsx +++ b/src/pages/Nominate/Active/Status/NominationStatus.tsx @@ -31,12 +31,7 @@ export const NominationStatus = ({ const { inSetup } = useStaking(); const { openModal } = useOverlay().modal; const { getBondedAccount } = useBonded(); - const { syncing } = useSyncing([ - 'initialization', - 'era-stakers', - 'balances', - 'nominator', - ]); + const { syncing } = useSyncing(['initialization', 'era-stakers', 'balances']); const { isReady, networkMetrics: { fastUnstakeErasToCheckPerBlock }, diff --git a/src/pages/Nominate/Active/index.tsx b/src/pages/Nominate/Active/index.tsx index 5dd1d67001..a22ea30758 100644 --- a/src/pages/Nominate/Active/index.tsx +++ b/src/pages/Nominate/Active/index.tsx @@ -28,20 +28,20 @@ import { MinimumNominatorBondStat } from './Stats/MinimumNominatorBond'; import { Status } from './Status'; import { UnstakePrompts } from './UnstakePrompts'; import { useSyncing } from 'hooks/useSyncing'; -import { useBonded } from 'contexts/Bonded'; +import { useBalances } from 'contexts/Balances'; export const Active = () => { const { t } = useTranslation(); const { openHelp } = useHelp(); const { inSetup } = useStaking(); const { syncing } = useSyncing('*'); + const { getNominations } = useBalances(); const { openCanvas } = useOverlay().canvas; const { isFastUnstaking } = useUnstaking(); const { formatWithPrefs } = useValidators(); - const { getAccountNominations } = useBonded(); const { activeAccount } = useActiveAccounts(); - const nominated = formatWithPrefs(getAccountNominations(activeAccount)); + const nominated = formatWithPrefs(getNominations(activeAccount)); const ROW_HEIGHT = 220; return ( diff --git a/src/static/ActivePoolsController/index.ts b/src/static/ActivePoolsController/index.ts index 32357f98bc..2e87ac693a 100644 --- a/src/static/ActivePoolsController/index.ts +++ b/src/static/ActivePoolsController/index.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: GPL-3.0-only import type { VoidFn } from '@polkadot/api/types'; -import type { Nominations } from 'contexts/Bonded/types'; import { defaultPoolNominations } from 'contexts/Pools/ActivePool/defaults'; import type { ActivePool, PoolRoles } from 'contexts/Pools/ActivePool/types'; import { APIController } from 'static/APIController'; @@ -10,6 +9,7 @@ import { IdentitiesController } from 'static/IdentitiesController'; import type { AnyApi } from 'types'; import type { ActivePoolItem, DetailActivePool } from './types'; import { SyncController } from 'static/SyncController'; +import type { Nominations } from 'contexts/Balances/types'; export class ActivePoolsController { // ------------------------------------------------------ diff --git a/src/static/ActivePoolsController/types.ts b/src/static/ActivePoolsController/types.ts index 100c85b98d..f3fe5c5df4 100644 --- a/src/static/ActivePoolsController/types.ts +++ b/src/static/ActivePoolsController/types.ts @@ -1,7 +1,7 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { Nominations } from 'contexts/Bonded/types'; +import type { Nominations } from 'contexts/Balances/types'; import type { ActivePool } from 'contexts/Pools/ActivePool/types'; export interface DetailActivePool { diff --git a/src/static/BalancesController/defaults.ts b/src/static/BalancesController/defaults.ts index 8ac3783e05..dd5f2bdfd2 100644 --- a/src/static/BalancesController/defaults.ts +++ b/src/static/BalancesController/defaults.ts @@ -3,7 +3,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import BigNumber from 'bignumber.js'; -import type { Balance, Ledger } from 'contexts/Balances/types'; +import type { Balance, Ledger, Nominations } from 'contexts/Balances/types'; import type { PayeeConfig } from 'contexts/Setup/types'; export const defaultBalance: Balance = { @@ -23,3 +23,8 @@ export const defaultPayee: PayeeConfig = { destination: 'Staked', account: null, }; + +export const defaultNominations: Nominations = { + targets: [], + submittedIn: 0, +}; diff --git a/src/static/BalancesController/index.ts b/src/static/BalancesController/index.ts index 52800e1d59..46e173ba70 100644 --- a/src/static/BalancesController/index.ts +++ b/src/static/BalancesController/index.ts @@ -10,11 +10,13 @@ import type { ActiveBalance, Balances, Ledger, + Nominations, UnlockChunkRaw, } from 'contexts/Balances/types'; import type { PayeeConfig, PayeeOptions } from 'contexts/Setup/types'; import type { PoolMembership } from 'contexts/Pools/types'; import { SyncController } from 'static/SyncController'; +import { defaultNominations } from './defaults'; export class BalancesController { // ------------------------------------------------------ @@ -36,6 +38,9 @@ export class BalancesController { // Account pool membership and claim commissions, populated by api callbacks. static poolMemberships: Record<string, PoolMembership> = {}; + // Account nominations, populated by api callbacks. + static nominations: Record<string, Nominations> = {}; + // Unsubscribe objects. static _unsubs: Record<string, VoidFn> = {}; @@ -74,6 +79,7 @@ export class BalancesController { [api.query.staking.payee, address], [api.query.nominationPools.poolMembers, address], [api.query.nominationPools.claimPermissions, address], + [api.query.staking.nominators, address], ], async ([ ledgerResult, @@ -82,6 +88,7 @@ export class BalancesController { payeeResult, poolMembersResult, claimPermissionsResult, + nominatorsResult, ]): Promise<void> => { this.handleLedgerCallback(address, ledgerResult); this.handleAccountCallback(address, accountResult, locksResult); @@ -94,6 +101,8 @@ export class BalancesController { claimPermissionsResult ); + this.handleNominations(address, nominatorsResult); + // Send updated account state back to UI. document.dispatchEvent( new CustomEvent('new-account-balance', { @@ -103,6 +112,7 @@ export class BalancesController { balances: this.balances[address], payee: this.payees[address], poolMembership: this.poolMemberships[address], + nominations: this.nominations[address], }, }) ); @@ -126,6 +136,7 @@ export class BalancesController { delete this.balances[account]; delete this.payees[account]; delete this.poolMemberships[account]; + delete this.nominations[account]; }); // Remove removed accounts from class. this.accounts = this.accounts.filter( @@ -256,12 +267,29 @@ export class BalancesController { }; }; + // Handle nominations callback. + static handleNominations = ( + address: string, + nominatorsResult: AnyApi + ): void => { + const nominators = nominatorsResult.unwrapOr(null); + + this.nominations[address] = + nominators === null + ? defaultNominations + : { + targets: nominators.targets.toHuman(), + submittedIn: nominators.submittedIn.toHuman(), + }; + }; + // Gets an `ActiveBalance` from class members for the given address if it exists. static getAccountBalances = (address: string): ActiveBalance | undefined => { const ledger = this.ledgers[address]; const balances = this.balances[address]; const payee = this.payees[address]; const poolMembership = this.poolMemberships[address]; + const nominations = this.nominations[address]; // Account info has not synced yet. Note that `ledger` may not exist and therefore cannot be // tested. @@ -273,6 +301,7 @@ export class BalancesController { balances, payee, poolMembership, + nominations, }; }; @@ -290,6 +319,7 @@ export class BalancesController { this.balances = {}; this.payees = {}; this.poolMemberships = {}; + this.nominations = {}; this._unsubs = {}; }; diff --git a/src/static/SyncController/types.ts b/src/static/SyncController/types.ts index 4fc13b07e7..915aa8b624 100644 --- a/src/static/SyncController/types.ts +++ b/src/static/SyncController/types.ts @@ -5,7 +5,6 @@ export type SyncID = | 'initialization' | 'balances' | 'era-stakers' - | 'nominator' | 'active-pools'; export interface SyncEvent { From b6312e60c62daef57f4fe14aaa0cc04db03eeaba Mon Sep 17 00:00:00 2001 From: Ross Bulat <ross@parity.io> Date: Sun, 11 Feb 2024 16:40:35 +0700 Subject: [PATCH 45/51] feat(refactor): persist Subscan results to class (#1942) --- src/canvas/PoolMembers/Lists/FetchPage.tsx | 6 +- src/contexts/Plugins/index.tsx | 5 - src/hooks/useSubscanData/index.tsx | 36 +++--- src/modals/ClaimPayouts/Forms.tsx | 4 +- src/static/SubscanController/index.ts | 124 ++++++++++++++------- src/static/SubscanController/types.ts | 5 + 6 files changed, 114 insertions(+), 66 deletions(-) diff --git a/src/canvas/PoolMembers/Lists/FetchPage.tsx b/src/canvas/PoolMembers/Lists/FetchPage.tsx index ceba60992c..0ae8f9c8d9 100644 --- a/src/canvas/PoolMembers/Lists/FetchPage.tsx +++ b/src/canvas/PoolMembers/Lists/FetchPage.tsx @@ -70,8 +70,10 @@ export const MembersListInner = ({ if (poolId > 0 && !fetchingMemberList.current) { fetchingMemberList.current = true; - const newMembers: PoolMember[] = - await SubscanController.handleFetchPoolMembers(poolId, page); + const newMembers = (await SubscanController.handleFetchPoolMembers( + poolId, + page + )) as PoolMember[]; fetchingMemberList.current = false; setPoolMembersApi([...newMembers]); diff --git a/src/contexts/Plugins/index.tsx b/src/contexts/Plugins/index.tsx index 3f91a9f13c..3bd9528776 100644 --- a/src/contexts/Plugins/index.tsx +++ b/src/contexts/Plugins/index.tsx @@ -47,11 +47,6 @@ export const PluginsProvider = ({ children }: { children: ReactNode }) => { // Check if a plugin is currently enabled. const pluginEnabled = (key: Plugin) => pluginsRef.current.includes(key); - // Reset payouts on Subscan network on `activeAccount` switch. - useEffectIgnoreInitial(() => { - SubscanController.resetData(); - }, [network, activeAccount]); - // Reset payouts on Subscan plugin not enabled. Otherwise fetch payouts. useEffectIgnoreInitial(() => { if (!plugins.includes('subscan')) { diff --git a/src/hooks/useSubscanData/index.tsx b/src/hooks/useSubscanData/index.tsx index 1f301c1fbc..630fa27efd 100644 --- a/src/hooks/useSubscanData/index.tsx +++ b/src/hooks/useSubscanData/index.tsx @@ -15,11 +15,13 @@ import { isCustomEvent } from 'static/utils'; import { useEventListener } from 'usehooks-ts'; import { useErasToTimeLeft } from '../useErasToTimeLeft'; import { useApi } from 'contexts/Api'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; export const useSubscanData = (keys: PayoutType[]) => { const { activeEra } = useApi(); const { pluginEnabled } = usePlugins(); const { erasToSeconds } = useErasToTimeLeft(); + const { activeAccount } = useActiveAccounts(); // Store the most up to date subscan data state. const [data, setData] = useState<SubscanData>({}); @@ -32,14 +34,18 @@ export const useSubscanData = (keys: PayoutType[]) => { if (isCustomEvent(e) && pluginEnabled('subscan')) { const { keys: receivedKeys }: { keys: PayoutType[] } = e.detail; - // Filter out any keys that are not provided to the hook. - const newData: SubscanData = {}; - receivedKeys - .filter((key) => keys.includes(key)) - .forEach((key) => { - newData[key] = SubscanController.data?.[key] || []; - }); - setStateWithRef({ ...dataRef.current, ...newData }, setData, dataRef); + // Filter out any keys that are not provided to the hook active account is still present. + if (activeAccount) { + const newData: SubscanData = {}; + receivedKeys + .filter((key) => keys.includes(key)) + .forEach((key) => { + newData[key] = + SubscanController.payoutData[activeAccount]?.[key] || []; + }); + + setStateWithRef({ ...dataRef.current, ...newData }, setData, dataRef); + } } }; @@ -79,12 +85,14 @@ export const useSubscanData = (keys: PayoutType[]) => { // Populate state on initial render if data is already available. useEffect(() => { - const newData: SubscanData = {}; - keys.forEach((key: PayoutType) => { - newData[key] = SubscanController.data?.[key] || []; - }); - setStateWithRef({ ...dataRef.current, ...newData }, setData, dataRef); - }, []); + if (activeAccount) { + const newData: SubscanData = {}; + keys.forEach((key: PayoutType) => { + newData[key] = SubscanController.payoutData[activeAccount]?.[key] || []; + }); + setStateWithRef({ ...dataRef.current, ...newData }, setData, dataRef); + } + }, [activeAccount]); return { data, getData, injectBlockTimestamp }; }; diff --git a/src/modals/ClaimPayouts/Forms.tsx b/src/modals/ClaimPayouts/Forms.tsx index 433ff146b0..bfb36c5227 100644 --- a/src/modals/ClaimPayouts/Forms.tsx +++ b/src/modals/ClaimPayouts/Forms.tsx @@ -115,13 +115,13 @@ export const Forms = forwardRef( setModalStatus('closing'); }, callbackInBlock: () => { - if (payouts) { + if (payouts && activeAccount) { // Remove Subscan unclaimed payout record(s) if they exist. const eraPayouts: string[] = []; payouts.forEach(({ era }) => { eraPayouts.push(String(era)); }); - SubscanController.removeUnclaimedPayouts(eraPayouts); + SubscanController.removeUnclaimedPayouts(activeAccount, eraPayouts); // Deduct from `unclaimedPayouts` in Payouts context. payouts.forEach(({ era, paginatedValidators }) => { diff --git a/src/static/SubscanController/index.ts b/src/static/SubscanController/index.ts index be591a3842..8b9ee343b3 100644 --- a/src/static/SubscanController/index.ts +++ b/src/static/SubscanController/index.ts @@ -8,6 +8,7 @@ import type { SubscanPoolDetails, SubscanPoolMember, SubscanRequestBody, + SubscanEraPoints, } from './types'; import type { Locale } from 'date-fns'; import { format, fromUnixTime, getUnixTime, subDays } from 'date-fns'; @@ -40,8 +41,14 @@ export class SubscanController { // The network to use for Subscan API calls. static network: string; - // Subscan data for the current account. - static data: SubscanData; + // Subscan payout data, keyed by address. + static payoutData: Record<string, SubscanData> = {}; + + // Subscan pool data, keyed by `<network>-<poolId>-<key1>-<key2>...`. + static poolData: Record<string, SubscanPoolDetails | PoolMember[]> = {}; + + // Subscan era points data, keyed by `<network>-<address>-<era>`. + static eraPointsData: Record<string, SubscanEraPoints[]> = {}; // The timestamp of the last 5 requests made. static _lastRequestTimes = []; @@ -57,6 +64,37 @@ export class SubscanController { SubscanController.network = network; } + // ------------------------------------------------------ + // Handling multiple requests. + // ------------------------------------------------------ + + // Handle fetching the various types of payout and set state in one render. + static handleFetchPayouts = async (address: string) => { + if (!this.payoutData[address]) { + const results = await Promise.all([ + this.fetchNominatorPayouts(address), + this.fetchPoolClaims(address), + ]); + const { payouts, unclaimedPayouts } = results[0]; + const poolClaims = results[1]; + + // Persist results to class. + this.payoutData[address] = { + payouts, + unclaimedPayouts, + poolClaims, + }; + + document.dispatchEvent( + new CustomEvent('subscan-data-updated', { + detail: { + keys: ['payouts', 'unclaimedPayouts', 'poolClaims'], + }, + }) + ); + } + }; + // ------------------------------------------------------ // Handling requests. // ------------------------------------------------------ @@ -143,7 +181,10 @@ export class SubscanController { }; // Fetch a pool's era points from Subscan. - static fetchEraPoints = async (address: string, era: number) => { + static fetchEraPoints = async ( + address: string, + era: number + ): Promise<SubscanEraPoints[]> => { const result = await this.makeRequest(this.ENDPOINTS.eraStat, { page: 0, row: 100, @@ -168,49 +209,47 @@ export class SubscanController { return list.reverse().splice(0, list.length - 1); }; - // ------------------------------------------------------ - // Handling multiple requests concurrently. - // ------------------------------------------------------ - - // Handle fetching the various types of payout and set state in one render. - static handleFetchPayouts = async (address: string) => { - const results = await Promise.all([ - this.fetchNominatorPayouts(address), - this.fetchPoolClaims(address), - ]); - const { payouts, unclaimedPayouts } = results[0]; - const poolClaims = results[1]; - - // Persist results to class. - this.data['payouts'] = payouts; - this.data['unclaimedPayouts'] = unclaimedPayouts; - this.data['poolClaims'] = poolClaims; - - document.dispatchEvent( - new CustomEvent('subscan-data-updated', { - detail: { - keys: ['payouts', 'unclaimedPayouts', 'poolClaims'], - }, - }) - ); - }; - // Handle fetching pool members. static handleFetchPoolMembers = async (poolId: number, page: number) => { - const poolMembers = await this.fetchPoolMembers(poolId, page); - return poolMembers; + const dataKey = `${this.network}-${poolId}-${page}-members}`; + const currentValue = this.poolData[dataKey]; + + if (currentValue) { + return currentValue; + } else { + const result = await this.fetchPoolMembers(poolId, page); + this.poolData[dataKey] = result; + + return result; + } }; // Handle fetching pool details. static handleFetchPoolDetails = async (poolId: number) => { - const poolDetails = await this.fetchPoolDetails(poolId); - return poolDetails; + const dataKey = `${this.network}-${poolId}-details}`; + const currentValue = this.poolData[dataKey]; + + if (currentValue) { + return currentValue as SubscanPoolDetails; + } else { + const result = await this.fetchPoolDetails(poolId); + this.poolData[dataKey] = result; + return result; + } }; // Handle fetching era point history. static handleFetchEraPoints = async (address: string, era: number) => { - const eraPoints = await this.fetchEraPoints(address, era); - return eraPoints; + const dataKey = `${this.network}-${address}-${era}}`; + const currentValue = this.eraPointsData[dataKey]; + + if (currentValue) { + return currentValue; + } else { + const result = await this.fetchEraPoints(address, era); + this.eraPointsData[dataKey] = result; + return result; + } }; // ------------------------------------------------------ @@ -219,19 +258,18 @@ export class SubscanController { // Resets all received data from class. static resetData = () => { - this.data = {}; + this.payoutData = {}; }; // Remove unclaimed payouts and dispatch update event. - static removeUnclaimedPayouts = (eraPayouts: string[]) => { - const updatedUnclaimedPayouts = this.data[ - 'unclaimedPayouts' - ] as SubscanPayout[]; + static removeUnclaimedPayouts = (address: string, eraPayouts: string[]) => { + const newUnclaimedPayouts = (this.payoutData[address]?.unclaimedPayouts || + []) as SubscanPayout[]; eraPayouts.forEach(([era]) => { - updatedUnclaimedPayouts.filter((u) => String(u.era) !== era); + newUnclaimedPayouts.filter((u) => String(u.era) !== era); }); - this.data['unclaimedPayouts'] = updatedUnclaimedPayouts; + this.payoutData[address].unclaimedPayouts = newUnclaimedPayouts; document.dispatchEvent( new CustomEvent('subscan-data-updated', { diff --git a/src/static/SubscanController/types.ts b/src/static/SubscanController/types.ts index a5e46d9d51..36fb6d8195 100644 --- a/src/static/SubscanController/types.ts +++ b/src/static/SubscanController/types.ts @@ -91,3 +91,8 @@ export interface SubscanPoolMember { export interface SubscanPoolDetails { member_count: number; } + +export interface SubscanEraPoints { + era: number; + reward_point: number; +} From 607ec6dee7cef57e223ddaa99ee6f8b4a8d9f5fa Mon Sep 17 00:00:00 2001 From: Ross Bulat <ross@parity.io> Date: Sun, 11 Feb 2024 17:52:38 +0700 Subject: [PATCH 46/51] chore: fix unlock values --- src/modals/UnlockChunks/Overview.tsx | 4 ++-- src/static/BalancesController/index.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modals/UnlockChunks/Overview.tsx b/src/modals/UnlockChunks/Overview.tsx index f40e08fe67..88847eea95 100644 --- a/src/modals/UnlockChunks/Overview.tsx +++ b/src/modals/UnlockChunks/Overview.tsx @@ -16,7 +16,7 @@ import { timeleftAsString } from 'hooks/useTimeLeft/utils'; import { useUnstaking } from 'hooks/useUnstaking'; import { StatWrapper, StatsWrapper } from 'library/Modal/Wrappers'; import { StaticNote } from 'modals/Utils/StaticNote'; -import type { AnyJson, BondFor } from 'types'; +import type { BondFor } from 'types'; import { useNetwork } from 'contexts/Network'; import { Chunk } from './Chunk'; import { ContentWrapper } from './Wrappers'; @@ -65,7 +65,7 @@ export const Overview = forwardRef( } } - const onRebondHandler = (chunk: AnyJson) => { + const onRebondHandler = (chunk: UnlockChunk) => { setTask('rebond'); setUnlock(chunk); setSection(1); diff --git a/src/static/BalancesController/index.ts b/src/static/BalancesController/index.ts index 46e173ba70..907739171c 100644 --- a/src/static/BalancesController/index.ts +++ b/src/static/BalancesController/index.ts @@ -171,7 +171,7 @@ export class BalancesController { total: this.stringToBigNumber(total.toString()), unlocking: unlocking.toHuman().map(({ era, value }: UnlockChunkRaw) => ({ era: Number(rmCommas(era)), - value: this.stringToBigNumber(value).toString(), + value: this.stringToBigNumber(value), })), }; }; From b168bdd5d6c80ff5383793e7b2ec43a2a5e21839 Mon Sep 17 00:00:00 2001 From: Ross Bulat <ross@parity.io> Date: Sun, 11 Feb 2024 18:28:58 +0700 Subject: [PATCH 47/51] chore: remove unsub code --- src/contexts/Connect/OtherAccounts/index.tsx | 25 +------------------- 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/contexts/Connect/OtherAccounts/index.tsx b/src/contexts/Connect/OtherAccounts/index.tsx index 665236222e..7a23f80384 100644 --- a/src/contexts/Connect/OtherAccounts/index.tsx +++ b/src/contexts/Connect/OtherAccounts/index.tsx @@ -12,7 +12,7 @@ import { getLocalLedgerAccounts, getLocalVaultAccounts, } from 'contexts/Hardware/Utils'; -import type { AnyFunction, MaybeAddress, NetworkName } from 'types'; +import type { MaybeAddress, NetworkName } from 'types'; import { setStateWithRef } from '@polkadot-cloud/utils'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -55,26 +55,13 @@ export const OtherAccountsProvider = ({ // different sources. const otherAccountsRef = useRef(otherAccounts); - // Store unsubscribe handlers for connected extensions. - const unsubs = useRef<Record<string, AnyFunction>>({}); - // Store whether all accounts have been synced. const [accountsInitialised, setAccountsInitialised] = useState<boolean>(false); // Handle forgetting of an imported other account. const forgetOtherAccounts = (forget: ImportedAccount[]) => { - // Unsubscribe and remove unsub from context ref. if (forget.length) { - for (const { address } of forget) { - if (otherAccountsRef.current.find((a) => a.address === address)) { - const unsub = unsubs.current[address]; - if (unsub) { - unsub(); - delete unsubs.current[address]; - } - } - } // Remove forgotten accounts from context state. setStateWithRef( [...otherAccountsRef.current].filter( @@ -143,13 +130,6 @@ export const OtherAccountsProvider = ({ ); }; - // Unsubscribe all account subscriptions. - const unsubscribe = () => { - Object.values(unsubs.current).forEach((unsub) => { - unsub(); - }); - }; - // Add other accounts to context state. const addOtherAccounts = (account: ImportedAccount[]) => { setStateWithRef( @@ -203,11 +183,8 @@ export const OtherAccountsProvider = ({ // Re-sync other accounts on network switch. Waits for `injectedWeb3` to be injected. useEffect(() => { if (!checkingInjectedWeb3) { - // unsubscribe from all accounts and reset state. - unsubscribe(); setStateWithRef([], setOtherAccounts, otherAccountsRef); } - return () => unsubscribe(); }, [network, checkingInjectedWeb3]); // Once extensions are fully initialised, fetch accounts from other sources. From bf856d0610a7dc2df844440ea895496e39666148 Mon Sep 17 00:00:00 2001 From: Ross Bulat <ross@parity.io> Date: Tue, 13 Feb 2024 12:27:05 +0700 Subject: [PATCH 48/51] feat(refactor): Menu refresh with mouse-based position (#1945) --- src/canvas/PoolMembers/Lists/Member.tsx | 22 ++--- src/contexts/Menu/defaults.ts | 15 ++- src/contexts/Menu/index.tsx | 97 ++++++++++--------- src/contexts/Menu/types.ts | 23 +++-- src/library/ListItem/Wrappers.ts | 9 -- src/library/Menu/List.tsx | 28 ++++++ src/library/Menu/index.tsx | 75 ++++++-------- src/library/Pool/index.tsx | 25 ++--- .../ValidatorList/ValidatorItem/Default.tsx | 23 ++--- 9 files changed, 156 insertions(+), 161 deletions(-) create mode 100644 src/library/Menu/List.tsx diff --git a/src/canvas/PoolMembers/Lists/Member.tsx b/src/canvas/PoolMembers/Lists/Member.tsx index 9c056c7f92..1ff9c25a12 100644 --- a/src/canvas/PoolMembers/Lists/Member.tsx +++ b/src/canvas/PoolMembers/Lists/Member.tsx @@ -7,6 +7,7 @@ import { faUnlockAlt, } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import type { MouseEvent as ReactMouseEvent } from 'react'; import { useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useMenu } from 'contexts/Menu'; @@ -16,18 +17,14 @@ import { useList } from 'library/List/context'; import { Identity } from 'library/ListItem/Labels/Identity'; import { PoolMemberBonded } from 'library/ListItem/Labels/PoolMemberBonded'; import { Select } from 'library/ListItem/Labels/Select'; -import { - Labels, - MenuPosition, - Separator, - Wrapper, -} from 'library/ListItem/Wrappers'; +import { Labels, Separator, Wrapper } from 'library/ListItem/Wrappers'; import type { AnyJson } from 'types'; import { usePrompt } from 'contexts/Prompt'; import { UnbondMember } from '../Prompts/UnbondMember'; import { WithdrawMember } from '../Prompts/WithdrawMember'; import { motion } from 'framer-motion'; import { useApi } from 'contexts/Api'; +import { MenuList } from 'library/Menu/List'; export const Member = ({ who, @@ -42,8 +39,8 @@ export const Member = ({ const { activeEra } = useApi(); const { meta } = usePoolMembers(); const { selectActive } = useList(); + const { openMenu, open } = useMenu(); const { openPromptWith } = usePrompt(); - const { setMenuPosition, setMenuItems, open } = useMenu(); const { activePool, isOwner, isBouncer } = useActivePool(); // Ref for the member container. @@ -109,12 +106,10 @@ export const Member = ({ } } - // configure floating menu - const posRef = useRef(null); - const toggleMenu = () => { + // Handler for opening menu. + const toggleMenu = (ev: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => { if (!open) { - setMenuItems(menuItems); - setMenuPosition(posRef); + openMenu(ev, <MenuList items={menuItems} />); } }; @@ -135,7 +130,6 @@ export const Member = ({ > <Wrapper className="member"> <div className="inner canvas"> - <MenuPosition ref={posRef} /> <div className="row top"> {selectActive && <Select item={{ address: who }} />} <Identity address={who} /> @@ -146,7 +140,7 @@ export const Member = ({ type="button" className="label" disabled={!member} - onClick={() => toggleMenu()} + onClick={(ev) => toggleMenu(ev)} > <FontAwesomeIcon icon={faBars} /> </button> diff --git a/src/contexts/Menu/defaults.ts b/src/contexts/Menu/defaults.ts index 704e40c1d1..9bae1c745b 100644 --- a/src/contexts/Menu/defaults.ts +++ b/src/contexts/Menu/defaults.ts @@ -5,13 +5,12 @@ import type { MenuContextInterface } from './types'; export const defaultMenuContext: MenuContextInterface = { - openMenu: () => {}, - closeMenu: () => {}, - setMenuPosition: (r) => {}, - checkMenuPosition: (r) => {}, - setMenuItems: (items) => {}, - open: 0, - show: 0, + open: false, + show: false, + inner: null, position: [0, 0], - items: [], + openMenu: (ev, newInner) => {}, + closeMenu: () => {}, + setMenuInner: (newInner) => {}, + checkMenuPosition: (menuRef) => {}, }; diff --git a/src/contexts/Menu/index.tsx b/src/contexts/Menu/index.tsx index e3789d3fa4..e30badb860 100644 --- a/src/contexts/Menu/index.tsx +++ b/src/contexts/Menu/index.tsx @@ -4,7 +4,7 @@ import type { ReactNode, RefObject } from 'react'; import { createContext, useContext, useState } from 'react'; import { defaultMenuContext } from './defaults'; -import type { MenuContextInterface, MenuItem } from './types'; +import type { MenuContextInterface, MenuMouseEvent } from './types'; export const MenuContext = createContext<MenuContextInterface>(defaultMenuContext); @@ -12,84 +12,87 @@ export const MenuContext = export const useMenu = () => useContext(MenuContext); export const MenuProvider = ({ children }: { children: ReactNode }) => { - const [open, setOpen] = useState<number>(0); - const [show, setShow] = useState<number>(0); - const [items, setItems] = useState<MenuItem[]>([]); + // Whether the menu is currently open. This initiates menu state but does not reflect whether the + // menu is being displayed. + const [open, setOpen] = useState<boolean>(false); + // Whether the menu is currently showing. + const [show, setShow] = useState<boolean>(false); + + // The components to be displayed in the menu. + const [inner, setInner] = useState<ReactNode>(null); + + // The menu position coordinates. const [position, setPosition] = useState<[number, number]>([0, 0]); - const openMenu = () => { + // Padding from the window edge. + const DocumentPadding = 20; + + // Sets the menu position and opens it. Only succeeds if the menu has been instantiated and is not + // currently open. + const openMenu = (ev: MenuMouseEvent, newInner?: ReactNode) => { if (open) { return; } - setOpen(1); + const bodyRect = document.body.getBoundingClientRect(); + const x = ev.clientX - bodyRect.left; + const y = ev.clientY - bodyRect.top; + + if (newInner) { + setInner(newInner); + } + + setPosition([x, y]); + setOpen(true); }; + // Hides the menu and closes. const closeMenu = () => { - setShow(0); - setTimeout(() => { - setOpen(0); - }, 100); + setShow(false); + setOpen(false); }; - const setMenuPosition = (ref: RefObject<HTMLDivElement>) => { - if (open || !ref?.current) { - return; - } - const bodyRect = document.body.getBoundingClientRect(); - const elemRect = ref.current.getBoundingClientRect(); - - const x = elemRect.left - bodyRect.left; - const y = elemRect.top - bodyRect.top; - - setPosition([x, y]); - openMenu(); + // Sets the inner JSX of the menu. + const setMenuInner = (newInner: ReactNode) => { + setInner(newInner); }; + // Adjusts menu position and shows the menu. const checkMenuPosition = (ref: RefObject<HTMLDivElement>) => { if (!ref?.current) { return; } + // Adjust menu position if it is leaking out of the window, otherwise keep it at the current + // position. const bodyRect = document.body.getBoundingClientRect(); const menuRect = ref.current.getBoundingClientRect(); + const hiddenRight = menuRect.right > bodyRect.right; + const hiddenBottom = menuRect.bottom > bodyRect.bottom; - let x = menuRect.left - bodyRect.left; - let y = menuRect.top - bodyRect.top; - const right = menuRect.right; - const bottom = menuRect.bottom; - - // small offset from menu start - y -= 10; + const x = hiddenRight + ? window.innerWidth - menuRect.width - DocumentPadding + : position[0]; - const documentPadding = 20; + const y = hiddenBottom + ? window.innerHeight - menuRect.height - DocumentPadding + : position[1]; - if (right > bodyRect.right) { - x = bodyRect.right - ref.current.offsetWidth - documentPadding; - } - if (bottom > bodyRect.bottom) { - y = bodyRect.bottom - ref.current.offsetHeight - documentPadding; - } setPosition([x, y]); - setShow(1); - }; - - const setMenuItems = (_items: MenuItem[]) => { - setItems(_items); + setShow(true); }; return ( <MenuContext.Provider value={{ - openMenu, - closeMenu, - setMenuPosition, - checkMenuPosition, - setMenuItems, open, show, + inner, position, - items, + closeMenu, + openMenu, + setMenuInner, + checkMenuPosition, }} > {children} diff --git a/src/contexts/Menu/types.ts b/src/contexts/Menu/types.ts index f4b24768a4..a1b379fc84 100644 --- a/src/contexts/Menu/types.ts +++ b/src/contexts/Menu/types.ts @@ -1,18 +1,21 @@ // Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ReactNode, RefObject } from 'react'; +import type { + ReactNode, + RefObject, + MouseEvent as ReactMouseEvent, +} from 'react'; export interface MenuContextInterface { - openMenu: () => void; + open: boolean; + show: boolean; + inner: ReactNode | null; + position: [number, number]; + openMenu: (ev: MenuMouseEvent, newInner?: ReactNode) => void; closeMenu: () => void; - setMenuPosition: (ref: RefObject<HTMLDivElement>) => void; + setMenuInner: (items: ReactNode) => void; checkMenuPosition: (ref: RefObject<HTMLDivElement>) => void; - setMenuItems: (items: MenuItem[]) => void; - open: number; - show: number; - position: [number, number]; - items: MenuItem[]; } export interface MenuItem { @@ -20,3 +23,7 @@ export interface MenuItem { title: string; cb: () => void; } + +export type MenuMouseEvent = + | MouseEvent + | ReactMouseEvent<HTMLButtonElement, MouseEvent>; diff --git a/src/library/ListItem/Wrappers.ts b/src/library/ListItem/Wrappers.ts index bac218f1dd..d114caa354 100644 --- a/src/library/ListItem/Wrappers.ts +++ b/src/library/ListItem/Wrappers.ts @@ -289,15 +289,6 @@ export const Separator = styled.div` opacity: 0.7; `; -export const MenuPosition = styled.div` - position: absolute; - top: -10px; - right: 10px; - width: 0; - height: 0; - opacity: 0; -`; - export const TooltipTrigger = styled.div` z-index: 1; width: 130%; diff --git a/src/library/Menu/List.tsx b/src/library/Menu/List.tsx new file mode 100644 index 0000000000..e334adde17 --- /dev/null +++ b/src/library/Menu/List.tsx @@ -0,0 +1,28 @@ +import type { MenuItem } from 'contexts/Menu/types'; +import { ItemWrapper } from './Wrappers'; +import { useMenu } from 'contexts/Menu'; + +export const MenuList = ({ items }: { items: MenuItem[] }) => { + const { closeMenu } = useMenu(); + + return ( + <> + {items.map((item, i: number) => { + const { icon, title, cb } = item; + + return ( + <ItemWrapper + key={`menu_item_${i}`} + onClick={() => { + cb(); + closeMenu(); + }} + > + {icon} + <div className="title">{title}</div> + </ItemWrapper> + ); + })} + </> + ); +}; diff --git a/src/library/Menu/index.tsx b/src/library/Menu/index.tsx index cf88b899ca..b9e2a9c6d9 100644 --- a/src/library/Menu/index.tsx +++ b/src/library/Menu/index.tsx @@ -3,23 +3,39 @@ import { useEffect, useRef } from 'react'; import { useMenu } from 'contexts/Menu'; -import { ItemWrapper, Wrapper } from './Wrappers'; -import type { AnyJson } from 'types'; +import { Wrapper } from './Wrappers'; import { useOutsideAlerter } from 'hooks/useOutsideAlerter'; export const Menu = () => { - const menu = useMenu(); - const { position } = menu; + const { + open, + show, + inner, + closeMenu, + position: [x, y], + checkMenuPosition, + } = useMenu(); + + const menuRef = useRef(null); + + // Handler for closing the menu on window resize. + const resizeCallback = () => { + closeMenu(); + }; - const ref = useRef(null); + // Close the menu if clicked outside of its container. + useOutsideAlerter(menuRef, () => { + closeMenu(); + }); + // Check position and show the menu if menu has been opened. useEffect(() => { - if (menu.open === 1) { - menu.checkMenuPosition(ref); - // check position + if (open) { + checkMenuPosition(menuRef); } - }, [menu.open]); + }, [open]); + // Close the menu on window resize. useEffect(() => { window.addEventListener('resize', resizeCallback); return () => { @@ -27,46 +43,19 @@ export const Menu = () => { }; }, []); - const resizeCallback = () => { - menu.closeMenu(); - }; - - useOutsideAlerter( - ref, - () => { - menu.closeMenu(); - }, - ['ignore-open-menu-button'] - ); - return ( - menu.open === 1 && ( + open && ( <Wrapper - ref={ref} + ref={menuRef} style={{ position: 'absolute', - left: `${position[0]}px`, - top: `${position[1]}px`, - zIndex: 99, - opacity: menu.show === 1 ? 1 : 0, + left: `${x}px`, + top: `${y}px`, + zIndex: 999, + opacity: show ? 1 : 0, }} > - {menu.items.map((item: AnyJson, i: number) => { - const { icon, title, cb } = item; - - return ( - <ItemWrapper - key={`menu_item_${i}`} - onClick={() => { - cb(); - menu.closeMenu(); - }} - > - {icon} - <div className="title">{title}</div> - </ItemWrapper> - ); - })} + {inner} </Wrapper> ) ); diff --git a/src/library/Pool/index.tsx b/src/library/Pool/index.tsx index 95340588fd..20eeb83249 100644 --- a/src/library/Pool/index.tsx +++ b/src/library/Pool/index.tsx @@ -4,7 +4,7 @@ import { faCopy } from '@fortawesome/free-regular-svg-icons'; import { faBars, faProjectDiagram } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { useRef } from 'react'; +import type { MouseEvent as ReactMouseEvent } from 'react'; import { useTranslation } from 'react-i18next'; import { useMenu } from 'contexts/Menu'; import type { NotificationText } from 'static/NotificationsController/types'; @@ -15,12 +15,7 @@ import { FavoritePool } from 'library/ListItem/Labels/FavoritePool'; import { PoolBonded } from 'library/ListItem/Labels/PoolBonded'; import { PoolCommission } from 'library/ListItem/Labels/PoolCommission'; import { PoolIdentity } from 'library/ListItem/Labels/PoolIdentity'; -import { - Labels, - MenuPosition, - Separator, - Wrapper, -} from 'library/ListItem/Wrappers'; +import { Labels, Separator, Wrapper } from 'library/ListItem/Wrappers'; import { usePoolsTabs } from 'pages/Pools/Home/context'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -34,10 +29,12 @@ import { NotificationsController } from 'static/NotificationsController'; import type { MenuItem } from 'contexts/Menu/types'; import { useBalances } from 'contexts/Balances'; import { useSyncing } from 'hooks/useSyncing'; +import { MenuList } from 'library/Menu/List'; export const Pool = ({ pool }: PoolProps) => { const { t } = useTranslation('library'); const { memberCounter, addresses, id, state } = pool; + const { openMenu, open } = useMenu(); const { validators } = useValidators(); const { setActiveTab } = usePoolsTabs(); const { openModal } = useOverlay().modal; @@ -47,7 +44,6 @@ export const Pool = ({ pool }: PoolProps) => { const { syncing } = useSyncing(['active-pools']); const { isReadOnlyAccount } = useImportedAccounts(); const { getCurrentCommission } = usePoolCommission(); - const { setMenuPosition, setMenuItems, open } = useMenu(); const membership = getPoolMembership(activeAccount); const currentCommission = getCurrentCommission(id); @@ -63,9 +59,6 @@ export const Pool = ({ pool }: PoolProps) => { targets.includes(address) ); - // configure floating menu position - const posRef = useRef(null); - // copy address notification const notificationCopyAddress = ( key: 'stash' | 'reward' @@ -121,11 +114,10 @@ export const Pool = ({ pool }: PoolProps) => { }, }); - // toggle menu handler - const toggleMenu = () => { + // Handler for opening menu. + const toggleMenu = (ev: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => { if (!open) { - setMenuItems(menuItems); - setMenuPosition(posRef); + openMenu(ev, <MenuList items={menuItems} />); } }; @@ -139,14 +131,13 @@ export const Pool = ({ pool }: PoolProps) => { return ( <Wrapper className={displayJoin ? 'pool-join' : 'pool'}> <div className="inner"> - <MenuPosition ref={posRef} /> <div className="row top"> <PoolIdentity pool={pool} /> <div> <Labels> <FavoritePool address={addresses.stash} /> <div className="label"> - <button type="button" onClick={() => toggleMenu()}> + <button type="button" onClick={(ev) => toggleMenu(ev)}> <FontAwesomeIcon icon={faBars} transform="shrink-2" /> </button> </div> diff --git a/src/library/ValidatorList/ValidatorItem/Default.tsx b/src/library/ValidatorList/ValidatorItem/Default.tsx index 1c0b79f022..b1218baced 100644 --- a/src/library/ValidatorList/ValidatorItem/Default.tsx +++ b/src/library/ValidatorList/ValidatorItem/Default.tsx @@ -7,17 +7,11 @@ import { faGlobe, } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useMenu } from 'contexts/Menu'; import { CopyAddress } from 'library/ListItem/Labels/CopyAddress'; import { ParaValidator } from 'library/ListItem/Labels/ParaValidator'; -import { - Labels, - MenuPosition, - Separator, - Wrapper, -} from 'library/ListItem/Wrappers'; +import { Labels, Separator, Wrapper } from 'library/ListItem/Wrappers'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { usePlugins } from 'contexts/Plugins'; import type { AnyJson } from 'types'; @@ -34,6 +28,7 @@ import { Select } from '../../ListItem/Labels/Select'; import { getIdentityDisplay } from './Utils'; import type { ValidatorItemProps } from './types'; import { Pulse } from './Pulse'; +import { MenuList } from 'library/Menu/List'; export const Default = ({ validator, @@ -43,9 +38,9 @@ export const Default = ({ }: ValidatorItemProps) => { const { t } = useTranslation('library'); const { selectActive } = useList(); + const { openMenu, open } = useMenu(); const { pluginEnabled } = usePlugins(); const { openModal } = useOverlay().modal; - const { setMenuPosition, setMenuItems, open } = useMenu(); const { validatorIdentities, validatorSupers } = useValidators(); const { address, prefs, validatorStatus, totalStake } = validator; @@ -56,8 +51,7 @@ export const Default = ({ validatorSupers[address] ); - // configure floating menu - const posRef = useRef(null); + // Configure menu. const menuItems: AnyJson[] = []; menuItems.push({ icon: <FontAwesomeIcon icon={faChartLine} transform="shrink-3" />, @@ -91,17 +85,16 @@ export const Default = ({ }); } - const toggleMenu = () => { + // Handler for opening menu. + const toggleMenu = (ev: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { if (!open) { - setMenuItems(menuItems); - setMenuPosition(posRef); + openMenu(ev, <MenuList items={menuItems} />); } }; return ( <Wrapper> <div className={`inner ${displayFor}`}> - <MenuPosition ref={posRef} /> <div className="row top"> {selectActive && <Select item={validator} />} <Identity address={address} /> @@ -113,7 +106,7 @@ export const Default = ({ {/* restrict opening modal within a canvas */} {displayFor === 'default' && showMenu && ( <div className="label"> - <button type="button" onClick={() => toggleMenu()}> + <button type="button" onClick={(ev) => toggleMenu(ev)}> <FontAwesomeIcon icon={faBars} transform="shrink-2" /> </button> </div> From 9b0de223ba29b1dfd6520b4eeb799624883d2dee Mon Sep 17 00:00:00 2001 From: Ross Bulat <ross@parity.io> Date: Tue, 13 Feb 2024 12:30:58 +0700 Subject: [PATCH 49/51] chore: bumps --- package.json | 4 +- yarn.lock | 114 +++++++++++++++++++++++++-------------------------- 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index 93edba6567..f7c46eb83f 100644 --- a/package.json +++ b/package.json @@ -71,8 +71,8 @@ "@types/react-helmet": "^6.1.11", "@types/react-scroll": "^1.8.10", "@types/styled-components": "^5.1.34", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.0.1", + "@typescript-eslint/parser": "^7.0.1", "@vitejs/plugin-react-swc": "^3.6.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", diff --git a/yarn.lock b/yarn.lock index 5c7e392d52..baa057132d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2270,15 +2270,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0" +"@typescript-eslint/eslint-plugin@npm:^7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/eslint-plugin@npm:7.0.1" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.21.0" - "@typescript-eslint/type-utils": "npm:6.21.0" - "@typescript-eslint/utils": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" + "@typescript-eslint/scope-manager": "npm:7.0.1" + "@typescript-eslint/type-utils": "npm:7.0.1" + "@typescript-eslint/utils": "npm:7.0.1" + "@typescript-eslint/visitor-keys": "npm:7.0.1" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -2286,73 +2286,73 @@ __metadata: semver: "npm:^7.5.4" ts-api-utils: "npm:^1.0.1" peerDependencies: - "@typescript-eslint/parser": ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 + "@typescript-eslint/parser": ^7.0.0 + eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: f911a79ee64d642f814a3b6cdb0d324b5f45d9ef955c5033e78903f626b7239b4aa773e464a38c3e667519066169d983538f2bf8e5d00228af587c9d438fb344 + checksum: 0340a406b6a9036b6b2d92ffa79364d9cbe509e26c9726a953a1b26b4a4413a7079110e94b8a56c7d9d5193885a77f52611af00dea2d60ac79221303f0b91b3d languageName: node linkType: hard -"@typescript-eslint/parser@npm:^6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/parser@npm:6.21.0" +"@typescript-eslint/parser@npm:^7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/parser@npm:7.0.1" dependencies: - "@typescript-eslint/scope-manager": "npm:6.21.0" - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/typescript-estree": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" + "@typescript-eslint/scope-manager": "npm:7.0.1" + "@typescript-eslint/types": "npm:7.0.1" + "@typescript-eslint/typescript-estree": "npm:7.0.1" + "@typescript-eslint/visitor-keys": "npm:7.0.1" debug: "npm:^4.3.4" peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: a8f99820679decd0d115c0af61903fb1de3b1b5bec412dc72b67670bf636de77ab07f2a68ee65d6da7976039bbf636907f9d5ca546db3f0b98a31ffbc225bc7d + checksum: 6e5c17faf94ced7fd5f5e0a44129f1369a691a39824303f947ed8f0089b03493b51e8c40e1f8a9f67e6420cec9aa084440d9362153525f55b20572bc111d4da5 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/scope-manager@npm:6.21.0" +"@typescript-eslint/scope-manager@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/scope-manager@npm:7.0.1" dependencies: - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" - checksum: eaf868938d811cbbea33e97e44ba7050d2b6892202cea6a9622c486b85ab1cf801979edf78036179a8ba4ac26f1dfdf7fcc83a68c1ff66be0b3a8e9a9989b526 + "@typescript-eslint/types": "npm:7.0.1" + "@typescript-eslint/visitor-keys": "npm:7.0.1" + checksum: a1da8ba1cba503887d7a576132857e2be3345a3b1682251b73f00b87199c20bd06662260895cb8d54ec26aca49902c7dc90fc7b0fde162c8415b63bb94c63e6d languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/type-utils@npm:6.21.0" +"@typescript-eslint/type-utils@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/type-utils@npm:7.0.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.21.0" - "@typescript-eslint/utils": "npm:6.21.0" + "@typescript-eslint/typescript-estree": "npm:7.0.1" + "@typescript-eslint/utils": "npm:7.0.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 7409c97d1c4a4386b488962739c4f1b5b04dc60cf51f8cd88e6b12541f84d84c6b8b67e491a147a2c95f9ec486539bf4519fb9d418411aef6537b9c156468117 + checksum: 55e2ea9a76fbd62e69124298e3c1a4cf713ffe437874d090b76e747837fd5ea4034a82002e799108f29606bbed1a853e3d24f59b8a4d685b1e17698ffeb83d81 languageName: node linkType: hard -"@typescript-eslint/types@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/types@npm:6.21.0" - checksum: 020631d3223bbcff8a0da3efbdf058220a8f48a3de221563996ad1dcc30d6c08dadc3f7608cc08830d21c0d565efd2db19b557b9528921c78aabb605eef2d74d +"@typescript-eslint/types@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/types@npm:7.0.1" + checksum: 04156d5423b4d00296f0e0154b68aeae0e59876029e7eabb2cc49bb45b57a379248051b281c12644ba5afb79794d828cffcd053f2c5fcb45aa23f244ec98ef45 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.21.0" +"@typescript-eslint/typescript-estree@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/typescript-estree@npm:7.0.1" dependencies: - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" + "@typescript-eslint/types": "npm:7.0.1" + "@typescript-eslint/visitor-keys": "npm:7.0.1" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -2362,34 +2362,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: af1438c60f080045ebb330155a8c9bb90db345d5069cdd5d01b67de502abb7449d6c75500519df829f913a6b3f490ade3e8215279b6bdc63d0fb0ae61034df5f + checksum: c8cff32a8d880de6228de900aeb20127e4663570a5f959195fda73f905ab06f3d9fbf46d60db0a6333456e0179e4706737293c90e8cce2d4ad7a220ccef2a8e7 languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/utils@npm:6.21.0" +"@typescript-eslint/utils@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/utils@npm:7.0.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.21.0" - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/typescript-estree": "npm:6.21.0" + "@typescript-eslint/scope-manager": "npm:7.0.1" + "@typescript-eslint/types": "npm:7.0.1" + "@typescript-eslint/typescript-estree": "npm:7.0.1" semver: "npm:^7.5.4" peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - checksum: ab2df3833b2582d4e5467a484d08942b4f2f7208f8e09d67de510008eb8001a9b7460f2f9ba11c12086fd3cdcac0c626761c7995c2c6b5657d5fa6b82030a32d + eslint: ^8.56.0 + checksum: 83038958695daaa2a91092b16a64109797af28ec419f734f9dffa71f852ffb57ebd67c72d0b84c70805e4a53d4ead08e4f87687e944a1db19aeb72fcc89208cd languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.21.0" +"@typescript-eslint/visitor-keys@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/visitor-keys@npm:7.0.1" dependencies: - "@typescript-eslint/types": "npm:6.21.0" + "@typescript-eslint/types": "npm:7.0.1" eslint-visitor-keys: "npm:^3.4.1" - checksum: 7395f69739cfa1cb83c1fb2fad30afa2a814756367302fb4facd5893eff66abc807e8d8f63eba94ed3b0fe0c1c996ac9a1680bcbf0f83717acedc3f2bb724fbf + checksum: a7a174d706f1b2ce60ebd17b9d20b36cc89c0ed45fcf510538734d13bca38d25ddbd4b6893a83ef5f344ad9aa7be76c22ea8407fa3c213c14dbcc52f9a2eadd0 languageName: node linkType: hard @@ -6300,8 +6300,8 @@ __metadata: "@types/react-helmet": "npm:^6.1.11" "@types/react-scroll": "npm:^1.8.10" "@types/styled-components": "npm:^5.1.34" - "@typescript-eslint/eslint-plugin": "npm:^6.21.0" - "@typescript-eslint/parser": "npm:^6.21.0" + "@typescript-eslint/eslint-plugin": "npm:^7.0.1" + "@typescript-eslint/parser": "npm:^7.0.1" "@vitejs/plugin-react-swc": "npm:^3.6.0" "@zondax/ledger-substrate": "npm:^0.41.3" bignumber.js: "npm:^9.1.2" From 57b2434ac2f16f1e57554d6f6492d3a57b54ce59 Mon Sep 17 00:00:00 2001 From: Ross Bulat <ross@parity.io> Date: Tue, 13 Feb 2024 22:35:25 +0700 Subject: [PATCH 50/51] fix import --- src/Utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utils.ts b/src/Utils.ts index 18d6bd4fdc..b0e3d81d89 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { getUnixTime } from 'date-fns'; -import type { AnyApi } from 'types/index'; +import type { AnyApi } from 'types'; export const registerLastVisited = (utmSource: string | null) => { const attributes = utmSource ? { utmSource } : {}; From 4406273e983262e98914399ee5efdddf63a42aa9 Mon Sep 17 00:00:00 2001 From: Ross Bulat <ross@parity.io> Date: Wed, 14 Feb 2024 11:42:36 +0700 Subject: [PATCH 51/51] fix changelog --- CHANGELOG.md | 26 -------------------------- docs/CHANGELOG.md | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 26 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 7cb7467187..0000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,26 +0,0 @@ -# Changelog - -## [1.2.0](https://github.com/paritytech/polkadot-staking-dashboard/compare/v1.1.3...v1.2.0) (2024-01-28) - - -### Features - -* **ci:** add markdown-link-check ([#1865](https://github.com/paritytech/polkadot-staking-dashboard/issues/1865)) ([7e7134e](https://github.com/paritytech/polkadot-staking-dashboard/commit/7e7134ea2a42ec56d095e0809a4eaa05f94ad793)) -* **fix:** account for staking rate in average reward rate ([#1886](https://github.com/paritytech/polkadot-staking-dashboard/issues/1886)) ([9938620](https://github.com/paritytech/polkadot-staking-dashboard/commit/9938620ee417bd42761a6e63828e3dd3ab8e7ee2)) -* **refacor:** Pool config to bootstrap app state ([#1900](https://github.com/paritytech/polkadot-staking-dashboard/issues/1900)) ([8f51a86](https://github.com/paritytech/polkadot-staking-dashboard/commit/8f51a8672b843b7cd7172144e2157148d03325ca)) -* **refactor:** Move balance syncing to static class, `activeBalances` in UI only. ([#1858](https://github.com/paritytech/polkadot-staking-dashboard/issues/1858)) ([a372487](https://github.com/paritytech/polkadot-staking-dashboard/commit/a3724879f377c38baf0e9b04e03749e0e2f65ee0)) -* **refactor:** Move Polkawatch to inline instantiation ([#1890](https://github.com/paritytech/polkadot-staking-dashboard/issues/1890)) ([d7b88bd](https://github.com/paritytech/polkadot-staking-dashboard/commit/d7b88bd57701c16ad143a6e2b70897b5086f82f6)) -* **refactor:** Move staking metrics `payee` to active balances ([#1904](https://github.com/paritytech/polkadot-staking-dashboard/issues/1904)) ([7b692e0](https://github.com/paritytech/polkadot-staking-dashboard/commit/7b692e06006448604929ec23fe510a7c7492141b)) -* **refactor:** network metrics to static, bootstrap all state before subscribe ([#1896](https://github.com/paritytech/polkadot-staking-dashboard/issues/1896)) ([08a813c](https://github.com/paritytech/polkadot-staking-dashboard/commit/08a813c7d47d2055984f7d32c1d8b34bec9791f4)) -* **refactor:** remove unneeded bonded getters ([7108e55](https://github.com/paritytech/polkadot-staking-dashboard/commit/7108e55d3f1aeeb07cdaa7cdb5608425b0d6641a)) -* **refactor:** rm `ExtrinsicsProvider`, nonces to `TxMeta` ([4194150](https://github.com/paritytech/polkadot-staking-dashboard/commit/41941509cb0d930d53f3fc98a12736b0356c6846)) -* **refactor:** Staking metrics to bootstrap and API ([#1905](https://github.com/paritytech/polkadot-staking-dashboard/issues/1905)) ([f6c0b93](https://github.com/paritytech/polkadot-staking-dashboard/commit/f6c0b93fc5fcaf2961c91cdeae770d5503766fe8)) -* **refactor:** Subscan refactor, remove fetching from Providers ([#1878](https://github.com/paritytech/polkadot-staking-dashboard/issues/1878)) ([57e2a1b](https://github.com/paritytech/polkadot-staking-dashboard/commit/57e2a1bed952b41441635dc9eba02b79d63d3fc0)) - - -### Bug Fixes - -* balances no accounts sync ([#1889](https://github.com/paritytech/polkadot-staking-dashboard/issues/1889)) ([5316283](https://github.com/paritytech/polkadot-staking-dashboard/commit/5316283029f9a28003bb25814623f768f3294fb8)) -* Render safe guards, pool useEffect fixes ([#1906](https://github.com/paritytech/polkadot-staking-dashboard/issues/1906)) ([7c0212f](https://github.com/paritytech/polkadot-staking-dashboard/commit/7c0212f428e56b2eba9c735a6299c3661dd47b25)) -* roll back Substrate Connect ([#1899](https://github.com/paritytech/polkadot-staking-dashboard/issues/1899)) ([33b5671](https://github.com/paritytech/polkadot-staking-dashboard/commit/33b5671caf5ec09240e164e82618efb15deebe81)) -* unlock Chunk unit type ([afe9b1c](https://github.com/paritytech/polkadot-staking-dashboard/commit/afe9b1c0256eb9840a375f1d7574895a07ba0f4a)) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1bee448bfc..fcd384bf24 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## [1.1.4](https://github.com/paritytech/polkadot-staking-dashboard/compare/v1.1.3...v1.1.4) (2024-01-28) + + +### Features + +* **ci:** add markdown-link-check ([#1865](https://github.com/paritytech/polkadot-staking-dashboard/issues/1865)) ([7e7134e](https://github.com/paritytech/polkadot-staking-dashboard/commit/7e7134ea2a42ec56d095e0809a4eaa05f94ad793)) +* **fix:** account for staking rate in average reward rate ([#1886](https://github.com/paritytech/polkadot-staking-dashboard/issues/1886)) ([9938620](https://github.com/paritytech/polkadot-staking-dashboard/commit/9938620ee417bd42761a6e63828e3dd3ab8e7ee2)) +* **refacor:** Pool config to bootstrap app state ([#1900](https://github.com/paritytech/polkadot-staking-dashboard/issues/1900)) ([8f51a86](https://github.com/paritytech/polkadot-staking-dashboard/commit/8f51a8672b843b7cd7172144e2157148d03325ca)) +* **refactor:** Move balance syncing to static class, `activeBalances` in UI only. ([#1858](https://github.com/paritytech/polkadot-staking-dashboard/issues/1858)) ([a372487](https://github.com/paritytech/polkadot-staking-dashboard/commit/a3724879f377c38baf0e9b04e03749e0e2f65ee0)) +* **refactor:** Move Polkawatch to inline instantiation ([#1890](https://github.com/paritytech/polkadot-staking-dashboard/issues/1890)) ([d7b88bd](https://github.com/paritytech/polkadot-staking-dashboard/commit/d7b88bd57701c16ad143a6e2b70897b5086f82f6)) +* **refactor:** Move staking metrics `payee` to active balances ([#1904](https://github.com/paritytech/polkadot-staking-dashboard/issues/1904)) ([7b692e0](https://github.com/paritytech/polkadot-staking-dashboard/commit/7b692e06006448604929ec23fe510a7c7492141b)) +* **refactor:** network metrics to static, bootstrap all state before subscribe ([#1896](https://github.com/paritytech/polkadot-staking-dashboard/issues/1896)) ([08a813c](https://github.com/paritytech/polkadot-staking-dashboard/commit/08a813c7d47d2055984f7d32c1d8b34bec9791f4)) +* **refactor:** remove unneeded bonded getters ([7108e55](https://github.com/paritytech/polkadot-staking-dashboard/commit/7108e55d3f1aeeb07cdaa7cdb5608425b0d6641a)) +* **refactor:** rm `ExtrinsicsProvider`, nonces to `TxMeta` ([4194150](https://github.com/paritytech/polkadot-staking-dashboard/commit/41941509cb0d930d53f3fc98a12736b0356c6846)) +* **refactor:** Staking metrics to bootstrap and API ([#1905](https://github.com/paritytech/polkadot-staking-dashboard/issues/1905)) ([f6c0b93](https://github.com/paritytech/polkadot-staking-dashboard/commit/f6c0b93fc5fcaf2961c91cdeae770d5503766fe8)) +* **refactor:** Subscan refactor, remove fetching from Providers ([#1878](https://github.com/paritytech/polkadot-staking-dashboard/issues/1878)) ([57e2a1b](https://github.com/paritytech/polkadot-staking-dashboard/commit/57e2a1bed952b41441635dc9eba02b79d63d3fc0)) + + +### Bug Fixes + +* balances no accounts sync ([#1889](https://github.com/paritytech/polkadot-staking-dashboard/issues/1889)) ([5316283](https://github.com/paritytech/polkadot-staking-dashboard/commit/5316283029f9a28003bb25814623f768f3294fb8)) +* Render safe guards, pool useEffect fixes ([#1906](https://github.com/paritytech/polkadot-staking-dashboard/issues/1906)) ([7c0212f](https://github.com/paritytech/polkadot-staking-dashboard/commit/7c0212f428e56b2eba9c735a6299c3661dd47b25)) +* roll back Substrate Connect ([#1899](https://github.com/paritytech/polkadot-staking-dashboard/issues/1899)) ([33b5671](https://github.com/paritytech/polkadot-staking-dashboard/commit/33b5671caf5ec09240e164e82618efb15deebe81)) +* unlock Chunk unit type ([afe9b1c](https://github.com/paritytech/polkadot-staking-dashboard/commit/afe9b1c0256eb9840a375f1d7574895a07ba0f4a)) + + ## [1.1.3](https://github.com/paritytech/polkadot-staking-dashboard/compare/v1.1.2...v1.1.3) (2024-01-15)