Add BondedRx component & display bonded value, refactor RecentlyOffline (#923)

* recently offline on own accounts

* wip bonded display

* RxBonded component

* travis-ci

* import mistake

* redo

* RRemove balance (overview) & index (accounts)

* Remove session.validators

* Re-add displaying only the last offline block

* Small setState cleanup (+ State in generic)
This commit is contained in:
kwingram25 2019-04-08 05:01:20 -07:00 committed by Jaco Greeff
parent 7cb6ba12a0
commit 56aef9398b
14 changed files with 257 additions and 87 deletions

View File

@ -110,6 +110,7 @@ class Creator extends React.PureComponent<Props, State> {
? address
: ''
}
withBonded
/>
{this.renderInput()}
</div>

View File

@ -43,6 +43,7 @@ class Creator extends React.PureComponent<Props, State> {
<AddressSummary
className='shrink'
value={address}
withBonded
/>
{this.renderInput()}
</div>

View File

@ -93,6 +93,7 @@ class Editor extends React.PureComponent<Props, State> {
<AddressSummary
className='shrink'
value={address || ''}
withBonded
/>
<div className='grow'>
<div className='ui--row'>

View File

@ -91,6 +91,9 @@ class Account extends React.PureComponent<Props, State> {
name={name}
value={accountId}
identIconSize={96}
withBonded
withIndex={false}
withNonce={false}
>
<div className='staking--Account-expand'>
{this.renderButtons()}
@ -205,7 +208,8 @@ class Account extends React.PureComponent<Props, State> {
<AddressMini
key={index}
value={nomineeId}
withBalance
withBalance={false}
withBonded
/>
))
}
@ -280,7 +284,7 @@ class Account extends React.PureComponent<Props, State> {
return (
<div className='staking--Account-detail'>
<label className='staking--label'>{t('stash')}</label>
<AddressMini value={stashId} />
<AddressMini withBalance={false} withBonded value={stashId} />
</div>
);
}

View File

@ -6,13 +6,14 @@ 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, StakingLedger } from '@polkadot/types';
import { withCalls, withMulti } from '@polkadot/ui-api/with';
import { AddressMini, AddressRow } from '@polkadot/ui-app';
import keyring from '@polkadot/ui-keyring';
import { formatBalance, formatNumber } from '@polkadot/util';
import { formatBalance } from '@polkadot/util';
import RecentlyOffline from '../RecentlyOffline';
import translate from '../translate';
@ -88,6 +89,8 @@ class Address extends React.PureComponent<Props, State> {
extraInfo={stashActive ? `bonded ${stashActive}` : undefined}
name={this.getDisplayName()}
value={stashId}
withBalance={false}
withBonded
withCopy={false}
withNonce={false}
>
@ -122,12 +125,6 @@ class Address extends React.PureComponent<Props, State> {
: defaultName;
}
private toggleBadge = (): void => {
const { badgeExpanded } = this.state;
this.setState({ badgeExpanded: !badgeExpanded });
}
private renderKeys () {
const { t } = this.props;
const { bondedId, sessionId } = this.state;
@ -180,7 +177,7 @@ class Address extends React.PureComponent<Props, State> {
<AddressMini
key={accountId.toString()}
value={accountId}
withBalance
withBonded
/>
)}
</details>
@ -188,35 +185,17 @@ class Address extends React.PureComponent<Props, State> {
}
private renderOffline () {
const { recentlyOffline, t } = this.props;
const { badgeExpanded, stashId } = this.state;
const { recentlyOffline } = this.props;
const { stashId } = this.state;
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.toggleBadge}
>
<div className='badge'>
{count.toString()}
</div>
<div className='detail'>
{t('Reported offline {{count}} times, last at {{blockNumber}}', {
replace: {
count: count.toString(),
blockNumber: blockNumbers[blockNumbers.length - 1]
}
})}
</div>
</div>
<RecentlyOffline offline={offline} />
);
}
}

View File

@ -85,48 +85,7 @@
}
.recentlyOffline {
position: absolute;
bottom: 0.75rem;
font-size: 12px;
cursor: help;
display: flex;
justify-content: center;
right: 0.75rem;
width: 22px;
height: 22px;
padding: 0;
background: red;
color: #eee;
border-radius: 16px;
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2);
transition: all ease .2s;
& > * {
line-height: 22px;
overflow: hidden;
transition: all ease 0.25;
}
.badge {
font-weight: bold;
width: auto;
}
.detail {
width: 0;
}
&.expand {
width: 300px;
.badge {
width: 0;
}
.detail {
width: auto;
}
}
}
}
}

