Extrinsic app selection as functional (#2523)
* Extrinsic app selection as functional * td .ui--Spinner spacing * Remove staking counter
This commit is contained in:
parent
84910c3466
commit
a7217bd9d5
@ -4,8 +4,7 @@
|
||||
|
||||
import { Route } from './types';
|
||||
|
||||
// import Staking from '@polkadot/app-staking';
|
||||
import Staking, { useCounter } from '@polkadot/app-staking';
|
||||
import Staking from '@polkadot/app-staking';
|
||||
|
||||
const route: Route = {
|
||||
Component: Staking,
|
||||
@ -18,8 +17,7 @@ const route: Route = {
|
||||
defaultValue: 'Staking'
|
||||
},
|
||||
icon: 'certificate',
|
||||
name: 'staking',
|
||||
useCounter
|
||||
name: 'staking'
|
||||
};
|
||||
|
||||
export default route;
|
||||
|
||||
@ -2,128 +2,67 @@
|
||||
// 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 { QueueTxExtrinsicAdd } from '@polkadot/react-components/Status/types';
|
||||
import { ApiProps } from '@polkadot/react-api/types';
|
||||
import { SubmittableExtrinsic } from '@polkadot/api/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Extrinsic, InputAddress, TxButton, TxComponent } from '@polkadot/react-components';
|
||||
import { withApi, withMulti } from '@polkadot/react-api/hoc';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Button, Extrinsic, InputAddress, TxButton } from '@polkadot/react-components';
|
||||
import { useApi } from '@polkadot/react-hooks';
|
||||
import { BalanceFree } from '@polkadot/react-query';
|
||||
|
||||
import translate from './translate';
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
interface Props extends ApiProps, I18nProps {
|
||||
queueExtrinsic: QueueTxExtrinsicAdd;
|
||||
}
|
||||
function Selection (): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
const { apiDefaultTxSudo } = useApi();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [extrinsic, setExtrinsic] = useState<SubmittableExtrinsic<'promise'> | null>(null);
|
||||
|
||||
interface State {
|
||||
isValid: boolean;
|
||||
isValidUnsigned: boolean;
|
||||
method: SubmittableExtrinsic<'promise'> | null;
|
||||
accountId?: string | null;
|
||||
}
|
||||
const _onExtrinsicChange = useCallback(
|
||||
(method?: SubmittableExtrinsic<'promise'>) =>
|
||||
setExtrinsic(() => method || null),
|
||||
[]
|
||||
);
|
||||
|
||||
class Selection extends TxComponent<Props, State> {
|
||||
public state: State = {
|
||||
isValid: false,
|
||||
isValidUnsigned: false,
|
||||
method: null
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { apiDefaultTxSudo, t } = this.props;
|
||||
const { accountId, isValid, isValidUnsigned } = this.state;
|
||||
const extrinsic = this.getExtrinsic() || apiDefaultTxSudo('0x00');
|
||||
|
||||
return (
|
||||
<div className='extrinsics--Selection'>
|
||||
<InputAddress
|
||||
label={t('using the selected account')}
|
||||
labelExtra={
|
||||
<BalanceFree
|
||||
label={<label>{t('free balance')}</label>}
|
||||
params={accountId}
|
||||
/>
|
||||
}
|
||||
onChange={this.onChangeSender}
|
||||
type='account'
|
||||
/>
|
||||
<Extrinsic
|
||||
defaultValue={apiDefaultTxSudo}
|
||||
label={t('submit the following extrinsic')}
|
||||
onChange={this.onChangeExtrinsic}
|
||||
onEnter={this.sendTx}
|
||||
/>
|
||||
<Button.Group>
|
||||
<TxButton
|
||||
extrinsic={extrinsic}
|
||||
icon='sign-in'
|
||||
isBasic
|
||||
isDisabled={!isValidUnsigned}
|
||||
isUnsigned
|
||||
label={t('Submit Unsigned')}
|
||||
withSpinner
|
||||
return (
|
||||
<div className='extrinsics--Selection'>
|
||||
<InputAddress
|
||||
label={t('using the selected account')}
|
||||
labelExtra={
|
||||
<BalanceFree
|
||||
label={<label>{t('free balance')}</label>}
|
||||
params={accountId}
|
||||
/>
|
||||
<Button.Or />
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
extrinsic={extrinsic}
|
||||
icon='sign-in'
|
||||
isDisabled={!isValid}
|
||||
isPrimary={false}
|
||||
label={t('Submit Transaction')}
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private nextState (newState: Partial<State>): void {
|
||||
this.setState(
|
||||
(prevState: State): State => {
|
||||
const { method = prevState.method, accountId = prevState.accountId } = newState;
|
||||
const isValid = !!(
|
||||
accountId &&
|
||||
accountId.length &&
|
||||
method
|
||||
);
|
||||
|
||||
return {
|
||||
accountId,
|
||||
isValid,
|
||||
isValidUnsigned: !!method,
|
||||
method
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private onChangeExtrinsic = (method: SubmittableExtrinsic<'promise'> | null = null): void => {
|
||||
this.nextState({ method });
|
||||
}
|
||||
|
||||
private onChangeSender = (accountId: string | null): void => {
|
||||
this.nextState({ accountId });
|
||||
}
|
||||
|
||||
private getExtrinsic (): SubmittableExtrinsic<'promise'> | null {
|
||||
const { api } = this.props;
|
||||
const { method } = this.state;
|
||||
|
||||
if (!method) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fn = api.findCall(method.callIndex);
|
||||
|
||||
return api.tx[fn.section][fn.method](...method.args);
|
||||
}
|
||||
}
|
||||
onChange={setAccountId}
|
||||
type='account'
|
||||
/>
|
||||
<Extrinsic
|
||||
defaultValue={apiDefaultTxSudo}
|
||||
label={t('submit the following extrinsic')}
|
||||
onChange={_onExtrinsicChange}
|
||||
/>
|
||||
<Button.Group>
|
||||
<TxButton
|
||||
extrinsic={extrinsic}
|
||||
icon='sign-in'
|
||||
isBasic
|
||||
isDisabled={!extrinsic}
|
||||
isUnsigned
|
||||
label={t('Submit Unsigned')}
|
||||
withSpinner
|
||||
/>
|
||||
<Button.Or />
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
extrinsic={extrinsic}
|
||||
icon='sign-in'
|
||||
isDisabled={!extrinsic || !accountId}
|
||||
isPrimary={false}
|
||||
label={t('Submit Transaction')}
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
Selection,
|
||||
translate,
|
||||
withApi
|
||||
);
|
||||
export default React.memo(Selection);
|
||||
|
||||
@ -2,78 +2,59 @@
|
||||
// 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 { KeyringPair } from '@polkadot/keyring/types';
|
||||
|
||||
import React from 'react';
|
||||
import { AddressRow, Button, Modal, Password, TxComponent } from '@polkadot/react-components';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { AddressRow, Button, Modal, Password } from '@polkadot/react-components';
|
||||
|
||||
import translate from './translate';
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
interface Props extends I18nProps {
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
onUnlock: () => void;
|
||||
pair: KeyringPair | null;
|
||||
}
|
||||
|
||||
interface State {
|
||||
address: string;
|
||||
password: string;
|
||||
unlockError: string | null;
|
||||
}
|
||||
function Unlock ({ onClose, onUnlock, pair }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const [address, setAddress] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [unlockError, setUnlockError] = useState<string | null>(null);
|
||||
|
||||
class Unlock extends TxComponent<Props, State> {
|
||||
public state: State = {
|
||||
address: '',
|
||||
password: '',
|
||||
unlockError: null
|
||||
};
|
||||
useEffect((): void => {
|
||||
setAddress(pair?.address || '');
|
||||
}, [pair]);
|
||||
|
||||
public static getDerivedStateFromProps ({ pair }: Props): Pick<State, never> {
|
||||
return {
|
||||
address: (pair && pair.address) || ''
|
||||
};
|
||||
useEffect((): void => {
|
||||
setUnlockError(null);
|
||||
}, [password]);
|
||||
|
||||
const _onUnlock = useCallback(
|
||||
(): void => {
|
||||
if (!pair || !pair.isLocked) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
pair.decodePkcs8(password);
|
||||
} catch (error) {
|
||||
return setUnlockError(error.message);
|
||||
}
|
||||
|
||||
onUnlock();
|
||||
},
|
||||
[onUnlock, pair, password]
|
||||
);
|
||||
|
||||
if (!pair) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { pair, t } = this.props;
|
||||
|
||||
if (!pair) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className='toolbox--Unlock'
|
||||
header={t('Unlock account')}
|
||||
>
|
||||
{this.renderContent()}
|
||||
{this.renderActions()}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
private renderActions (): React.ReactNode {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Modal.Actions onCancel={this.onCancel}>
|
||||
<Button
|
||||
icon='unlock'
|
||||
isPrimary
|
||||
label={t('Unlock')}
|
||||
onClick={this.onUnlock}
|
||||
ref={this.button}
|
||||
/>
|
||||
</Modal.Actions>
|
||||
);
|
||||
}
|
||||
|
||||
private renderContent (): React.ReactNode {
|
||||
const { t } = this.props;
|
||||
const { address, password, unlockError } = this.state;
|
||||
|
||||
return (
|
||||
return (
|
||||
<Modal
|
||||
className='toolbox--Unlock'
|
||||
header={t('Unlock account')}
|
||||
>
|
||||
<Modal.Content>
|
||||
<AddressRow
|
||||
isInline
|
||||
@ -86,58 +67,23 @@ class Unlock extends TxComponent<Props, State> {
|
||||
help={t('The account\'s password specified at the creation of this account.')}
|
||||
isError={!!unlockError}
|
||||
label={t('password')}
|
||||
onChange={this.onChangePassword}
|
||||
onEnter={this.submit}
|
||||
onChange={setPassword}
|
||||
onEnter={_onUnlock}
|
||||
value={password}
|
||||
/>
|
||||
</div>
|
||||
</AddressRow>
|
||||
</Modal.Content>
|
||||
);
|
||||
}
|
||||
|
||||
private unlockAccount (password?: string): string | null {
|
||||
const { pair } = this.props;
|
||||
|
||||
if (!pair || !pair.isLocked) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
pair.decodePkcs8(password);
|
||||
} catch (error) {
|
||||
return error.message;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private onChangePassword = (password: string): void => {
|
||||
this.setState({
|
||||
password,
|
||||
unlockError: null
|
||||
});
|
||||
}
|
||||
|
||||
private onCancel = (): void => {
|
||||
const { onClose } = this.props;
|
||||
|
||||
onClose();
|
||||
}
|
||||
|
||||
private onUnlock = (): void => {
|
||||
const { onUnlock } = this.props;
|
||||
const { password } = this.state;
|
||||
const unlockError = this.unlockAccount(password);
|
||||
|
||||
if (unlockError) {
|
||||
this.setState({ unlockError });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
onUnlock();
|
||||
}
|
||||
<Modal.Actions onCancel={onClose}>
|
||||
<Button
|
||||
icon='unlock'
|
||||
isPrimary
|
||||
label={t('Unlock')}
|
||||
onClick={_onUnlock}
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(Unlock);
|
||||
export default React.memo(Unlock);
|
||||
|
||||
@ -163,7 +163,7 @@ export default React.memo(styled(Table)`
|
||||
}
|
||||
|
||||
.ui--Spinner {
|
||||
margin: 0.5rem auto;
|
||||
margin: 0 auto;
|
||||
|
||||
.text {
|
||||
margin-bottom: 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user