Pass targets from root (on-demand render) (#2643)

* Pass targets from root (on-demand render)

* Fix type import
This commit is contained in:
Jaco Greeff 2020-04-27 23:58:28 +02:00 committed by GitHub
parent 6823c44227
commit 5e60d4d1e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 114 deletions

View File

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

View File

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

View File

@ -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[];
}

View File

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

View File

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

View File

@ -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[];
}

View File

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