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:
parent
8c87b37259
commit
a668d76758
@ -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}
|
||||
|
||||
@ -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' })
|
||||
);
|
||||
|
||||
@ -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) => (
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user