Refactor TxButton and expand use (#1023)

* Refactor TxButton and expand use

* Set button to loading while broadcasting

* Travis CI

* add extrinsic prop, req changes

* restore loading indicator
This commit is contained in:
kwingram25 2019-05-02 01:25:51 -07:00 committed by Jaco Greeff
parent 347632af2b
commit 6435293ff4
9 changed files with 103 additions and 124 deletions

View File

@ -3,13 +3,11 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { I18nProps } from '@polkadot/ui-app/types';
import { QueueProps } from '@polkadot/ui-app/Status/types';
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
import BN from 'bn.js';
import React from 'react';
import { InputAddress } from '@polkadot/ui-app';
import { QueueConsumer } from '@polkadot/ui-app/Status/Context';
import accountObservable from '@polkadot/ui-keyring/observable/accounts';
import { withMulti, withObservable } from '@polkadot/ui-api';
@ -60,15 +58,10 @@ class Voting extends React.PureComponent<Props, State> {
}
return (
<QueueConsumer>
{({ queueExtrinsic }: QueueProps) => (
<VotingButtons
accountId={accountId}
queueExtrinsic={queueExtrinsic}
referendumId={referendumId}
/>
)}
</QueueConsumer>
<VotingButtons
accountId={accountId}
referendumId={referendumId}
/>
);
}

View File

@ -3,65 +3,46 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { I18nProps } from '@polkadot/ui-app/types';
import { QueueTx$ExtrinsicAdd } from '@polkadot/ui-app/Status/types';
import { ApiProps } from '@polkadot/ui-api/types';
import BN from 'bn.js';
import React from 'react';
import { Button } from '@polkadot/ui-app';
import { Button, TxButton } from '@polkadot/ui-app';
import { withApi, withMulti } from '@polkadot/ui-api';
import translate from './translate';
type Props = ApiProps & I18nProps & {
accountId?: string,
queueExtrinsic: QueueTx$ExtrinsicAdd,
referendumId: BN
};
class VotingButton extends React.PureComponent<Props> {
render () {
const { accountId, t } = this.props;
const { accountId, referendumId, t } = this.props;
return (
<Button.Group>
<Button
<TxButton
accountId={accountId}
isDisabled={!accountId}
isNegative
label={t('Nay')}
onClick={this.onClickNo}
params={[referendumId, -1]}
tx='democracy.vote'
/>
<Button.Or />
<Button
<TxButton
accountId={accountId}
isDisabled={!accountId}
isPositive
label={t('Aye')}
onClick={this.onClickYes}
params={[referendumId, 0]}
tx='democracy.vote'
/>
</Button.Group>
);
}
private doVote (vote: boolean) {
const { accountId, api, queueExtrinsic, referendumId } = this.props;
if (!accountId) {
return;
}
queueExtrinsic({
extrinsic: api.tx.democracy.vote(referendumId, vote ? -1 : 0),
accountId
});
}
private onClickYes = () => {
this.doVote(true);
}
private onClickNo = () => {
this.doVote(false);
}
}
export default withMulti(

View File

@ -6,10 +6,11 @@ import BN from 'bn.js';
import { I18nProps } from '@polkadot/ui-app/types';
import { QueueTx$ExtrinsicAdd } from '@polkadot/ui-app/Status/types';
import { ApiProps } from '@polkadot/ui-api/types';
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
import React from 'react';
import { Method } from '@polkadot/types';
import { Button, Extrinsic, InputAddress, Labelled } from '@polkadot/ui-app';
import { Button, Extrinsic, InputAddress, Labelled, TxButton } from '@polkadot/ui-app';
import { withApi, withMulti } from '@polkadot/ui-api';
import { Nonce } from '@polkadot/ui-reactive';
@ -42,6 +43,7 @@ class Selection extends React.PureComponent<Props, State> {
return apiDefaultTx;
}
})();
const extrinsic = this.getExtrinsic() || defaultExtrinsic;
return (
<div className='extrinsics--Selection'>
@ -75,17 +77,18 @@ class Selection extends React.PureComponent<Props, State> {
/>
<br></br>
<Button.Group>
<Button
<TxButton
isDisabled={!isValid}
onClick={this.onQueueInherent}
label={t('Submit Inherent')}
extrinsic={extrinsic}
/>
<Button.Or />
<Button
<TxButton
accountId={accountId}
isDisabled={!isValid}
isPrimary
onClick={this.onQueueExtrinsic}
label={t('Submit Transaction')}
extrinsic={extrinsic}
/>
</Button.Group>
</div>
@ -124,32 +127,16 @@ class Selection extends React.PureComponent<Props, State> {
this.nextState({ accountId, accountNonce: new BN(0) } as State);
}
private onQueue (isUnsigned: boolean): void {
const { api, queueExtrinsic } = this.props;
const { method, isValid, accountId } = this.state;
private getExtrinsic (): SubmittableExtrinsic | null {
const { api } = this.props;
const { method, isValid } = this.state;
if (!isValid || !method) {
return;
return null;
}
const fn = Method.findFunction(method.callIndex);
const extrinsic = api.tx[fn.section][fn.method](...method.args);
queueExtrinsic({
accountId: isUnsigned
? undefined
: accountId,
extrinsic,
isUnsigned
});
}
private onQueueExtrinsic = (): void => {
this.onQueue(false);
}
private onQueueInherent = (): void => {
this.onQueue(true);
return api.tx[fn.section][fn.method](...method.args);
}
}

View File

@ -2,13 +2,11 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { QueueProps } from '@polkadot/ui-app/Status/types';
import { AppProps, I18nProps } from '@polkadot/ui-app/types';
import './index.css';
import React from 'react';
import { QueueConsumer } from '@polkadot/ui-app/Status/Context';
import { Tabs } from '@polkadot/ui-app';
import Selection from './Selection';
@ -31,11 +29,7 @@ class ExtrinsicsApp extends React.PureComponent<Props> {
}]}
/>
</header>
<QueueConsumer>
{({ queueExtrinsic }: QueueProps) => (
<Selection queueExtrinsic={queueExtrinsic} />
)}
</QueueConsumer>
<Selection />
</main>
);
}

