Display unique nominators (#2252)

This commit is contained in:
Jaco Greeff 2020-02-07 14:21:30 +01:00 committed by GitHub
parent e041701a8f
commit 97c3b2df3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 51 additions and 15 deletions

View File

@ -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 => {

View File

@ -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}
/>
));

View File

@ -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

View File

@ -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>

View File

@ -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}

View File

@ -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

View File

@ -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>