NewStake as a self-contained unit (#2515)

This commit is contained in:
Jaco Greeff 2020-04-04 21:04:54 +02:00 committed by GitHub
parent c7561c570a
commit 5b4dee377f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 109 additions and 176 deletions

View File

@ -2,174 +2,124 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { I18nProps } from '@polkadot/react-components/types';
import { ApiProps } from '@polkadot/react-api/types';
import { CalculateBalanceProps } from '../types';
import { SubmittableExtrinsic } from '@polkadot/api/types';
import BN from 'bn.js';
import React from 'react';
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
import { Dropdown, InputAddress, InputBalanceBonded, Modal, TxButton, TxComponent } from '@polkadot/react-components';
import { withApi, withMulti } from '@polkadot/react-api/hoc';
import React, { useEffect, useState } from 'react';
import { Button, Dropdown, InputAddress, InputBalanceBonded, Modal, TxButton } from '@polkadot/react-components';
import { useApi, useToggle } from '@polkadot/react-hooks';
import translate from '../translate';
import { useTranslation } from '../translate';
import InputValidateAmount from './Account/InputValidateAmount';
import InputValidationController from './Account/InputValidationController';
import { rewardDestinationOptions } from './constants';
interface Props extends ApiProps, I18nProps, CalculateBalanceProps {
onClose: () => void;
interface Props {
className?: string;
}
interface State {
amountError: string | null;
bondValue?: BN;
controllerError: string | null;
controllerId: string | null;
destination: number;
extrinsic: SubmittableExtrinsic | null;
stashId: string | null;
}
function NewStake ({ className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [isVisible, toggleVisible] = useToggle();
const [amount, setAmount] = useState<BN | undefined>();
const [amountError, setAmountError] = useState<string | null>(null);
const [, setControllerError] = useState<string | null>(null);
const [controllerId, setControllerId] = useState<string | null>(null);
const [destination, setDestination] = useState(0);
const [extrinsic, setExtrinsic] = useState<SubmittableExtrinsic<'promise'> | null>(null);
const [stashId, setStashId] = useState<string | null>(null);
class NewStake extends TxComponent<Props, State> {
public state: State;
constructor (props: Props) {
super(props);
this.state = {
amountError: null,
controllerError: null,
controllerId: null,
destination: 0,
extrinsic: null,
stashId: null
};
}
public render (): React.ReactNode {
const { onClose, t } = this.props;
const { amountError, bondValue, controllerId, destination, extrinsic, stashId } = this.state;
const hasValue = !!bondValue && bondValue.gtn(0);
const canSubmit = hasValue && !!controllerId;
return (
<Modal
className='staking--Bonding'
header={t('Bonding Preferences')}
size='small'
>
<Modal.Content className='ui--signer-Signer-Content'>
<InputAddress
className='medium'
label={t('stash account')}
onChange={this.onChangeStash}
type='account'
value={stashId}
/>
<InputAddress
className='medium'
help={t('The controller is the account that will be used to control any nominating or validating actions. Should not match another stash or controller.')}
label={t('controller account')}
onChange={this.onChangeController}
type='account'
value={controllerId}
/>
<InputValidationController
accountId={stashId}
controllerId={controllerId}
onError={this.onControllerError}
/>
<InputBalanceBonded
autoFocus
className='medium'
controllerId={controllerId}
destination={destination}
extrinsicProp={'staking.bond'}
help={t('The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the total amount available)')}
isError={!hasValue || !!amountError}
label={t('value bonded')}
onChange={this.onChangeValue}
onEnter={this.sendTx}
stashId={stashId}
/>
<InputValidateAmount
accountId={stashId}
onError={this.onAmountError}
value={bondValue}
/>
<Dropdown
className='medium'
defaultValue={0}
help={t('The destination account for any payments as either a nominator or validator')}
label={t('payment destination')}
onChange={this.onChangeDestination}
options={rewardDestinationOptions}
value={destination}
/>
</Modal.Content>
<Modal.Actions onCancel={onClose}>
<TxButton
accountId={stashId}
extrinsic={extrinsic}
icon='sign-in'
isDisabled={!canSubmit}
isPrimary
label={t('Bond')}
onStart={onClose}
/>
</Modal.Actions>
</Modal>
useEffect((): void => {
setExtrinsic(
() => (amount && controllerId)
? api.tx.staking.bond(controllerId, amount, destination)
: null
);
}
}, [api, amount, controllerId, destination]);
private nextState (newState: Partial<State>): void {
this.setState((prevState: State): State => {
const { api } = this.props;
const { amountError = prevState.amountError, bondValue = prevState.bondValue, controllerError = prevState.controllerError, controllerId = prevState.controllerId, destination = prevState.destination, stashId = prevState.stashId } = newState;
const extrinsic = (bondValue && controllerId)
? api.tx.staking.bond(controllerId, bondValue, destination)
: null;
const hasValue = !!amount?.gtn(0);
const canSubmit = hasValue && !!controllerId;
return {
amountError,
bondValue,
controllerError,
controllerId,
destination,
extrinsic,
stashId
};
});
}
private onAmountError = (amountError: string | null): void => {
this.nextState({ amountError });
}
private onChangeController = (controllerId: string | null): void => {
this.nextState({ controllerId });
}
private onChangeDestination = (destination: number): void => {
this.nextState({ destination });
}
private onChangeStash = (stashId: string | null): void => {
this.nextState({ stashId });
}
private onChangeValue = (bondValue?: BN): void => {
this.nextState({ bondValue });
}
private onControllerError = (controllerError: string | null): void => {
this.setState({ controllerError });
}
return (
<div className={className}>
<Button.Group>
<Button
icon='add'
key='new-stake'
label={t('New stake')}
onClick={toggleVisible}
/>
</Button.Group>
{isVisible && (
<Modal
className='staking--Bonding'
header={t('Bonding Preferences')}
size='small'
>
<Modal.Content className='ui--signer-Signer-Content'>
<InputAddress
className='medium'
label={t('stash account')}
onChange={setStashId}
type='account'
value={stashId}
/>
<InputAddress
className='medium'
help={t('The controller is the account that will be used to control any nominating or validating actions. Should not match another stash or controller.')}
label={t('controller account')}
onChange={setControllerId}
type='account'
value={controllerId}
/>
<InputValidationController
accountId={stashId}
controllerId={controllerId}
onError={setControllerError}
/>
<InputBalanceBonded
autoFocus
className='medium'
controllerId={controllerId}
destination={destination}
extrinsicProp={'staking.bond'}
help={t('The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the total amount available)')}
isError={!hasValue || !!amountError}
label={t('value bonded')}
onChange={setAmount}
stashId={stashId}
/>
<InputValidateAmount
accountId={stashId}
onError={setAmountError}
value={amount}
/>
<Dropdown
className='medium'
defaultValue={0}
help={t('The destination account for any payments as either a nominator or validator')}
label={t('payment destination')}
onChange={setDestination}
options={rewardDestinationOptions}
value={destination}
/>
</Modal.Content>
<Modal.Actions onCancel={toggleVisible}>
<TxButton
accountId={stashId}
extrinsic={extrinsic}
icon='sign-in'
isDisabled={!canSubmit}
isPrimary
label={t('Bond')}
onStart={toggleVisible}
/>
</Modal.Actions>
</Modal>
)}
</div>
);
}
export default withMulti(
NewStake,
translate,
withApi
);
export default React.memo(NewStake);

View File

@ -6,12 +6,12 @@ import { DeriveStakingOverview, DeriveStakerReward } from '@polkadot/api-derive/
import { ActiveEraInfo, EraIndex } from '@polkadot/types/interfaces';
import React, { useCallback, useEffect, useState } from 'react';
import { Button, Table } from '@polkadot/react-components';
import { Table } from '@polkadot/react-components';
import { useCall, useApi, useOwnStashes } from '@polkadot/react-hooks';
import { Option } from '@polkadot/types';
import Account from './Account';
import StartStaking from './NewStake';
import NewStake from './NewStake';
import { useTranslation } from '../translate';
interface Props {
@ -33,7 +33,6 @@ function Actions ({ allRewards, allStashes, className, isVisible, next, stakingO
: undefined
});
const ownStashes = useOwnStashes();
const [isNewStakeOpen, setIsNewStateOpen] = useState(false);
const [foundStashes, setFoundStashes] = useState<[string, boolean][] | null>(null);
const [stashTypes, setStashTypes] = useState<Record<string, number>>({});
@ -45,12 +44,6 @@ function Actions ({ allRewards, allStashes, className, isVisible, next, stakingO
);
}, [ownStashes, stashTypes]);
const _toggleNewStake = useCallback(
(): void => setIsNewStateOpen(
(isNewStakeOpen: boolean) => !isNewStakeOpen
),
[]
);
const _onUpdateType = useCallback(
(stashId: string, type: 'validator' | 'nominator' | 'started' | 'other'): void =>
setStashTypes((stashTypes: Record<string, number>) => ({
@ -66,17 +59,7 @@ function Actions ({ allRewards, allStashes, className, isVisible, next, stakingO
return (
<div className={`${className} ${!isVisible && 'staking--hidden'}`}>
<Button.Group>
<Button
icon='add'
key='new-stake'
label={t('New stake')}
onClick={_toggleNewStake}
/>
</Button.Group>
{isNewStakeOpen && (
<StartStaking onClose={_toggleNewStake} />
)}
<NewStake />
<Table
empty={t('No funds staked yet. Bond funds to validate or nominate a validator')}
header={[