diff --git a/packages/page-staking/src/Targets/Validator.tsx b/packages/page-staking/src/Targets/Validator.tsx index 68de5eb275..44d4965662 100644 --- a/packages/page-staking/src/Targets/Validator.tsx +++ b/packages/page-staking/src/Targets/Validator.tsx @@ -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'; diff --git a/packages/page-staking/src/Targets/index.tsx b/packages/page-staking/src/Targets/index.tsx index 0f5a839374..b065563198 100644 --- a/packages/page-staking/src/Targets/index.tsx +++ b/packages/page-staking/src/Targets/index.tsx @@ -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 { +function Targets ({ className, ownStashes, targets: { calcWith, lastReward, nominators, setCalcWith, toggleFavorite, totalStaked, validators } }: Props): React.ReactElement { const { t } = useTranslation(); const ownNominators = useOwnNominators(ownStashes); - const [_amount, setAmount] = useState(new BN(1_000)); - const amount = useDebounce(_amount); - const { lastReward, nominators, toggleFavorite, totalStaked, validators } = useSortedTargets(amount); const [selected, setSelected] = useState([]); const [sorted, setSorted] = useState(); - 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 { }, [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 { 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 (
diff --git a/packages/page-staking/src/Targets/types.ts b/packages/page-staking/src/Targets/types.ts deleted file mode 100644 index 4382633fe5..0000000000 --- a/packages/page-staking/src/Targets/types.ts +++ /dev/null @@ -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[]; -} diff --git a/packages/page-staking/src/constants.ts b/packages/page-staking/src/constants.ts index f29cd4b640..46f86d0d9a 100644 --- a/packages/page-staking/src/constants.ts +++ b/packages/page-staking/src/constants.ts @@ -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'; diff --git a/packages/page-staking/src/index.tsx b/packages/page-staking/src/index.tsx index 32d6dee67c..3e017076d7 100644 --- a/packages/page-staking/src/index.tsx +++ b/packages/page-staking/src/index.tsx @@ -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 const [{ next, validators }, setValidators] = useState({}); const allStashes = useStashIds(); const ownStashes = useOwnStashInfos(); + const targets = useSortedTargets(); const stakingOverview = useCall(api.derive.staking.overview, []); const isInElection = useCall(api.query.staking?.eraElectionStatus, [], { transform: (status: ElectionStatus) => status.isOpen @@ -120,6 +122,12 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement + + + ownStashes={ownStashes} validators={validators} /> - ; 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[]; +} diff --git a/packages/page-staking/src/Targets/useSortedTargets.ts b/packages/page-staking/src/useSortedTargets.ts similarity index 71% rename from packages/page-staking/src/Targets/useSortedTargets.ts rename to packages/page-staking/src/useSortedTargets.ts index ca17c66ade..001363c352 100644 --- a/packages/page-staking/src/Targets/useSortedTargets.ts +++ b/packages/page-staking/src/useSortedTargets.ts @@ -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(lastEra && api.query.staking.erasValidatorReward, [lastEra], { transform: (optBalance: Option) => optBalance.unwrapOrDefault() }); - const [state, setState] = useState({ toggleFavorite }); + const [calcWith, setCalcWith] = useState(new BN(1_000)); + const calcWithDebounce = useDebounce(calcWith); + const [state, setState] = useState({ 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; }