View File

@ -0,0 +1,60 @@
// Copyright 2017-2019 @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 { OfflineStatus } from './types';
import { I18nProps } from '@polkadot/ui-app/types';
import BN from 'bn.js';
import React from 'react';
import { formatNumber } from '@polkadot/util';
import translate from './translate';
type Props = I18nProps & {
offline: Array<OfflineStatus>
};
type State = {
isOpen: boolean
};
class RecentlyOffline extends React.PureComponent<Props, State> {
state: State = {
isOpen: false
};
render () {
const { offline, t } = this.props;
const { isOpen } = this.state;
const count = offline.reduce((total, { count }) => total.add(count), new BN(0));
const blockNumbers = offline.map(({ blockNumber }) => `#${formatNumber(blockNumber)}`);
return (
<div
className={['staking--Account-recentlyOffline', isOpen ? 'expand' : ''].join(' ')}
onClick={this.toggleOpen}
>
<div className='badge'>
{count.toString()}
</div>
<div className='detail'>
{t('Reported offline {{count}} times, last at {{blockNumber}}', {
replace: {
count,
blockNumber: blockNumbers[blockNumbers.length - 1]
}
})}
</div>
</div>
);
}
private toggleOpen = (): void => {
this.setState(({ isOpen }) => ({
isOpen: !isOpen
}));
}
}
export default translate(RecentlyOffline);

View File

@ -131,3 +131,48 @@
margin-bottom: -0.5rem;
}
}
.staking--Account-recentlyOffline {
position: absolute;
bottom: 0.75rem;
font-size: 12px;
cursor: help;
display: flex;
justify-content: center;
right: 0.75rem;
width: 22px;
height: 22px;
padding: 0;
background: red;
color: #eee;
border-radius: 16px;
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2);
transition: all ease .2s;
& > * {
line-height: 22px;
overflow: hidden;
transition: all ease 0.25;
}
.badge {
font-weight: bold;
width: auto;
}
.detail {
width: 0;
}
&.expand {
width: 300px;
.badge {
width: 0;
}
.detail {
width: auto;
}
}
}

View File

@ -10,6 +10,7 @@ import { AccountId, AccountIndex, Address, Balance } from '@polkadot/types';
import { classes, toShortAddress } from './util';
import BalanceDisplay from './Balance';
import BondedDisplay from './Bonded';
import IdentityIcon from './IdentityIcon';
type Props = BareProps & {
@ -19,7 +20,8 @@ type Props = BareProps & {
isShort?: boolean,
value?: AccountId | AccountIndex | Address | string,
withAddress?: boolean,
withBalance?: boolean
withBalance?: boolean,
withBonded?: boolean
};
export default class AddressMini extends React.PureComponent<Props> {
@ -46,6 +48,7 @@ export default class AddressMini extends React.PureComponent<Props> {
{children}
</div>
{this.renderBalance()}
{this.renderBonded()}
</div>
);
}
@ -77,4 +80,20 @@ export default class AddressMini extends React.PureComponent<Props> {
/>
);
}
private renderBonded () {
const { value, withBonded = false } = this.props;
if (!withBonded || !value) {
return null;
}
return (
<BondedDisplay
className='ui--AddressSummary-balance'
label=''
value={value}
/>
);
}
}

View File

