Allow sorting of staking returns (#2183)

This commit is contained in:
Jaco Greeff 2020-01-23 15:41:15 +01:00 committed by GitHub
parent fbcdac2890
commit 58e3dab9bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 33 deletions

View File

@ -43,10 +43,10 @@ export default function Validator ({ info: { accountId, bondOther, bondOwn, bond
: <FormatBalance label={<label>{t('commission')}</label>} value={validatorPayment} />
}
</td>
<td className='number'><FormatBalance label={<label>{t('total stake')}</label>} value={bondTotal} /></td>
<td className='number'><FormatBalance label={<label>{t('own stake')}</label>} value={bondOwn} /></td>
<td className='number'><FormatBalance label={<label>{t('other stake')}</label>} value={bondOther} >&nbsp;({formatNumber(numNominators)})</FormatBalance></td>
<td className='number'><FormatBalance label={<label>{t('payout (est.)')}</label>} value={rewardPayout} /></td>
<td className='number together'><FormatBalance label={<label>{t('total stake')}</label>} value={bondTotal} /></td>
<td className='number together'><FormatBalance label={<label>{t('own stake')}</label>} value={bondOwn} /></td>
<td className='number together'><FormatBalance label={<label>{t('other stake')}</label>} value={bondOther} >&nbsp;({formatNumber(numNominators)})</FormatBalance></td>
<td className='number together'><FormatBalance label={<label>{t('payout (est.)')}</label>} value={rewardPayout} /></td>
<td>
<Icon
className='staking--stats'

View File

@ -11,7 +11,7 @@ import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { registry } from '@polkadot/react-api';
import { InputBalance, Table } from '@polkadot/react-components';
import { Icon, InputBalance, Table } from '@polkadot/react-components';
import { useAccounts, useApi, useDebounce, useFavorites, useCall } from '@polkadot/react-hooks';
import { createType } from '@polkadot/types';
@ -32,11 +32,31 @@ interface AllInfo {
validators: ValidatorInfo[];
}
type SortBy = 'rankOverall' | 'rankBondOwn' | 'rankBondOther' | 'rankBondTotal' | 'rankComm';
function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
return list
.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.rankBonded = index + 1;
info.rankBondTotal = index + 1;
return info;
})
@ -59,30 +79,15 @@ function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
? cmp
: a.rankReward === b.rankReward
? a.rankPayment === b.rankPayment
? a.rankBonded === b.rankBonded
? 0
: a.rankBonded < b.rankBonded
? 1
: -1
: a.rankPayment < b.rankPayment
? 1
: -1
: a.rankReward < b.rankReward
? 1
: -1;
? b.rankBondTotal - a.rankBondTotal
: 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
);
});
}
function extractInfo (allAccounts: string[], amount: BN = new BN(0), electedInfo: DerivedStakingElected, favorites: string[], lastReward: BN): AllInfo {
@ -126,7 +131,10 @@ function extractInfo (allAccounts: string[], amount: BN = new BN(0), electedInfo
key,
commissionPer: (((prefs as ValidatorPrefs).commission?.unwrap() || new BN(0)).muln(10000).div(PERBILL).toNumber() / 100),
numNominators: exposure.others.length,
rankBonded: 0,
rankBondOther: 0,
rankBondOwn: 0,
rankBondTotal: 0,
rankComm: 0,
rankOverall: 0,
rankPayment: 0,
rankReward: 0,
@ -149,8 +157,31 @@ function Targets ({ className, sessionRewards }: Props): React.ReactElement<Prop
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE);
const [lastReward, setLastReward] = useState(new BN(0));
const [{ validators, totalStaked }, setWorkable] = useState<AllInfo>({ totalStaked: new BN(0), validators: [] });
const [{ sorted, sortBy, sortFromMax }, setSorted] = useState<{ sorted: ValidatorInfo[]; sortBy: SortBy; sortFromMax: boolean }>({ sorted: [], sortBy: 'rankOverall', sortFromMax: true });
const amount = useDebounce(_amount);
const _sort = (newSortBy: SortBy, unsorted = validators, isAdjust = true): void => {
const newSortFromMax = isAdjust && newSortBy === sortBy ? !sortFromMax : true;
setSorted({
sortBy: newSortBy,
sortFromMax: newSortFromMax,
sorted: unsorted
.sort((a, b): number =>
newSortFromMax
? a[newSortBy] - b[newSortBy]
: b[newSortBy] - a[newSortBy]
)
.sort((a, b): number =>
a.isFavorite === b.isFavorite
? 0
: a.isFavorite
? -1
: 1
)
});
};
useEffect((): void => {
if (sessionRewards && sessionRewards.length) {
const lastRewardSession = sessionRewards.filter(({ reward }): boolean => reward.gtn(0));
@ -165,17 +196,22 @@ function Targets ({ className, sessionRewards }: Props): React.ReactElement<Prop
useEffect((): void => {
if (electedInfo) {
setWorkable(extractInfo(allAccounts, amount, electedInfo, favorites, lastReward));
const { totalStaked, validators } = extractInfo(allAccounts, amount, electedInfo, favorites, lastReward);
setWorkable({ totalStaked, validators });
_sort('rankOverall', validators, false);
}
}, [allAccounts, amount, electedInfo, favorites, lastReward]);
const sortIcon = `chevron ${sortFromMax ? 'down' : 'up'}` as 'chevron up';
return (
<div className={className}>
<Summary
lastReward={lastReward}
totalStaked={totalStaked}
/>
{validators.length
{sorted.length
? (
<>
<InputBalance
@ -187,8 +223,21 @@ function Targets ({ className, sessionRewards }: Props): React.ReactElement<Prop
value={_amount}
/>
<Table>
<Table.Head>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
{['rankComm', 'rankBondTotal', 'rankBondOwn', 'rankBondOther', 'rankOverall'].map((header): React.ReactNode => (
<th
className={`isClickable ${sortBy === header && 'isSelected'}`}
key={header}
onClick={(): void => _sort(header as 'rankComm')}
>{sortBy === header ? <Icon name={sortIcon} /> : ''}</th>
))}
<th>&nbsp;</th>
</Table.Head>
<Table.Body>
{validators.map((info): React.ReactNode =>
{sorted.map((info): React.ReactNode =>
<Validator
info={info}
key={info.key}

View File

@ -18,7 +18,10 @@ export interface ValidatorInfo {
isNominating: boolean;
key: string;
numNominators: number;
rankBonded: number;
rankBondOther: number;
rankBondOwn: number;
rankBondTotal: number;
rankComm: number;
rankOverall: number;
rankPayment: number;
rankReward: number;

View File

@ -101,7 +101,7 @@ export default styled(Table)`
}
td, th {
text-align: left;
padding: 0.5rem 0.75rem;
&.all {
width: 100%;
@ -146,7 +146,8 @@ export default styled(Table)`
border: 1px solid #f2f2f2;
border-left-width: 0;
border-right-width: 0;
padding: 0.5rem 0.75rem;
text-align: left;
label {
display: block !important;
@ -181,7 +182,7 @@ export default styled(Table)`
color: #eee;
font-family: sans-serif;
font-weight: normal;
padding: 0.75rem;
text-align: right;
&:first-child {
border-top-left-radius: 0.25rem;
@ -190,6 +191,14 @@ export default styled(Table)`
&:last-child {
border-top-right-radius: 0.25rem;
}
&.isClickable {
cursor: pointer;
}
&.isSelected {
background: #888;
}
}
}
}