Displayed chilled nominators in waiting (#3361)
This commit is contained in:
parent
a0dda54777
commit
e498669d50
@ -2,8 +2,6 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Slash } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import { AddressMini, Expander } from '@polkadot/react-components';
|
||||
|
||||
@ -13,12 +11,11 @@ import useInactives from '../useInactives';
|
||||
interface Props {
|
||||
nominating?: string[];
|
||||
stashId: string;
|
||||
slashes: Slash[];
|
||||
}
|
||||
|
||||
function ListNominees ({ nominating, slashes, stashId }: Props): React.ReactElement<Props> {
|
||||
function ListNominees ({ nominating, stashId }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { nomsActive, nomsChilled, nomsInactive, nomsWaiting } = useInactives(stashId, slashes, nominating);
|
||||
const { nomsActive, nomsChilled, nomsInactive, nomsWaiting } = useInactives(stashId, nominating);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -202,7 +202,6 @@ function Account ({ allSlashes, className = '', info: { controllerId, destinatio
|
||||
{isStashNominating && (
|
||||
<ListNominees
|
||||
nominating={nominating}
|
||||
slashes={slashes}
|
||||
stashId={stashId}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -3,8 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DeriveSessionIndexes } from '@polkadot/api-derive/types';
|
||||
import { EraIndex, Exposure, Nominations } from '@polkadot/types/interfaces';
|
||||
import { Slash } from './types';
|
||||
import { EraIndex, Exposure, Nominations, SlashingSpans } from '@polkadot/types/interfaces';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useApi, useCall, useIsMountedRef } from '@polkadot/react-hooks';
|
||||
@ -17,18 +16,10 @@ interface Inactives {
|
||||
nomsWaiting?: string[];
|
||||
}
|
||||
|
||||
function extractState (stashId: string, slashes: Slash[], nominees: string[], activeEra: EraIndex, submittedIn: EraIndex, exposures: Exposure[]): Inactives {
|
||||
function extractState (stashId: string, slashes: Option<SlashingSpans>[], nominees: string[], activeEra: EraIndex, submittedIn: EraIndex, exposures: Exposure[]): Inactives {
|
||||
// chilled
|
||||
const nomsChilled = nominees.filter((nominee) =>
|
||||
slashes.some(({ era, slashes }) =>
|
||||
submittedIn.lt(era) && (
|
||||
slashes
|
||||
.filter(({ validator }) => validator.eq(nominee))
|
||||
.some(({ others }) =>
|
||||
others.some(([accountId]) => accountId.eq(stashId))
|
||||
)
|
||||
)
|
||||
)
|
||||
const nomsChilled = nominees.filter((_, index) =>
|
||||
slashes[index].isSome && slashes[index].unwrap().lastNonzeroSlash.gte(submittedIn)
|
||||
);
|
||||
|
||||
// first a blanket find of nominations not in the active set
|
||||
@ -64,7 +55,7 @@ function extractState (stashId: string, slashes: Slash[], nominees: string[], ac
|
||||
};
|
||||
}
|
||||
|
||||
export default function useInactives (stashId: string, slashes: Slash[], nominees?: string[]): Inactives {
|
||||
export default function useInactives (stashId: string, nominees?: string[]): Inactives {
|
||||
const { api } = useApi();
|
||||
const mountedRef = useIsMountedRef();
|
||||
const [state, setState] = useState<Inactives>({});
|
||||
@ -76,12 +67,19 @@ export default function useInactives (stashId: string, slashes: Slash[], nominee
|
||||
if (mountedRef.current && nominees && nominees.length && indexes) {
|
||||
api
|
||||
.queryMulti(
|
||||
[[api.query.staking.nominators, stashId] as any].concat(
|
||||
api.query.staking.erasStakers
|
||||
? nominees.map((id) => [api.query.staking.erasStakers, [indexes.activeEra, id]])
|
||||
: nominees.map((id) => [api.query.staking.stakers, id])
|
||||
),
|
||||
([optNominators, ...exposures]: [Option<Nominations>, ...Exposure[]]): void => {
|
||||
[[api.query.staking.nominators, stashId] as any]
|
||||
.concat(
|
||||
api.query.staking.erasStakers
|
||||
? nominees.map((id) => [api.query.staking.erasStakers, [indexes.activeEra, id]])
|
||||
: nominees.map((id) => [api.query.staking.stakers, id])
|
||||
)
|
||||
.concat(
|
||||
nominees.map((id) => [api.query.staking.slashingSpans, id])
|
||||
),
|
||||
([optNominators, ...exposuresAndSpans]: [Option<Nominations>, ...(Exposure | Option<SlashingSpans>)[]]): void => {
|
||||
const exposures = exposuresAndSpans.slice(0, nominees.length) as Exposure[];
|
||||
const slashes = exposuresAndSpans.slice(nominees.length) as Option<SlashingSpans>[];
|
||||
|
||||
mountedRef.current && setState(
|
||||
extractState(stashId, slashes, nominees, indexes.activeEra, optNominators.unwrapOrDefault().submittedIn, exposures)
|
||||
);
|
||||
@ -95,7 +93,7 @@ export default function useInactives (stashId: string, slashes: Slash[], nominee
|
||||
return (): void => {
|
||||
unsub && unsub();
|
||||
};
|
||||
}, [api, indexes, mountedRef, nominees, slashes, stashId]);
|
||||
}, [api, indexes, mountedRef, nominees, stashId]);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -2,32 +2,70 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import React from 'react';
|
||||
import { EraIndex, SlashingSpans } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { AddressMini, Expander, Spinner } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../../translate';
|
||||
|
||||
interface Props {
|
||||
nominators?: [string, number][];
|
||||
nominators?: [string, EraIndex, number][];
|
||||
slashingSpans?: SlashingSpans | null;
|
||||
}
|
||||
|
||||
function NominatedBy ({ nominators }: Props): React.ReactElement<Props> {
|
||||
interface Chilled {
|
||||
active: string[];
|
||||
chilled: string[];
|
||||
}
|
||||
|
||||
function NominatedBy ({ nominators, slashingSpans }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [{ active, chilled }, setChilled] = useState<Chilled>({ active: [], chilled: [] });
|
||||
|
||||
useEffect((): void => {
|
||||
if (nominators) {
|
||||
const chilled = slashingSpans
|
||||
? nominators
|
||||
.filter(([, submittedIn]) => submittedIn.lte(slashingSpans.lastNonzeroSlash))
|
||||
.map(([who]) => who)
|
||||
: [];
|
||||
const active = nominators
|
||||
.filter(([who]) => !chilled.includes(who))
|
||||
.map(([who]) => who);
|
||||
|
||||
setChilled({ active, chilled });
|
||||
}
|
||||
}, [nominators, slashingSpans]);
|
||||
|
||||
return (
|
||||
<td className='start all'>
|
||||
{nominators
|
||||
? (nominators.length !== 0 && (
|
||||
<Expander summary={t<string>('Nominations ({{count}})', { replace: { count: formatNumber(nominators.length) } })}>
|
||||
{nominators.map(([who]): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who}
|
||||
value={who}
|
||||
/>
|
||||
? (
|
||||
<>
|
||||
{active.length !== 0 && (
|
||||
<Expander summary={t<string>('Nominations ({{count}})', { replace: { count: formatNumber(active.length) } })}>
|
||||
{active.map((who): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
</Expander>
|
||||
))
|
||||
{chilled.length !== 0 && (
|
||||
<Expander summary={t<string>('Chilled ({{count}})', { replace: { count: formatNumber(chilled.length) } })}>
|
||||
{chilled.map((who): React.ReactNode =>
|
||||
<AddressMini
|
||||
key={who}
|
||||
value={who}
|
||||
/>
|
||||
)}
|
||||
</Expander>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
: <Spinner variant='mini' />
|
||||
}
|
||||
</td>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Balance } from '@polkadot/types/interfaces';
|
||||
import { Balance, EraIndex, SlashingSpans } from '@polkadot/types/interfaces';
|
||||
import { DeriveAccountInfo, DeriveStakingQuery } from '@polkadot/api-derive/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
@ -11,6 +11,7 @@ import { AddressSmall, Icon } from '@polkadot/react-components';
|
||||
import { checkVisibility } from '@polkadot/react-components/util';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { Option } from '@polkadot/types';
|
||||
|
||||
import Favorite from './Favorite';
|
||||
import NominatedBy from './NominatedBy';
|
||||
@ -26,7 +27,7 @@ interface Props {
|
||||
isFavorite: boolean;
|
||||
isMain?: boolean;
|
||||
lastBlock?: string;
|
||||
nominatedBy?: [string, number][];
|
||||
nominatedBy?: [string, EraIndex, number][];
|
||||
onlineCount?: false | number;
|
||||
onlineMessage?: boolean;
|
||||
points?: string;
|
||||
@ -74,6 +75,9 @@ function Address ({ address, className = '', filterName, hasQueries, isElected,
|
||||
const { api } = useApi();
|
||||
const accountInfo = useCall<DeriveAccountInfo>(api.derive.accounts.info, [address]);
|
||||
const stakingInfo = useCall<DeriveStakingQuery>(api.derive.staking.query, [address]);
|
||||
const slashingSpans = useCall<SlashingSpans | null>(!isMain && api.query.staking.slashingSpans, [address], {
|
||||
transform: (opt: Option<SlashingSpans>) => opt.unwrapOr(null)
|
||||
});
|
||||
const [{ commission, nominators, stakeOther, stakeOwn }, setStakingState] = useState<StakingState>({ nominators: [] });
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
|
||||
@ -123,7 +127,12 @@ function Address ({ address, className = '', filterName, hasQueries, isElected,
|
||||
stakeOther={stakeOther}
|
||||
/>
|
||||
)
|
||||
: <NominatedBy nominators={nominatedBy} />
|
||||
: (
|
||||
<NominatedBy
|
||||
nominators={nominatedBy}
|
||||
slashingSpans={slashingSpans}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<td className='number'>
|
||||
{stakeOwn?.gtn(0) && (
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DeriveHeartbeats, DeriveStakingOverview } from '@polkadot/api-derive/types';
|
||||
import { AccountId, Nominations } from '@polkadot/types/interfaces';
|
||||
import { AccountId, EraIndex, Nominations } from '@polkadot/types/interfaces';
|
||||
import { Authors } from '@polkadot/react-query/BlockAuthors';
|
||||
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
@ -69,14 +69,15 @@ function getFiltered (stakingOverview: DeriveStakingOverview, favorites: string[
|
||||
};
|
||||
}
|
||||
|
||||
function extractNominators (nominations: [StorageKey, Option<Nominations>][]): Record<string, [string, number][]> {
|
||||
return nominations.reduce((mapped: Record<string, [string, number][]>, [key, optNoms]) => {
|
||||
function extractNominators (nominations: [StorageKey, Option<Nominations>][]): Record<string, [string, EraIndex, number][]> {
|
||||
return nominations.reduce((mapped: Record<string, [string, EraIndex, number][]>, [key, optNoms]) => {
|
||||
if (optNoms.isSome) {
|
||||
const nominatorId = key.args[0].toString();
|
||||
const { submittedIn, targets } = optNoms.unwrap();
|
||||
|
||||
optNoms.unwrap().targets.forEach((_validatorId, index): void => {
|
||||
targets.forEach((_validatorId, index): void => {
|
||||
const validatorId = _validatorId.toString();
|
||||
const info: [string, number] = [nominatorId, index + 1];
|
||||
const info: [string, EraIndex, number] = [nominatorId, submittedIn, index + 1];
|
||||
|
||||
if (!mapped[validatorId]) {
|
||||
mapped[validatorId] = [info];
|
||||
@ -98,7 +99,7 @@ function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOvervi
|
||||
const nominators = useCall<[StorageKey, Option<Nominations>][]>(isIntentions && api.query.staking.nominators.entries as any, []);
|
||||
const [{ elected, validators, waiting }, setFiltered] = useState<Filtered>({});
|
||||
const [nameFilter, setNameFilter] = useState<string>('');
|
||||
const [nominatedBy, setNominatedBy] = useState<Record<string, [string, number][]> | null>();
|
||||
const [nominatedBy, setNominatedBy] = useState<Record<string, [string, EraIndex, number][]> | null>();
|
||||
const [withIdentity, setWithIdentity] = useState(false);
|
||||
|
||||
// we have a very large list, so we use a loading delay
|
||||
|
||||
Loading…
Reference in New Issue
Block a user