Split waiting/inactive nominees (#2655)
This commit is contained in:
parent
215395f31d
commit
0d11423ea3
62
packages/page-staking/src/Actions/Account/ListNominees.tsx
Normal file
62
packages/page-staking/src/Actions/Account/ListNominees.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2017-2020 @polkadot/app-staking authors & contributors
|
||||
// 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 { AddressMini, Expander } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../../translate';
|
||||
import useInactives from '../useInactives';
|
||||
|
||||
interface Props {
|
||||
nominating?: string[];
|
||||
stashId: string;
|
||||
}
|
||||
|
||||
function ListNominees ({ nominating, stashId }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { nomsActive, nomsInactive, nomsWaiting } = useInactives(stashId, nominating);
|
||||
|
||||
return (
|
||||
<>
|
||||
{nomsActive && nomsActive.length !== 0 && (
|
||||
<Expander summary={t('Active nominations ({{count}})', { replace: { count: nomsActive.length } })}>
|
||||
{nomsActive.map((nomineeId, index): React.ReactNode => (
|
||||
<AddressMini
|
||||
key={index}
|
||||
value={nomineeId}
|
||||
withBalance={false}
|
||||
withBonded
|
||||
/>
|
||||
))}
|
||||
</Expander>
|
||||
)}
|
||||
{nomsInactive && nomsInactive.length !== 0 && (
|
||||
<Expander summary={t('Inactive nominations ({{count}})', { replace: { count: nomsInactive.length } })}>
|
||||
{nomsInactive.map((nomineeId, index): React.ReactNode => (
|
||||
<AddressMini
|
||||
key={index}
|
||||
value={nomineeId}
|
||||
withBalance={false}
|
||||
withBonded
|
||||
/>
|
||||
))}
|
||||
</Expander>
|
||||
)}
|
||||
{nomsWaiting && nomsWaiting.length !== 0 && (
|
||||
<Expander summary={t('Waiting nominations ({{count}})', { replace: { count: nomsWaiting.length } })}>
|
||||
{nomsWaiting.map((nomineeId, index): React.ReactNode => (
|
||||
<AddressMini
|
||||
key={index}
|
||||
value={nomineeId}
|
||||
withBalance={false}
|
||||
withBonded
|
||||
/>
|
||||
))}
|
||||
</Expander>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(ListNominees);
|
||||
@ -7,15 +7,15 @@ import { EraIndex } from '@polkadot/types/interfaces';
|
||||
import { StakerState } from '@polkadot/react-hooks/types';
|
||||
import { SortedTargets } from '../../types';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { AddressInfo, AddressMini, AddressSmall, Button, Expander, Menu, Popup, StakingBonded, StakingRedeemable, StakingUnbonding, TxButton } from '@polkadot/react-components';
|
||||
import { AddressInfo, AddressMini, AddressSmall, Button, Menu, Popup, StakingBonded, StakingRedeemable, StakingUnbonding, TxButton } from '@polkadot/react-components';
|
||||
import { useApi, useCall, useToggle } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../../translate';
|
||||
import useInactives from '../useInactives';
|
||||
import BondExtra from './BondExtra';
|
||||
import InjectKeys from './InjectKeys';
|
||||
import ListNominees from './ListNominees';
|
||||
import Nominate from './Nominate';
|
||||
import SetControllerAccount from './SetControllerAccount';
|
||||
import SetRewardDestination from './SetRewardDestination';
|
||||
@ -39,8 +39,6 @@ function Account ({ className, info: { controllerId, destination, destinationId,
|
||||
const { api } = useApi();
|
||||
const balancesAll = useCall<DeriveBalancesAll>(api.derive.balances.all, [stashId]);
|
||||
const stakingAccount = useCall<DeriveStakingAccount>(api.derive.staking.account, [stashId]);
|
||||
const [activeNoms, setActiveNoms] = useState<string[]>([]);
|
||||
const inactiveNoms = useInactives(stashId, nominating);
|
||||
const [isBondExtraOpen, toggleBondExtra] = useToggle();
|
||||
const [isInjectOpen, toggleInject] = useToggle();
|
||||
const [isNominateOpen, toggleNominate] = useToggle();
|
||||
@ -51,12 +49,6 @@ function Account ({ className, info: { controllerId, destination, destinationId,
|
||||
const [isUnbondOpen, toggleUnbond] = useToggle();
|
||||
const [isValidateOpen, toggleValidate] = useToggle();
|
||||
|
||||
useEffect((): void => {
|
||||
nominating && setActiveNoms(
|
||||
nominating.filter((id): boolean => !inactiveNoms.includes(id))
|
||||
);
|
||||
}, [inactiveNoms, nominating]);
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
<td className='address'>
|
||||
@ -141,32 +133,10 @@ function Account ({ className, info: { controllerId, destination, destinationId,
|
||||
: (
|
||||
<td className='all'>
|
||||
{isStashNominating && (
|
||||
<>
|
||||
{activeNoms.length !== 0 && (
|
||||
<Expander summary={t('Active nominations ({{count}})', { replace: { count: activeNoms.length } })}>
|
||||
{activeNoms.map((nomineeId, index): React.ReactNode => (
|
||||
<AddressMini
|
||||
key={index}
|
||||
value={nomineeId}
|
||||
withBalance={false}
|
||||
withBonded
|
||||
/>
|
||||
))}
|
||||
</Expander>
|
||||
)}
|
||||
{inactiveNoms.length !== 0 && (
|
||||
<Expander summary={t('Inactive nominations ({{count}})', { replace: { count: inactiveNoms.length } })}>
|
||||
{inactiveNoms.map((nomineeId, index): React.ReactNode => (
|
||||
<AddressMini
|
||||
key={index}
|
||||
value={nomineeId}
|
||||
withBalance={false}
|
||||
withBonded
|
||||
/>
|
||||
))}
|
||||
</Expander>
|
||||
)}
|
||||
</>
|
||||
<ListNominees
|
||||
nominating={nominating}
|
||||
stashId={stashId}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
)
|
||||
|
||||
@ -3,15 +3,54 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DeriveSessionIndexes } from '@polkadot/api-derive/types';
|
||||
import { Exposure } from '@polkadot/types/interfaces';
|
||||
import { EraIndex, Exposure, Nominations } from '@polkadot/types/interfaces';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useApi, useCall, useIsMountedRef } from '@polkadot/react-hooks';
|
||||
import { Option } from '@polkadot/types';
|
||||
|
||||
export default function useInactives (stashId: string, nominees?: string[]): string[] {
|
||||
interface Inactives {
|
||||
nomsActive?: string[];
|
||||
nomsInactive?: string[];
|
||||
nomsWaiting?: string[];
|
||||
}
|
||||
|
||||
function extractState (stashId: string, nominees: string[], activeEra: EraIndex, submittedIn: EraIndex, exposures: Exposure[]): Inactives {
|
||||
// first a blanket find of nominations not in the active set
|
||||
let nomsInactive = exposures
|
||||
.map((exposure, index) =>
|
||||
exposure.others.some(({ who }) => who.eq(stashId))
|
||||
? null
|
||||
: nominees[index]
|
||||
)
|
||||
.filter((inactiveId): inactiveId is string => !!inactiveId);
|
||||
|
||||
// waiting if validator is inactive or we have not submitted long enough ago
|
||||
const nomsWaiting = exposures
|
||||
.map((exposure, index) =>
|
||||
exposure.total.unwrap().isZero() || (nomsInactive.includes(nominees[index]) && activeEra.sub(submittedIn).lten(2))
|
||||
? nominees[index]
|
||||
: null
|
||||
)
|
||||
.filter((waitingId): waitingId is string => !!waitingId);
|
||||
|
||||
// filter based on all inactives
|
||||
const nomsActive = nominees.filter((nominee) => !nomsInactive.includes(nominee));
|
||||
|
||||
// inactive also contains waiting, remove those
|
||||
nomsInactive = nomsInactive.filter((inactiveId) => !nomsWaiting.includes(inactiveId));
|
||||
|
||||
return {
|
||||
nomsActive,
|
||||
nomsInactive,
|
||||
nomsWaiting
|
||||
};
|
||||
}
|
||||
|
||||
export default function useInactives (stashId: string, nominees?: string[]): Inactives {
|
||||
const { api } = useApi();
|
||||
const mountedRef = useIsMountedRef();
|
||||
const [inactives, setInactives] = useState<string[]>([]);
|
||||
const [state, setState] = useState<Inactives>({});
|
||||
const indexes = useCall<DeriveSessionIndexes>(api.derive.session.indexes, []);
|
||||
|
||||
useEffect((): () => void => {
|
||||
@ -20,18 +59,14 @@ export default function useInactives (stashId: string, nominees?: string[]): str
|
||||
if (mountedRef.current && nominees?.length && indexes) {
|
||||
api
|
||||
.queryMulti(
|
||||
api.query.staking.erasStakers
|
||||
? nominees.map((id) => [api.query.staking.erasStakers, [indexes.activeEra, id]])
|
||||
: nominees.map((id) => [api.query.staking.stakers, id]),
|
||||
(exposures: Exposure[]): void => {
|
||||
mountedRef.current && setInactives(
|
||||
exposures
|
||||
.map((exposure, index) =>
|
||||
exposure.others.some(({ who }) => who.eq(stashId))
|
||||
? null
|
||||
: nominees[index].toString()
|
||||
)
|
||||
.filter((inactiveId): inactiveId is string => !!inactiveId)
|
||||
[[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 => {
|
||||
mountedRef.current && setState(
|
||||
extractState(stashId, nominees, indexes.activeEra, optNominators.unwrapOrDefault().submittedIn, exposures)
|
||||
);
|
||||
}
|
||||
)
|
||||
@ -45,5 +80,5 @@ export default function useInactives (stashId: string, nominees?: string[]): str
|
||||
};
|
||||
}, [api, indexes, mountedRef, nominees, stashId]);
|
||||
|
||||
return inactives;
|
||||
return state;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user