diff --git a/README.md b/README.md index 8abfad787f..14f9224de5 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,8 @@ To start off, this repo (along with others in the [@polkadot](https://github.com To get started - 1. Clone the repo locally, via `git clone https://github.com/polkadot-js/apps ` -2. Ensure that you have a recent LTS version of Node.js, for development purposes [Node >=10.13.0](https://nodejs.org/en/) is recommended. -3. Ensure that you have a recent version of Yarn, for development purposes [Yarn >=1.10.1](https://yarnpkg.com/docs/install) is required. +2. Ensure that you have a recent LTS version of Node.js, for development purposes [Node >= 16](https://nodejs.org/en/) is recommended. +3. Ensure that you have a recent version of Yarn, for development purposes [Yarn >= 1.22](https://yarnpkg.com/docs/install) is required. 4. Install the dependencies by running `yarn` 5. Ready! Now you can launch the UI (assuming you have a local Polkadot Node running), via `yarn run start` 6. Access the UI via [http://localhost:3000](http://localhost:3000) diff --git a/package.json b/package.json index 7f8139f707..d78a0a7501 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,9 @@ "type": "git", "url": "https://github.com/polkadot-js/apps.git" }, + "engines": { + "node": ">=14.0.0" + }, "sideEffects": false, "version": "0.112.2-15", "workspaces": [ diff --git a/packages/page-staking/src/Targets/Summary.tsx b/packages/page-staking/src/Targets/Summary.tsx index d45039628c..3cfbe7c6d9 100644 --- a/packages/page-staking/src/Targets/Summary.tsx +++ b/packages/page-staking/src/Targets/Summary.tsx @@ -1,7 +1,6 @@ // Copyright 2017-2022 @polkadot/app-staking authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { DeriveSessionIndexes } from '@polkadot/api-derive/types'; import type { Option } from '@polkadot/types'; import type { Balance } from '@polkadot/types/interfaces'; import type { BN } from '@polkadot/util'; @@ -11,12 +10,13 @@ import React, { useMemo } from 'react'; import { CardSummary, SummaryBox } from '@polkadot/react-components'; import { useApi, useCall } from '@polkadot/react-hooks'; import { FormatBalance } from '@polkadot/react-query'; -import { BN_ONE, BN_ZERO } from '@polkadot/util'; +import { BN_ZERO } from '@polkadot/util'; import { useTranslation } from '../translate'; interface Props { avgStaked?: BN; + lastEra?: BN; lowStaked?: BN; minNominated?: BN; minNominatorBond?: BN; @@ -27,46 +27,46 @@ interface Props { totalStaked?: BN; } -const transformReward = { - transform: (optBalance: Option) => optBalance.unwrapOrDefault() +interface ProgressInfo { + hideValue: true; + total: BN; + value: BN; +} + +const OPT_REWARD = { + transform: (optBalance: Option) => + optBalance.unwrapOrDefault() }; -const transformEra = { - transform: ({ activeEra }: DeriveSessionIndexes) => activeEra.gt(BN_ZERO) ? activeEra.sub(BN_ONE) : undefined -}; +function getProgressInfo (value?: BN, total?: BN): ProgressInfo | undefined { + return value && total && !total.isZero() + ? { + hideValue: true, + total, + value + } + : undefined; +} -function Summary ({ avgStaked, lowStaked, minNominated, minNominatorBond, stakedReturn, totalIssuance, totalStaked }: Props): React.ReactElement { +function Summary ({ avgStaked, lastEra, lowStaked, minNominated, minNominatorBond, stakedReturn, totalIssuance, totalStaked }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); - const lastEra = useCall(api.derive.session.indexes, undefined, transformEra); - const lastReward = useCall(lastEra && api.query.staking.erasValidatorReward, [lastEra], transformReward); + const lastReward = useCall(lastEra && api.query.staking.erasValidatorReward, [lastEra], OPT_REWARD); const progressStake = useMemo( - () => totalIssuance && totalStaked && totalStaked.gtn(0) - ? { - hideValue: true, - total: totalIssuance, - value: totalStaked - } - : undefined, + () => getProgressInfo(totalStaked, totalIssuance), [totalIssuance, totalStaked] ); const progressAvg = useMemo( - () => avgStaked && lowStaked && avgStaked.gtn(0) - ? { - hideValue: true, - total: avgStaked, - value: lowStaked - } - : undefined, + () => getProgressInfo(lowStaked, avgStaked), [avgStaked, lowStaked] ); return (
- {totalIssuance && totalStaked?.gt(BN_ZERO) && ( + {progressStake && ( ('total staked')} progress={progressStake} @@ -86,7 +86,7 @@ function Summary ({ avgStaked, lowStaked, minNominated, minNominatorBond, staked )}
- {avgStaked?.gtn(0) && lowStaked?.gtn(0) && ( + {progressAvg && ( ('lowest / avg staked')}`} progress={progressAvg} @@ -131,7 +131,7 @@ function Summary ({ avgStaked, lowStaked, minNominated, minNominatorBond, staked )}
- {lastReward?.gtn(0) && ( + {lastReward?.gt(BN_ZERO) && ( ('last reward')}> { +function Targets ({ className = '', isInElection, nominatedBy, ownStashes, targets: { avgStaked, inflation: { stakedReturn }, lastEra, lowStaked, medianComm, minNominated, minNominatorBond, nominators, totalIssuance, totalStaked, validatorIds, validators }, toggleFavorite, toggleLedger, toggleNominatedBy }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const allSlashes = useAvailableSlashes(); @@ -362,6 +362,7 @@ function Targets ({ className = '', isInElection, nominatedBy, ownStashes, targe
| null { const { t } = useTranslation(); diff --git a/packages/page-staking/src/Overview/Address/Favorite.tsx b/packages/page-staking/src/Validators/Address/Favorite.tsx similarity index 100% rename from packages/page-staking/src/Overview/Address/Favorite.tsx rename to packages/page-staking/src/Validators/Address/Favorite.tsx diff --git a/packages/page-staking/src/Overview/Address/NominatedBy.tsx b/packages/page-staking/src/Validators/Address/NominatedBy.tsx similarity index 100% rename from packages/page-staking/src/Overview/Address/NominatedBy.tsx rename to packages/page-staking/src/Validators/Address/NominatedBy.tsx diff --git a/packages/page-staking/src/Overview/Address/StakeOther.tsx b/packages/page-staking/src/Validators/Address/StakeOther.tsx similarity index 100% rename from packages/page-staking/src/Overview/Address/StakeOther.tsx rename to packages/page-staking/src/Validators/Address/StakeOther.tsx diff --git a/packages/page-staking/src/Overview/Address/Status.tsx b/packages/page-staking/src/Validators/Address/Status.tsx similarity index 100% rename from packages/page-staking/src/Overview/Address/Status.tsx rename to packages/page-staking/src/Validators/Address/Status.tsx diff --git a/packages/page-staking/src/Overview/Address/index.tsx b/packages/page-staking/src/Validators/Address/index.tsx similarity index 100% rename from packages/page-staking/src/Overview/Address/index.tsx rename to packages/page-staking/src/Validators/Address/index.tsx diff --git a/packages/page-staking/src/Overview/Address/types.ts b/packages/page-staking/src/Validators/Address/types.ts similarity index 100% rename from packages/page-staking/src/Overview/Address/types.ts rename to packages/page-staking/src/Validators/Address/types.ts diff --git a/packages/page-staking/src/Overview/CurrentList.tsx b/packages/page-staking/src/Validators/CurrentList.tsx similarity index 100% rename from packages/page-staking/src/Overview/CurrentList.tsx rename to packages/page-staking/src/Validators/CurrentList.tsx diff --git a/packages/page-staking/src/Overview/Summary.tsx b/packages/page-staking/src/Validators/Summary.tsx similarity index 90% rename from packages/page-staking/src/Overview/Summary.tsx rename to packages/page-staking/src/Validators/Summary.tsx index 32840ae3e1..4fd33b396f 100644 --- a/packages/page-staking/src/Overview/Summary.tsx +++ b/packages/page-staking/src/Validators/Summary.tsx @@ -15,17 +15,16 @@ import { useTranslation } from '../translate'; interface Props { className?: string; - isVisible: boolean; nominators?: string[]; stakingOverview?: DeriveStakingOverview; targets: SortedTargets; } -function Summary ({ className = '', isVisible, stakingOverview, targets: { counterForNominators, inflation: { idealStake, inflation, stakedFraction }, nominators, waitingIds } }: Props): React.ReactElement { +function Summary ({ className = '', stakingOverview, targets: { counterForNominators, inflation: { idealStake, inflation, stakedFraction }, nominators, waitingIds } }: Props): React.ReactElement { const { t } = useTranslation(); return ( - +
('validators')}> {stakingOverview diff --git a/packages/page-staking/src/Overview/index.tsx b/packages/page-staking/src/Validators/index.tsx similarity index 85% rename from packages/page-staking/src/Overview/index.tsx rename to packages/page-staking/src/Validators/index.tsx index ed2abf34b1..f0f7d99d8d 100644 --- a/packages/page-staking/src/Overview/index.tsx +++ b/packages/page-staking/src/Validators/index.tsx @@ -12,11 +12,14 @@ import { Button, ToggleGroup } from '@polkadot/react-components'; import { useApi, useCall } from '@polkadot/react-hooks'; import { useTranslation } from '../translate'; +import ActionsBanner from './ActionsBanner'; import CurrentList from './CurrentList'; +import Summary from './Summary'; interface Props { className?: string; favorites: string[]; + hasAccounts: boolean; hasQueries: boolean; minCommission?: BN; nominatedBy?: NominatedByMap; @@ -29,7 +32,7 @@ interface Props { toggleNominatedBy: () => void; } -function Overview ({ className = '', favorites, hasQueries, minCommission, nominatedBy, ownStashes, paraValidators, stakingOverview, targets, toggleFavorite, toggleLedger, toggleNominatedBy }: Props): React.ReactElement { +function Overview ({ className = '', favorites, hasAccounts, hasQueries, minCommission, nominatedBy, ownStashes, paraValidators, stakingOverview, targets, toggleFavorite, toggleLedger, toggleNominatedBy }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const [intentIndex, _setIntentIndex] = useState(0); @@ -67,6 +70,13 @@ function Overview ({ className = '', favorites, hasQueries, minCommission, nomin return (
+ + {hasAccounts && (ownStashes?.length === 0) && ( + + )} ], transform: ([eraElectionStatus, minValidatorBond, validators, activeValidatorIndices]: [ElectionStatus | null, BN | undefined, ValidatorId[] | null, ParaValidatorIndex[] | null]): [boolean, BN | undefined, Record] => [ !!eraElectionStatus && eraElectionStatus.isOpen, @@ -61,7 +59,7 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement - @@ -189,12 +182,10 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement - {basePath === pathname && hasAccounts && (ownStashes?.length === 0) && ( - - )} - { - return validatorIds.reduce((result: Record, validatorId, index): Record => { - result[validatorId] = hasIdentities[index]; - - return result; - }, {}); -} - type Result = Record; +const OPT_CALL = { + transform: ([[validatorIds], hasIdentities]: [[string[]], DeriveHasIdentity[]]): Record => { + return validatorIds.reduce((result: Record, validatorId, index): Record => { + result[validatorId] = hasIdentities[index]; + + return result; + }, {}); + }, + withParamsTransform: true +}; + function useIdentitiesImpl (validatorIds: string[] = []): Result | undefined { const { api } = useApi(); - const allIdentity = useCall(api.derive.accounts.hasIdentityMulti, [validatorIds], { transform: transformIdentity, withParamsTransform: true }); + const allIdentity = useCall(api.derive.accounts.hasIdentityMulti, [validatorIds], OPT_CALL); return allIdentity; } diff --git a/packages/page-staking/src/useSortedTargets.ts b/packages/page-staking/src/useSortedTargets.ts index 2861c0d82b..92fa4c7daa 100644 --- a/packages/page-staking/src/useSortedTargets.ts +++ b/packages/page-staking/src/useSortedTargets.ts @@ -231,6 +231,7 @@ function extractBaseInfo (api: ApiPromise, allAccounts: string[], electedDerive: return { avgStaked, + lastEra: lastEraInfo.lastEra, lowStaked: activeTotals[0] || BN_ZERO, medianComm, minNominated, @@ -244,16 +245,18 @@ function extractBaseInfo (api: ApiPromise, allAccounts: string[], electedDerive: }; } -const transformEra = { +const OPT_ERA = { transform: ({ activeEra, eraLength, sessionLength }: DeriveSessionInfo): LastEra => ({ activeEra, eraLength, - lastEra: activeEra.isZero() ? BN_ZERO : activeEra.subn(1), + lastEra: activeEra.isZero() + ? BN_ZERO + : activeEra.sub(BN_ONE), sessionLength }) }; -const transformMulti = { +const OPT_MULTI = { defaultValue: {}, transform: ([historyDepth, counterForNominators, counterForValidators, optMaxNominatorsCount, optMaxValidatorsCount, minNominatorBond, minValidatorBond, totalIssuance]: [BN, BN?, BN?, Option?, Option?, BN?, BN?, BN?]): MultiResult => ({ counterForNominators, @@ -283,10 +286,10 @@ function useSortedTargetsImpl (favorites: string[], withLedger: boolean): Sorted api.query.staking.minNominatorBond, api.query.staking.minValidatorBond, api.query.balances?.totalIssuance - ], transformMulti); + ], OPT_MULTI); const electedInfo = useCall(api.derive.staking.electedInfo, [{ ...DEFAULT_FLAGS_ELECTED, withLedger }]); const waitingInfo = useCall(api.derive.staking.waitingInfo, [{ ...DEFAULT_FLAGS_WAITING, withLedger }]); - const lastEraInfo = useCall(api.derive.session.info, undefined, transformEra); + const lastEraInfo = useCall(api.derive.session.info, undefined, OPT_ERA); const baseInfo = useMemo( () => electedInfo && lastEraInfo && totalIssuance && waitingInfo