Pass targets from root (on-demand render) (#2643)
* Pass targets from root (on-demand render) * Fix type import
This commit is contained in:
parent
6823c44227
commit
5e60d4d1e2
@ -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.v
|
||||
|
||||
import { ValidatorInfo } from './types';
|
||||
import { ValidatorInfo } from '../types';
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { AddressSmall, Icon, Toggle } from '@polkadot/react-components';
|
||||
|
||||
@ -3,31 +3,26 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { StakerState } from '@polkadot/react-hooks/types';
|
||||
import { ValidatorInfo } from './types';
|
||||
import { SortedTargets, TargetSortBy, ValidatorInfo } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Icon, InputBalance, Table, Button } from '@polkadot/react-components';
|
||||
import { useDebounce } from '@polkadot/react-hooks';
|
||||
|
||||
import { MAX_NOMINATIONS } from '../constants';
|
||||
import { useTranslation } from '../translate';
|
||||
import Nominate from './Nominate';
|
||||
import Summary from './Summary';
|
||||
import Validator from './Validator';
|
||||
import useOwnNominators from './useOwnNominators';
|
||||
import useSortedTargets from './useSortedTargets';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
ownStashes?: StakerState[];
|
||||
targets: SortedTargets;
|
||||
}
|
||||
|
||||
type SortBy = 'rankOverall' | 'rankBondOwn' | 'rankBondOther' | 'rankBondTotal' | 'rankComm';
|
||||
|
||||
const MAX_NOMINATIONS = 16;
|
||||
|
||||
function sort (sortBy: SortBy, sortFromMax: boolean, validators: ValidatorInfo[]): number[] {
|
||||
function sort (sortBy: TargetSortBy, sortFromMax: boolean, validators: ValidatorInfo[]): number[] {
|
||||
return [...Array(validators.length).keys()]
|
||||
.sort((a, b) =>
|
||||
sortFromMax
|
||||
@ -41,15 +36,12 @@ function sort (sortBy: SortBy, sortFromMax: boolean, validators: ValidatorInfo[]
|
||||
);
|
||||
}
|
||||
|
||||
function Targets ({ className, ownStashes }: Props): React.ReactElement<Props> {
|
||||
function Targets ({ className, ownStashes, targets: { calcWith, lastReward, nominators, setCalcWith, toggleFavorite, totalStaked, validators } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const ownNominators = useOwnNominators(ownStashes);
|
||||
const [_amount, setAmount] = useState<BN | undefined>(new BN(1_000));
|
||||
const amount = useDebounce(_amount);
|
||||
const { lastReward, nominators, toggleFavorite, totalStaked, validators } = useSortedTargets(amount);
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const [sorted, setSorted] = useState<number[] | undefined>();
|
||||
const [{ sortBy, sortFromMax }, setSortBy] = useState<{ sortBy: SortBy; sortFromMax: boolean }>({ sortBy: 'rankOverall', sortFromMax: true });
|
||||
const [{ sortBy, sortFromMax }, setSortBy] = useState<{ sortBy: TargetSortBy; sortFromMax: boolean }>({ sortBy: 'rankOverall', sortFromMax: true });
|
||||
|
||||
useEffect((): void => {
|
||||
validators && setSorted(
|
||||
@ -58,7 +50,7 @@ function Targets ({ className, ownStashes }: Props): React.ReactElement<Props> {
|
||||
}, [sortBy, sortFromMax, validators]);
|
||||
|
||||
const _sort = useCallback(
|
||||
(newSortBy: SortBy) => setSortBy(({ sortBy, sortFromMax }) => ({
|
||||
(newSortBy: TargetSortBy) => setSortBy(({ sortBy, sortFromMax }) => ({
|
||||
sortBy: newSortBy,
|
||||
sortFromMax: newSortBy === sortBy
|
||||
? !sortFromMax
|
||||
@ -113,10 +105,10 @@ function Targets ({ className, ownStashes }: Props): React.ReactElement<Props> {
|
||||
help={t('The amount that will be used on a per-validator basis to calculate profits for that validator.')}
|
||||
isFull
|
||||
label={t('amount to use for estimation')}
|
||||
onChange={setAmount}
|
||||
value={_amount}
|
||||
onChange={setCalcWith}
|
||||
value={calcWith}
|
||||
/>
|
||||
), [_amount, t]);
|
||||
), [calcWith, setCalcWith, t]);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
// 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 { AccountId, Balance } from '@polkadot/types/interfaces';
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
export interface ValidatorInfo {
|
||||
accountId: AccountId;
|
||||
bondOther: BN;
|
||||
bondOwn: Balance;
|
||||
bondShare: number;
|
||||
bondTotal: Balance;
|
||||
commissionPer: number;
|
||||
isCommission: boolean;
|
||||
isFavorite: boolean;
|
||||
isNominating: boolean;
|
||||
key: string;
|
||||
numNominators: number;
|
||||
rankBondOther: number;
|
||||
rankBondOwn: number;
|
||||
rankBondTotal: number;
|
||||
rankComm: number;
|
||||
rankOverall: number;
|
||||
rankPayment: number;
|
||||
rankReward: number;
|
||||
rewardPayout: BN;
|
||||
rewardSplit: BN;
|
||||
validatorPayment: BN;
|
||||
}
|
||||
|
||||
export interface SortedTargets {
|
||||
lastReward?: BN;
|
||||
nominators?: string[];
|
||||
toggleFavorite: (accountId: string) => void;
|
||||
totalStaked?: BN;
|
||||
validators?: ValidatorInfo[];
|
||||
}
|
||||
@ -2,4 +2,6 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export const MAX_NOMINATIONS = 16;
|
||||
|
||||
export const STORE_FAVS_BASE = 'staking:favorites';
|
||||
|
||||
@ -22,6 +22,7 @@ import Query from './Query';
|
||||
import Summary from './Overview/Summary';
|
||||
import Targets from './Targets';
|
||||
import { useTranslation } from './translate';
|
||||
import useSortedTargets from './useSortedTargets';
|
||||
|
||||
interface Validators {
|
||||
next?: string[];
|
||||
@ -40,6 +41,7 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
const [{ next, validators }, setValidators] = useState<Validators>({});
|
||||
const allStashes = useStashIds();
|
||||
const ownStashes = useOwnStashInfos();
|
||||
const targets = useSortedTargets();
|
||||
const stakingOverview = useCall<DeriveStakingOverview>(api.derive.staking.overview, []);
|
||||
const isInElection = useCall<boolean>(api.query.staking?.eraElectionStatus, [], {
|
||||
transform: (status: ElectionStatus) => status.isOpen
|
||||
@ -120,6 +122,12 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
<Route path={[`${basePath}/query/:value`, `${basePath}/query`]}>
|
||||
<Query />
|
||||
</Route>
|
||||
<Route path={`${basePath}/targets`}>
|
||||
<Targets
|
||||
ownStashes={ownStashes}
|
||||
targets={targets}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${basePath}/waiting`}>
|
||||
<Overview
|
||||
className={`${basePath}/waiting` === pathname ? '' : 'staking--hidden'}
|
||||
@ -137,10 +145,6 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
ownStashes={ownStashes}
|
||||
validators={validators}
|
||||
/>
|
||||
<Targets
|
||||
className={pathname === `${basePath}/targets` ? '' : 'staking--hidden'}
|
||||
ownStashes={ownStashes}
|
||||
/>
|
||||
<Overview
|
||||
className={basePath === pathname ? '' : 'staking--hidden'}
|
||||
hasQueries={hasQueries}
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
|
||||
import { AccountId, Balance, BlockNumber, Hash, SessionIndex } from '@polkadot/types/interfaces';
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
export type Nominators = Record<string, string[]>;
|
||||
|
||||
export type AccountFilter = 'all' | 'controller' | 'session' | 'stash' | 'unbonded';
|
||||
@ -25,3 +27,42 @@ export interface SessionRewards {
|
||||
slashes: Slash[];
|
||||
treasury: Balance;
|
||||
}
|
||||
|
||||
interface ValidatorInfoRank {
|
||||
rankBondOther: number;
|
||||
rankBondOwn: number;
|
||||
rankBondTotal: number;
|
||||
rankComm: number;
|
||||
rankOverall: number;
|
||||
rankPayment: number;
|
||||
rankReward: number;
|
||||
}
|
||||
|
||||
export interface ValidatorInfo extends ValidatorInfoRank {
|
||||
accountId: AccountId;
|
||||
bondOther: BN;
|
||||
bondOwn: Balance;
|
||||
bondShare: number;
|
||||
bondTotal: Balance;
|
||||
commissionPer: number;
|
||||
isCommission: boolean;
|
||||
isFavorite: boolean;
|
||||
isNominating: boolean;
|
||||
key: string;
|
||||
numNominators: number;
|
||||
rewardPayout: BN;
|
||||
rewardSplit: BN;
|
||||
validatorPayment: BN;
|
||||
}
|
||||
|
||||
export type TargetSortBy = keyof ValidatorInfoRank;
|
||||
|
||||
export interface SortedTargets {
|
||||
calcWith?: BN;
|
||||
lastReward?: BN;
|
||||
nominators?: string[];
|
||||
setCalcWith: (amount?: BN) => void;
|
||||
toggleFavorite: (accountId: string) => void;
|
||||
totalStaked?: BN;
|
||||
validators?: ValidatorInfo[];
|
||||
}
|
||||
|
||||
@ -4,57 +4,41 @@
|
||||
|
||||
import { DeriveSessionIndexes, DeriveStakingElected } from '@polkadot/api-derive/types';
|
||||
import { Balance, ValidatorPrefs, ValidatorPrefsTo196 } from '@polkadot/types/interfaces';
|
||||
import { SortedTargets, ValidatorInfo } from './types';
|
||||
import { SortedTargets, TargetSortBy, ValidatorInfo } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { registry } from '@polkadot/react-api';
|
||||
import { useAccounts, useApi, useCall, useFavorites } from '@polkadot/react-hooks';
|
||||
import { useAccounts, useApi, useCall, useDebounce, useFavorites } from '@polkadot/react-hooks';
|
||||
import { Option } from '@polkadot/types';
|
||||
|
||||
import { STORE_FAVS_BASE } from '../constants';
|
||||
import { STORE_FAVS_BASE } from './constants';
|
||||
|
||||
const PERBILL = new BN(1_000_000_000);
|
||||
|
||||
function mapIndex (mapBy: TargetSortBy): (info: ValidatorInfo, index: number) => ValidatorInfo {
|
||||
return (info, index): ValidatorInfo => {
|
||||
info[mapBy] = index + 1;
|
||||
|
||||
return info;
|
||||
};
|
||||
}
|
||||
|
||||
function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
|
||||
return list
|
||||
.filter((a) => a.bondTotal.gtn(0))
|
||||
.sort((a, b): number => b.commissionPer - a.commissionPer)
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankComm = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.bondOther.cmp(a.bondOther))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankBondOther = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.bondOwn.cmp(a.bondOwn))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankBondOwn = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.bondTotal.cmp(a.bondTotal))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankBondTotal = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.validatorPayment.cmp(a.validatorPayment))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankPayment = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => a.rewardSplit.cmp(b.rewardSplit))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankReward = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b) => b.commissionPer - a.commissionPer)
|
||||
.map(mapIndex('rankComm'))
|
||||
.sort((a, b) => b.bondOther.cmp(a.bondOther))
|
||||
.map(mapIndex('rankBondOther'))
|
||||
.sort((a, b) => b.bondOwn.cmp(a.bondOwn))
|
||||
.map(mapIndex('rankBondOwn'))
|
||||
.sort((a, b) => b.bondTotal.cmp(a.bondTotal))
|
||||
.map(mapIndex('rankBondTotal'))
|
||||
.sort((a, b) => b.validatorPayment.cmp(a.validatorPayment))
|
||||
.map(mapIndex('rankPayment'))
|
||||
.sort((a, b) => a.rewardSplit.cmp(b.rewardSplit))
|
||||
.map(mapIndex('rankReward'))
|
||||
.sort((a, b): number => {
|
||||
const cmp = b.rewardPayout.cmp(a.rewardPayout);
|
||||
|
||||
@ -66,14 +50,11 @@ function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
|
||||
: b.rankPayment - a.rankPayment
|
||||
: b.rankReward - a.rankReward;
|
||||
})
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankOverall = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => a.isFavorite === b.isFavorite
|
||||
? 0
|
||||
: (a.isFavorite ? -1 : 1)
|
||||
.map(mapIndex('rankOverall'))
|
||||
.sort((a, b) =>
|
||||
a.isFavorite === b.isFavorite
|
||||
? 0
|
||||
: (a.isFavorite ? -1 : 1)
|
||||
);
|
||||
}
|
||||
|
||||
@ -142,7 +123,7 @@ function extractInfo (allAccounts: string[], amount: BN = new BN(0), electedInfo
|
||||
return { nominators, totalStaked, validators };
|
||||
}
|
||||
|
||||
export default function useSortedTargets (amount?: BN): SortedTargets {
|
||||
export default function useSortedTargets (): SortedTargets {
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE);
|
||||
@ -153,15 +134,26 @@ export default function useSortedTargets (amount?: BN): SortedTargets {
|
||||
const lastReward = useCall<BN>(lastEra && api.query.staking.erasValidatorReward, [lastEra], {
|
||||
transform: (optBalance: Option<Balance>) => optBalance.unwrapOrDefault()
|
||||
});
|
||||
const [state, setState] = useState<SortedTargets>({ toggleFavorite });
|
||||
const [calcWith, setCalcWith] = useState<BN | undefined>(new BN(1_000));
|
||||
const calcWithDebounce = useDebounce(calcWith);
|
||||
const [state, setState] = useState<SortedTargets>({ setCalcWith, toggleFavorite });
|
||||
|
||||
useEffect((): void => {
|
||||
electedInfo && setState(({ toggleFavorite }) => ({
|
||||
...extractInfo(allAccounts, amount, electedInfo, favorites, lastReward),
|
||||
electedInfo && setState(({ calcWith, setCalcWith, toggleFavorite }) => ({
|
||||
...extractInfo(allAccounts, calcWithDebounce, electedInfo, favorites, lastReward),
|
||||
calcWith,
|
||||
lastReward,
|
||||
setCalcWith,
|
||||
toggleFavorite
|
||||
}));
|
||||
}, [allAccounts, amount, electedInfo, favorites, lastReward]);
|
||||
}, [allAccounts, calcWithDebounce, electedInfo, favorites, lastReward]);
|
||||
|
||||
useEffect((): void => {
|
||||
calcWith && setState((state) => ({
|
||||
...state,
|
||||
calcWith
|
||||
}));
|
||||
}, [calcWith]);
|
||||
|
||||
return state;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user