View File

@ -2,13 +2,12 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
import { IExtrinsic } from '@polkadot/types/types';
import { I18nProps } from '@polkadot/ui-app/types';
import { QueueTx$Extrinsic, QueueTx$ExtrinsicAdd } from '@polkadot/ui-app/Status/types';
import React from 'react';
import { Index } from '@polkadot/types';
import Button from '@polkadot/ui-app/Button';
import { Button, TxButton } from '@polkadot/ui-app';
import { withCalls, withMulti } from '@polkadot/ui-api';
import translate from './translate';
@ -17,39 +16,26 @@ type Props = I18nProps & {
isDisabled: boolean,
accountId?: string,
system_accountNonce?: Index,
extrinsic: SubmittableExtrinsic | null,
queueExtrinsic: QueueTx$ExtrinsicAdd
extrinsic: IExtrinsic | null
};
class Submit extends React.PureComponent<Props> {
render () {
const { extrinsic, isDisabled, t } = this.props;
const { accountId, system_accountNonce, extrinsic, isDisabled, t } = this.props;
return (
<Button.Group>
<Button
isDisabled={isDisabled || !extrinsic}
<TxButton
accountId={accountId}
accountNonce={system_accountNonce}
isDisabled={isDisabled}
isPrimary
onClick={this.onMakeTransfer}
label={t('Make Transfer')}
extrinsic={extrinsic}
/>
</Button.Group>
);
}
private onMakeTransfer = () => {
const { accountId, system_accountNonce, extrinsic, queueExtrinsic } = this.props;
if (!extrinsic) {
return;
}
queueExtrinsic({
extrinsic,
accountId,
accountNonce: system_accountNonce
} as QueueTx$Extrinsic);
}
}
export default withMulti(

View File

@ -3,7 +3,6 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { I18nProps } from '@polkadot/ui-app/types';
import { QueueProps } from '@polkadot/ui-app/Status/types';
import { ApiProps } from '@polkadot/ui-api/types';
import BN from 'bn.js';
@ -11,7 +10,6 @@ import React from 'react';
import { IExtrinsic } from '@polkadot/types/types';
import { AddressSummary, InputAddress, InputBalance } from '@polkadot/ui-app';
import { withApi, withMulti } from '@polkadot/ui-api';
import { QueueConsumer } from '@polkadot/ui-app/Status/Context';
import keyring from '@polkadot/ui-keyring';
import Checks from '@polkadot/ui-signer/Checks';
@ -73,16 +71,11 @@ class Transfer extends React.PureComponent<Props, State> {
isSendable
onChange={this.onChangeFees}
/>
<QueueConsumer>
{({ queueExtrinsic }: QueueProps) => (
<Submit
accountId={accountId}
isDisabled={!hasAvailable}
extrinsic={extrinsic}
queueExtrinsic={queueExtrinsic}
/>
)}
</QueueConsumer>
<Submit
accountId={accountId}
isDisabled={!hasAvailable}
extrinsic={extrinsic}
/>
</div>
{this.renderAddress(recipientId, 'large')}
</div>

View File

@ -11,7 +11,7 @@ import { isUndefined } from '@polkadot/util';
export default class Button extends React.PureComponent<ButtonProps> {
render () {
const { children, className, floated, icon, isBasic = false, isCircular = false, isDisabled = false, isNegative = false, isPositive = false, isPrimary = false, label, onClick, size, style, tabIndex } = this.props;
const { children, className, floated, icon, isBasic = false, isCircular = false, isDisabled = false, isLoading = false, isNegative = false, isPositive = false, isPrimary = false, label, onClick, size, style, tabIndex } = this.props;
const props = {
basic: isBasic,
@ -20,6 +20,7 @@ export default class Button extends React.PureComponent<ButtonProps> {
disabled: isDisabled,
floated,
icon,
loading: isLoading,
negative: isNegative,
onClick,
positive: isPositive,

View File

@ -13,6 +13,7 @@ export type ButtonProps = BareProps & {
isBasic?: boolean,
isCircular?: boolean,
isDisabled?: boolean,
isLoading?: boolean,
isNegative?: boolean,
isPositive?: boolean,
isPrimary?: boolean,

View File

@ -2,8 +2,11 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Index } from '@polkadot/types';
import { IExtrinsic } from '@polkadot/types/types';
import { ApiProps } from '@polkadot/ui-api/types';
import { QueueTx$ExtrinsicAdd, TxCallback } from './Status/types';
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
import { QueueTx, QueueTx$ExtrinsicAdd, TxCallback } from './Status/types';
import React from 'react';
import { withApi } from '@polkadot/ui-api';
@ -16,10 +19,12 @@ type ConstructFn = () => Array<any>;
type InjectedProps = {
queueExtrinsic: QueueTx$ExtrinsicAdd;
txqueue: Array<QueueTx>;
};
type Props = ApiProps & {
accountId?: string,
accountNonce?: Index,
isPrimary?: boolean,
isDisabled?: boolean,
isNegative?: boolean,
@ -29,16 +34,60 @@ type Props = ApiProps & {
onSuccess?: TxCallback,
onUpdate?: TxCallback,
params?: Array<any> | ConstructFn,
tx: string
tx?: string,
extrinsic?: IExtrinsic | SubmittableExtrinsic
};
class TxButtonInner extends React.PureComponent<Props & InjectedProps> {
type InnerProps = Props & InjectedProps;
type State = {
extrinsic: SubmittableExtrinsic,
isSending: boolean
};
class TxButtonInner extends React.PureComponent<InnerProps> {
state = {
isSending: false
} as State;
static getDerivedStateFromProps ({ api, params = [], txqueue = [], tx = '', extrinsic: propsExtrinsic }: InnerProps): State | null {
if (!propsExtrinsic && (!tx || tx.length === 0)) {
return null;
}
let extrinsic: any;
if (propsExtrinsic) {
extrinsic = propsExtrinsic;
} else {
const [section, method] = tx.split('.');
assert(api.tx[section] && api.tx[section][method], `Unable to find api.tx.${section}.${method}`);
extrinsic = api.tx[section][method](
...(isFunction(params) ? params() : params)
);
}
let isSending = false;
const queuedTx = txqueue.find(({ extrinsic: ex }) => ex === extrinsic);
if (queuedTx) {
isSending = ['broadcast', 'sending'].includes(queuedTx.status);
}
return {
extrinsic,
isSending
};
}
render () {
const { accountId, isDisabled, isNegative, isPrimary, label } = this.props;
const { isSending } = this.state;
return (
<Button
isDisabled={isDisabled || !accountId}
isDisabled={isSending || isDisabled || !accountId}
isLoading={isSending}
isNegative={isNegative}
isPrimary={isUndefined(isPrimary) ? !isNegative : isPrimary}
label={label}
@ -48,17 +97,10 @@ class TxButtonInner extends React.PureComponent<Props & InjectedProps> {
}
private send = (): void => {
const { accountId, api, onClick, onFailed, onSuccess, onUpdate, params = [], queueExtrinsic, tx } = this.props;
const { accountId, onClick, onFailed, onSuccess, onUpdate, queueExtrinsic } = this.props;
const { extrinsic } = this.state;
assert(tx, 'Expected tx param passed to TxButton');
const [section, method] = tx.split('.');
assert(api.tx[section] && api.tx[section][method], `Unable to find api.tx.${section}.${method}`);
const extrinsic: any = api.tx[section][method](
...(isFunction(params) ? params() : params)
);
assert(extrinsic, 'Expected generated extrinsic passed to TxButton');
queueExtrinsic({
accountId,
@ -76,10 +118,11 @@ class TxButton extends React.PureComponent<Props> {
render () {
return (
<QueueConsumer>
{({ queueExtrinsic }) => (
{({ queueExtrinsic, txqueue }) => (
<TxButtonInner
{...this.props}
queueExtrinsic={queueExtrinsic}
txqueue={txqueue}
/>
)}
</QueueConsumer>