From 62176288ad098883dca026c0eacc91099b8418e6 Mon Sep 17 00:00:00 2001 From: Arjun Porwal Date: Thu, 3 Apr 2025 13:21:03 +0530 Subject: [PATCH 01/38] feat: initialized staking-next page --- packages/apps-routing/src/index.ts | 3 + packages/apps-routing/src/staking-next.ts | 20 +++++ packages/apps-routing/tsconfig.build.json | 1 + .../apps/public/locales/en/apps-routing.json | 1 + packages/page-staking-next/.skip-build | 0 packages/page-staking-next/.skip-npm | 0 packages/page-staking-next/README.md | 3 + packages/page-staking-next/package.json | 23 ++++++ packages/page-staking-next/src/index.tsx | 78 +++++++++++++++++++ .../page-staking-next/tsconfig.build.json | 12 +++ packages/react-components/src/i18n/index.ts | 1 + tsconfig.base.json | 1 + tsconfig.build.json | 1 + yarn.lock | 10 +++ 14 files changed, 154 insertions(+) create mode 100644 packages/apps-routing/src/staking-next.ts create mode 100644 packages/page-staking-next/.skip-build create mode 100644 packages/page-staking-next/.skip-npm create mode 100644 packages/page-staking-next/README.md create mode 100644 packages/page-staking-next/package.json create mode 100644 packages/page-staking-next/src/index.tsx create mode 100644 packages/page-staking-next/tsconfig.build.json diff --git a/packages/apps-routing/src/index.ts b/packages/apps-routing/src/index.ts index 10624d4b1c73..65ba15b671f8 100644 --- a/packages/apps-routing/src/index.ts +++ b/packages/apps-routing/src/index.ts @@ -38,6 +38,7 @@ import settings from './settings.js'; import signing from './signing.js'; import society from './society.js'; import staking from './staking.js'; +import stakingNext from './staking-next.js'; import staking2 from './staking2.js'; import stakingLegacy from './stakingLegacy.js'; import storage from './storage.js'; @@ -58,6 +59,8 @@ export default function create (t: TFunction): Routes { poll(t), transfer(t), teleport(t), + // Staking for AssetHub Migration + stakingNext(t), staking(t), staking2(t), // Legacy staking Pre v14 pallet version. diff --git a/packages/apps-routing/src/staking-next.ts b/packages/apps-routing/src/staking-next.ts new file mode 100644 index 000000000000..7945baee3461 --- /dev/null +++ b/packages/apps-routing/src/staking-next.ts @@ -0,0 +1,20 @@ +// Copyright 2017-2025 @polkadot/apps-routing authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { Route, TFunction } from './types.js'; + +import Component from '@polkadot/app-staking-next'; + +export default function create (t: TFunction): Route { + return { + Component, + display: { + // TODO: Add check when to show this page + needsApi: [] + }, + group: 'network', + icon: 'certificate', + name: 'staking-next', + text: t('nav.staking-next', 'Staking Next', { ns: 'apps-routing' }) + }; +} diff --git a/packages/apps-routing/tsconfig.build.json b/packages/apps-routing/tsconfig.build.json index b1f8b42f3575..325e6e3a6e59 100644 --- a/packages/apps-routing/tsconfig.build.json +++ b/packages/apps-routing/tsconfig.build.json @@ -41,6 +41,7 @@ { "path": "../page-signing/tsconfig.build.json" }, { "path": "../page-society/tsconfig.build.json" }, { "path": "../page-staking/tsconfig.build.json" }, + { "path": "../page-staking-next/tsconfig.build.json" }, { "path": "../page-staking2/tsconfig.build.json" }, { "path": "../page-staking-legacy/tsconfig.build.json" }, { "path": "../page-storage/tsconfig.build.json" }, diff --git a/packages/apps/public/locales/en/apps-routing.json b/packages/apps/public/locales/en/apps-routing.json index c5e93800442b..f60995dedb6c 100644 --- a/packages/apps/public/locales/en/apps-routing.json +++ b/packages/apps/public/locales/en/apps-routing.json @@ -34,6 +34,7 @@ "nav.signing": "Sign and verify", "nav.society": "Society", "nav.staking": "Staking", + "nav.staking-next": "Staking Next", "nav.storage": "Chain state", "nav.sudo": "Sudo", "nav.tech-comm": "Tech. comm.", diff --git a/packages/page-staking-next/.skip-build b/packages/page-staking-next/.skip-build new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/page-staking-next/.skip-npm b/packages/page-staking-next/.skip-npm new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/page-staking-next/README.md b/packages/page-staking-next/README.md new file mode 100644 index 000000000000..bcee3ef8047f --- /dev/null +++ b/packages/page-staking-next/README.md @@ -0,0 +1,3 @@ +# @polkadot/app-staking-next + +More Info can be found here - https://hackmd.io/7PiBrGxxRG2ib-WRZYJZhQ \ No newline at end of file diff --git a/packages/page-staking-next/package.json b/packages/page-staking-next/package.json new file mode 100644 index 000000000000..484e3377ca76 --- /dev/null +++ b/packages/page-staking-next/package.json @@ -0,0 +1,23 @@ +{ + "bugs": "https://github.com/polkadot-js/apps/issues", + "engines": { + "node": ">=18" + }, + "homepage": "https://github.com/polkadot-js/apps/tree/master/packages/page-staking-next#readme", + "license": "Apache-2.0", + "name": "@polkadot/app-staking-next", + "private": true, + "repository": { + "directory": "packages/page-staking-next", + "type": "git", + "url": "https://github.com/polkadot-js/apps.git" + }, + "sideEffects": false, + "type": "module", + "version": "0.152.2-11-x", + "peerDependencies": { + "react": "*", + "react-dom": "*", + "react-is": "*" + } +} diff --git a/packages/page-staking-next/src/index.tsx b/packages/page-staking-next/src/index.tsx new file mode 100644 index 000000000000..cbcd5148da63 --- /dev/null +++ b/packages/page-staking-next/src/index.tsx @@ -0,0 +1,78 @@ +// Copyright 2017-2025 @polkadot/app-staking-next authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { AppProps as Props } from '@polkadot/react-components/types'; + +import React from 'react'; +import { Route, Routes } from 'react-router'; + +import { styled, Tabs } from '@polkadot/react-components'; +import { useAccounts } from '@polkadot/react-hooks'; + +const HIDDEN_ACC = ['actions', 'payout']; + +function StakingApp ({ basePath, className = '' }: Props): React.ReactElement { + const { areAccountsLoaded, hasAccounts } = useAccounts(); + + return ( + + + + ); +} + +const StyledMain = styled.main` + .staking--Chart { + margin-top: 1.5rem; + + h1 { + margin-bottom: 0.5rem; + } + + .ui--Spinner { + margin: 2.5rem auto; + } + } + + .staking--optionsBar { + margin: 0.5rem 0 1rem; + text-align: center; + white-space: normal; + + .staking--buttonToggle { + display: inline-block; + margin-right: 1rem; + margin-top: 0.5rem; + } + } + + .ui--Expander.stakeOver { + .ui--Expander-summary { + color: var(--color-error); + } + } +`; + +export default React.memo(StakingApp); diff --git a/packages/page-staking-next/tsconfig.build.json b/packages/page-staking-next/tsconfig.build.json new file mode 100644 index 000000000000..9da3e5471bbe --- /dev/null +++ b/packages/page-staking-next/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "..", + "outDir": "./build", + "rootDir": "./src" + }, + "references": [ + { "path": "../react-components/tsconfig.build.json" }, + { "path": "../react-hooks/tsconfig.build.json" } + ] +} diff --git a/packages/react-components/src/i18n/index.ts b/packages/react-components/src/i18n/index.ts index a97f75d0d51f..08f4d352606c 100644 --- a/packages/react-components/src/i18n/index.ts +++ b/packages/react-components/src/i18n/index.ts @@ -68,6 +68,7 @@ i18next 'app-signing', 'app-society', 'app-staking', + 'app-staking-next', 'app-staking-legacy', 'app-storage', 'app-sudo', diff --git a/tsconfig.base.json b/tsconfig.base.json index 158c1f661e9a..d1c994eb3a21 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -70,6 +70,7 @@ "@polkadot/app-signing": ["page-signing/src/index.tsx"], "@polkadot/app-society": ["page-society/src/index.tsx"], "@polkadot/app-staking": ["page-staking/src/index.tsx"], + "@polkadot/app-staking-next": ["page-staking-next/src/index.tsx"], "@polkadot/app-staking2": ["page-staking2/src/index.tsx"], "@polkadot/app-staking2/Legend": ["page-staking2/src/Legend.tsx"], "@polkadot/app-staking2/Pools": ["page-staking2/src/Pools/index.tsx"], diff --git a/tsconfig.build.json b/tsconfig.build.json index 53428eb1a1a0..46fde44d1458 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -51,6 +51,7 @@ { "path": "./packages/page-signing/tsconfig.build.json" }, { "path": "./packages/page-society/tsconfig.build.json" }, { "path": "./packages/page-staking/tsconfig.build.json" }, + { "path": "./packages/page-staking-next/tsconfig.build.json" }, { "path": "./packages/page-staking2/tsconfig.build.json" }, { "path": "./packages/page-staking-legacy/tsconfig.build.json" }, { "path": "./packages/page-storage/tsconfig.build.json" }, diff --git a/yarn.lock b/yarn.lock index ef48e56adb51..85c2bb770ca2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2048,6 +2048,16 @@ __metadata: languageName: unknown linkType: soft +"@polkadot/app-staking-next@workspace:packages/page-staking-next": + version: 0.0.0-use.local + resolution: "@polkadot/app-staking-next@workspace:packages/page-staking-next" + peerDependencies: + react: "*" + react-dom: "*" + react-is: "*" + languageName: unknown + linkType: soft + "@polkadot/app-staking2@workspace:packages/page-staking2": version: 0.0.0-use.local resolution: "@polkadot/app-staking2@workspace:packages/page-staking2" From a2d53d92979031600c472642c1f41cdd960f0cdb Mon Sep 17 00:00:00 2001 From: Arjun Porwal Date: Thu, 3 Apr 2025 14:02:29 +0530 Subject: [PATCH 02/38] feat: enhance staking-next page with dynamic tabs and translation support --- packages/page-staking-next/src/index.tsx | 85 ++++++++++++++++----- packages/page-staking-next/src/translate.ts | 8 ++ 2 files changed, 73 insertions(+), 20 deletions(-) create mode 100644 packages/page-staking-next/src/translate.ts diff --git a/packages/page-staking-next/src/index.tsx b/packages/page-staking-next/src/index.tsx index cbcd5148da63..caf70353b809 100644 --- a/packages/page-staking-next/src/index.tsx +++ b/packages/page-staking-next/src/index.tsx @@ -3,16 +3,67 @@ import type { AppProps as Props } from '@polkadot/react-components/types'; -import React from 'react'; +import React, { useMemo } from 'react'; import { Route, Routes } from 'react-router'; import { styled, Tabs } from '@polkadot/react-components'; -import { useAccounts } from '@polkadot/react-hooks'; +import { useAccounts, useApi, useAvailableSlashes, useOwnStashInfos } from '@polkadot/react-hooks'; +import { isFunction } from '@polkadot/util'; + +import { useTranslation } from './translate.js'; const HIDDEN_ACC = ['actions', 'payout']; function StakingApp ({ basePath, className = '' }: Props): React.ReactElement { + const { t } = useTranslation(); + const { api } = useApi(); const { areAccountsLoaded, hasAccounts } = useAccounts(); + const ownStashes = useOwnStashInfos(); + const slashes = useAvailableSlashes(); + + const hasStashes = useMemo( + () => hasAccounts && !!ownStashes && (ownStashes.length !== 0), + [hasAccounts, ownStashes] + ); + + const items = useMemo(() => [ + { + isRoot: true, + name: 'overview', + text: t('Overview') + }, + { + name: 'actions', + text: t('Accounts') + }, + hasStashes && isFunction(api.query.staking.activeEra) && { + name: 'payout', + text: t('Payouts') + }, + isFunction(api.query.nominationPools?.minCreateBond) && { + name: 'pools', + text: t('Pools') + }, + { + alias: 'returns', + name: 'targets', + text: t('Targets') + }, + hasStashes && isFunction((api.query.voterBagsList || api.query.bagsList || api.query.voterList)?.counterForListNodes) && { + name: 'bags', + text: t('Bags') + }, + { + count: slashes.reduce((count, [, unapplied]) => count + unapplied.length, 0), + name: 'slashes', + text: t('Slashes') + }, + { + hasParams: true, + name: 'query', + text: t('Validator stats') + } + ].filter((q): q is { name: string; text: string } => !!q), [api, hasStashes, slashes, t]); return ( @@ -23,24 +74,18 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement - - - Staking Next Page

- // - } - index - // path='bags' - /> -
- -
- - -
); + items={items} + /> + + + Root Page} + index + /> + + + + ); } const StyledMain = styled.main` diff --git a/packages/page-staking-next/src/translate.ts b/packages/page-staking-next/src/translate.ts new file mode 100644 index 000000000000..09db304c3e3a --- /dev/null +++ b/packages/page-staking-next/src/translate.ts @@ -0,0 +1,8 @@ +// Copyright 2017-2025 @polkadot/app-staking-next authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { useTranslation as useTranslationBase } from 'react-i18next'; + +export function useTranslation (): { t: (key: string, options?: { replace: Record }) => string } { + return useTranslationBase('app-staking-next'); +} From d31da2bf4d34e4c1d253f840d6e1fb73ad2370a8 Mon Sep 17 00:00:00 2001 From: Arjun Porwal Date: Thu, 10 Apr 2025 11:21:00 +0530 Subject: [PATCH 03/38] feat: enabled payouts page for staking-next --- packages/page-staking-next/src/constants.ts | 4 ++ packages/page-staking-next/src/index.tsx | 49 ++++++++++++++++++++- tsconfig.base.json | 2 + 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 packages/page-staking-next/src/constants.ts diff --git a/packages/page-staking-next/src/constants.ts b/packages/page-staking-next/src/constants.ts new file mode 100644 index 000000000000..fcb9fadb8a46 --- /dev/null +++ b/packages/page-staking-next/src/constants.ts @@ -0,0 +1,4 @@ +// Copyright 2017-2025 @polkadot/app-staking-next authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export const STORE_FAVS_BASE = 'staking:favorites'; diff --git a/packages/page-staking-next/src/index.tsx b/packages/page-staking-next/src/index.tsx index caf70353b809..3e6eb2422494 100644 --- a/packages/page-staking-next/src/index.tsx +++ b/packages/page-staking-next/src/index.tsx @@ -2,30 +2,64 @@ // SPDX-License-Identifier: Apache-2.0 import type { AppProps as Props } from '@polkadot/react-components/types'; +import type { ElectionStatus, ParaValidatorIndex, ValidatorId } from '@polkadot/types/interfaces'; +import type { BN } from '@polkadot/util'; -import React, { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; import { Route, Routes } from 'react-router'; +import Payouts from '@polkadot/app-staking/Payouts'; +import useSortedTargets from '@polkadot/app-staking/useSortedTargets'; +import useOwnPools from '@polkadot/app-staking2/Pools/useOwnPools'; import { styled, Tabs } from '@polkadot/react-components'; -import { useAccounts, useApi, useAvailableSlashes, useOwnStashInfos } from '@polkadot/react-hooks'; +import { useAccounts, useApi, useAvailableSlashes, useCallMulti, useFavorites, useOwnStashInfos } from '@polkadot/react-hooks'; import { isFunction } from '@polkadot/util'; +import { STORE_FAVS_BASE } from './constants.js'; import { useTranslation } from './translate.js'; const HIDDEN_ACC = ['actions', 'payout']; +const OPT_MULTI = { + defaultValue: [false, undefined, {}] as [boolean, BN | undefined, Record], + transform: ([eraElectionStatus, minValidatorBond, validators, activeValidatorIndices]: [ElectionStatus | null, BN | undefined, ValidatorId[] | null, ParaValidatorIndex[] | null]): [boolean, BN | undefined, Record] => [ + !!eraElectionStatus && eraElectionStatus.isOpen, + minValidatorBond && !minValidatorBond.isZero() + ? minValidatorBond + : undefined, + validators && activeValidatorIndices + ? activeValidatorIndices.reduce((all, index) => ({ ...all, [validators[index.toNumber()].toString()]: true }), {}) + : {} + ] +}; + function StakingApp ({ basePath, className = '' }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); + const [withLedger, setWithLedger] = useState(false); + const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE); const { areAccountsLoaded, hasAccounts } = useAccounts(); const ownStashes = useOwnStashInfos(); const slashes = useAvailableSlashes(); + const targets = useSortedTargets(favorites, withLedger); + const [isInElection, minCommission, paraValidators] = useCallMulti<[boolean, BN | undefined, Record]>([ + api.query.staking.eraElectionStatus, + api.query.staking.minCommission, + api.query.session.validators, + (api.query.parasShared || api.query.shared)?.activeValidatorIndices + ], OPT_MULTI); + const ownPools = useOwnPools(); const hasStashes = useMemo( () => hasAccounts && !!ownStashes && (ownStashes.length !== 0), [hasAccounts, ownStashes] ); + const ownValidators = useMemo( + () => (ownStashes || []).filter(({ isStashValidating }) => isStashValidating), + [ownStashes] + ); + const items = useMemo(() => [ { isRoot: true, @@ -78,6 +112,17 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement + + } + path='payout' + /> Root Page} index diff --git a/tsconfig.base.json b/tsconfig.base.json index d1c994eb3a21..7e6a3bb4f335 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -70,6 +70,8 @@ "@polkadot/app-signing": ["page-signing/src/index.tsx"], "@polkadot/app-society": ["page-society/src/index.tsx"], "@polkadot/app-staking": ["page-staking/src/index.tsx"], + "@polkadot/app-staking/Payouts": ["page-staking/src/Payouts/index.tsx"], + "@polkadot/app-staking/*": ["page-staking/src/*.ts"], "@polkadot/app-staking-next": ["page-staking-next/src/index.tsx"], "@polkadot/app-staking2": ["page-staking2/src/index.tsx"], "@polkadot/app-staking2/Legend": ["page-staking2/src/Legend.tsx"], From 739064bc63ffb8f84cd3b040fafe89b98cb33b04 Mon Sep 17 00:00:00 2001 From: Arjun Porwal Date: Thu, 10 Apr 2025 11:52:10 +0530 Subject: [PATCH 04/38] feat: enabled pools page for staking-next --- packages/page-staking-next/src/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/page-staking-next/src/index.tsx b/packages/page-staking-next/src/index.tsx index 3e6eb2422494..d009217afc1a 100644 --- a/packages/page-staking-next/src/index.tsx +++ b/packages/page-staking-next/src/index.tsx @@ -10,6 +10,7 @@ import { Route, Routes } from 'react-router'; import Payouts from '@polkadot/app-staking/Payouts'; import useSortedTargets from '@polkadot/app-staking/useSortedTargets'; +import Pools from '@polkadot/app-staking2/Pools'; import useOwnPools from '@polkadot/app-staking2/Pools/useOwnPools'; import { styled, Tabs } from '@polkadot/react-components'; import { useAccounts, useApi, useAvailableSlashes, useCallMulti, useFavorites, useOwnStashInfos } from '@polkadot/react-hooks'; @@ -112,6 +113,12 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement + + } + path='pools' + /> Date: Thu, 10 Apr 2025 12:03:46 +0530 Subject: [PATCH 05/38] feat: enabled bags page for staking-next --- packages/page-staking-next/src/index.tsx | 11 +++++++++-- tsconfig.base.json | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/page-staking-next/src/index.tsx b/packages/page-staking-next/src/index.tsx index d009217afc1a..f10fe6590989 100644 --- a/packages/page-staking-next/src/index.tsx +++ b/packages/page-staking-next/src/index.tsx @@ -8,6 +8,7 @@ import type { BN } from '@polkadot/util'; import React, { useMemo, useState } from 'react'; import { Route, Routes } from 'react-router'; +import Bags from '@polkadot/app-staking/Bags'; import Payouts from '@polkadot/app-staking/Payouts'; import useSortedTargets from '@polkadot/app-staking/useSortedTargets'; import Pools from '@polkadot/app-staking2/Pools'; @@ -115,9 +116,9 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement + } - path='pools' + path='bags' /> + + } + path='pools' + /> Root Page} index diff --git a/tsconfig.base.json b/tsconfig.base.json index 7e6a3bb4f335..2ae1a2937f99 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -71,6 +71,7 @@ "@polkadot/app-society": ["page-society/src/index.tsx"], "@polkadot/app-staking": ["page-staking/src/index.tsx"], "@polkadot/app-staking/Payouts": ["page-staking/src/Payouts/index.tsx"], + "@polkadot/app-staking/Bags": ["page-staking/src/Bags/index.tsx"], "@polkadot/app-staking/*": ["page-staking/src/*.ts"], "@polkadot/app-staking-next": ["page-staking-next/src/index.tsx"], "@polkadot/app-staking2": ["page-staking2/src/index.tsx"], From 5df46e46ed13507e779742ffcd06c24ecf1695d5 Mon Sep 17 00:00:00 2001 From: Arjun Porwal Date: Thu, 10 Apr 2025 12:04:01 +0530 Subject: [PATCH 06/38] fix: build errors --- packages/page-staking-next/tsconfig.build.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/page-staking-next/tsconfig.build.json b/packages/page-staking-next/tsconfig.build.json index 9da3e5471bbe..fca1f4c867fe 100644 --- a/packages/page-staking-next/tsconfig.build.json +++ b/packages/page-staking-next/tsconfig.build.json @@ -7,6 +7,8 @@ }, "references": [ { "path": "../react-components/tsconfig.build.json" }, - { "path": "../react-hooks/tsconfig.build.json" } + { "path": "../react-hooks/tsconfig.build.json" }, + { "path": "../page-staking2/tsconfig.build.json" }, + { "path": "../page-staking/tsconfig.build.json" }, ] } From a18b2b804af692d5a10befdceaba7fcee1361a16 Mon Sep 17 00:00:00 2001 From: Arjun Porwal Date: Thu, 10 Apr 2025 12:04:15 +0530 Subject: [PATCH 07/38] chore: added translation files --- packages/apps/public/locales/en/app-staking-next.json | 10 ++++++++++ packages/apps/public/locales/en/index.json | 1 + 2 files changed, 11 insertions(+) create mode 100644 packages/apps/public/locales/en/app-staking-next.json diff --git a/packages/apps/public/locales/en/app-staking-next.json b/packages/apps/public/locales/en/app-staking-next.json new file mode 100644 index 000000000000..aff4523c2e4b --- /dev/null +++ b/packages/apps/public/locales/en/app-staking-next.json @@ -0,0 +1,10 @@ +{ + "Accounts": "Accounts", + "Bags": "Bags", + "Overview": "Overview", + "Payouts": "Payouts", + "Pools": "Pools", + "Slashes": "Slashes", + "Targets": "Targets", + "Validator stats": "Validator stats" +} \ No newline at end of file diff --git a/packages/apps/public/locales/en/index.json b/packages/apps/public/locales/en/index.json index afeac98eba19..dcba0639aad2 100644 --- a/packages/apps/public/locales/en/index.json +++ b/packages/apps/public/locales/en/index.json @@ -31,6 +31,7 @@ "app-signing.json", "app-society.json", "app-staking-legacy.json", + "app-staking-next.json", "app-staking.json", "app-staking2.json", "app-storage.json", From cea2d33fae63907010d137402d37ba37770ad85c Mon Sep 17 00:00:00 2001 From: Arjun Porwal Date: Fri, 11 Apr 2025 10:12:49 +0530 Subject: [PATCH 08/38] refactor: relay and system staking page --- packages/page-staking-next/src/index.tsx | 152 +++--------------- .../page-staking-next/src/relay/index.tsx | 12 ++ .../page-staking-next/src/system/index.tsx | 150 +++++++++++++++++ 3 files changed, 183 insertions(+), 131 deletions(-) create mode 100644 packages/page-staking-next/src/relay/index.tsx create mode 100644 packages/page-staking-next/src/system/index.tsx diff --git a/packages/page-staking-next/src/index.tsx b/packages/page-staking-next/src/index.tsx index f10fe6590989..ce7d61b203d2 100644 --- a/packages/page-staking-next/src/index.tsx +++ b/packages/page-staking-next/src/index.tsx @@ -2,147 +2,37 @@ // SPDX-License-Identifier: Apache-2.0 import type { AppProps as Props } from '@polkadot/react-components/types'; -import type { ElectionStatus, ParaValidatorIndex, ValidatorId } from '@polkadot/types/interfaces'; -import type { BN } from '@polkadot/util'; -import React, { useMemo, useState } from 'react'; -import { Route, Routes } from 'react-router'; +import React from 'react'; -import Bags from '@polkadot/app-staking/Bags'; -import Payouts from '@polkadot/app-staking/Payouts'; -import useSortedTargets from '@polkadot/app-staking/useSortedTargets'; -import Pools from '@polkadot/app-staking2/Pools'; -import useOwnPools from '@polkadot/app-staking2/Pools/useOwnPools'; -import { styled, Tabs } from '@polkadot/react-components'; -import { useAccounts, useApi, useAvailableSlashes, useCallMulti, useFavorites, useOwnStashInfos } from '@polkadot/react-hooks'; -import { isFunction } from '@polkadot/util'; +import { styled } from '@polkadot/react-components'; +import { useApi } from '@polkadot/react-hooks'; -import { STORE_FAVS_BASE } from './constants.js'; -import { useTranslation } from './translate.js'; +import StakingRelayApp from './relay/index.js'; +import StakingSystemApp from './system/index.js'; -const HIDDEN_ACC = ['actions', 'payout']; +function StakingApp ({ basePath, className = '', onStatusChange }: Props): React.ReactElement { + const { api, apiEndpoint } = useApi(); -const OPT_MULTI = { - defaultValue: [false, undefined, {}] as [boolean, BN | undefined, Record], - transform: ([eraElectionStatus, minValidatorBond, validators, activeValidatorIndices]: [ElectionStatus | null, BN | undefined, ValidatorId[] | null, ParaValidatorIndex[] | null]): [boolean, BN | undefined, Record] => [ - !!eraElectionStatus && eraElectionStatus.isOpen, - minValidatorBond && !minValidatorBond.isZero() - ? minValidatorBond - : undefined, - validators && activeValidatorIndices - ? activeValidatorIndices.reduce((all, index) => ({ ...all, [validators[index.toNumber()].toString()]: true }), {}) - : {} - ] -}; - -function StakingApp ({ basePath, className = '' }: Props): React.ReactElement { - const { t } = useTranslation(); - const { api } = useApi(); - const [withLedger, setWithLedger] = useState(false); - const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE); - const { areAccountsLoaded, hasAccounts } = useAccounts(); - const ownStashes = useOwnStashInfos(); - const slashes = useAvailableSlashes(); - const targets = useSortedTargets(favorites, withLedger); - const [isInElection, minCommission, paraValidators] = useCallMulti<[boolean, BN | undefined, Record]>([ - api.query.staking.eraElectionStatus, - api.query.staking.minCommission, - api.query.session.validators, - (api.query.parasShared || api.query.shared)?.activeValidatorIndices - ], OPT_MULTI); - const ownPools = useOwnPools(); - - const hasStashes = useMemo( - () => hasAccounts && !!ownStashes && (ownStashes.length !== 0), - [hasAccounts, ownStashes] - ); - - const ownValidators = useMemo( - () => (ownStashes || []).filter(({ isStashValidating }) => isStashValidating), - [ownStashes] - ); - - const items = useMemo(() => [ - { - isRoot: true, - name: 'overview', - text: t('Overview') - }, - { - name: 'actions', - text: t('Accounts') - }, - hasStashes && isFunction(api.query.staking.activeEra) && { - name: 'payout', - text: t('Payouts') - }, - isFunction(api.query.nominationPools?.minCreateBond) && { - name: 'pools', - text: t('Pools') - }, - { - alias: 'returns', - name: 'targets', - text: t('Targets') - }, - hasStashes && isFunction((api.query.voterBagsList || api.query.bagsList || api.query.voterList)?.counterForListNodes) && { - name: 'bags', - text: t('Bags') - }, - { - count: slashes.reduce((count, [, unapplied]) => count + unapplied.length, 0), - name: 'slashes', - text: t('Slashes') - }, - { - hasParams: true, - name: 'query', - text: t('Validator stats') - } - ].filter((q): q is { name: string; text: string } => !!q), [api, hasStashes, slashes, t]); + // TODO: Must be removed in production + const isRelayGenesis = (api.genesisHash.toHex()) === '0x0e268177d92e92c5fa1a2374e4224da33bc9600c0318a9c25389a35f91238986'; return ( - ); } diff --git a/packages/page-staking-next/src/relay/index.tsx b/packages/page-staking-next/src/relay/index.tsx new file mode 100644 index 000000000000..f8c1307fa643 --- /dev/null +++ b/packages/page-staking-next/src/relay/index.tsx @@ -0,0 +1,12 @@ +// Copyright 2017-2025 @polkadot/app-staking-next authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { AppProps as Props } from '@polkadot/react-components/types'; + +import React from 'react'; + +function StakingApp ({ basePath, className = '' }: Props): React.ReactElement { + return

Relay Staking Page

; +} + +export default React.memo(StakingApp); diff --git a/packages/page-staking-next/src/system/index.tsx b/packages/page-staking-next/src/system/index.tsx new file mode 100644 index 000000000000..3ace9a917175 --- /dev/null +++ b/packages/page-staking-next/src/system/index.tsx @@ -0,0 +1,150 @@ +// Copyright 2017-2025 @polkadot/app-staking-next authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { AppProps as Props } from '@polkadot/react-components/types'; +import type { ElectionStatus, ParaValidatorIndex, ValidatorId } from '@polkadot/types/interfaces'; +import type { BN } from '@polkadot/util'; + +import React, { useMemo, useState } from 'react'; +import { Route, Routes } from 'react-router'; + +import Bags from '@polkadot/app-staking/Bags'; +import Payouts from '@polkadot/app-staking/Payouts'; +import useSortedTargets from '@polkadot/app-staking/useSortedTargets'; +import Pools from '@polkadot/app-staking2/Pools'; +import useOwnPools from '@polkadot/app-staking2/Pools/useOwnPools'; +import { Tabs } from '@polkadot/react-components'; +import { useAccounts, useApi, useAvailableSlashes, useCallMulti, useFavorites, useOwnStashInfos } from '@polkadot/react-hooks'; +import { isFunction } from '@polkadot/util'; + +import { STORE_FAVS_BASE } from '../constants.js'; +import { useTranslation } from '../translate.js'; + +const HIDDEN_ACC = ['actions', 'payout']; + +const OPT_MULTI = { + defaultValue: [false, undefined, {}] as [boolean, BN | undefined, Record], + transform: ([eraElectionStatus, minValidatorBond, validators, activeValidatorIndices]: [ElectionStatus | null, BN | undefined, ValidatorId[] | null, ParaValidatorIndex[] | null]): [boolean, BN | undefined, Record] => [ + !!eraElectionStatus && eraElectionStatus.isOpen, + minValidatorBond && !minValidatorBond.isZero() + ? minValidatorBond + : undefined, + validators && activeValidatorIndices + ? activeValidatorIndices.reduce((all, index) => ({ ...all, [validators[index.toNumber()].toString()]: true }), {}) + : {} + ] +}; + +function StakingApp ({ basePath, className = '' }: Props): React.ReactElement { + const { t } = useTranslation(); + const { api } = useApi(); + const [withLedger, setWithLedger] = useState(false); + const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE); + const { areAccountsLoaded, hasAccounts } = useAccounts(); + const ownStashes = useOwnStashInfos(); + const slashes = useAvailableSlashes(); + const targets = useSortedTargets(favorites, withLedger); + const [isInElection, minCommission, paraValidators] = useCallMulti<[boolean, BN | undefined, Record]>([ + api.query.staking.eraElectionStatus, + api.query.staking.minCommission, + api.query.session.validators, + (api.query.parasShared || api.query.shared)?.activeValidatorIndices + ], OPT_MULTI); + const ownPools = useOwnPools(); + + const hasStashes = useMemo( + () => hasAccounts && !!ownStashes && (ownStashes.length !== 0), + [hasAccounts, ownStashes] + ); + + const ownValidators = useMemo( + () => (ownStashes || []).filter(({ isStashValidating }) => isStashValidating), + [ownStashes] + ); + + const items = useMemo(() => [ + { + isRoot: true, + name: 'overview', + text: t('Overview') + }, + { + name: 'actions', + text: t('Accounts') + }, + hasStashes && isFunction(api.query.staking.activeEra) && { + name: 'payout', + text: t('Payouts') + }, + isFunction(api.query.nominationPools?.minCreateBond) && { + name: 'pools', + text: t('Pools') + }, + { + alias: 'returns', + name: 'targets', + text: t('Targets') + }, + hasStashes && isFunction((api.query.voterBagsList || api.query.bagsList || api.query.voterList)?.counterForListNodes) && { + name: 'bags', + text: t('Bags') + }, + { + count: slashes.reduce((count, [, unapplied]) => count + unapplied.length, 0), + name: 'slashes', + text: t('Slashes') + }, + { + hasParams: true, + name: 'query', + text: t('Validator stats') + } + ].filter((q): q is { name: string; text: string } => !!q), [api, hasStashes, slashes, t]); + + return ( + <> +