Display unique nominators (#2252)
This commit is contained in:
parent
e041701a8f
commit
97c3b2df3e
@ -31,6 +31,7 @@ interface Props {
|
||||
lastBlock?: string;
|
||||
myAccounts: string[];
|
||||
points?: Points;
|
||||
setNominators?: (nominators: string[]) => void;
|
||||
toggleFavorite: (accountId: string) => void;
|
||||
withNominations?: boolean;
|
||||
}
|
||||
@ -113,7 +114,7 @@ function checkVisibility (api: ApiPromise, address: string, filterName: string,
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
export default function Address ({ address, className, filter, filterName, hasQueries, heartbeat, isAuthor, isElected, isFavorite, lastBlock, myAccounts, points, toggleFavorite, withNominations }: Props): React.ReactElement<Props> | null {
|
||||
export default function Address ({ address, className, filter, filterName, hasQueries, heartbeat, isAuthor, isElected, isFavorite, lastBlock, myAccounts, points, setNominators, toggleFavorite, withNominations }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
// FIXME Any horrors, caused by derive type mismatches
|
||||
@ -124,9 +125,12 @@ export default function Address ({ address, className, filter, filterName, hasQu
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
|
||||
useEffect((): void => {
|
||||
stakingInfo && setStakingState(
|
||||
expandInfo(stakingInfo, myAccounts, withNominations)
|
||||
);
|
||||
if (stakingInfo) {
|
||||
const info = expandInfo(stakingInfo, myAccounts, withNominations);
|
||||
|
||||
setNominators && setNominators(info.nominators.map(([who]): string => who.toString()));
|
||||
setStakingState(info);
|
||||
}
|
||||
}, [stakingInfo]);
|
||||
|
||||
useEffect((): void => {
|
||||
|
||||
@ -23,6 +23,7 @@ interface Props {
|
||||
lastAuthors?: string[];
|
||||
next: string[];
|
||||
recentlyOnline?: DerivedHeartbeats;
|
||||
setNominators: (nominators: string[]) => void;
|
||||
stakingOverview?: DerivedStakingOverview;
|
||||
}
|
||||
|
||||
@ -64,7 +65,7 @@ function reduceDetails (state: Record<string, AddressDetails>, _details: Address
|
||||
}, { ...state });
|
||||
}
|
||||
|
||||
export default function CurrentList ({ authorsMap, hasQueries, isIntentions, isVisible, lastAuthors, next, recentlyOnline, stakingOverview }: Props): React.ReactElement<Props> | null {
|
||||
export default function CurrentList ({ authorsMap, hasQueries, isIntentions, isVisible, lastAuthors, next, recentlyOnline, setNominators, stakingOverview }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { allAccounts } = useAccounts();
|
||||
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE);
|
||||
@ -137,6 +138,7 @@ export default function CurrentList ({ authorsMap, hasQueries, isIntentions, isV
|
||||
? addressDetails[address] && addressDetails[address].points
|
||||
: undefined
|
||||
}
|
||||
setNominators={isIntentions ? undefined : setNominators}
|
||||
toggleFavorite={toggleFavorite}
|
||||
/>
|
||||
));
|
||||
|
||||
@ -16,11 +16,12 @@ interface Props {
|
||||
className?: string;
|
||||
isVisible: boolean;
|
||||
next: string[];
|
||||
nominators: string[];
|
||||
stakingOverview?: DerivedStakingOverview;
|
||||
style?: any;
|
||||
}
|
||||
|
||||
function Summary ({ className, isVisible, next, stakingOverview, style }: Props): React.ReactElement<Props> {
|
||||
function Summary ({ className, isVisible, next, nominators, stakingOverview, style }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { lastBlockAuthors, lastBlockNumber } = useContext(BlockAuthorsContext);
|
||||
|
||||
@ -35,11 +36,16 @@ function Summary ({ className, isVisible, next, stakingOverview, style }: Props)
|
||||
{stakingOverview.validators.length}{`/${stakingOverview.validatorCount.toString()}`}
|
||||
</CardSummary>
|
||||
)}
|
||||
{next && (
|
||||
{next && next.length !== 0 && (
|
||||
<CardSummary label={t('waiting')}>
|
||||
{next.length}
|
||||
</CardSummary>
|
||||
)}
|
||||
{nominators.length !== 0 && (
|
||||
<CardSummary label={t('nominators')}>
|
||||
{nominators.length}
|
||||
</CardSummary>
|
||||
)}
|
||||
</section>
|
||||
<section>
|
||||
<CardSummary
|
||||
|
||||
@ -16,10 +16,11 @@ interface Props extends BareProps {
|
||||
isVisible: boolean;
|
||||
recentlyOnline?: DerivedHeartbeats;
|
||||
next: string[];
|
||||
setNominators: (nominators: string[]) => void;
|
||||
stakingOverview?: DerivedStakingOverview;
|
||||
}
|
||||
|
||||
export default function Overview ({ hasQueries, isVisible, className, recentlyOnline, next, stakingOverview }: Props): React.ReactElement<Props> {
|
||||
export default function Overview ({ hasQueries, isVisible, className, recentlyOnline, next, setNominators, stakingOverview }: Props): React.ReactElement<Props> {
|
||||
const { pathname } = useLocation();
|
||||
const { byAuthor, lastBlockAuthors } = useContext(BlockAuthorsContext);
|
||||
const isIntentions = pathname !== '/staking';
|
||||
@ -34,6 +35,7 @@ export default function Overview ({ hasQueries, isVisible, className, recentlyOn
|
||||
lastAuthors={lastBlockAuthors}
|
||||
next={next}
|
||||
recentlyOnline={recentlyOnline}
|
||||
setNominators={setNominators}
|
||||
stakingOverview={stakingOverview}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -14,6 +14,8 @@ import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
lastReward: BN;
|
||||
numNominators: number;
|
||||
numValidators: number;
|
||||
totalStaked: BN;
|
||||
}
|
||||
|
||||
@ -21,7 +23,7 @@ interface StakeInfo {
|
||||
percentage: string;
|
||||
}
|
||||
|
||||
export default function Summary ({ lastReward, totalStaked }: Props): React.ReactElement<Props> {
|
||||
export default function Summary ({ lastReward, numNominators, numValidators, totalStaked }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const totalInsurance = useCall<Balance>(api.query.balances.totalIssuance, []);
|
||||
@ -55,6 +57,9 @@ export default function Summary ({ lastReward, totalStaked }: Props): React.Reac
|
||||
<CardSummary label={t('staked')}>
|
||||
{percentage}
|
||||
</CardSummary>
|
||||
<CardSummary label={t('validators/nominators')}>
|
||||
{numValidators}/{numNominators}
|
||||
</CardSummary>
|
||||
<CardSummary label={t('last reward')}>
|
||||
<FormatBalance
|
||||
value={lastReward}
|
||||
|
||||
@ -28,6 +28,7 @@ interface Props {
|
||||
}
|
||||
|
||||
interface AllInfo {
|
||||
nominators: string[];
|
||||
totalStaked: BN;
|
||||
validators: ValidatorInfo[];
|
||||
}
|
||||
@ -91,6 +92,7 @@ function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
|
||||
}
|
||||
|
||||
function extractInfo (allAccounts: string[], amount: BN = new BN(0), electedInfo: DerivedStakingElected, favorites: string[], lastReward: BN): AllInfo {
|
||||
const nominators: string[] = [];
|
||||
let totalStaked = new BN(0);
|
||||
const perValidatorReward = lastReward.divn(electedInfo.info.length);
|
||||
const validators = sortValidators(
|
||||
@ -114,7 +116,13 @@ function extractInfo (allAccounts: string[], amount: BN = new BN(0), electedInfo
|
||||
? amount.mul(rewardSplit).div(amount.add(bondTotal))
|
||||
: new BN(0);
|
||||
const isNominating = exposure.others.reduce((isNominating, indv): boolean => {
|
||||
return isNominating || allAccounts.includes(indv.who.toString());
|
||||
const nominator = indv.who.toString();
|
||||
|
||||
if (!nominators.includes(nominator)) {
|
||||
nominators.push(nominator);
|
||||
}
|
||||
|
||||
return isNominating || allAccounts.includes(nominator);
|
||||
}, allAccounts.includes(key));
|
||||
|
||||
totalStaked = totalStaked.add(bondTotal);
|
||||
@ -145,7 +153,7 @@ function extractInfo (allAccounts: string[], amount: BN = new BN(0), electedInfo
|
||||
})
|
||||
);
|
||||
|
||||
return { totalStaked, validators };
|
||||
return { nominators, totalStaked, validators };
|
||||
}
|
||||
|
||||
function Targets ({ className, sessionRewards }: Props): React.ReactElement<Props> {
|
||||
@ -156,7 +164,7 @@ function Targets ({ className, sessionRewards }: Props): React.ReactElement<Prop
|
||||
const electedInfo = useCall<DerivedStakingElected>(api.derive.staking.electedInfo, []);
|
||||
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE);
|
||||
const [lastReward, setLastReward] = useState(new BN(0));
|
||||
const [{ validators, totalStaked }, setWorkable] = useState<AllInfo>({ totalStaked: new BN(0), validators: [] });
|
||||
const [{ nominators, validators, totalStaked }, setWorkable] = useState<AllInfo>({ nominators: [], totalStaked: new BN(0), validators: [] });
|
||||
const [{ sorted, sortBy, sortFromMax }, setSorted] = useState<{ sorted: ValidatorInfo[]; sortBy: SortBy; sortFromMax: boolean }>({ sorted: [], sortBy: 'rankOverall', sortFromMax: true });
|
||||
const amount = useDebounce(_amount);
|
||||
|
||||
@ -196,9 +204,9 @@ function Targets ({ className, sessionRewards }: Props): React.ReactElement<Prop
|
||||
|
||||
useEffect((): void => {
|
||||
if (electedInfo) {
|
||||
const { totalStaked, validators } = extractInfo(allAccounts, amount, electedInfo, favorites, lastReward);
|
||||
const { nominators, totalStaked, validators } = extractInfo(allAccounts, amount, electedInfo, favorites, lastReward);
|
||||
|
||||
setWorkable({ totalStaked, validators });
|
||||
setWorkable({ nominators, totalStaked, validators });
|
||||
_sort('rankOverall', validators, false);
|
||||
}
|
||||
}, [allAccounts, amount, electedInfo, favorites, lastReward]);
|
||||
@ -207,6 +215,8 @@ function Targets ({ className, sessionRewards }: Props): React.ReactElement<Prop
|
||||
<div className={className}>
|
||||
<Summary
|
||||
lastReward={lastReward}
|
||||
numNominators={nominators.length}
|
||||
numValidators={validators.length}
|
||||
totalStaked={totalStaked}
|
||||
/>
|
||||
{sorted.length
|
||||
|
||||
@ -6,7 +6,7 @@ import { DerivedHeartbeats, DerivedStakingOverview } from '@polkadot/api-derive/
|
||||
import { AppProps as Props } from '@polkadot/react-components/types';
|
||||
import { AccountId } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useReducer, useState } from 'react';
|
||||
import { Route, Switch } from 'react-router';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
@ -24,6 +24,10 @@ import { MAX_SESSIONS } from './constants';
|
||||
import { useTranslation } from './translate';
|
||||
import useSessionRewards from './useSessionRewards';
|
||||
|
||||
function reduceNominators (nominators: string[], additional: string[]): string[] {
|
||||
return nominators.concat(...additional.filter((nominator): boolean => !nominators.includes(nominator)));
|
||||
}
|
||||
|
||||
function StakingApp ({ basePath, className }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
@ -39,6 +43,7 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
const stakingOverview = useCall<DerivedStakingOverview>(api.derive.staking.overview, []);
|
||||
const sessionRewards = useSessionRewards(MAX_SESSIONS);
|
||||
const hasQueries = hasAccounts && !!(api.query.imOnline?.authoredBlocks);
|
||||
const [nominators, dispatchNominators] = useReducer(reduceNominators, [] as string[]);
|
||||
const items = useMemo(() => [
|
||||
{
|
||||
isRoot: true,
|
||||
@ -89,6 +94,7 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
<Summary
|
||||
isVisible={pathname === basePath}
|
||||
next={next}
|
||||
nominators={nominators}
|
||||
stakingOverview={stakingOverview}
|
||||
/>
|
||||
<Switch>
|
||||
@ -111,6 +117,7 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
isVisible={[basePath, `${basePath}/waiting`].includes(pathname)}
|
||||
recentlyOnline={recentlyOnline}
|
||||
next={next}
|
||||
setNominators={dispatchNominators}
|
||||
stakingOverview={stakingOverview}
|
||||
/>
|
||||
</main>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user