recentlyOffline returns multiple entries per account (#895)

* recentlyOffline returns multiple entries per account

* ed25519 warning (help expansion)

* Use staking_ledger to map to stash
This commit is contained in:
Jaco Greeff 2019-04-06 09:24:16 +02:00 committed by GitHub
parent 8c87b37259
commit a668d76758
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 120 additions and 92 deletions

View File

@ -68,7 +68,7 @@ class Key extends React.PureComponent<Props, State> {
<Modal.Content className='ui--signer-Signer-Content'>
<InputAddress
className='medium'
help={t('Changing the key only takes effect at the start of the next session.')}
help={t('Changing the key only takes effect at the start of the next session. If validating, you should (currently) use an ed25519 key.')}
isDisabled
label={t('session key')}
value={accountId}

View File

@ -6,8 +6,9 @@ import { DerivedBalancesMap } from '@polkadot/api-derive/types';
import { I18nProps } from '@polkadot/ui-app/types';
import { Nominators, RecentlyOfflineMap } from '../types';
import BN from 'bn.js';
import React from 'react';
import { AccountId, Balance, Option } from '@polkadot/types';
import { AccountId, Balance, Option, StakingLedger } from '@polkadot/types';
import { withCall, withMulti } from '@polkadot/ui-api/with';
import { AddressMini, AddressRow } from '@polkadot/ui-app';
import keyring from '@polkadot/ui-keyring';
@ -24,7 +25,7 @@ type Props = I18nProps & {
lastBlock: string,
nominators: Nominators,
recentlyOffline: RecentlyOfflineMap,
staking_bonded?: Option<AccountId>
staking_ledger?: Option<StakingLedger>
};
type State = {
@ -36,6 +37,31 @@ class Address extends React.PureComponent<Props, State> {
badgeExpanded: false
};
render () {
const { address, balanceArray, isAuthor, lastBlock } = this.props;
return (
<article key={address}>
<AddressRow
balance={balanceArray(address)}
name={this.getDisplayName()}
value={address}
withCopy={false}
withNonce={false}
>
{this.renderNominators()}
{this.renderOffline()}
</AddressRow>
<div
className={['blockNumber', isAuthor ? 'latest' : ''].join(' ')}
key='lastBlock'
>
{isAuthor ? lastBlock : ''}
</div>
</article>
);
}
private getDisplayName = (): string | undefined => {
const { address, defaultName } = this.props;
@ -54,84 +80,68 @@ class Address extends React.PureComponent<Props, State> {
this.setState({ badgeExpanded: !badgeExpanded });
}
render () {
const { address, balanceArray, isAuthor, lastBlock, nominators, recentlyOffline, staking_bonded, t } = this.props;
const { badgeExpanded } = this.state;
private renderNominators () {
const { address, nominators, t } = this.props;
const myNominators = Object.keys(nominators).filter((nominator) =>
nominators[nominator].indexOf(address) !== -1
);
const bondedId: string | null = staking_bonded && staking_bonded.isSome
? staking_bonded.unwrap().toString()
: null;
const hasNominators = !!myNominators.length;
const isRecentlyOffline = bondedId && recentlyOffline[bondedId];
const children = (hasNominators || isRecentlyOffline) ? (
<>
<details className='staking--Account-detail'>
{myNominators.length && (
<>
<summary>
{t('Nominators ({{count}})', {
replace: {
count: myNominators.length
}
})}
</summary>
{myNominators.map((accountId) =>
<AddressMini
key={accountId.toString()}
value={accountId}
withBalance
/>
)}
</>
)}
</details>
{(bondedId && recentlyOffline[bondedId]) && (() => {
const { blockNumber, instances } = recentlyOffline[bondedId];
return (
<div
onClick={this.onClickBadge}
className={['recentlyOffline', badgeExpanded ? 'expand' : ''].join(' ')}
>
<div className='badge'>
{instances.toString()}
</div>
<div className='detail'>
{t('Reported offline {{instances}} times, last at block #{{blockNumber}}', {
replace: {
instances: instances.toString(),
blockNumber: formatNumber(blockNumber)
}
})}
</div>
</div>
);
})()}
</>
) : undefined;
if (!myNominators.length) {
return null;
}
return (
<article key={address}>
<AddressRow
balance={balanceArray(address)}
name={this.getDisplayName()}
value={address}
withCopy={false}
withNonce={false}
>
{children}
</AddressRow>
<div
className={['blockNumber', isAuthor ? 'latest' : ''].join(' ')}
key='lastBlock'
>
{isAuthor ? lastBlock : ''}
<details className='staking--Account-detail'>
<summary>
{t('Nominators ({{count}})', {
replace: {
count: myNominators.length
}
})}
</summary>
{myNominators.map((accountId) =>
<AddressMini
key={accountId.toString()}
value={accountId}
withBalance
/>
)}
</details>
);
}
private renderOffline () {
const { recentlyOffline, staking_ledger, t } = this.props;
const { badgeExpanded } = this.state;
const stashId: string | null = staking_ledger && staking_ledger.isSome
? staking_ledger.unwrap().stash.toString()
: null;
if (!stashId || !recentlyOffline[stashId]) {
return null;
}
const offline = recentlyOffline[stashId];
const count = offline.reduce((total, { count }) => total.add(count), new BN(0));
const blockNumbers = offline.map(({ blockNumber }) => `#${formatNumber(blockNumber)}`);
return (
<div
className={['recentlyOffline', badgeExpanded ? 'expand' : ''].join(' ')}
onClick={this.onClickBadge}
>
<div className='badge'>
{count.toString()}
</div>
</article>
<div className='detail'>
{t('Reported offline {{count}} times, at {{blockNumbers}}', {
replace: {
count: count.toString(),
blockNumbers: blockNumbers.join(', ')
}
})}
</div>
</div>
);
}
}
@ -139,5 +149,5 @@ class Address extends React.PureComponent<Props, State> {
export default withMulti(
Address,
translate,
withCall('query.staking.bonded', { paramName: 'address' })
withCall('query.staking.ledger', { paramName: 'address' })
);

View File

@ -24,7 +24,33 @@ type Props = I18nProps & {
staking_recentlyOffline?: RecentlyOffline
};
class CurrentList extends React.PureComponent<Props> {
type State = {
recentlyOffline: RecentlyOfflineMap
};
class CurrentList extends React.PureComponent<Props, State> {
state: State = { recentlyOffline: {} };
static getDerivedStateFromProps ({ staking_recentlyOffline = [] }: Props): State {
return {
recentlyOffline: staking_recentlyOffline.reduce(
(result, [accountId, blockNumber, count]) => {
const account = accountId.toString();
if (!result[account]) {
result[account] = [];
}
result[account].push({
blockNumber,
count
});
return result;
}, {} as RecentlyOfflineMap)
};
}
render () {
return (
<div className='validator--ValidatorsList ui--flex-medium'>
@ -67,7 +93,8 @@ class CurrentList extends React.PureComponent<Props> {
}
private renderColumn (addresses: Array<string>, defaultName: string) {
const { balances, balanceArray, chain_subscribeNewHead, nominators, staking_recentlyOffline, t } = this.props;
const { balances, balanceArray, chain_subscribeNewHead, nominators, t } = this.props;
const { recentlyOffline } = this.state;
if (addresses.length === 0) {
return (
@ -83,17 +110,6 @@ class CurrentList extends React.PureComponent<Props> {
lastAuthor = (chain_subscribeNewHead.author || '').toString();
}
const recentlyOffline: RecentlyOfflineMap = (staking_recentlyOffline || []).reduce(
(result, [accountId, blockNumber, instances]) => ({
...result,
[accountId.toString()]: {
blockNumber,
instances
}
}),
{}
);
return (
<div>
{addresses.map((address) => (

View File

@ -86,12 +86,12 @@
.recentlyOffline {
position: absolute;
bottom: 0.75rem;
font-size: 12px;
cursor: help;
display: flex;
justify-content: center;
right: 12px;
top: 88px;
right: 0.75rem;
width: 22px;
height: 22px;
padding: 0;

View File

@ -62,6 +62,8 @@ class App extends React.PureComponent<Props, State> {
}
static getDerivedStateFromProps ({ staking_controllers = [[], []], session_validators = [], staking_nominators = [[], []] }: Props): State {
console.error(staking_controllers[0].map((id) => id.toString()));
return {
intentions: staking_controllers[1].filter((optId) => optId.isSome).map((accountId) =>
accountId.unwrap().toString()

View File

@ -22,10 +22,10 @@ export type ComponentProps = {
export type RecentlyOffline = Array<[AccountId, BlockNumber, BN]>;
export type RecentlyOfflineMap = {
[s: string]: OfflineStatus
[s: string]: Array<OfflineStatus>
};
export interface OfflineStatus {
blockNumber: BlockNumber;
instances: BN;
count: BN;
}