Rename staking validators page to Validators (#7378)

* Rename staking validators page to Validators

* Adjust

* Dedupe summary

* Cleanups
This commit is contained in:
Jaco 2022-04-15 08:25:42 +02:00 committed by GitHub
parent 8770164425
commit ba90464cb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 75 additions and 64 deletions

View File

@ -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 <optional local path>`
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)

View File

@ -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": [

View File

@ -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<Balance>) => optBalance.unwrapOrDefault()
interface ProgressInfo {
hideValue: true;
total: BN;
value: BN;
}
const OPT_REWARD = {
transform: (optBalance: Option<Balance>) =>
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<Props> {
function Summary ({ avgStaked, lastEra, lowStaked, minNominated, minNominatorBond, stakedReturn, totalIssuance, totalStaked }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const lastEra = useCall<BN | undefined>(api.derive.session.indexes, undefined, transformEra);
const lastReward = useCall<BN>(lastEra && api.query.staking.erasValidatorReward, [lastEra], transformReward);
const lastReward = useCall<BN>(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 (
<SummaryBox>
<section className='media--800'>
{totalIssuance && totalStaked?.gt(BN_ZERO) && (
{progressStake && (
<CardSummary
label={t<string>('total staked')}
progress={progressStake}
@ -86,7 +86,7 @@ function Summary ({ avgStaked, lowStaked, minNominated, minNominatorBond, staked
)}
</section>
<section className='media--1000'>
{avgStaked?.gtn(0) && lowStaked?.gtn(0) && (
{progressAvg && (
<CardSummary
label={`${t<string>('lowest / avg staked')}`}
progress={progressAvg}
@ -131,7 +131,7 @@ function Summary ({ avgStaked, lowStaked, minNominated, minNominatorBond, staked
)}
</section>
<section>
{lastReward?.gtn(0) && (
{lastReward?.gt(BN_ZERO) && (
<CardSummary label={t<string>('last reward')}>
<FormatBalance
value={lastReward}

View File

@ -14,8 +14,8 @@ import { FormatBalance } from '@polkadot/react-query';
import { formatNumber } from '@polkadot/util';
import MaxBadge from '../MaxBadge';
import Favorite from '../Overview/Address/Favorite';
import { useTranslation } from '../translate';
import Favorite from '../Validators/Address/Favorite';
interface Props {
allSlashes?: [BN, UnappliedSlash[]][];

View File

@ -192,7 +192,7 @@ const DEFAULT_NAME = { isQueryFiltered: false, nameFilter: '' };
const DEFAULT_SORT: SortState = { sortBy: 'rankOverall', sortFromMax: true };
function Targets ({ className = '', isInElection, nominatedBy, ownStashes, targets: { avgStaked, inflation: { stakedReturn }, lowStaked, medianComm, minNominated, minNominatorBond, nominators, totalIssuance, totalStaked, validatorIds, validators }, toggleFavorite, toggleLedger, toggleNominatedBy }: Props): React.ReactElement<Props> {
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<Props> {
const { t } = useTranslation();
const { api } = useApi();
const allSlashes = useAvailableSlashes();
@ -362,6 +362,7 @@ function Targets ({ className = '', isInElection, nominatedBy, ownStashes, targe
<div className={className}>
<Summary
avgStaked={avgStaked}
lastEra={lastEra}
lowStaked={lowStaked}
minNominated={minNominated}
minNominatorBond={minNominatorBond}

View File

@ -5,7 +5,7 @@ import React from 'react';
import { MarkWarning } from '@polkadot/react-components';
import { useTranslation } from './translate';
import { useTranslation } from '../translate';
function ActionsBanner (): React.ReactElement<null> | null {
const { t } = useTranslation();

View File

@ -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<Props> {
function Summary ({ className = '', stakingOverview, targets: { counterForNominators, inflation: { idealStake, inflation, stakedFraction }, nominators, waitingIds } }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
return (
<SummaryBox className={`${className} ${isVisible ? '' : ' --hidden'}`}>
<SummaryBox className={className}>
<section>
<CardSummary label={t<string>('validators')}>
{stakingOverview

View File

@ -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<Props> {
function Overview ({ className = '', favorites, hasAccounts, hasQueries, minCommission, nominatedBy, ownStashes, paraValidators, stakingOverview, targets, toggleFavorite, toggleLedger, toggleNominatedBy }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [intentIndex, _setIntentIndex] = useState(0);
@ -67,6 +70,13 @@ function Overview ({ className = '', favorites, hasQueries, minCommission, nomin
return (
<div className={`staking--Overview ${className}`}>
<Summary
stakingOverview={stakingOverview}
targets={targets}
/>
{hasAccounts && (ownStashes?.length === 0) && (
<ActionsBanner />
)}
<Button.Group>
<ToggleGroup
onChange={setTypeIndex}

View File

@ -16,12 +16,9 @@ import { useAccounts, useApi, useAvailableSlashes, useCall, useCallMulti, useFav
import { isFunction } from '@polkadot/util';
import basicMd from './md/basic.md';
import Summary from './Overview/Summary';
import Actions from './Actions';
import ActionsBanner from './ActionsBanner';
import Bags from './Bags';
import { STORE_FAVS_BASE } from './constants';
import Overview from './Overview';
import Payouts from './Payouts';
import Pools from './Pools';
import Query from './Query';
@ -30,10 +27,11 @@ import Targets from './Targets';
import { useTranslation } from './translate';
import useNominations from './useNominations';
import useSortedTargets from './useSortedTargets';
import Validators from './Validators';
const HIDDEN_ACC = ['actions', 'payout'];
const optionsParaValidators = {
const OPT_MULTI = {
defaultValue: [false, undefined, {}] as [boolean, BN | undefined, Record<string, boolean>],
transform: ([eraElectionStatus, minValidatorBond, validators, activeValidatorIndices]: [ElectionStatus | null, BN | undefined, ValidatorId[] | null, ParaValidatorIndex[] | null]): [boolean, BN | undefined, Record<string, boolean>] => [
!!eraElectionStatus && eraElectionStatus.isOpen,
@ -61,7 +59,7 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
api.query.staking.minCommission,
api.query.session.validators,
(api.query.parasShared || api.query.shared)?.activeValidatorIndices
], optionsParaValidators);
], OPT_MULTI);
const ownStashes = useOwnStashInfos();
const slashes = useAvailableSlashes();
const targets = useSortedTargets(favorites, withLedger);
@ -142,11 +140,6 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
}
items={items}
/>
<Summary
isVisible={pathname === basePath}
stakingOverview={stakingOverview}
targets={targets}
/>
<Switch>
<Route path={`${basePath}/bags`}>
<Bags ownStashes={ownStashes} />
@ -189,12 +182,10 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
ownStashes={ownStashes}
targets={targets}
/>
{basePath === pathname && hasAccounts && (ownStashes?.length === 0) && (
<ActionsBanner />
)}
<Overview
<Validators
className={basePath === pathname ? '' : '--hidden'}
favorites={favorites}
hasAccounts={hasAccounts}
hasQueries={hasQueries}
minCommission={minCommission}
nominatedBy={nominatedBy}

View File

@ -78,6 +78,7 @@ export interface SortedTargets {
counterForValidators?: BN;
electedIds?: string[];
inflation: Inflation;
lastEra?: BN;
lowStaked?: BN;
medianComm: number;
maxNominatorsCount?: BN;

View File

@ -5,19 +5,22 @@ import type { DeriveHasIdentity } from '@polkadot/api-derive/types';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
function transformIdentity ([[validatorIds], hasIdentities]: [[string[]], DeriveHasIdentity[]]): Record<string, DeriveHasIdentity> {
return validatorIds.reduce((result: Record<string, DeriveHasIdentity>, validatorId, index): Record<string, DeriveHasIdentity> => {
result[validatorId] = hasIdentities[index];
return result;
}, {});
}
type Result = Record<string, DeriveHasIdentity>;
const OPT_CALL = {
transform: ([[validatorIds], hasIdentities]: [[string[]], DeriveHasIdentity[]]): Record<string, DeriveHasIdentity> => {
return validatorIds.reduce((result: Record<string, DeriveHasIdentity>, validatorId, index): Record<string, DeriveHasIdentity> => {
result[validatorId] = hasIdentities[index];
return result;
}, {});
},
withParamsTransform: true
};
function useIdentitiesImpl (validatorIds: string[] = []): Result | undefined {
const { api } = useApi();
const allIdentity = useCall<Result>(api.derive.accounts.hasIdentityMulti, [validatorIds], { transform: transformIdentity, withParamsTransform: true });
const allIdentity = useCall<Result>(api.derive.accounts.hasIdentityMulti, [validatorIds], OPT_CALL);
return allIdentity;
}

View File

@ -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<u32>?, Option<u32>?, 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<DeriveStakingElected>(api.derive.staking.electedInfo, [{ ...DEFAULT_FLAGS_ELECTED, withLedger }]);
const waitingInfo = useCall<DeriveStakingWaiting>(api.derive.staking.waitingInfo, [{ ...DEFAULT_FLAGS_WAITING, withLedger }]);
const lastEraInfo = useCall<LastEra>(api.derive.session.info, undefined, transformEra);
const lastEraInfo = useCall<LastEra>(api.derive.session.info, undefined, OPT_ERA);
const baseInfo = useMemo(
() => electedInfo && lastEraInfo && totalIssuance && waitingInfo