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/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) 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/package.json b/package.json index aa3955af13..811a4221b4 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,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", @@ -47,8 +47,9 @@ "date-fns": "^3.3.1", "framer-motion": "^11.0.3", "html5-qrcode": "^2.3.8", - "i18next": "^23.7.20", + "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", @@ -57,24 +58,25 @@ "react-dom": "^18.2.0", "react-error-boundary": "^4.0.12", "react-helmet": "^6.1.0", - "react-i18next": "^14.0.1", - "react-router-dom": "^6.21.3", + "react-i18next": "^14.0.5", + "react-router-dom": "^6.22.0", "react-scroll": "^1.9.0", "styled-components": "^6.1.8", - "usehooks-ts": "2.10.0" + "usehooks-ts": "2.14.0" }, "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.48", - "@types/react-dom": "^18.2.17", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", "@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/parser": "^6.19.1", - "@vitejs/plugin-react-swc": "^3.5.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", "eslint-import-resolver-typescript": "^3.6.1", @@ -86,13 +88,13 @@ "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", - "vite": "^5.0.12", - "vite-bundle-visualizer": "^1.0.0", - "vite-plugin-checker": "^0.6.2", + "vite": "^5.1.1", + "vite-bundle-visualizer": "^1.0.1", + "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/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 ( - - - - - - - - - - ); -}; +export const App: FC = () => ( + + + + + + + + + +); 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 ( + + + {`${t(key, { ns: 'base' })} : ${t('title', { + context: `${network}`, + ns: 'base', + })}`} + + + + ); +}; diff --git a/src/Providers.tsx b/src/Providers.tsx index 54ca44430a..638e1ab413 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -14,16 +14,14 @@ 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'; 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 { 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'; @@ -37,9 +35,6 @@ 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 { withProviders } from 'library/Hooks'; import { OtherAccountsProvider } from 'contexts/Connect/OtherAccounts'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { DappName } from 'consts'; @@ -47,8 +42,10 @@ import { ImportedAccountsProvider } from 'contexts/Connect/ImportedAccounts'; import { PoolPerformanceProvider } from 'contexts/Pools/PoolPerformance'; import { registerSaEvent } from 'Utils'; import { ExternalAccountsProvider } from 'contexts/Connect/ExternalAccounts'; +import type { Provider } from 'hooks/withProviders'; +import { withProviders } from 'hooks/withProviders'; +import { CommunityProvider } from 'contexts/Community'; -// Embed providers from hook. export const Providers = () => { const { network, @@ -56,8 +53,9 @@ export const Providers = () => { } = useNetwork(); const { activeAccount, setActiveAccount } = useActiveAccounts(); - // !! Provider order matters - const providers: (FC | [FC, AnyJson])[] = [ + // !! Provider order matters. + const providers: Provider[] = [ + UIProvider, [APIProvider, { network }], VaultAccountsProvider, LedgerHardwareProvider, @@ -85,22 +83,19 @@ export const Providers = () => { ProxiesProvider, HelpProvider, PluginsProvider, - IdentitiesProvider, BondedProvider, BalancesProvider, StakingProvider, - PoolsConfigProvider, + FavoritePoolsProvider, BondedPoolsProvider, - PoolMembershipsProvider, PoolMembersProvider, - ActivePoolsProvider, + ActivePoolProvider, TransferOptionsProvider, ValidatorsProvider, FavoriteValidatorsProvider, FastUnstakeProvider, PayoutsProvider, PoolPerformanceProvider, - UIProvider, SetupProvider, MenuProvider, TooltipProvider, @@ -109,6 +104,7 @@ export const Providers = () => { PromptProvider, MigrateProvider, FiltersProvider, + CommunityProvider, ]; return withProviders(providers, ThemedRouter); diff --git a/src/Router.tsx b/src/Router.tsx index f243b48797..98b83b15b4 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -4,12 +4,10 @@ import { registerLastVisited, registerSaEvent } from 'Utils'; import { usePrompt } from 'contexts/Prompt'; import { Disclaimer } from 'library/NetworkBar/Disclaimer'; -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, @@ -33,21 +31,19 @@ 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, search } = useLocation(); const { accounts } = useImportedAccounts(); const { accountsInitialised } = useOtherAccounts(); const { activeAccount, setActiveAccount } = useActiveAccounts(); - const { sideMenuOpen, sideMenuMinimised, setContainerRefs } = useUi(); const { openPromptWith } = usePrompt(); + const { setContainerRefs } = useUi(); // register landing source from URL useEffect(() => { @@ -64,27 +60,21 @@ export const RouterInner = () => { registerLastVisited(utmSource); }, []); + // 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) { @@ -105,9 +95,6 @@ export const RouterInner = () => { } }, [accountsInitialised]); - // References to outer containers - const mainInterfaceRef = useRef(null); - return ( {/* Notification popups */} @@ -130,50 +117,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/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 } : {}; diff --git a/src/canvas/ManageNominations/index.tsx b/src/canvas/ManageNominations/index.tsx index a8b515513f..f18539b2c7 100644 --- a/src/canvas/ManageNominations/index.tsx +++ b/src/canvas/ManageNominations/index.tsx @@ -15,10 +15,10 @@ 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'; +import { useActivePool } from 'contexts/Pools/ActivePool'; import { SubmitTx } from 'library/SubmitTx'; import type { NominationSelection, @@ -38,13 +38,14 @@ export const ManageNominations = () => { } = useOverlay().canvas; const { openHelp } = useHelp(); const { consts, api } = useApi(); + const { activePool } = useActivePool(); const { getBondedAccount } = useBonded(); const { activeAccount } = useActiveAccounts(); - const { selectedActivePool } = useActivePools(); - const { openPromptWith, closePrompt } = usePrompt(); 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..0ae8f9c8d9 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,13 +65,15 @@ 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; - const newMembers: PoolMember[] = - await SubscanController.handleFetchPoolMembers(poolId, page); + const newMembers = (await SubscanController.handleFetchPoolMembers( + poolId, + page + )) as PoolMember[]; fetchingMemberList.current = false; setPoolMembersApi([...newMembers]); @@ -105,7 +107,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..1ff9c25a12 100644 --- a/src/canvas/PoolMembers/Lists/Member.tsx +++ b/src/canvas/PoolMembers/Lists/Member.tsx @@ -7,27 +7,24 @@ 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'; -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'; 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,14 +39,14 @@ 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 { 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 = @@ -109,12 +106,10 @@ export const Member = ({ } } - // configure floating menu - const posRef = useRef(null); - const toggleMenu = () => { + // Handler for opening menu. + const toggleMenu = (ev: ReactMouseEvent) => { if (!open) { - setMenuItems(menuItems); - setMenuPosition(posRef); + openMenu(ev, ); } }; @@ -135,7 +130,6 @@ export const Member = ({ >
-
{selectActive && } @@ -113,7 +106,7 @@ export const Default = ({ {/* restrict opening modal within a canvas */} {displayFor === 'default' && showMenu && (
-
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 e22e520b66..ccab7612f5 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, @@ -28,34 +27,38 @@ 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'; import { FilterBadges } from './Filters/FilterBadges'; 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'); @@ -74,14 +77,14 @@ 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; 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(() => { @@ -314,7 +321,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/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 389ec99eef..09b535ee24 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": "作为提名池存款人", @@ -44,7 +50,7 @@ "connectionLost": "连接已丢失", "continue": "继续", "copyAddress": "复制地址", - "copyPoolAddress": "复制池地址", + "copyPoolAddress": "复制池{{type}}地址", "createPool": "创建提名池", "dashboardDisclaimer": "该应用免责声明", "dayAverage": "日平均值", 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/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 2ec108a6fa..1ceb67213b 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.", @@ -44,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", "dashboardDisclaimer": "Dashboard Disclaimer", "dayAverage": "Day Average", diff --git a/src/locale/en/modals.json b/src/locale/en/modals.json index f529ff67de..301a7abb0d 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", @@ -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/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 2273ce9c46..3f792c545f 100644 --- a/src/locale/utils.ts +++ b/src/locale/utils.ts @@ -3,9 +3,13 @@ import { registerSaEvent } from 'Utils'; 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/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 07cedf694e..be07d113b3 100644 --- a/src/modals/AccountPoolRoles/index.tsx +++ b/src/modals/AccountPoolRoles/index.tsx @@ -4,24 +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 { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; +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 { options } = useOverlay().modal.config; - const { getAccountPools } = useBondedPools(); - const { membership } = usePoolMemberships(); - const { who } = options; - const accountPools = getAccountPools(who); - const totalAccountPools = Object.entries(accountPools).length; + const { activeAccount } = useActiveAccounts(); + + const { who, activePools } = options; + const membership = getPoolMembership(activeAccount); const { label } = useStatusButtons(); + // Delete membership from activePools if it exists + delete activePools[membership?.poolId || -1]; + return ( <> @@ -31,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}`} /> @@ -55,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} /> @@ -81,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/Accounts/index.tsx b/src/modals/Accounts/index.tsx index 9f1da77ca1..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'; @@ -31,7 +30,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'; @@ -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<ImportedAccount[]>(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/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..98b13903a0 100644 --- a/src/modals/Bond/index.tsx +++ b/src/modals/Bond/index.tsx @@ -7,13 +7,13 @@ 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'; -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'; @@ -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/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..bfb36c5227 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'; @@ -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/modals/ClaimReward/index.tsx b/src/modals/ClaimReward/index.tsx index 3166cf0272..22b114fb3b 100644 --- a/src/modals/ClaimReward/index.tsx +++ b/src/modals/ClaimReward/index.tsx @@ -3,14 +3,13 @@ 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 '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'; @@ -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/JoinPool/index.tsx b/src/modals/JoinPool/index.tsx index a54a0bb2cb..b18199c65e 100644 --- a/src/modals/JoinPool/index.tsx +++ b/src/modals/JoinPool/index.tsx @@ -14,16 +14,16 @@ 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'; 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/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..1f6084e3c1 100644 --- a/src/modals/ManagePool/Forms/ClaimCommission/index.tsx +++ b/src/modals/ManagePool/Forms/ClaimCommission/index.tsx @@ -15,10 +15,10 @@ 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 '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'; @@ -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 38343a0bcc..6aebcaefa1 100644 --- a/src/modals/ManagePool/Forms/LeavePool/index.tsx +++ b/src/modals/ManagePool/Forms/LeavePool/index.tsx @@ -13,19 +13,18 @@ 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 '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'; @@ -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 aa0a88380a..90f359ee7a 100644 --- a/src/modals/ManagePool/Forms/ManageCommission/index.tsx +++ b/src/modals/ManagePool/Forms/ManageCommission/index.tsx @@ -15,12 +15,12 @@ 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 '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'; @@ -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 3d968a1ea4..c1c31a35da 100644 --- a/src/modals/ManagePool/Forms/RenamePool/index.tsx +++ b/src/modals/ManagePool/Forms/RenamePool/index.tsx @@ -12,11 +12,11 @@ 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 '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'; @@ -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 e8a94f8d8c..5aa29142da 100644 --- a/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx +++ b/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx @@ -11,16 +11,16 @@ 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 { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; +import { useActivePool } from 'contexts/Pools/ActivePool'; 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'; -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 { isOwner, isMember } = useActivePool(); const { getSignerWarnings } = useSignerWarnings(); - const { membership } = usePoolMemberships(); + + const membership = getPoolMembership(activeAccount); // Valid to submit transaction. const [valid, setValid] = useState<boolean>(false); diff --git a/src/modals/ManagePool/Forms/SetPoolState/index.tsx b/src/modals/ManagePool/Forms/SetPoolState/index.tsx index c51634a849..888ce2ee2c 100644 --- a/src/modals/ManagePool/Forms/SetPoolState/index.tsx +++ b/src/modals/ManagePool/Forms/SetPoolState/index.tsx @@ -12,11 +12,11 @@ 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 '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'; @@ -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..2236a65141 100644 --- a/src/modals/ManagePool/Tasks.tsx +++ b/src/modals/ManagePool/Tasks.tsx @@ -1,17 +1,19 @@ // 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'; -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'; -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<HTMLDivElement>) => { @@ -19,129 +21,167 @@ 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'; + + const stash = activePool?.addresses.stash || ''; + const reward = activePool?.addresses.reward || ''; return ( <ContentWrapper> - <div className="items" ref={ref} style={{ paddingBottom: '1.5rem' }}> - <div style={{ paddingBottom: '0.75rem' }}> - {poolDestroying && <Warning text={t('beingDestroyed')} />} - </div> - {isOwner() && globalMaxCommission > 0 && ( - <> - <ButtonOption - onClick={() => { - setSection(1); - setTask('claim_commission'); - }} - > - <div> - <h3>{t('claimCommission')}</h3> - <p>{t('claimOutstandingCommission')}</p> + <div ref={ref}> + <div className="items" style={{ paddingBottom: '1rem' }}> + {poolDestroying && ( + <div style={{ marginBottom: '0.75rem' }}> + <Warning text={t('beingDestroyed')} /> + </div> + )} + + <ButtonRowWrapper> + <section> + <div className="inner"> + <span className="icon"> + <Polkicon address={stash} size={remToUnit('3rem')} /> + </span> + <div> + <h3> + {t('poolAddress', { type: 'Stash' })}{' '} + <CopyAddress address={stash} /> + </h3> + <h4>{ellipsisFn(stash, 5)}</h4> + </div> </div> - </ButtonOption> - <ButtonOption - onClick={() => { - setSection(1); - setTask('manage_commission'); - }} - > - <div> - <h3>{t('manageCommission')}</h3> - <p>{t('updatePoolCommission')}</p> + </section> + <section> + <div className="inner"> + <span className="icon"> + <Polkicon address={reward} size={remToUnit('3rem')} /> + </span> + <div> + <h3> + {t('poolAddress', { type: 'Reward' })}{' '} + <CopyAddress address={reward} /> + </h3> + <h4>{ellipsisFn(reward, 5)}</h4> + </div> </div> - </ButtonOption> - </> - )} - <ButtonOption - onClick={() => { - setSection(1); - setTask('set_claim_permission'); - }} - > - <div> - <h3>{t('updateClaimPermission')}</h3> - <p>{t('updateWhoClaimRewards')}</p> - </div> - </ButtonOption> + </section> + </ButtonRowWrapper> - {isOwner() && ( - <ButtonOption - disabled={poolDestroying} - onClick={() => { - setSection(1); - setTask('set_pool_metadata'); - }} - > - <div> - <h3>{t('renamePool')}</h3> - <p>{t('updateName')}</p> - </div> - </ButtonOption> - )} - {(isOwner() || isBouncer()) && ( - <> - {poolLocked ? ( + {isOwner() && globalMaxCommission > 0 && ( + <> <ButtonOption - disabled={poolDestroying} onClick={() => { setSection(1); - setTask('unlock_pool'); + setTask('claim_commission'); }} > <div> - <h3>{t('unlockPool')}</h3> - <p>{t('allowToJoin')}</p> + <h3>{t('claimCommission')}</h3> + <p>{t('claimOutstandingCommission')}</p> </div> </ButtonOption> - ) : ( <ButtonOption - disabled={poolDestroying} onClick={() => { setSection(1); - setTask('lock_pool'); + setTask('manage_commission'); }} > <div> - <h3>{t('lockPool')}</h3> - <p>{t('stopJoiningPool')}</p> + <h3>{t('manageCommission')}</h3> + <p>{t('updatePoolCommission')}</p> </div> </ButtonOption> - )} + </> + )} + <ButtonOption + onClick={() => { + setSection(1); + setTask('set_claim_permission'); + }} + > + <div> + <h3>{t('updateClaimPermission')}</h3> + <p>{t('updateWhoClaimRewards')}</p> + </div> + </ButtonOption> + + {isOwner() && ( <ButtonOption disabled={poolDestroying} onClick={() => { setSection(1); - setTask('destroy_pool'); + setTask('set_pool_metadata'); }} > <div> - <h3>{t('destroyPool')}</h3> - <p>{t('changeToDestroy')}</p> + <h3>{t('renamePool')}</h3> + <p>{t('updateName')}</p> </div> </ButtonOption> - </> - )} - {isMember() && !isDepositor() && active?.isGreaterThan(0) && ( - <ButtonOption - onClick={() => { - setSection(1); - setTask('leave_pool'); - }} - > - <div> - <h3>{t('leavePool')}</h3> - <p>{t('unbondFundsLeavePool')}</p> - </div> - </ButtonOption> - )} + )} + {(isOwner() || isBouncer()) && ( + <> + {poolLocked ? ( + <ButtonOption + disabled={poolDestroying} + onClick={() => { + setSection(1); + setTask('unlock_pool'); + }} + > + <div> + <h3>{t('unlockPool')}</h3> + <p>{t('allowToJoin')}</p> + </div> + </ButtonOption> + ) : ( + <ButtonOption + disabled={poolDestroying} + onClick={() => { + setSection(1); + setTask('lock_pool'); + }} + > + <div> + <h3>{t('lockPool')}</h3> + <p>{t('stopJoiningPool')}</p> + </div> + </ButtonOption> + )} + <ButtonOption + disabled={poolDestroying} + onClick={() => { + setSection(1); + setTask('destroy_pool'); + }} + > + <div> + <h3>{t('destroyPool')}</h3> + <p>{t('changeToDestroy')}</p> + </div> + </ButtonOption> + </> + )} + {isMember() && !isDepositor() && active?.isGreaterThan(0) && ( + <ButtonOption + onClick={() => { + setSection(1); + setTask('leave_pool'); + }} + > + <div> + <h3>{t('leavePool')}</h3> + <p>{t('unbondFundsLeavePool')}</p> + </div> + </ButtonOption> + )} + </div> </div> </ContentWrapper> ); 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 4964bbc4ea..7b9e2c6a31 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'; @@ -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, selectedActivePool } = useActivePools(); const { setModalHeight, modalMaxHeight } = useOverlay().modal; // modal task @@ -59,7 +59,7 @@ export const ManagePool = () => { task, notEnoughFunds, calculateHeight, - selectedActivePool?.bondedPool?.state, + activePool?.bondedPool?.state, ]); useEffect(() => { @@ -72,10 +72,7 @@ export const ManagePool = () => { return ( <ModalSection type="carousel"> <ModalFixedTitle ref={headerRef}> - <Title - title={`${t('managePool')}${!isOwner() ? ` Membership` : ``}`} - fixed - /> + <Title title={`${t('managePool')}`} fixed /> </ModalFixedTitle> <ModalMotionTwoSection style={{ diff --git a/src/modals/StopNominations/index.tsx b/src/modals/StopNominations/index.tsx index 5c2977072d..a620e00e4b 100644 --- a/src/modals/StopNominations/index.tsx +++ b/src/modals/StopNominations/index.tsx @@ -10,32 +10,34 @@ 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 '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'; 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 }, 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,8 +45,8 @@ export const StopNominations = () => { const nominations = isPool === true - ? poolNominations?.targets || [] - : getAccountNominations(activeAccount); + ? activePoolNominations?.targets || [] + : getNominations(activeAccount); // valid to submit transaction const [valid, setValid] = useState<boolean>(false); @@ -69,7 +71,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 0059337e17..0bb7d2ec29 100644 --- a/src/modals/Unbond/index.tsx +++ b/src/modals/Unbond/index.tsx @@ -9,15 +9,15 @@ 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'; 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'; @@ -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/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 d7f2be9e12..3a849e3769 100644 --- a/src/modals/UnlockChunks/Forms.tsx +++ b/src/modals/UnlockChunks/Forms.tsx @@ -15,20 +15,20 @@ 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 { 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'; +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'; 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 { activePool } = useActivePool(); const { activeAccount } = useActiveAccounts(); - const { removeFavorite: removeFavoritePool } = usePoolsConfig(); - const { membership } = usePoolMemberships(); - const { selectedActivePool } = useActivePools(); - const { removeFromBondedPools } = useBondedPools(); const { removePoolMember } = usePoolMembers(); + const { removeFromBondedPools } = useBondedPools(); 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); @@ -76,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() @@ -95,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/Overview.tsx b/src/modals/UnlockChunks/Overview.tsx index 6ee5505e7c..88847eea95 100644 --- a/src/modals/UnlockChunks/Overview.tsx +++ b/src/modals/UnlockChunks/Overview.tsx @@ -11,12 +11,12 @@ 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'; +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/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/modals/Unstake/index.tsx b/src/modals/Unstake/index.tsx index b657a82a9d..00ad3e78de 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'; @@ -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/modals/UpdateController/index.tsx b/src/modals/UpdateController/index.tsx index 8e451dc2c4..cd3493ca29 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/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<ValidatorEntry[]>( validatorCommunity.filter((v) => v.validators[network] !== undefined) 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 6ae2db6bf4..b8fe418415 100644 --- a/src/pages/Nominate/Active/ManageBond.tsx +++ b/src/pages/Nominate/Active/ManageBond.tsx @@ -15,14 +15,14 @@ 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 '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'; 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 b40ee7ae23..1c0f1032dd 100644 --- a/src/pages/Nominate/Active/Status/NominationStatus.tsx +++ b/src/pages/Nominate/Active/Status/NominationStatus.tsx @@ -11,9 +11,8 @@ 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 '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 { useTranslation } from 'react-i18next'; import { registerSaEvent } from 'Utils'; @@ -21,6 +20,7 @@ import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useNetwork } from 'contexts/Network'; +import { useSyncing } from 'hooks/useSyncing'; export const NominationStatus = ({ showButtons = true, @@ -32,17 +32,17 @@ export const NominationStatus = ({ const { t } = useTranslation('pages'); const { network } = useNetwork(); const { inSetup } = useStaking(); - const { isNetworkSyncing } = useUi(); const { openModal } = useOverlay().modal; const { getBondedAccount } = useBonded(); + const { syncing } = useSyncing(['initialization', 'era-stakers', 'balances']); 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(); @@ -85,31 +85,29 @@ export const NominationStatus = ({ helpKey="Nomination Status" stat={nominationStatus.message} buttons={ - !showButtons + !showButtons || syncing ? [] : !inSetup() ? !isUnstaking ? [unstakeButton] : [] - : isNetworkSyncing - ? [] - : [ - { - title: startTitle, - icon: faChevronCircleRight, - transform: 'grow-1', - disabled: - !isReady || - isReadOnlyAccount(activeAccount) || - !activeAccount, - onClick: () => { - registerSaEvent( - `${network.toLowerCase()}_nominate_setup_button_pressed` - ); - setOnNominatorSetup(true); - }, + : [ + { + title: startTitle, + icon: faChevronCircleRight, + transform: 'grow-1', + disabled: + !isReady || + isReadOnlyAccount(activeAccount) || + !activeAccount, + onClick: () => { + registerSaEvent( + `${network.toLowerCase()}_nominate_setup_button_pressed` + ); + setOnNominatorSetup(true); }, - ] + }, + ] } buttonType={buttonType} /> diff --git a/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx b/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx index e9a0e3056c..6ac3f467c5 100644 --- a/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx +++ b/src/pages/Nominate/Active/Status/PayoutDestinationStatus.tsx @@ -4,20 +4,20 @@ 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'; 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 c3b8230e86..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 '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'; +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 a98e757078..a22ea30758 100644 --- a/src/pages/Nominate/Active/index.tsx +++ b/src/pages/Nominate/Active/index.tsx @@ -12,9 +12,8 @@ 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 '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'; @@ -28,17 +27,21 @@ import { MinimumActiveStakeStat } from './Stats/MinimumActiveStake'; import { MinimumNominatorBondStat } from './Stats/MinimumNominatorBond'; import { Status } from './Status'; import { UnstakePrompts } from './UnstakePrompts'; +import { useSyncing } from 'hooks/useSyncing'; +import { useBalances } from 'contexts/Balances'; export const Active = () => { const { t } = useTranslation(); - const { isSyncing } = useUi(); const { openHelp } = useHelp(); const { inSetup } = useStaking(); - const { nominated } = useValidators(); - const { isFastUnstaking } = useUnstaking(); + const { syncing } = useSyncing('*'); + const { getNominations } = useBalances(); const { openCanvas } = useOverlay().canvas; + const { isFastUnstaking } = useUnstaking(); + const { formatWithPrefs } = useValidators(); const { activeAccount } = useActiveAccounts(); + const nominated = formatWithPrefs(getNominations(activeAccount)); const ROW_HEIGHT = 220; return ( @@ -63,7 +66,7 @@ export const Active = () => { </PageRow> <PageRow> <CardWrapper> - {nominated?.length || inSetup() || isSyncing ? ( + {nominated?.length || inSetup() || syncing ? ( <Nominations bondFor="nominator" nominator={activeAccount} /> ) : ( <> @@ -80,7 +83,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/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 68aeade2bc..c7e6ecc6b6 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..e58a219fd3 100644 --- a/src/pages/Overview/BalanceChart.tsx +++ b/src/pages/Overview/BalanceChart.tsx @@ -13,16 +13,16 @@ 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'; 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'; 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/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 34fa727d85..25d1fa7a4f 100644 --- a/src/pages/Overview/NetworkSats/index.tsx +++ b/src/pages/Overview/NetworkSats/index.tsx @@ -5,10 +5,10 @@ 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 'library/Hooks/useAverageRewardRate'; +import { useAverageRewardRate } from 'hooks/useAverageRewardRate'; import { useApi } from 'contexts/Api'; export const NetworkStats = () => { @@ -52,7 +52,7 @@ export const NetworkStats = () => { <h3>{t('overview.networkStats')}</h3> </CardHeaderWrapper> <Wrapper> - <StatsHead items={items} /> + <Header items={items} /> <Announcements /> </Wrapper> </CardWrapper> diff --git a/src/pages/Overview/Payouts.tsx b/src/pages/Overview/Payouts.tsx index e6d8b27ded..f3a7864efd 100644 --- a/src/pages/Overview/Payouts.tsx +++ b/src/pages/Overview/Payouts.tsx @@ -5,40 +5,39 @@ 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'; 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'; +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 = () => { 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/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..8503f1c54a 100644 --- a/src/pages/Overview/StakeStatus/Tips/index.tsx +++ b/src/pages/Overview/StakeStatus/Tips/index.tsx @@ -6,13 +6,11 @@ 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 { useActivePools } from 'contexts/Pools/ActivePools'; -import { usePoolMemberships } from 'contexts/Pools/PoolMemberships'; +import { TipsThresholdMedium, TipsThresholdSmall } from 'consts'; +import { useActivePool } from 'contexts/Pools/ActivePool'; 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'; @@ -22,21 +20,25 @@ import { Syncing } from './Syncing'; import { TipsWrapper } from './Wrappers'; 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(); const { network } = useNetwork(); - const { isNetworkSyncing } = useUi(); - const { activeAccount } = useActiveAccounts(); - const { fillVariables } = useFillVariables(); - const { membership } = usePoolMemberships(); const { stakingMetrics: { minNominatorBond }, } = useApi(); - + const { isOwner } = useActivePool(); const { isNominating } = useStaking(); - const { isOwner } = useActivePools(); + const { getPoolMembership } = useBalances(); + const { activeAccount } = useActiveAccounts(); + const { fillVariables } = useFillVariables(); + const { syncing } = useSyncing(['initialization']); const { feeReserve, getTransferOptions } = useTransferOptions(); + + const membership = getPoolMembership(activeAccount); const transferOptions = getTransferOptions(activeAccount); // multiple tips per row is currently turned off. @@ -64,7 +66,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) { @@ -160,10 +162,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); @@ -175,7 +177,7 @@ export const Tips = () => { return ( <TipsWrapper> <div style={{ flexGrow: 1 }}> - {isNetworkSyncing ? ( + {syncing ? ( <Syncing /> ) : ( <Items 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/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 bfe4967d78..2ed708e6d7 100644 --- a/src/pages/Payouts/index.tsx +++ b/src/pages/Payouts/index.tsx @@ -4,39 +4,39 @@ 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'; -import { useUi } from 'contexts/UI'; import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'; 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'; +import { DefaultLocale, 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 4ac8d29d25..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'); @@ -81,7 +81,6 @@ export const PoolRoles = ({ section }: SetupStepProps) => { </h4> <Roles inline - batchKey="pool_roles_create" listenIsValid={setRolesValid} defaultRoles={initialValue} setters={[ diff --git a/src/pages/Pools/Create/Summary/index.tsx b/src/pages/Pools/Create/Summary/index.tsx index fc69e344d4..9f8d48dc00 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/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 3f3316a1e8..70716bd1f2 100644 --- a/src/pages/Pools/Home/Favorites/index.tsx +++ b/src/pages/Pools/Home/Favorites/index.tsx @@ -6,20 +6,20 @@ 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 { useUi } from 'contexts/UI'; +import { useFavoritePools } from 'contexts/Pools/FavoritePools'; 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 { favorites, removeFavorite } = usePoolsConfig(); + const { syncing } = useSyncing(['active-pools']); + const { favorites, removeFavorite } = useFavoritePools(); // Store local favorite list and update when favorites list is mutated. const [favoritesList, setFavoritesList] = useState<BondedPool[]>([]); @@ -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..35d9511109 100644 --- a/src/pages/Pools/Home/ManagePool/index.tsx +++ b/src/pages/Pools/Home/ManagePool/index.tsx @@ -5,26 +5,30 @@ 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 { useSyncing } from 'hooks/useSyncing'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; export const ManagePool = () => { const { t } = useTranslation(); - const { isSyncing } = useUi(); - const { poolNominated } = useValidators(); + const { syncing } = useSyncing(['active-pools']); const { openCanvas } = useOverlay().canvas; + const { formatWithPrefs } = useValidators(); 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 poolNominated = activePoolNominations + ? formatWithPrefs(activePoolNominations.targets) + : []; + + const isNominating = !!activePoolNominations?.targets?.length; + const nominator = activePool?.addresses?.stash ?? null; + const { state } = activePool?.bondedPool || {}; const { openHelp } = useHelp(); const canNominate = isOwner() || isNominator(); @@ -32,7 +36,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..31a570f1bd 100644 --- a/src/pages/Pools/Home/PoolStats/Announcements.tsx +++ b/src/pages/Pools/Home/PoolStats/Announcements.tsx @@ -8,10 +8,10 @@ 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'; +import { Item } from 'library/Announcements/Wrappers'; export const Announcements = () => { const { t } = useTranslation('pages'); @@ -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/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 512c64848e..9d82748f67 100644 --- a/src/pages/Pools/Home/PoolStats/index.tsx +++ b/src/pages/Pools/Home/PoolStats/index.tsx @@ -4,15 +4,15 @@ 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 'library/Hooks/usePoolCommission'; -import { StatsHead } from 'library/StatsHead'; +import { usePoolCommission } from 'hooks/usePoolCommission'; +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'); @@ -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, }, }, { @@ -86,7 +86,7 @@ export const PoolStats = () => { <h3>{t('pools.poolStats')}</h3> </CardHeaderWrapper> <Wrapper> - <StatsHead items={items} /> + <Header items={items} /> <Announcements /> </Wrapper> </CardWrapper> diff --git a/src/pages/Pools/Home/Status/MembershipStatus.tsx b/src/pages/Pools/Home/Status/MembershipStatus.tsx index 81b0c35fda..2b7e0e64c6 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 { syncing } = useSyncing('*'); const { openModal } = useOverlay().modal; + const { poolsMetaData } = useBondedPools(); const { activeAccount } = useActiveAccounts(); const { label, buttons } = useStatusButtons(); 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 ad5ecfd39b..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 { useNominationStatus } from 'library/Hooks/useNominationStatus'; +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 125fb31847..86b8c1e022 100644 --- a/src/pages/Pools/Home/Status/useStatusButtons.tsx +++ b/src/pages/Pools/Home/Status/useStatusButtons.tsx @@ -5,15 +5,15 @@ import { faPlusCircle, faUserPlus } from '@fortawesome/free-solid-svg-icons'; import { registerSaEvent } from '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 { 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 { useNetwork } from 'contexts/Network'; import { usePoolsTabs } from '../context'; +import { useBalances } from 'contexts/Balances'; export const useStatusButtons = () => { const { t } = useTranslation('pages'); @@ -22,15 +22,16 @@ export const useStatusButtons = () => { isReady, poolsConfig: { maxPools }, } = useApi(); - const { isOwner } = useActivePools(); + const { isOwner } = useActivePool(); 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/pages/Pools/Home/index.tsx b/src/pages/Pools/Home/index.tsx index 0155caffd8..be05ca7819 100644 --- a/src/pages/Pools/Home/index.tsx +++ b/src/pages/Pools/Home/index.tsx @@ -5,12 +5,12 @@ 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'; 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'; @@ -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 { favorites } = usePoolsConfig(); 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,15 +118,12 @@ export const HomeInner = () => { </CardWrapper> </RowSection> </PageRow> - {selectedActivePool !== null && ( + {activePool !== null && ( <> <ManagePool /> <PageRow> <CardWrapper> - <Roles - batchKey="pool_roles_manage" - defaultRoles={getPoolRoles()} - /> + <Roles defaultRoles={getPoolRoles()} /> </CardWrapper> </PageRow> <PageRow> 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..9551b5c2c6 100644 --- a/src/pages/Pools/Roles/index.tsx +++ b/src/pages/Pools/Roles/index.tsx @@ -15,9 +15,7 @@ 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 { useActivePool } from 'contexts/Pools/ActivePool'; import { CardHeaderWrapper } from 'library/Card/Wrappers'; import { useOverlay } from '@polkadot-cloud/react/hooks'; import { useNetwork } from 'contexts/Network'; @@ -27,10 +25,9 @@ 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'; +import { useSyncing } from 'hooks/useSyncing'; export const Roles = ({ - batchKey, defaultRoles, setters = [], inline = false, @@ -40,13 +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 { fetchIdentitiesMetaBatch } = useIdentities(); - const { isOwner, selectedActivePool } = useActivePools(); - const { id } = selectedActivePool || { id: 0 }; + + const { id } = activePool || { id: 0 }; const roles = defaultRoles; const initialiseEdits = (() => { @@ -69,17 +66,11 @@ export const Roles = ({ // store whether roles are being edited const [isEditing, setIsEditing] = useState<boolean>(false); - // store role accounts - const [accounts, setAccounts] = useState<MaybeAddress[]>( - Object.values(roles) - ); - // is this the initial fetch const [fetched, setFetched] = useState<boolean>(false); // update default roles on account switch useEffect(() => { - setAccounts(Object.values(roles)); setIsEditing(false); setRoleEdits(initialiseEdits); setFetched(false); @@ -89,7 +80,6 @@ export const Roles = ({ useEffect(() => { if (isReady && !fetched) { setFetched(true); - fetchIdentitiesMetaBatch(batchKey, Object.values(roles), true); } }, [isReady, fetched]); @@ -173,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> @@ -185,7 +175,7 @@ export const Roles = ({ iconTransform="grow-1" text={isEditing ? t('pools.save') : t('pools.edit')} disabled={ - isPoolSyncing || + syncing || isReadOnlyAccount(activeAccount) || !isRoleEditsValid() } @@ -199,11 +189,7 @@ export const Roles = ({ <section> <div className="inner"> <h4>{t('pools.depositor')}</h4> - <PoolAccount - address={roles.depositor ?? null} - batchIndex={accounts.indexOf(roles.depositor ?? '-1')} - batchKey={batchKey} - /> + <PoolAccount address={roles.depositor ?? null} pool={activePool} /> </div> </section> <section> @@ -216,11 +202,7 @@ export const Roles = ({ setRoleEdit={setRoleEditHandler} /> ) : ( - <PoolAccount - address={roles.root ?? null} - batchIndex={accounts.indexOf(roles.root ?? '-1')} - batchKey={batchKey} - /> + <PoolAccount address={roles.root ?? null} pool={activePool} /> )} </div> </section> @@ -236,8 +218,7 @@ export const Roles = ({ ) : ( <PoolAccount address={roles.nominator ?? null} - batchIndex={accounts.indexOf(roles.nominator ?? '-1')} - batchKey={batchKey} + pool={activePool} /> )} </div> @@ -252,11 +233,7 @@ export const Roles = ({ setRoleEdit={setRoleEditHandler} /> ) : ( - <PoolAccount - address={roles.bouncer ?? null} - batchIndex={accounts.indexOf(roles.bouncer ?? '-1')} - batchKey={batchKey} - /> + <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 a07d789c56..c98fe04db9 100644 --- a/src/pages/Pools/Roles/types.ts +++ b/src/pages/Pools/Roles/types.ts @@ -1,11 +1,10 @@ // 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 { - batchKey: string; defaultRoles: PoolRoles; listenIsValid?: AnyFunction; setters?: AnyFunction; diff --git a/src/pages/Pools/types.ts b/src/pages/Pools/types.ts index 6ca44855e7..3cf1cab391 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/ActivePool/types'; import type { ListFormat } from 'library/PoolList/types'; export interface PoolAccountProps { address: string | null; - batchKey: string; - batchIndex: number; + pool: ActivePool | null; } export interface PoolsTabsContextInterface { diff --git a/src/static/APIController/index.ts b/src/static/APIController/index.ts index c617970ede..3ae0132539 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,9 @@ export class APIController { await this.disconnect(); } + // Add initial syncing items. + SyncController.dispatch('initialization', 'syncing'); + const config: APIConfig = { type, network, @@ -647,11 +652,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(); } }; @@ -760,6 +763,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..2e87ac693a --- /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 { 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'; +import type { Nominations } from 'contexts/Balances/types'; + +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..f3fe5c5df4 --- /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/Balances/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/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 14d7bf1c96..907739171c 100644 --- a/src/static/BalancesController/index.ts +++ b/src/static/BalancesController/index.ts @@ -10,9 +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 { // ------------------------------------------------------ @@ -31,6 +35,12 @@ export class BalancesController { // Account payees, populated by api callbacks. static payees: Record<string, PayeeConfig> = {}; + // 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> = {}; @@ -50,6 +60,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); @@ -59,17 +77,32 @@ 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], + [api.query.staking.nominators, address], ], async ([ ledgerResult, accountResult, locksResult, payeeResult, + poolMembersResult, + claimPermissionsResult, + nominatorsResult, ]): Promise<void> => { 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 + ); + + this.handleNominations(address, nominatorsResult); + // Send updated account state back to UI. document.dispatchEvent( new CustomEvent('new-account-balance', { @@ -78,6 +111,8 @@ export class BalancesController { ledger: this.ledgers[address], balances: this.balances[address], payee: this.payees[address], + poolMembership: this.poolMemberships[address], + nominations: this.nominations[address], }, }) ); @@ -97,6 +132,11 @@ 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]; + delete this.nominations[account]; }); // Remove removed accounts from class. this.accounts = this.accounts.filter( @@ -131,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), })), }; }; @@ -183,11 +223,73 @@ export class BalancesController { this.payees[address] = payeeFinal; }; + // Handle pool membership and claim commission callback. + static handlePoolMembershipCallback = async ( + address: string, + poolMembersResult: AnyApi, + claimPermissionsResult: AnyApi + ): Promise<void> => { + // 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, + }; + }; + + // 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. @@ -198,6 +300,8 @@ export class BalancesController { ledger, balances, payee, + poolMembership, + nominations, }; }; @@ -214,6 +318,8 @@ export class BalancesController { this.ledgers = {}; this.balances = {}; this.payees = {}; + this.poolMemberships = {}; + this.nominations = {}; this._unsubs = {}; }; 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/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; +} 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.ts similarity index 94% rename from src/types/index.ts rename to src/types.ts index 8696bc79da..19e0e60016 100644 --- a/src/types/index.ts +++ b/src/types.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[] }>; 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", diff --git a/yarn.lock b/yarn.lock index 43401fcc94..baa057132d 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" +"@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: 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 @@ -918,39 +918,27 @@ __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" - dependencies: - "@ledgerhq/devices": "npm:^8.2.0" - "@ledgerhq/errors": "npm:^6.16.1" - "@ledgerhq/hw-transport": "npm:^6.30.2" - "@ledgerhq/logs": "npm:^6.12.0" - checksum: 9529ba663470cfe8adb3d6808096982eb34ba341729db4dedc4b91185486fb38d178b418771b7d606be63652966c96e74b7076cdd311ccb34538ad71a88a8365 - languageName: node - linkType: hard - -"@ledgerhq/hw-transport@npm:^6.27.1": - version: 6.30.1 - resolution: "@ledgerhq/hw-transport@npm:6.30.1" +"@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.3" "@ledgerhq/logs": "npm:^6.12.0" - events: "npm:^3.3.0" - checksum: ef53219330296f81f84fc116debd27218ee97da4477aa68a58a1baae9f1d5e0fa220d293a40c0415f60a977634efa7ebe6c8bce84ff817a898b0aa6a7719a8f1 + checksum: b2017d283f2030e28ab663fc7f032db66ef3bfefb29ceb4cecf29561f7ed54f9e0c17c1e1e30a09ee9957ed8f1b8bada7391c4030554370a122a6654979600b8 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.27.1, @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 @@ -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: @@ -1662,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 @@ -1987,90 +1947,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 +2059,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 719283fa1bb7e25a9e3bfb4bb2fca8dec98a1cbbcd53cdd6a8cc9d9e2ac701d0fe409b2f3f9b6ddd466b2c1592cdae9c2302a114036bb5adc08e8f95549543e0 + checksum: 1f5c3b42443f7437e8b46621db6078babf292cc0855d83b2c45f43fd57a7af098243d9f5e2cdebc5fd5219ec8d9c0429cc17601497d7e301336d104618f775b2 languageName: node linkType: hard @@ -2126,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 @@ -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 @@ -2206,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 @@ -2233,14 +2202,25 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:^18.2.48": - version: 18.2.48 - resolution: "@types/react@npm:18.2.48" +"@types/react@npm:*": + 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: 409f684dc1cb03ed542e1f185da7e84f81186d7945119adb553fe0e4e28bad3468213fce9096e1867b358c87fb57fc80b53a3e544a3779f7080ddde7c1b99eb1 + languageName: node + linkType: hard + +"@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: 7e89f18ea2928b1638f564b156d692894dcb9352a7e0a807873c97e858abe1f23dbd165a25dd088a991344e973fdeef88ba5724bfb64504b74072cbc9c220c3a + checksum: 6b1c73beafbbc582dc54ffd92b3779f6d850e8f6b5ce5d04b31e9498a3d77bfc416bb08f0d8d63ab4f4649ccd6639996472416871c01c74a528b16a55faeaf38 languageName: node linkType: hard @@ -2290,15 +2270,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:^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.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: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" @@ -2306,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: 01f1d643219b51bfad76734c6eb19a480309f0e66877ddc00bca89368e5aee3eb907e366977a8d11094c49e807773f5cfba306c861cd690d70044a7925173823 + checksum: 0340a406b6a9036b6b2d92ffa79364d9cbe509e26c9726a953a1b26b4a4413a7079110e94b8a56c7d9d5193885a77f52611af00dea2d60ac79221303f0b91b3d languageName: node linkType: hard -"@typescript-eslint/parser@npm:^6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/parser@npm:6.19.1" +"@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.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/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: 442e860fbc4786fe999205528cc74b31d933008e170a707ddaec0c9e2c374f62c36c8d05d3dd446c9ceb802f2b403806d72c78ffd97867cf1672028b754b6262 + checksum: 6e5c17faf94ced7fd5f5e0a44129f1369a691a39824303f947ed8f0089b03493b51e8c40e1f8a9f67e6420cec9aa084440d9362153525f55b20572bc111d4da5 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" +"@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.19.1" - "@typescript-eslint/visitor-keys": "npm:6.19.1" - checksum: a81315b4a2888343d3be781fe8d6b4c229c656d7bf1bd74bc44a89bba96bb6a10a0319d301f24ca91adb898374eaadbd38979e6567ac9085b5d7076163794281 + "@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.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/type-utils@npm:6.19.1" +"@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.19.1" - "@typescript-eslint/utils": "npm:6.19.1" + "@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: 78c185c64a8c92d7b5f2132ef4880b974a2e07e9ae7913ad53e327972af540a8a8bf75bc319c8aaa82445615e2680f3c85736ee67aa174a5ba91798fe5068f95 + checksum: 55e2ea9a76fbd62e69124298e3c1a4cf713ffe437874d090b76e747837fd5ea4034a82002e799108f29606bbed1a853e3d24f59b8a4d685b1e17698ffeb83d81 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 +"@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.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/typescript-estree@npm:6.19.1" +"@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.19.1" - "@typescript-eslint/visitor-keys": "npm:6.19.1" + "@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" @@ -2382,34 +2362,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: dec16f873084e9eeb1a696dff82c42164e75908221f7868d900ad7b7fcec6fc62a9a7dddb8bc17c78c19bf35f07acee81b3778b20b9735ffdaeee732ecb643d3 + checksum: c8cff32a8d880de6228de900aeb20127e4663570a5f959195fda73f905ab06f3d9fbf46d60db0a6333456e0179e4706737293c90e8cce2d4ad7a220ccef2a8e7 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/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.19.1" - "@typescript-eslint/types": "npm:6.19.1" - "@typescript-eslint/typescript-estree": "npm:6.19.1" + "@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: 5fa58a32722e9915bfe8433fda2f46be894352549e8406acc4e29a04a8ddb0ea5988fddda2a3145f8952129a267cb51b666206b30489d2ff36b7911f540f1d57 + eslint: ^8.56.0 + checksum: 83038958695daaa2a91092b16a64109797af28ec419f734f9dffa71f852ffb57ebd67c72d0b84c70805e4a53d4ead08e4f87687e944a1db19aeb72fcc89208cd 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" +"@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.19.1" + "@typescript-eslint/types": "npm:7.0.1" eslint-visitor-keys: "npm:^3.4.1" - checksum: b0370a9bc6fd8d243aa8b7ccd1657ec2fbd25ceb7b067aac64322f03aa0f64b97444b13b0946f52a53d6bc5edd43e0b447f72160be4a5b72e073c1d3679b6b4c + checksum: a7a174d706f1b2ce60ebd17b9d20b36cc89c0ed45fcf510538734d13bca38d25ddbd4b6893a83ef5f344ad9aa7be76c22ea8407fa3c213c14dbcc52f9a2eadd0 languageName: node linkType: hard @@ -2420,14 +2400,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 @@ -2772,9 +2752,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 +2918,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 +3029,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 +3470,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 +3672,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 +3747,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 0fd913124089e26d30ec30f73b94d4ef9607935251df3253f869106980a5d4c78aa517738c8746abe6e933262e91a77d31427ce468ed8fc7fe498a20f7f92fbc + checksum: 0f2d21ffe24ebead64843f87c3aebe2e703a5ed9feb086a0728b24907fac2eb9923e4a79857d3df9059c915739bd7a870dd667972eae325c67f478b592b8582d languageName: node linkType: hard @@ -4177,11 +4157,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 @@ -4830,12 +4810,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.2": + version: 23.8.2 + resolution: "i18next@npm:23.8.2" dependencies: "@babel/runtime": "npm:^7.23.2" - checksum: 95460fff030860af7b5c65485c966fc5894372bff09f0f2f6b3fb72f053763d0b253aba977f52ddf7e32f763bf463efcf025da17a108cb1010b85fe3bd278cbd + checksum: c16ccee81bc1e096fec8d10000de42f10137f7cd27b295eca22492cb174b364ebca72327b2e6be066c4308c79bf72c8585d1c7cde2aedf556e3f423af60cd66c languageName: node linkType: hard @@ -4856,16 +4836,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 @@ -5502,13 +5482,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" @@ -5553,9 +5526,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 @@ -5578,11 +5551,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 @@ -5855,13 +5828,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 @@ -6306,7 +6279,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" @@ -6319,16 +6292,17 @@ __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.48" - "@types/react-dom": "npm:^18.2.17" + "@types/react": "npm:^18.2.55" + "@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" - "@typescript-eslint/eslint-plugin": "npm:^6.19.1" - "@typescript-eslint/parser": "npm:^6.19.1" - "@vitejs/plugin-react-swc": "npm:^3.5.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" bn.js: "npm:^5.2.1" @@ -6349,10 +6323,11 @@ __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.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: "npm:^3.2.5" prettier-plugin-organize-imports: "npm:^3.2.4" qrcode-generator: "npm:1.4.4" rc-slider: "npm:^10.5.0" @@ -6361,16 +6336,16 @@ __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-router-dom: "npm:^6.21.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" styled-components: "npm:^6.1.8" typescript: "npm:^5.3.3" - usehooks-ts: "npm:2.10.0" - vite: "npm:^5.0.12" - vite-bundle-visualizer: "npm:^1.0.0" - vite-plugin-checker: "npm:^0.6.2" + 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" vite-plugin-eslint: "npm:^1.8.1" vite-plugin-svgr: "npm:^4.2.0" vite-tsconfig-paths: "npm:^4.3.1" @@ -6442,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" @@ -6475,12 +6461,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 @@ -6639,11 +6625,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.5": + version: 14.0.5 + resolution: "react-i18next@npm:14.0.5" 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" @@ -6653,7 +6639,7 @@ __metadata: optional: true react-native: optional: true - checksum: 9104c51c5d185e6d1b8ad71714b9ef490286f87b8833c7f67949da34f46f65ebcc994f6f3d087ffe180b7f811ba367c64e5e500c8e442fedbdf54c193bad0257 + checksum: 60e3bedc6889689cfb96005b134da79d50097d3ff088284b35f213820aacedbdad1ff32e97b8447db70834c16d472ded829675e05c2a099baea9fd96b3a5b72f languageName: node linkType: hard @@ -6671,27 +6657,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 @@ -7986,13 +7972,14 @@ __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.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.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: cb5b3ab4f9269b103bfbd85ef624b0dc10205015134233626a0ea2d4b9e2849e401ca3c167f3876fcd51387751696746bd8e8b7460402923a80354245a3d5202 + react: ^16.8.0 || ^17 || ^18 + checksum: 032d8e39687592cad9e00fa853a32c0edf286dd1c5843cfa06167b080f9da9e24479b28096e11531912c9c47b219fb969041a4dc831dfe3a3ca007fe11a2b454 languageName: node linkType: hard @@ -8032,9 +8019,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 +8029,7 @@ __metadata: tmp: "npm:^0.2.1" bin: vite-bundle-visualizer: bin.js - checksum: 9113c78bf47bcc673cdf8487c2023a2679d8ec78c4fff2a4c6be83bbe0f3596b1fa5c7fdf4fe7b9abd487cf8b4cf10dbd956183da019de4b72ce9a94385225a6 + checksum: 686585028eef05d9134c86cdc5b98e8dc792e10340f6ea3cf1263bdf5b16c3fc29189d6291a49e921696f052155e1de97b7ba3452716965adbc55e25b62b584a languageName: node linkType: hard @@ -8061,9 +8048,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.4 + resolution: "vite-plugin-checker@npm:0.6.4" dependencies: "@babel/code-frame": "npm:^7.12.13" ansi-escapes: "npm:^4.3.0" @@ -8072,8 +8059,6 @@ __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" npm-run-path: "npm:^4.0.1" semver: "npm:^7.5.0" strip-ansi: "npm:^6.0.0" @@ -8109,7 +8094,7 @@ __metadata: optional: true vue-tsc: optional: true - checksum: 8c991c63b61e52fc69a22033aa34c6cb698d6af7dd7505a9fecb62c6e8486bc6f9667c23f5ad6f7289d9d2780bdb4711740433a5cabc27c4c7d763d028d54b2a + checksum: ae61f01b620c458e355ad05ff632e3143312e75c67acdaaa1fe5160d679283364867a4a8d6c6a3f85838f0251033275af96a1aa9b52eed227151cdbca0c996cf languageName: node linkType: hard @@ -8156,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: @@ -8196,6 +8181,46 @@ __metadata: languageName: node linkType: hard +"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" + 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: d7c23284aeb3a8333e0ea208fab7da4dd4f69134f22ff959d865f56d48c3afd04fbc548368c15a5fbc0c6453ee950db42bd3532aa0dd50b85464eabefaa51396 + languageName: node + linkType: hard + "vitest@npm:^1.2.2": version: 1.2.2 resolution: "vitest@npm:1.2.2"