Add unbond button (#1057)

* don't show 'Bond additionnal' if the stash already bond everything it could

* add unbound button

* clean up

* clean up bis

* linting

* add comments + some more cleanup

* add group active and total

* refactor the isOpen props for all modals

* conditionnaly call the renderer

* revert isOpen changes

* add comment regarding the modal loading choice
This commit is contained in:
Thibaut Sardan 2019-05-01 17:44:50 +02:00 committed by GitHub
parent 405dfecd8f
commit 21f76ca0ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 159 additions and 11 deletions

View File

@ -0,0 +1,98 @@
// Copyright 2017-2019 @polkadot/ui-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 } from '@polkadot/types';
import BN from 'bn.js';
import { Button, InputAddress, InputBalance, Modal, TxButton } from '@polkadot/ui-app';
import { I18nProps } from '@polkadot/ui-app/types';
import React from 'react';
import translate from '../translate';
type Props = I18nProps & {
controllerId?: AccountId | null,
isOpen: boolean,
onClose: () => void
};
type State = {
maxUnbond?: BN
};
class Unbond extends React.PureComponent<Props, State> {
state: State = {};
render () {
const { controllerId, isOpen, onClose, t } = this.props;
const { maxUnbond } = this.state;
const canSubmit = !!maxUnbond && maxUnbond.gtn(0);
if (!isOpen) {
return null;
}
return (
<Modal
className='staking--Unbond'
dimmer='inverted'
open
size='small'
>
{this.renderContent()}
<Modal.Actions>
<Button.Group>
<Button
isNegative
onClick={onClose}
label={t('Cancel')}
/>
<Button.Or />
<TxButton
accountId={controllerId && controllerId.toString()}
isDisabled={!canSubmit}
isPrimary
label={t('Unbond')}
onClick={onClose}
params={[maxUnbond]}
tx='staking.unbond'
/>
</Button.Group>
</Modal.Actions>
</Modal>
);
}
private renderContent () {
const { controllerId, t } = this.props;
return (
<>
<Modal.Header>
{t('Unbond')}
</Modal.Header>
<Modal.Content className='ui--signer-Signer-Content'>
<InputAddress
className='medium'
defaultValue={controllerId && controllerId.toString()}
isDisabled
label={t('controler account')}
/>
<InputBalance
autoFocus
className='medium'
help={t('The maximum amount to unbond, this is adjusted using the bonded funds on the account.')}
label={t('unbond amount')}
onChange={this.onChangeValue}
/>
</Modal.Content>
</>
);
}
private onChangeValue = (maxUnbond?: BN) => {
this.setState({ maxUnbond });
}
}
export default translate(Unbond);

View File

@ -2,23 +2,23 @@
// 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, Exposure, Option, StakingLedger, ValidatorPrefs } from '@polkadot/types';
import { ApiProps } from '@polkadot/ui-api/types';
import { AddressMini, AddressSummary, Button, TxButton } from '@polkadot/ui-app';
import { DerivedBalancesMap } from '@polkadot/api-derive/types';
import { I18nProps } from '@polkadot/ui-app/types';
import { ApiProps } from '@polkadot/ui-api/types';
import { KeyringSectionOption } from '@polkadot/ui-keyring/options/types';
import { AccountFilter, RecentlyOfflineMap } from '../types';
import React from 'react';
import { AccountId, Exposure, Option, StakingLedger, ValidatorPrefs } from '@polkadot/types';
import { AddressMini, AddressSummary, Button, TxButton } from '@polkadot/ui-app';
import { withCalls } from '@polkadot/ui-api';
import { AccountFilter, RecentlyOfflineMap } from '../types';
import Bond from './Bond';
import BondExtra from './BondExtra';
import Nominating from './Nominating';
import SessionKey from './SessionKey';
import Validating from './Validating';
import translate from '../translate';
import Unbond from './Unbond';
import Validating from './Validating';
type Props = ApiProps & I18nProps & {
accountId: string,
@ -30,11 +30,12 @@ type Props = ApiProps & I18nProps & {
recentlyOffline: RecentlyOfflineMap,
sessionId?: AccountId | null,
stashId?: AccountId | null,
freeBalance?: Balance,
staking_stakers?: Exposure,
staking_nominators?: [Array<AccountId>],
staking_validators?: [ValidatorPrefs],
stashOptions: Array<KeyringSectionOption>,
validators: Array<string>
staking_ledger?: StakingLedger
};
type State = {
@ -63,12 +64,17 @@ class Account extends React.PureComponent<Props, State> {
return null;
}
// Each component is rendered and gets a `is[Component]Openwill` passed in a `isOpen` props.
// These components will be loaded and return null at the first load (because is[Component]Open === false).
// This is deliberate in order to display the Component modals in a performant matter later on
// because their state will already be loaded.
return (
<article className='staking--Account'>
{this.renderBond()}
{this.renderBondExtra()}
{this.renderNominating()}
{this.renderSessionKey()}
{this.renderUnbond()}
{this.renderValidating()}
<AddressSummary
name={name}
@ -118,6 +124,19 @@ class Account extends React.PureComponent<Props, State> {
);
}
private renderUnbond () {
const { controllerId } = this.props;
const { isUnbondOpen } = this.state;
return (
<Unbond
controllerId={controllerId}
isOpen={isUnbondOpen}
onClose={this.toggleUnbond}
/>
);
}
private renderValidating () {
const { accountId, stashId, staking_validators } = this.props;
const { isValidatingOpen } = this.state;
@ -290,7 +309,7 @@ class Account extends React.PureComponent<Props, State> {
}
private renderButtons () {
const { accountId, controllerId, sessionId, stashId, staking_nominators, staking_validators, t } = this.props;
const { accountId, controllerId, sessionId, staking_nominators, staking_validators,stashId, freeBalance, staking_ledger, t } = this.props;
const buttons = [];
if (!stashId) {
@ -304,14 +323,31 @@ class Account extends React.PureComponent<Props, State> {
/>
);
} else {
buttons.push(
// only show a "Bond Additional" button if this stash account actually doesn't bond everything already
// staking_ledger.total gives the total amount that can be slashed (any active amount + what is being unlocked)
if (freeBalance && staking_ledger && staking_ledger.total && (freeBalance.gt(staking_ledger.total))) {
buttons.push(
<Button
isPrimary
key='bond'
onClick={this.toggleBondExtra}
label={t('Bond Additional')}
/>
);
);
}
// don't show the `unbond` button if there's nothing to unbond
// staking_ledger.active gives the amount that can be unbonded (total - what's being unlocked).
if (staking_ledger && staking_ledger.active && staking_ledger.active.gtn(0)) {
buttons.length && buttons.push(<Button.Or key='bondAdditional.or' />);
buttons.push(
<Button
isNegative
key='unbond'
onClick={this.toggleUnbond}
label={t('Unbond')}
/>
);
}
}
} else {
const isNominating = !!staking_nominators && !!staking_nominators[0] && staking_nominators[0].length;
@ -394,6 +430,12 @@ class Account extends React.PureComponent<Props, State> {
}));
}
private toggleUnbond = () => {
this.setState(({ isUnbondOpen }) => ({
isUnbondOpen: !isUnbondOpen
}));
}
private toggleValidating = () => {
this.setState(({ isValidatingOpen }) => ({
isValidatingOpen: !isValidatingOpen
@ -422,6 +464,15 @@ export default translate(
transform: (ledger: Option<StakingLedger>) =>
ledger.unwrapOr({ stash: null }).stash
}],
['query.staking.ledger', {
paramName: 'controllerId',
transform: (ledger: Option<StakingLedger>) =>
ledger.unwrapOr({ stash: null, total: null, active: null })
}],
['query.balances.freeBalance', {
paramName: 'accountId',
propName: 'freeBalance'
}],
['query.staking.stakers', { paramName: 'accountId' }],
['query.staking.nominators', { paramName: 'stashId' }],
['query.staking.validators', { paramName: 'stashId' }]

View File

@ -96,7 +96,6 @@ class Accounts extends React.PureComponent<Props, State> {
name={name}
recentlyOffline={recentlyOffline}
stashOptions={this.getStashOptions()}
validators={validators}
/>
);
})}