@ -23,7 +23,7 @@ class AddressRow extends AddressSummary {
<div className='ui--AddressRow-details'>
{this.renderAddress()}
{this.renderBalance()}
{this.renderExtra()}
{this.renderBonded()}
{this.renderNonce()}
</div>
</div>

View File

@ -12,17 +12,20 @@ import BaseIdentityIcon from '@polkadot/ui-identicon';
import { classes, toShortAddress } from './util';
import BalanceDisplay from './Balance';
import BondedDisplay from './Bonded';
import IdentityIcon from './IdentityIcon';
import translate from './translate';
export type Props = I18nProps & {
accounts_idAndIndex?: [AccountId?, AccountIndex?],
balance?: Balance | Array<Balance>,
bonded?: Balance | Array<Balance>,
children?: React.ReactNode,
extraInfo?: React.ReactNode,
name?: string,
value: AccountId | AccountIndex | Address | string | null,
withBalance?: boolean,
withBonded?: boolean,
withIndex?: boolean,
identIconSize?: number,
isShort?: boolean,
@ -50,7 +53,7 @@ class AddressSummary extends React.PureComponent<Props> {
{this.renderAccountId()}
{this.renderAccountIndex()}
{this.renderBalance()}
{this.renderExtra()}
{this.renderBonded()}
{this.renderNonce()}
</div>
{this.renderChildren()}
@ -113,10 +116,10 @@ class AddressSummary extends React.PureComponent<Props> {
}
protected renderAccountIndex () {
const { accounts_idAndIndex = [] } = this.props;
const { accounts_idAndIndex = [], withIndex = true } = this.props;
const [, accountIndex] = accounts_idAndIndex;
if (!accountIndex) {
if (!accountIndex || !withIndex) {
return null;
}
@ -151,17 +154,21 @@ class AddressSummary extends React.PureComponent<Props> {
);
}
protected renderExtra () {
const { extraInfo } = this.props;
protected renderBonded () {
const { accounts_idAndIndex = [], t, value, withBonded } = this.props;
const [_accountId] = accounts_idAndIndex;
const accountId = _accountId || value;
if (!extraInfo) {
if (!withBonded || !accountId) {
return null;
}
return (
<div className='ui--AddressSummary-extra'>
{extraInfo}
</div>
<BondedDisplay
className='ui--AddressSummary-bonded'
label={t('bonded ')}
value={accountId}
/>
);
}

View File

@ -0,0 +1,35 @@
// Copyright 2017-2019 @polkadot/ui-app 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 { BareProps } from './types';
import React from 'react';
import { AccountId, AccountIndex, Address } from '@polkadot/types';
import RxBonded from '@polkadot/ui-reactive/Bonded';
import { classes } from './util';
export type Props = BareProps & {
label?: string,
value?: AccountId | AccountIndex | Address | string | Uint8Array | null,
withLabel?: boolean
};
export default class BondedDisplay extends React.PureComponent<Props> {
render () {
const { value, className, label, style } = this.props;
if (!value) {
return null;
}
return (
<RxBonded
className={classes('ui--Bonded', className)}
label={label}
value={value}
style={style}
/>
);
}
}

View File

@ -63,7 +63,8 @@ header .ui--Button-Group {
margin-right: 0.5rem;
}
.ui--AddressMini .ui--Balance {
.ui--AddressMini .ui--Balance,
.ui--AddressMini .ui--Bonded {
font-size: 0.75rem;
margin-top: -0.5rem;
text-align: right;
@ -113,13 +114,20 @@ header .ui--Button-Group {
.ui--Balance,
.ui--AddressSummary-balance,
.ui--AddressSummary-extra,
.ui--AddressSummary-bonded,
.ui--AddressSummary-nonce {
font-weight: 100;
opacity: 0.75;
}
.ui--AddressSummary-balance {
.ui--Bonded {
opacity: 1;
color: green;
}
.ui--AddressSummary-data+.ui--AddressSummary-balance,
.ui--AddressSummary-data+.ui--AddressSummary-bonded,
.ui--AddressSummary-data+.ui--AddressSummary-nonce {
padding-top: 0.5em;
}

View File

@ -0,0 +1,51 @@
// Copyright 2017-2019 @polkadot/ui-reactive 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, AccountIndex, Address, Option, StakingLedger } from '@polkadot/types';
import { BareProps, CallProps } from '@polkadot/ui-api/types';
import React from 'react';
import { withCalls } from '@polkadot/ui-api';
import { formatBalance } from '@polkadot/util';
type Props = BareProps & CallProps & {
children?: React.ReactNode,
value?: AccountId | AccountIndex | Address | string | Uint8Array | null,
label?: string,
staking_ledger?: Option<StakingLedger>
};
class BondedDisplay extends React.PureComponent<Props> {
render () {
const { children, className, label = '', style, staking_ledger } = this.props;
if (!staking_ledger || staking_ledger.isNone) {
return null;
}
const { active: bonded } = staking_ledger.unwrap();
return (
<div
className={className}
style={style}
>
{label}{
bonded
? formatBalance(bonded)
: '0'
}{children}
</div>
);
}
}
export default withCalls<Props>(
['query.staking.bonded', {
paramName: 'value',
transform: (value) => value.unwrapOr(null)
}],
['query.staking.ledger', { paramName: 'staking_bonded' }]
)(BondedDisplay);