Some more additional func conversions (#1692)

* Some more additional func conversions

* Create Address functional

* Minor cleanups

* Adapt ExtrinsicDisplay

* Linting updates
This commit is contained in:
Jaco Greeff 2019-10-04 17:45:10 +02:00 committed by GitHub
parent aabe4b084e
commit 7415befba2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 174 additions and 280 deletions

View File

@ -3,63 +3,45 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
import React from 'react';
import { Button, InputAddress, InputBalance, TxButton, TxComponent } from '@polkadot/react-components';
import React, { useState } from 'react';
import { Button, InputAddress, InputBalance, TxButton } from '@polkadot/react-components';
import Summary from './Summary';
interface Props {
accountId?: string | null;
}
interface State {
amount?: BN;
recipientId?: string | null;
}
export default class Transfer extends TxComponent<Props, State> {
public state: State = {};
export default function Transfer ({ accountId }: Props): React.ReactElement<Props> {
const [amount, setAmount] = useState<BN | undefined | null>(null);
const [recipientId, setRecipientId] = useState<string | null>(null);
public render (): React.ReactNode {
const { accountId } = this.props;
const { amount, recipientId } = this.state;
return (
<section>
<h1>transfer</h1>
<div className='ui--row'>
<div className='large'>
<InputAddress
label='recipient address for this transfer'
onChange={this.onChangeRecipient}
type='all'
return (
<section>
<h1>transfer</h1>
<div className='ui--row'>
<div className='large'>
<InputAddress
label='recipient address for this transfer'
onChange={setRecipientId}
type='all'
/>
<InputBalance
label='amount to transfer'
onChange={setAmount}
/>
<Button.Group>
<TxButton
accountId={accountId}
icon='send'
label='make transfer'
params={[recipientId, amount]}
tx='balances.transfer'
/>
<InputBalance
label='amount to transfer'
onChange={this.onChangeAmount}
onEnter={this.sendTx}
/>
<Button.Group>
<TxButton
accountId={accountId}
icon='send'
label='make transfer'
params={[recipientId, amount]}
tx='balances.transfer'
ref={this.button}
/>
</Button.Group>
</div>
<Summary className='small'>Make a transfer from any account you control to another account. Transfer fees and per-transaction fees apply and will be calculated upon submission.</Summary>
</Button.Group>
</div>
</section>
);
}
private onChangeAmount = (amount?: BN): void => {
this.setState({ amount });
}
private onChangeRecipient = (recipientId?: string | null): void => {
this.setState({ recipientId });
}
<Summary className='small'>Make a transfer from any account you control to another account. Transfer fees and per-transaction fees apply and will be calculated upon submission.</Summary>
</div>
</section>
);
}

View File

@ -6,7 +6,7 @@ import { I18nProps } from '@polkadot/react-components/types';
import { ActionStatus } from '@polkadot/react-components/Status/types';
import { ModalProps } from '../types';
import React from 'react';
import React, { useState } from 'react';
import { AddressRow, Button, Input, InputAddress, Modal } from '@polkadot/react-components';
import keyring from '@polkadot/ui-keyring';
@ -15,149 +15,50 @@ import translate from '../translate';
interface Props extends ModalProps, I18nProps {}
interface State {
address: string;
isAddressExisting: boolean;
isAddressValid: boolean;
isNameValid: boolean;
isValid: boolean;
name: string;
tags: string[];
}
function Create ({ onClose, onStatusChange, t }: Props): React.ReactElement<Props> {
const [{ isNameValid, name }, setName] = useState<{ isNameValid: boolean; name: string }>({ isNameValid: true, name: 'new address' });
const [{ address, isAddressExisting, isAddressValid }, setAddress] = useState<{ address: string; isAddressExisting: boolean; isAddressValid: boolean }>({ address: '', isAddressExisting: false, isAddressValid: false });
const isValid = isAddressValid && isNameValid;
class Create extends React.PureComponent<Props, State> {
public state: State = {
address: '',
isAddressExisting: false,
isAddressValid: false,
isNameValid: true,
isValid: false,
name: 'new address',
tags: []
};
const _onChangeAddress = (input: string): void => {
let address = '';
let isAddressValid = true;
let isAddressExisting = false;
public render (): React.ReactNode {
const { t } = this.props;
const { address, isAddressValid, isNameValid, isValid, name } = this.state;
try {
address = keyring.encodeAddress(
keyring.decodeAddress(input)
);
isAddressValid = keyring.isAvailable(address);
return (
<Modal
dimmer='inverted'
open
>
<Modal.Header>{t('Add an address')}</Modal.Header>
<Modal.Content>
<AddressRow
defaultName={name}
value={address}
>
<Input
autoFocus
className='full'
help={t('Paste here the address of the contact you want to add to your address book.')}
isError={!isAddressValid}
label={t('address')}
onChange={this.onChangeAddress}
onEnter={this.onCommit}
value={address}
/>
<Input
className='full'
help={t('Type the name of your contact. This name will be used across all the apps. It can be edited later on.')}
isError={!isNameValid}
label={t('name')}
onChange={this.onChangeName}
onEnter={this.onCommit}
value={name}
/>
</AddressRow>
</Modal.Content>
<Modal.Actions>
<Button.Group>
<Button
icon='cancel'
isNegative
onClick={this.onDiscard}
label={t('Cancel')}
/>
<Button.Or />
<Button
icon='save'
isDisabled={!isValid}
isPrimary
onClick={this.onCommit}
label={t('Save')}
/>
</Button.Group>
</Modal.Actions>
</Modal>
);
}
if (!isAddressValid) {
const old = keyring.getAddress(address);
private nextState (newState: Partial<State>, allowEdit = false): void {
this.setState(
(prevState: State): State => {
let { address = prevState.address, name = prevState.name, tags = prevState.tags } = newState;
let isAddressValid = true;
let isAddressExisting = false;
let newAddress = address;
if (old) {
const newName = old.meta.name || name;
try {
newAddress = keyring.encodeAddress(
keyring.decodeAddress(address)
);
isAddressValid = keyring.isAvailable(newAddress);
isAddressExisting = true;
isAddressValid = true;
if (!isAddressValid) {
const old = keyring.getAddress(newAddress);
if (old) {
if (!allowEdit) {
name = old.meta.name || name;
}
isAddressExisting = true;
isAddressValid = true;
}
}
} catch (error) {
isAddressValid = false;
setName({ isNameValid: !!newName, name: newName });
}
const isNameValid = !!name;
return {
address: newAddress,
isAddressExisting,
isAddressValid,
isNameValid,
isValid: isAddressValid && isNameValid,
name,
tags
};
}
);
}
} catch (error) {
isAddressValid = false;
}
private onChangeAddress = (address: string): void => {
this.nextState({ address });
}
private onChangeName = (name: string): void => {
this.nextState({ name }, true);
}
private onCommit = (): void => {
const { onClose, onStatusChange, t } = this.props;
const { address, isAddressExisting, isValid, name, tags } = this.state;
const status: Partial<ActionStatus> = { action: 'create' };
setAddress({ address: address || input, isAddressExisting, isAddressValid });
};
const _onChangeName = (name: string): void => setName({ isNameValid: !!name, name });
const _onCommit = (): void => {
const status = { action: 'create' } as ActionStatus;
if (!isValid) {
return;
}
try {
keyring.saveAddress(address, { name, genesisHash: keyring.genesisHash, tags });
keyring.saveAddress(address, { name, genesisHash: keyring.genesisHash, tags: [] });
status.account = address;
status.status = address ? 'success' : 'error';
@ -171,15 +72,62 @@ class Create extends React.PureComponent<Props, State> {
status.message = error.message;
}
onStatusChange(status as ActionStatus);
onStatusChange(status);
onClose();
}
};
private onDiscard = (): void => {
const { onClose } = this.props;
onClose();
}
return (
<Modal
dimmer='inverted'
open
>
<Modal.Header>{t('Add an address')}</Modal.Header>
<Modal.Content>
<AddressRow
defaultName={name}
value={address}
>
<Input
autoFocus
className='full'
help={t('Paste here the address of the contact you want to add to your address book.')}
isError={!isAddressValid}
label={t('address')}
onChange={_onChangeAddress}
onEnter={_onCommit}
value={address}
/>
<Input
className='full'
help={t('Type the name of your contact. This name will be used across all the apps. It can be edited later on.')}
isError={!isNameValid}
label={t('name')}
onChange={_onChangeName}
onEnter={_onCommit}
value={name}
/>
</AddressRow>
</Modal.Content>
<Modal.Actions>
<Button.Group>
<Button
icon='cancel'
isNegative
onClick={onClose}
label={t('Cancel')}
/>
<Button.Or />
<Button
icon='save'
isDisabled={!isValid}
isPrimary
onClick={_onCommit}
label={t('Save')}
/>
</Button.Group>
</Modal.Actions>
</Modal>
);
}
export default translate(Create);

View File

@ -124,7 +124,7 @@ class Propose extends TxModal<Props, State> {
this.setState({ threshold });
}
private onChangeExtrinsic = (method: Call): void => {
private onChangeExtrinsic = (method?: Call): void => {
if (!method) {
return;
}

View File

@ -101,7 +101,7 @@ class Propose extends TxComponent<Props, State> {
this.nextState({ accountId });
}
private onChangeExtrinsic = (method: Call): void => {
private onChangeExtrinsic = (method?: Call): void => {
if (!method) {
return;
}

View File

@ -80,7 +80,7 @@ class Propose extends TxComponent<Props, State> {
);
}
private onChangeExtrinsic = (method: Call): void => {
private onChangeExtrinsic = (method?: Call): void => {
if (!method) {
return;
}

View File

@ -2,125 +2,89 @@
// 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 '@polkadot/react-components/types';
import { RawParam } from '@polkadot/react-params/types';
import { Call } from '@polkadot/types/interfaces';
import { CallFunction, TypeDef } from '@polkadot/types/types';
import { BareProps } from '@polkadot/react-components/types';
import { ApiProps } from '@polkadot/react-api/types';
import { RawParam } from '@polkadot/react-params/types';
import React from 'react';
import React, { useEffect, useState } from 'react';
import { GenericCall, getTypeDef } from '@polkadot/types';
import { InputExtrinsic } from '@polkadot/react-components';
import Params from '@polkadot/react-params';
import { withApi } from '@polkadot/react-api';
import { isUndefined } from '@polkadot/util';
import paramComponents from './Params';
interface Props extends BareProps, ApiProps {
interface Props extends BareProps {
defaultValue: CallFunction;
isDisabled?: boolean;
isError?: boolean;
isPrivate?: boolean;
label?: React.ReactNode;
onChange: (method?: Call) => void;
onEnter: () => void;
onEnter?: () => void;
withLabel?: boolean;
}
interface State {
methodfn: CallFunction;
params: { name: string; type: TypeDef }[];
values: RawParam[];
function getParams ({ meta }: CallFunction): { name: string; type: TypeDef }[] {
return GenericCall.filterOrigin(meta).map((arg): { name: string; type: TypeDef } => ({
name: arg.name.toString(),
type: getTypeDef(arg.type.toString())
}));
}
class ExtrinsicDisplay extends React.PureComponent<Props, State> {
public state: State;
export default function ExtrinsicDisplay ({ defaultValue, isDisabled, isError, isPrivate, label, onChange, onEnter, withLabel }: Props): React.ReactElement<Props> {
const [extrinsic, setCall] = useState<{ fn: CallFunction; params: { name: string; type: TypeDef }[] }>({ fn: defaultValue, params: getParams(defaultValue) });
const [values, setValues] = useState<RawParam[]>([]);
public constructor (props: Props) {
super(props);
useEffect((): void => {
setValues([]);
}, [extrinsic]);
this.state = {
methodfn: props.defaultValue,
params: this.getParams(props.defaultValue),
values: []
};
}
public render (): React.ReactNode {
const { defaultValue, isDisabled, isError, isPrivate, label, onEnter, withLabel } = this.props;
const { methodfn: { method, section, meta }, params } = this.state;
return (
<div className='extrinsics--Extrinsic'>
<InputExtrinsic
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
isPrivate={isPrivate}
label={label}
onChange={this.onChangeMethod}
withLabel={withLabel}
help={meta && meta.documentation && meta.documentation.join(' ')}
/>
<Params
key={`${section}.${method}:params` /* force re-render on change */}
onChange={this.onChangeValues}
onEnter={onEnter}
overrides={paramComponents}
params={params}
/>
</div>
useEffect((): void => {
const isValid = values.reduce((isValid, value): boolean =>
isValid &&
!isUndefined(value) &&
!isUndefined(value.value) &&
value.isValid, extrinsic.params.length === values.length
);
}
private nextState (newState: Pick< State, never>): void {
this.setState(newState, (): void => {
const { onChange } = this.props;
const { methodfn, params, values } = this.state;
let method;
const isValid = values.reduce((isValid, value): boolean =>
isValid &&
!isUndefined(value) &&
!isUndefined(value.value) &&
value.isValid, params.length === values.length
);
let method;
if (isValid) {
try {
method = methodfn(
...values.map(({ value }): any => value)
);
} catch (error) {
// swallow
}
if (isValid) {
try {
method = extrinsic.fn(...values.map(({ value }): any => value));
} catch (error) {
// swallow
}
}
onChange(method);
});
}
onChange(method);
}, [extrinsic, values]);
private onChangeMethod = (methodfn: CallFunction): void => {
this.nextState({
methodfn,
params: this.getParams(methodfn),
values: []
});
}
const _onChangeMethod = (fn: CallFunction): void => setCall({ fn, params: getParams(fn) });
private onChangeValues = (values: RawParam[]): void => {
this.nextState({ values });
}
const { fn: { meta, method, section }, params } = extrinsic;
private getParams (methodfn: CallFunction): { name: string; type: TypeDef }[] {
return GenericCall.filterOrigin(methodfn.meta).map((arg): { name: string; type: TypeDef } => ({
name: arg.name.toString(),
type: getTypeDef(arg.type.toString())
}));
}
return (
<div className='extrinsics--Extrinsic'>
<InputExtrinsic
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
isPrivate={isPrivate}
label={label}
onChange={_onChangeMethod}
withLabel={withLabel}
help={meta && meta.documentation && meta.documentation.join(' ')}
/>
<Params
key={`${section}.${method}:params` /* force re-render on change */}
onChange={setValues}
onEnter={onEnter}
overrides={paramComponents}
params={params}
/>
</div>
);
}
export default withApi(ExtrinsicDisplay);

View File

@ -22,8 +22,8 @@ interface Props extends BareProps {
withLabel?: boolean;
}
function onChange ({ onChange }: Props): (_: Call) => void {
return function (method: Call): void {
function onChange ({ onChange }: Props): (method?: Call) => void {
return (method?: Call): void => {
onChange && onChange({
isValid: !!method,
value: method