More functional component conversions (#1640)
* More functional component conversions * Expose systemVersion on api * Signer checks * KeyRing uses local IdentityIcon
This commit is contained in:
parent
3a9d67f398
commit
6fb8916e6f
@ -3,12 +3,12 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AccountId, RuntimeVersion } from '@polkadot/types/interfaces';
|
||||
import { AccountId } from '@polkadot/types/interfaces';
|
||||
import { BareProps, I18nProps } from '@polkadot/react-components/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { withCalls } from '@polkadot/react-api/with';
|
||||
import React, { useContext, useState, useEffect } from 'react';
|
||||
import { ApiContext, withCalls } from '@polkadot/react-api';
|
||||
import { Bubble, IdentityIcon } from '@polkadot/react-components';
|
||||
import { formatBalance, formatNumber } from '@polkadot/util';
|
||||
|
||||
@ -20,13 +20,10 @@ interface Props extends BareProps, I18nProps {
|
||||
chain_bestNumberLag?: BN;
|
||||
session_validators?: AccountId[];
|
||||
staking_intentions?: AccountId[];
|
||||
state_getRuntimeVersion?: RuntimeVersion;
|
||||
system_chain?: string;
|
||||
system_name?: string;
|
||||
system_version?: string;
|
||||
}
|
||||
|
||||
function SummaryBar ({ balances_totalIssuance, chain_bestNumber, chain_bestNumberLag, staking_intentions = [], session_validators = [], state_getRuntimeVersion, system_chain, system_name, system_version }: Props): React.ReactElement<Props> {
|
||||
function SummaryBar ({ balances_totalIssuance, chain_bestNumber, chain_bestNumberLag, staking_intentions = [], session_validators = [] }: Props): React.ReactElement<Props> {
|
||||
const { api, systemChain, systemName, systemVersion } = useContext(ApiContext);
|
||||
const [nextUp, setNextUp] = useState<AccountId[]>([]);
|
||||
|
||||
useEffect((): void => {
|
||||
@ -43,15 +40,14 @@ function SummaryBar ({ balances_totalIssuance, chain_bestNumber, chain_bestNumbe
|
||||
<summary>
|
||||
<div>
|
||||
<Bubble icon='tty' label='node'>
|
||||
{system_name} v{system_version}
|
||||
{systemName} v{systemVersion}
|
||||
</Bubble>
|
||||
<Bubble icon='chain' label='chain'>
|
||||
{system_chain}
|
||||
{systemChain}
|
||||
</Bubble>
|
||||
<Bubble icon='code' label='runtime'>
|
||||
{api.runtimeVersion.implName} v{api.runtimeVersion.implVersion}
|
||||
</Bubble>
|
||||
<Bubble icon='code' label='runtime'>{
|
||||
state_getRuntimeVersion &&
|
||||
`${state_getRuntimeVersion.implName} v${state_getRuntimeVersion.implVersion}`
|
||||
}</Bubble>
|
||||
<Bubble icon='bullseye' label='best #'>
|
||||
{formatNumber(chain_bestNumber)} ({formatNumber(chain_bestNumberLag)} lag)
|
||||
</Bubble>
|
||||
@ -79,10 +75,6 @@ export default translate(
|
||||
'derive.chain.bestNumber',
|
||||
'derive.chain.bestNumberLag',
|
||||
'query.balances.totalIssuance',
|
||||
'query.session.validators',
|
||||
'rpc.state.getRuntimeVersion',
|
||||
'rpc.system.chain',
|
||||
'rpc.system.name',
|
||||
'rpc.system.version'
|
||||
'query.session.validators'
|
||||
)(SummaryBar)
|
||||
);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Compact, Text } from '@polkadot/types';
|
||||
import { Compact } from '@polkadot/types';
|
||||
import { Balance, EcdsaSignature, EthereumAddress } from '@polkadot/types/interfaces';
|
||||
import { AppProps, I18nProps } from '@polkadot/react-components/types';
|
||||
import { ApiProps } from '@polkadot/react-api/types';
|
||||
@ -12,7 +12,7 @@ import React from 'react';
|
||||
import { Trans } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import { withCalls, withMulti } from '@polkadot/react-api';
|
||||
import { withApi, withMulti } from '@polkadot/react-api';
|
||||
import { Button, Card, Columar, Column, InputAddress, Tooltip } from '@polkadot/react-components';
|
||||
import { InputNumber } from '@polkadot/react-components/InputNumber';
|
||||
import TxModal, { TxModalState, TxModalProps } from '@polkadot/react-components/TxModal';
|
||||
@ -30,9 +30,7 @@ enum Step {
|
||||
Claim = 2,
|
||||
}
|
||||
|
||||
interface Props extends AppProps, ApiProps, I18nProps, TxModalProps {
|
||||
system_chain?: Text;
|
||||
}
|
||||
interface Props extends AppProps, ApiProps, I18nProps, TxModalProps {}
|
||||
|
||||
interface State extends TxModalState {
|
||||
didCopy: boolean;
|
||||
@ -100,7 +98,7 @@ class App extends TxModal<Props, State> {
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { api, system_chain = '', t } = this.props;
|
||||
const { api, systemChain = '', t } = this.props;
|
||||
const { accountId, didCopy, ethereumAddress, signature, step } = this.state;
|
||||
|
||||
const payload = accountId
|
||||
@ -121,7 +119,7 @@ class App extends TxModal<Props, State> {
|
||||
<Card withBottomMargin>
|
||||
<h3>{t('1. Select your {{chain}} account', {
|
||||
replace: {
|
||||
chain: system_chain.toString()
|
||||
chain: systemChain
|
||||
}
|
||||
})}</h3>
|
||||
<InputAddress
|
||||
@ -257,5 +255,5 @@ class App extends TxModal<Props, State> {
|
||||
export default withMulti(
|
||||
App,
|
||||
translate,
|
||||
withCalls<Props>('rpc.system.chain')
|
||||
withApi
|
||||
);
|
||||
|
||||
@ -25,8 +25,8 @@ function renderEntry (route: Route): React.ReactNode {
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: This _looks_ weird and it looks weird, because it is weird. Basically we want all
|
||||
// the entries of an equal width. So here we add a non-content spacers at the end that just
|
||||
// NOTE: This _looks_ weird, because it is weird. Basically we want all the entries
|
||||
// of an equal width. So here we add a non-content spacers at the end that just
|
||||
// ensures flex has enough items to render something usable to the user. Since we don't
|
||||
// quite know how many items per row, we just render a bunch, n === routes.length
|
||||
function renderSpacer (route: Route, index: number): React.ReactNode {
|
||||
|
||||
@ -22,7 +22,6 @@ import detectUnsafe from '../../unsafeChains';
|
||||
interface Props extends I18nProps, ApiProps, CalculateBalanceProps {
|
||||
controllerId: string;
|
||||
isOpen: boolean;
|
||||
isUnsafeChain: boolean;
|
||||
onClose: () => void;
|
||||
stashId: string;
|
||||
}
|
||||
@ -96,9 +95,10 @@ class BondExtra extends TxComponent<Props, State> {
|
||||
}
|
||||
|
||||
private renderContent (): React.ReactNode {
|
||||
const { isUnsafeChain, stashId, t } = this.props;
|
||||
const { stashId, systemChain, t } = this.props;
|
||||
const { maxBalance } = this.state;
|
||||
const available = <span className='label'>{t('available ')}</span>;
|
||||
const isUnsafeChain = detectUnsafe(systemChain);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -186,10 +186,6 @@ export default withMulti(
|
||||
withCalls<Props>(
|
||||
'derive.balances.fees',
|
||||
['derive.balances.all', { paramName: 'stashId' }],
|
||||
['query.system.accountNonce', { paramName: 'stashId' }],
|
||||
['rpc.system.chain', {
|
||||
propName: 'isUnsafeChain',
|
||||
transform: detectUnsafe
|
||||
}]
|
||||
['query.system.accountNonce', { paramName: 'stashId' }]
|
||||
)
|
||||
);
|
||||
|
||||
@ -2,19 +2,19 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ApiProps } from '@polkadot/react-api/types';
|
||||
import { I18nProps } from '@polkadot/react-components/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Icon, InputAddress, Modal, TxButton, TxComponent } from '@polkadot/react-components';
|
||||
import { withCalls, withMulti } from '@polkadot/react-api';
|
||||
import { withApi, withMulti } from '@polkadot/react-api';
|
||||
|
||||
import translate from '../../translate';
|
||||
import detectUnsafe from '../../unsafeChains';
|
||||
import InputValidationController from '../Account/InputValidationController';
|
||||
|
||||
interface Props extends I18nProps {
|
||||
interface Props extends ApiProps, I18nProps {
|
||||
defaultControllerId: string;
|
||||
isUnsafeChain?: boolean;
|
||||
isValidating?: boolean;
|
||||
onClose: () => void;
|
||||
stashId: string;
|
||||
@ -36,8 +36,9 @@ class SetControllerAccount extends TxComponent<Props, State> {
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { defaultControllerId, isUnsafeChain, onClose, stashId, t } = this.props;
|
||||
const { defaultControllerId, isValidating, onClose, stashId, systemChain, t } = this.props;
|
||||
const { controllerError, controllerId } = this.state;
|
||||
const isUnsafeChain = detectUnsafe(systemChain);
|
||||
const canSubmit = isUnsafeChain || (!controllerError && !!controllerId && (defaultControllerId !== controllerId));
|
||||
|
||||
return (
|
||||
@ -47,7 +48,42 @@ class SetControllerAccount extends TxComponent<Props, State> {
|
||||
open
|
||||
size='small'
|
||||
>
|
||||
{this.renderContent()}
|
||||
<Modal.Header>
|
||||
{t('Change controller account')}
|
||||
</Modal.Header>
|
||||
<Modal.Content className='ui--signer-Signer-Content'>
|
||||
{isValidating && (
|
||||
<article className='warning'>
|
||||
<div className='warning'>
|
||||
<Icon name='warning sign' />
|
||||
{t('Warning - Changing the controller while validating will modify the associated session account. It is advised to stop validating before changing the controller account.')}
|
||||
</div>
|
||||
</article>
|
||||
)}
|
||||
<InputAddress
|
||||
className='medium'
|
||||
isDisabled
|
||||
label={t('stash account')}
|
||||
value={stashId}
|
||||
/>
|
||||
<InputAddress
|
||||
className='medium'
|
||||
defaultValue={defaultControllerId}
|
||||
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.')}
|
||||
isError={!isUnsafeChain && !!controllerError}
|
||||
label={t('controller account')}
|
||||
onChange={this.onChangeController}
|
||||
type='account'
|
||||
value={controllerId}
|
||||
/>
|
||||
<InputValidationController
|
||||
accountId={stashId}
|
||||
defaultController={defaultControllerId}
|
||||
controllerId={controllerId}
|
||||
isUnsafeChain={isUnsafeChain}
|
||||
onError={this.onControllerError}
|
||||
/>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
@ -74,62 +110,6 @@ class SetControllerAccount extends TxComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
private renderContent (): React.ReactNode {
|
||||
const { defaultControllerId, isUnsafeChain, stashId, t } = this.props;
|
||||
const { controllerId, controllerError } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal.Header>
|
||||
{t('Change controller account')}
|
||||
</Modal.Header>
|
||||
<Modal.Content className='ui--signer-Signer-Content'>
|
||||
{this.renderSessionAccountWarning()}
|
||||
<InputAddress
|
||||
className='medium'
|
||||
isDisabled
|
||||
label={t('stash account')}
|
||||
value={stashId}
|
||||
/>
|
||||
<InputAddress
|
||||
className='medium'
|
||||
defaultValue={defaultControllerId}
|
||||
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.')}
|
||||
isError={!isUnsafeChain && !!controllerError}
|
||||
label={t('controller account')}
|
||||
onChange={this.onChangeController}
|
||||
type='account'
|
||||
value={controllerId}
|
||||
/>
|
||||
<InputValidationController
|
||||
accountId={stashId}
|
||||
defaultController={defaultControllerId}
|
||||
controllerId={controllerId}
|
||||
isUnsafeChain={isUnsafeChain}
|
||||
onError={this.onControllerError}
|
||||
/>
|
||||
</Modal.Content>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
private renderSessionAccountWarning (): React.ReactNode {
|
||||
const { isValidating = false, t } = this.props;
|
||||
|
||||
if (!isValidating) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<article className='warning'>
|
||||
<div className='warning'>
|
||||
<Icon name='warning sign' />
|
||||
{t('Warning - Changing the controller while validating will modify the associated session account. It is advised to stop validating before changing the controller account.')}
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
private onChangeController = (controllerId: string): void => {
|
||||
this.setState({ controllerId });
|
||||
}
|
||||
@ -142,10 +122,5 @@ class SetControllerAccount extends TxComponent<Props, State> {
|
||||
export default withMulti(
|
||||
SetControllerAccount,
|
||||
translate,
|
||||
withCalls<Props>(
|
||||
['rpc.system.chain', {
|
||||
propName: 'isUnsafeChain',
|
||||
transform: detectUnsafe
|
||||
}]
|
||||
)
|
||||
withApi
|
||||
);
|
||||
|
||||
@ -10,7 +10,7 @@ import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
||||
import { Button, Dropdown, InputAddress, InputBalanceBonded, Modal, TxButton, TxComponent } from '@polkadot/react-components';
|
||||
import { withCalls, withMulti } from '@polkadot/react-api';
|
||||
import { withApi, withMulti } from '@polkadot/react-api';
|
||||
|
||||
import translate from '../translate';
|
||||
import detectUnsafe from '../unsafeChains';
|
||||
@ -18,7 +18,6 @@ import InputValidationController from './Account/InputValidationController';
|
||||
import { rewardDestinationOptions } from './constants';
|
||||
|
||||
interface Props extends ApiProps, I18nProps, CalculateBalanceProps {
|
||||
isUnsafeChain?: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
@ -47,9 +46,10 @@ class NewStake extends TxComponent<Props, State> {
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { isUnsafeChain, onClose, t } = this.props;
|
||||
const { bondValue, controllerError, controllerId, extrinsic, stashId } = this.state;
|
||||
const { onClose, systemChain, t } = this.props;
|
||||
const { bondValue, controllerError, controllerId, destination, extrinsic, stashId } = this.state;
|
||||
const hasValue = !!bondValue && bondValue.gtn(0);
|
||||
const isUnsafeChain = detectUnsafe(systemChain);
|
||||
const canSubmit = (hasValue && (isUnsafeChain || (!controllerError && !!controllerId)));
|
||||
|
||||
return (
|
||||
@ -59,39 +59,6 @@ class NewStake extends TxComponent<Props, State> {
|
||||
open
|
||||
size='small'
|
||||
>
|
||||
{this.renderContent()}
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={onClose}
|
||||
label={t('Cancel')}
|
||||
labelIcon='cancel'
|
||||
/>
|
||||
<Button.Or />
|
||||
<TxButton
|
||||
accountId={stashId}
|
||||
isDisabled={!canSubmit}
|
||||
isPrimary
|
||||
label={t('Bond')}
|
||||
labelIcon='sign-in'
|
||||
onClick={onClose}
|
||||
extrinsic={extrinsic}
|
||||
ref={this.button}
|
||||
/>
|
||||
</Button.Group>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
private renderContent (): React.ReactNode {
|
||||
const { isUnsafeChain, t } = this.props;
|
||||
const { controllerId, controllerError, bondValue, destination, stashId } = this.state;
|
||||
const hasValue = !!bondValue && bondValue.gtn(0);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal.Header>
|
||||
{t('Bonding Preferences')}
|
||||
</Modal.Header>
|
||||
@ -142,7 +109,28 @@ class NewStake extends TxComponent<Props, State> {
|
||||
value={destination}
|
||||
/>
|
||||
</Modal.Content>
|
||||
</>
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={onClose}
|
||||
label={t('Cancel')}
|
||||
labelIcon='cancel'
|
||||
/>
|
||||
<Button.Or />
|
||||
<TxButton
|
||||
accountId={stashId}
|
||||
isDisabled={!canSubmit}
|
||||
isPrimary
|
||||
label={t('Bond')}
|
||||
labelIcon='sign-in'
|
||||
onClick={onClose}
|
||||
extrinsic={extrinsic}
|
||||
ref={this.button}
|
||||
/>
|
||||
</Button.Group>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@ -189,10 +177,5 @@ class NewStake extends TxComponent<Props, State> {
|
||||
export default withMulti(
|
||||
NewStake,
|
||||
translate,
|
||||
withCalls<Props>(
|
||||
['rpc.system.chain', {
|
||||
propName: 'isUnsafeChain',
|
||||
transform: detectUnsafe
|
||||
}]
|
||||
)
|
||||
withApi
|
||||
);
|
||||
|
||||
@ -14,7 +14,7 @@ import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Responsive } from 'semantic-ui-react';
|
||||
import routing from '@polkadot/apps-routing';
|
||||
import { withCalls, withMulti } from '@polkadot/react-api';
|
||||
import { withApi, withMulti } from '@polkadot/react-api';
|
||||
import { Button, ChainImg, Icon, Menu, media } from '@polkadot/react-components';
|
||||
import { classes } from '@polkadot/react-components/util';
|
||||
import { BestNumber, Chain } from '@polkadot/react-query';
|
||||
@ -29,7 +29,6 @@ interface Props extends ApiProps, I18nProps {
|
||||
handleResize: () => void;
|
||||
isCollapsed: boolean;
|
||||
menuOpen: boolean;
|
||||
system_chain?: string;
|
||||
toggleMenu: () => void;
|
||||
}
|
||||
|
||||
@ -257,7 +256,5 @@ export default withMulti(
|
||||
}
|
||||
`,
|
||||
translate,
|
||||
withCalls<Props>(
|
||||
'rpc.system.chain'
|
||||
)
|
||||
withApi
|
||||
);
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ProviderInterface } from '@polkadot/rpc-provider/types';
|
||||
import { ChainProperties } from '@polkadot/types/interfaces';
|
||||
import { QueueTxPayloadAdd, QueueTxMessageSetStatus } from '@polkadot/react-components/Status/types';
|
||||
import { Prefix } from '@polkadot/util-crypto/address/types';
|
||||
import { ApiProps } from './types';
|
||||
@ -17,7 +16,7 @@ import { InputNumber } from '@polkadot/react-components/InputNumber';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import uiSettings from '@polkadot/ui-settings';
|
||||
import ApiSigner from '@polkadot/react-signer/ApiSigner';
|
||||
import { Text, u32 as U32 } from '@polkadot/types';
|
||||
import { u32 as U32 } from '@polkadot/types';
|
||||
import { formatBalance, isTestChain } from '@polkadot/util';
|
||||
import addressDefaults from '@polkadot/util-crypto/address/defaults';
|
||||
|
||||
@ -122,9 +121,11 @@ export default class Api extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
private async loadOnReady (api: ApiPromise): Promise<void> {
|
||||
const [properties, value, injectedAccounts] = await Promise.all([
|
||||
api.rpc.system.properties<ChainProperties>(),
|
||||
api.rpc.system.chain<Text>(),
|
||||
const [properties, _systemChain, _systemName, _systemVersion, injectedAccounts] = await Promise.all([
|
||||
api.rpc.system.properties(),
|
||||
api.rpc.system.chain(),
|
||||
api.rpc.system.name(),
|
||||
api.rpc.system.version(),
|
||||
web3Accounts().then((accounts): InjectedAccountExt[] =>
|
||||
accounts.map(({ address, meta }): InjectedAccountExt => ({
|
||||
address,
|
||||
@ -142,12 +143,12 @@ export default class Api extends React.PureComponent<Props, State> {
|
||||
) as Prefix;
|
||||
const tokenSymbol = properties.tokenSymbol.unwrapOr('DEV').toString();
|
||||
const tokenDecimals = properties.tokenDecimals.unwrapOr(DEFAULT_DECIMALS).toNumber();
|
||||
const chain = value
|
||||
? value.toString()
|
||||
: null;
|
||||
const isDevelopment = isTestChain(chain);
|
||||
const systemChain = _systemChain
|
||||
? _systemChain.toString()
|
||||
: '<unknown>';
|
||||
const isDevelopment = isTestChain(systemChain);
|
||||
|
||||
console.log('api: found chain', chain, JSON.stringify(properties));
|
||||
console.log('api: found chain', systemChain, JSON.stringify(properties));
|
||||
|
||||
// first setup the UI helpers
|
||||
formatBalance.setDefaults({
|
||||
@ -177,15 +178,17 @@ export default class Api extends React.PureComponent<Props, State> {
|
||||
this.setState({
|
||||
apiDefaultTx,
|
||||
apiDefaultTxSudo,
|
||||
chain,
|
||||
isApiReady: true,
|
||||
isDevelopment,
|
||||
isSubstrateV2
|
||||
isSubstrateV2,
|
||||
systemChain,
|
||||
systemName: _systemName.toString(),
|
||||
systemVersion: _systemVersion.toString()
|
||||
});
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { api, apiDefaultTx, apiDefaultTxSudo, chain, isApiConnected, isApiReady, isDevelopment, isSubstrateV2, isWaitingInjected, setApiUrl } = this.state;
|
||||
const { api, apiDefaultTx, apiDefaultTxSudo, isApiConnected, isApiReady, isDevelopment, isSubstrateV2, isWaitingInjected, setApiUrl, systemChain, systemName, systemVersion } = this.state;
|
||||
|
||||
return (
|
||||
<ApiContext.Provider
|
||||
@ -193,13 +196,15 @@ export default class Api extends React.PureComponent<Props, State> {
|
||||
api,
|
||||
apiDefaultTx,
|
||||
apiDefaultTxSudo,
|
||||
currentChain: chain || '<unknown>',
|
||||
isApiConnected,
|
||||
isApiReady: isApiReady && !!chain,
|
||||
isApiReady: isApiReady && !!systemChain,
|
||||
isDevelopment,
|
||||
isSubstrateV2,
|
||||
isWaitingInjected,
|
||||
setApiUrl
|
||||
setApiUrl,
|
||||
systemChain,
|
||||
systemName,
|
||||
systemVersion
|
||||
}}
|
||||
>
|
||||
{this.props.children}
|
||||
|
||||
@ -19,13 +19,15 @@ export interface ApiProps {
|
||||
api: ApiPromise;
|
||||
apiDefaultTx: SubmittableExtrinsicFunction;
|
||||
apiDefaultTxSudo: SubmittableExtrinsicFunction;
|
||||
currentChain: string;
|
||||
isApiConnected: boolean;
|
||||
isApiReady: boolean;
|
||||
isDevelopment: boolean;
|
||||
isSubstrateV2: boolean;
|
||||
isWaitingInjected: boolean;
|
||||
setApiUrl: (url?: string) => void;
|
||||
systemChain: string;
|
||||
systemName: string;
|
||||
systemVersion: string;
|
||||
}
|
||||
|
||||
export interface OnChangeCbObs {
|
||||
|
||||
@ -3,8 +3,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AccountId, AccountIndex, Address } from '@polkadot/types/interfaces';
|
||||
import { ApiProps } from '@polkadot/react-api/types';
|
||||
import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { AccountId, AccountIndex, Address } from '@polkadot/types/interfaces';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
@ -16,7 +17,7 @@ import keyring from '@polkadot/ui-keyring';
|
||||
import AddressInfo, { BalanceActiveType } from './AddressInfo';
|
||||
import { classes, getAddressName, getAddressTags, toShortAddress } from './util';
|
||||
import CopyButton from './CopyButton';
|
||||
import IdentityIcon from './IdentityIcon';
|
||||
import IdentityIcon, { getIdentityTheme } from './IdentityIcon';
|
||||
import Row, { RowProps, RowState as State, styles } from './Row';
|
||||
import translate from './translate';
|
||||
|
||||
@ -34,10 +35,10 @@ export interface Props extends I18nProps, RowProps {
|
||||
const DEFAULT_ADDR = '5'.padEnd(16, 'x');
|
||||
const ICON_SIZE = 48;
|
||||
|
||||
class AddressRow extends Row<Props, State> {
|
||||
class AddressRow extends Row<ApiProps & Props, State> {
|
||||
public state: State;
|
||||
|
||||
public constructor (props: Props) {
|
||||
public constructor (props: ApiProps & Props) {
|
||||
super(props);
|
||||
|
||||
this.state = this.createState();
|
||||
@ -177,7 +178,7 @@ class AddressRow extends Row<Props, State> {
|
||||
}
|
||||
|
||||
private renderIcon (): React.ReactNode {
|
||||
const { accounts_idAndIndex = [], iconInfo, withIcon = true } = this.props;
|
||||
const { accounts_idAndIndex = [], iconInfo, systemName, withIcon = true } = this.props;
|
||||
const { address } = this.state;
|
||||
const [accountId] = accounts_idAndIndex;
|
||||
|
||||
@ -190,11 +191,13 @@ class AddressRow extends Row<Props, State> {
|
||||
const Component = accountId
|
||||
? IdentityIcon
|
||||
: BaseIdentityIcon;
|
||||
const theme = getIdentityTheme(systemName);
|
||||
|
||||
return (
|
||||
<div className='ui--Row-icon'>
|
||||
<Component
|
||||
size={ICON_SIZE}
|
||||
theme={theme}
|
||||
value={address}
|
||||
/>
|
||||
{iconInfo && (
|
||||
@ -267,7 +270,7 @@ export {
|
||||
};
|
||||
|
||||
export default withMulti(
|
||||
styled(AddressRow as React.ComponentClass<Props, State>)`
|
||||
styled(AddressRow as React.ComponentClass<Props & ApiProps, State>)`
|
||||
${styles}
|
||||
`,
|
||||
translate,
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
// Copyright 2017-2019 @polkadot/apps 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 React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { withCalls, withMulti } from '@polkadot/react-api';
|
||||
import { Text } from '@polkadot/types';
|
||||
import { ApiContext } from '@polkadot/react-api';
|
||||
|
||||
// the imports here as a bit all-over, non-aphabetical - since we expect this to grow,
|
||||
// rather organise based on type, grouping chains and nodes as opposed to location
|
||||
@ -51,14 +49,13 @@ const LOGOS: Record<string, any> = {
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
injectedLogoChain?: any;
|
||||
injectedLogoNode?: any;
|
||||
logo?: keyof typeof LOGOS;
|
||||
onClick?: () => any;
|
||||
}
|
||||
|
||||
function ChainImg ({ className, injectedLogoChain, injectedLogoNode, logo = '', onClick }: Props): React.ReactElement<Props> {
|
||||
const img = LOGOS[logo] || injectedLogoChain || injectedLogoNode || EMPTY;
|
||||
function ChainImg ({ className, logo = '', onClick }: Props): React.ReactElement<Props> {
|
||||
const { systemChain, systemName } = useContext(ApiContext);
|
||||
const img = LOGOS[logo] || CHAINS[systemChain] || NODES[systemName] || EMPTY;
|
||||
|
||||
return (
|
||||
<img
|
||||
@ -70,19 +67,7 @@ function ChainImg ({ className, injectedLogoChain, injectedLogoNode, logo = '',
|
||||
);
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
styled(ChainImg)`
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
`,
|
||||
withCalls<Props>(
|
||||
['rpc.system.chain', {
|
||||
propName: 'injectedLogoChain',
|
||||
transform: (chain: Text): any | null => CHAINS[chain.toString()]
|
||||
}],
|
||||
['rpc.system.name', {
|
||||
propName: 'injectedLogoNode',
|
||||
transform: (node: Text): any | null => NODES[node.toString()]
|
||||
}]
|
||||
)
|
||||
);
|
||||
export default styled(ChainImg)`
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
`;
|
||||
|
||||
@ -5,35 +5,23 @@
|
||||
|
||||
import { AccountId } from '@polkadot/types/interfaces';
|
||||
import { IdentityProps } from '@polkadot/react-identicon/types';
|
||||
import { QueueAction$Add } from './Status/types';
|
||||
import { I18nProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Option } from '@polkadot/types';
|
||||
import { withCalls } from '@polkadot/react-api/with';
|
||||
import { ApiContext, withCalls, withMulti } from '@polkadot/react-api';
|
||||
import BaseIdentityIcon from '@polkadot/react-identicon';
|
||||
import uiSettings, { ICON_DEFAULT_HOST } from '@polkadot/ui-settings';
|
||||
import uiSettings from '@polkadot/ui-settings';
|
||||
|
||||
import { QueueConsumer } from './Status/Context';
|
||||
import StatusContext from './Status/Context';
|
||||
import translate from './translate';
|
||||
|
||||
interface CopyProps extends IdentityProps, I18nProps {
|
||||
queueAction?: QueueAction$Add;
|
||||
}
|
||||
|
||||
interface IconProps extends IdentityProps {
|
||||
interface Props extends IdentityProps, I18nProps {
|
||||
session_validators?: AccountId[];
|
||||
staking_bonded?: string | null;
|
||||
system_name?: string;
|
||||
}
|
||||
|
||||
interface Props extends IconProps, IdentityProps {}
|
||||
|
||||
interface State {
|
||||
isValidator: boolean;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
// overrides based on the actual software node type
|
||||
const NODES: Record<string, string> = {
|
||||
'edgeware-node': 'substrate',
|
||||
@ -49,83 +37,63 @@ const NULL_TOSTRING = {
|
||||
toString: (): null => null
|
||||
};
|
||||
|
||||
class CopyIcon extends React.PureComponent<CopyProps> {
|
||||
public render (): React.ReactNode {
|
||||
return (
|
||||
<BaseIdentityIcon
|
||||
{...this.props}
|
||||
onCopy={this.onCopy}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private onCopy = (account: string): void => {
|
||||
const { onCopy, queueAction, t } = this.props;
|
||||
|
||||
if (onCopy) {
|
||||
onCopy(account);
|
||||
}
|
||||
|
||||
if (queueAction) {
|
||||
queueAction({
|
||||
account,
|
||||
action: t('clipboard'),
|
||||
status: 'queued',
|
||||
message: t('address copied')
|
||||
});
|
||||
}
|
||||
}
|
||||
export function getIdentityTheme (systemName: string): 'empty' {
|
||||
return ((uiSettings.icon === 'default' && NODES[systemName]) || uiSettings.icon) as 'empty';
|
||||
}
|
||||
|
||||
const CopyIconI18N = translate(CopyIcon);
|
||||
function IdentityIcon ({ className, onCopy, prefix, session_validators, size, staking_bonded, style, t, theme, value }: Props): React.ReactElement<Props> {
|
||||
const { systemName } = useContext(ApiContext);
|
||||
const { queueAction } = useContext(StatusContext);
|
||||
const [isValidator, setIsValidator] = useState(false);
|
||||
const thisTheme = theme || getIdentityTheme(systemName);
|
||||
|
||||
class IdentityIcon extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
isValidator: false,
|
||||
theme: ICON_DEFAULT_HOST
|
||||
};
|
||||
|
||||
public static getDerivedStateFromProps ({ session_validators = [], staking_bonded, system_name, value }: Props): State {
|
||||
useEffect((): void => {
|
||||
const address = value
|
||||
? value.toString()
|
||||
: null;
|
||||
const isValidator = !!session_validators.find((validator): boolean =>
|
||||
[address, staking_bonded].includes(validator.toString())
|
||||
|
||||
setIsValidator(
|
||||
session_validators
|
||||
? session_validators.some((validator): boolean =>
|
||||
[address, staking_bonded].includes(validator.toString())
|
||||
)
|
||||
: false
|
||||
);
|
||||
}, [session_validators, value]);
|
||||
|
||||
return {
|
||||
isValidator,
|
||||
theme: (system_name && uiSettings.icon === 'default' && NODES[system_name]) || uiSettings.icon
|
||||
};
|
||||
}
|
||||
const _onCopy = (account: string): void => {
|
||||
onCopy && onCopy(account);
|
||||
queueAction && queueAction({
|
||||
account,
|
||||
action: t('clipboard'),
|
||||
status: 'queued',
|
||||
message: t('address copied')
|
||||
});
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { isValidator, theme } = this.state;
|
||||
|
||||
return (
|
||||
<QueueConsumer>
|
||||
{({ queueAction }): React.ReactNode =>
|
||||
<CopyIconI18N
|
||||
isHighlight={isValidator}
|
||||
theme={theme as 'substrate'}
|
||||
{...this.props}
|
||||
queueAction={queueAction}
|
||||
/>
|
||||
}
|
||||
</QueueConsumer>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<BaseIdentityIcon
|
||||
className={className}
|
||||
isHighlight={isValidator}
|
||||
onCopy={_onCopy}
|
||||
prefix={prefix}
|
||||
size={size}
|
||||
style={style}
|
||||
theme={thisTheme as 'substrate'}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default withCalls<Props>(
|
||||
'query.session.validators',
|
||||
['query.staking.bonded', {
|
||||
paramName: 'value',
|
||||
transform: (bonded: Option<AccountId>): string | null =>
|
||||
bonded.unwrapOr(NULL_TOSTRING).toString()
|
||||
}],
|
||||
['rpc.system.name', {
|
||||
transform: (node: Text): string =>
|
||||
node.toString()
|
||||
}]
|
||||
)(IdentityIcon);
|
||||
export default withMulti(
|
||||
IdentityIcon,
|
||||
translate,
|
||||
withCalls<Props>(
|
||||
'query.session.validators',
|
||||
['query.staking.bonded', {
|
||||
paramName: 'value',
|
||||
transform: (bonded: Option<AccountId>): string | null =>
|
||||
bonded.unwrapOr(NULL_TOSTRING).toString()
|
||||
}]
|
||||
)
|
||||
);
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import { BareProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import SUIInput from 'semantic-ui-react/dist/commonjs/elements/Input/Input';
|
||||
import { isUndefined } from '@polkadot/util';
|
||||
|
||||
@ -44,10 +44,6 @@ interface Props extends BareProps {
|
||||
withEllipsis?: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
name: string;
|
||||
}
|
||||
|
||||
// Find decimal separator used in current locale
|
||||
const getDecimalSeparator = (): string => 1.1
|
||||
.toLocaleString()
|
||||
@ -89,122 +85,91 @@ const isSelectAll = (key: string, isPreKeyDown: boolean): boolean =>
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export default class Input extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
name: `in_${counter++}_at_${Date.now()}`
|
||||
export default function Input ({ autoFocus = false, children, className, defaultValue, help, icon, isEditable = false, isAction = false, isDisabled = false, isError = false, isHidden = false, isReadOnly = false, label, max, maxLength, min, name, onBlur, onChange, onEnter, onKeyDown, onKeyUp, onPaste, placeholder, style, tabIndex, type = 'text', value, withEllipsis, withLabel }: Props): React.ReactElement<Props> {
|
||||
const [stateName] = useState(`in_${counter++}_at_${Date.now()}`);
|
||||
|
||||
const _onBlur = (): void => {
|
||||
onBlur && onBlur();
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { autoFocus = false, children, className, defaultValue, help, icon, isEditable = false, isAction = false, isDisabled = false, isError = false, isHidden = false, isReadOnly = false, label, max, maxLength, min, name, placeholder, style, tabIndex, type = 'text', value, withEllipsis, withLabel } = this.props;
|
||||
|
||||
return (
|
||||
<Labelled
|
||||
className={className}
|
||||
help={help}
|
||||
label={label}
|
||||
style={style}
|
||||
withEllipsis={withEllipsis}
|
||||
withLabel={withLabel}
|
||||
>
|
||||
<SUIInput
|
||||
action={isAction}
|
||||
autoFocus={autoFocus}
|
||||
className={
|
||||
isEditable
|
||||
? 'ui--Input edit icon'
|
||||
: 'ui--Input'
|
||||
}
|
||||
defaultValue={
|
||||
isUndefined(value)
|
||||
? (defaultValue || '')
|
||||
: undefined
|
||||
}
|
||||
disabled={isDisabled}
|
||||
error={!isDisabled && isError}
|
||||
hidden={isHidden}
|
||||
id={name}
|
||||
iconPosition={
|
||||
isUndefined(icon)
|
||||
? undefined
|
||||
: 'left'
|
||||
}
|
||||
max={max}
|
||||
maxLength={maxLength}
|
||||
min={min}
|
||||
name={name || this.state.name}
|
||||
onBlur={this.onBlur}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={this.onKeyUp}
|
||||
placeholder={placeholder}
|
||||
readOnly={isReadOnly}
|
||||
tabIndex={tabIndex}
|
||||
type={type}
|
||||
value={value}
|
||||
>
|
||||
<input
|
||||
autoComplete={
|
||||
type === 'password'
|
||||
? 'new-password'
|
||||
: 'off'
|
||||
}
|
||||
onPaste={this.onPaste}
|
||||
/>
|
||||
{
|
||||
isEditable
|
||||
? <i className='edit icon' />
|
||||
: undefined
|
||||
}
|
||||
{icon}
|
||||
{children}
|
||||
</SUIInput>
|
||||
</Labelled>
|
||||
);
|
||||
}
|
||||
|
||||
private onChange = (event: React.SyntheticEvent<Element>): void => {
|
||||
const { onChange } = this.props;
|
||||
const { value } = event.target as HTMLInputElement;
|
||||
|
||||
onChange && onChange(value);
|
||||
}
|
||||
|
||||
private onKeyDown = (event: React.KeyboardEvent<Element>): void => {
|
||||
const { onKeyDown } = this.props;
|
||||
|
||||
if (onKeyDown) {
|
||||
onKeyDown(event);
|
||||
}
|
||||
}
|
||||
|
||||
private onBlur = (): void => {
|
||||
const { onBlur } = this.props;
|
||||
|
||||
if (onBlur) {
|
||||
onBlur();
|
||||
}
|
||||
}
|
||||
|
||||
private onKeyUp = (event: React.KeyboardEvent<Element>): void => {
|
||||
const { onEnter, onKeyUp } = this.props;
|
||||
|
||||
if (onKeyUp) {
|
||||
onKeyUp(event);
|
||||
}
|
||||
const _onChange = ({ target }: React.SyntheticEvent<HTMLInputElement>): void => {
|
||||
onChange && onChange((target as HTMLInputElement).value);
|
||||
};
|
||||
const _onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {
|
||||
onKeyDown && onKeyDown(event);
|
||||
};
|
||||
const _onKeyUp = (event: React.KeyboardEvent<HTMLInputElement>): void => {
|
||||
onKeyUp && onKeyUp(event);
|
||||
|
||||
if (onEnter && event.keyCode === 13) {
|
||||
(event.target as any).blur();
|
||||
onEnter();
|
||||
}
|
||||
}
|
||||
};
|
||||
const _onPaste = (event: React.ClipboardEvent<HTMLInputElement>): void => {
|
||||
onPaste && onPaste(event);
|
||||
};
|
||||
|
||||
private onPaste = (event: React.ClipboardEvent<Element>): void => {
|
||||
const { onPaste } = this.props;
|
||||
|
||||
if (onPaste) {
|
||||
onPaste(event);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Labelled
|
||||
className={className}
|
||||
help={help}
|
||||
label={label}
|
||||
style={style}
|
||||
withEllipsis={withEllipsis}
|
||||
withLabel={withLabel}
|
||||
>
|
||||
<SUIInput
|
||||
action={isAction}
|
||||
autoFocus={autoFocus}
|
||||
className={
|
||||
isEditable
|
||||
? 'ui--Input edit icon'
|
||||
: 'ui--Input'
|
||||
}
|
||||
defaultValue={
|
||||
isUndefined(value)
|
||||
? (defaultValue || '')
|
||||
: undefined
|
||||
}
|
||||
disabled={isDisabled}
|
||||
error={!isDisabled && isError}
|
||||
hidden={isHidden}
|
||||
id={name}
|
||||
iconPosition={
|
||||
isUndefined(icon)
|
||||
? undefined
|
||||
: 'left'
|
||||
}
|
||||
max={max}
|
||||
maxLength={maxLength}
|
||||
min={min}
|
||||
name={name || stateName}
|
||||
onBlur={_onBlur}
|
||||
onChange={_onChange}
|
||||
onKeyDown={_onKeyDown}
|
||||
onKeyUp={_onKeyUp}
|
||||
placeholder={placeholder}
|
||||
readOnly={isReadOnly}
|
||||
tabIndex={tabIndex}
|
||||
type={type}
|
||||
value={value}
|
||||
>
|
||||
<input
|
||||
autoComplete={
|
||||
type === 'password'
|
||||
? 'new-password'
|
||||
: 'off'
|
||||
}
|
||||
onPaste={_onPaste}
|
||||
/>
|
||||
{isEditable && (
|
||||
<i className='edit icon' />
|
||||
)}
|
||||
{icon}
|
||||
{children}
|
||||
</SUIInput>
|
||||
</Labelled>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import IdentityIcon from '@polkadot/react-identicon';
|
||||
|
||||
import IdentityIcon from '../IdentityIcon';
|
||||
|
||||
interface Props {
|
||||
address: string;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import { BareProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import addressToAddress from './util/toAddress';
|
||||
@ -17,46 +17,32 @@ interface Props extends BareProps {
|
||||
onChange?: (address: string | null) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
address: string | null;
|
||||
isError: boolean;
|
||||
}
|
||||
function InputAddressSimple ({ className, help, label, onChange }: Props): React.ReactElement<Props> {
|
||||
const [address, setAddress] = useState<string | null>(null);
|
||||
|
||||
class InputAddressSimple extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
address: null,
|
||||
isError: true
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, help, label } = this.props;
|
||||
const { address, isError } = this.state;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Input
|
||||
help={help}
|
||||
isError={isError}
|
||||
label={label}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<IdentityIcon
|
||||
className='ui--InputAddressSimpleIcon'
|
||||
size={32}
|
||||
value={address}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private onChange = (_address: string): void => {
|
||||
const { onChange } = this.props;
|
||||
const _onChange = (_address: string): void => {
|
||||
const address = addressToAddress(_address) || null;
|
||||
|
||||
this.setState({ address, isError: !address }, (): void => {
|
||||
onChange && onChange(address);
|
||||
});
|
||||
}
|
||||
setAddress(address);
|
||||
|
||||
onChange && onChange(address);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Input
|
||||
help={help}
|
||||
isError={!address}
|
||||
label={label}
|
||||
onChange={_onChange}
|
||||
/>
|
||||
<IdentityIcon
|
||||
className='ui--InputAddressSimpleIcon'
|
||||
size={32}
|
||||
value={address}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default styled(InputAddressSimple)`
|
||||
|
||||
@ -32,60 +32,48 @@ interface Props extends BareProps {
|
||||
withLabel?: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
options: Option[];
|
||||
}
|
||||
|
||||
function loadTags (): string[] {
|
||||
return store.get('tags') || ['Default'];
|
||||
}
|
||||
|
||||
function valueToOption (value: string): Option {
|
||||
return { key: value, text: value, value };
|
||||
}
|
||||
|
||||
const tags = loadTags();
|
||||
const options = tags.map(valueToOption);
|
||||
|
||||
function saveTags (tags: string[]): void {
|
||||
store.set('tags', tags);
|
||||
}
|
||||
|
||||
const tags = loadTags();
|
||||
function onAddTag (value: string): void {
|
||||
tags.push(value);
|
||||
options.push(valueToOption(value));
|
||||
|
||||
export default class InputTags extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
options: tags.map((value): { key: string; text: string; value: string } => ({
|
||||
key: value, text: value, value
|
||||
}))
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, defaultValue, help, isDisabled, isError, label, onBlur, onChange, onClose, placeholder, searchInput, value, withLabel } = this.props;
|
||||
const { options } = this.state;
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
allowAdd={!isDisabled}
|
||||
className={className}
|
||||
defaultValue={defaultValue}
|
||||
help={help}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
isMultiple
|
||||
label={label}
|
||||
onAdd={this.onAdd}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
onClose={onClose}
|
||||
options={options}
|
||||
placeholder={placeholder}
|
||||
searchInput={searchInput}
|
||||
value={value}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private onAdd = (value: string): void => {
|
||||
tags.push(value);
|
||||
saveTags(tags);
|
||||
|
||||
this.setState(({ options }: State): Pick<State, never> => ({
|
||||
options: [...options, { key: value, text: value, value }]
|
||||
}));
|
||||
}
|
||||
saveTags(tags);
|
||||
}
|
||||
|
||||
export default function InputTags ({ className, defaultValue, help, isDisabled, isError, label, onBlur, onChange, onClose, placeholder, searchInput, value, withLabel }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<Dropdown
|
||||
allowAdd={!isDisabled}
|
||||
className={className}
|
||||
defaultValue={defaultValue}
|
||||
help={help}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
isMultiple
|
||||
label={label}
|
||||
onAdd={onAddTag}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
onClose={onClose}
|
||||
options={options}
|
||||
placeholder={placeholder}
|
||||
searchInput={searchInput}
|
||||
value={value}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
@ -22,77 +22,57 @@ export interface InsetProps extends RouteComponentProps {
|
||||
withBottomMargin?: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
isCollapsed: boolean;
|
||||
}
|
||||
function Inset ({ children, className, header, history, href, isCollapsible, isError, isSuccess, withBottomMargin, withTopMargin }: InsetProps): React.ReactElement<InsetProps> | null {
|
||||
const [isCollapsed, setIsCollapsed] = useState(true);
|
||||
|
||||
class Inset extends React.PureComponent<InsetProps, State> {
|
||||
public state: State = {
|
||||
isCollapsed: true
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { children, className, isCollapsible, isError, isSuccess, header, href, withBottomMargin, withTopMargin } = this.props;
|
||||
const { isCollapsed } = this.state;
|
||||
|
||||
if (!children) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
classes(
|
||||
'ui--Inset',
|
||||
href && 'as-link',
|
||||
isCollapsible && 'collapsible',
|
||||
(isError && !isSuccess) && 'error',
|
||||
(!isError && isSuccess) && 'success',
|
||||
withBottomMargin && 'bottom-margin',
|
||||
withTopMargin && 'top-margin',
|
||||
className
|
||||
)
|
||||
}
|
||||
>
|
||||
{isCollapsible && (
|
||||
<div
|
||||
className='header'
|
||||
onClick={this.toggleCollapsed}
|
||||
>
|
||||
<h3>{header}</h3>
|
||||
<Icon
|
||||
className={classes(isCollapsed && 'collapsed')}
|
||||
name='angle up'
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={classes('children', (isCollapsible && isCollapsed) && 'collapsed')}
|
||||
onClick={href ? this.onClick : undefined}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
if (!children) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private onClick = (): void => {
|
||||
const { history, href } = this.props;
|
||||
|
||||
const _onClick = (): void => {
|
||||
href && history.push(href);
|
||||
}
|
||||
};
|
||||
const _toggleCollapsed = (): void => setIsCollapsed(!isCollapsed);
|
||||
|
||||
private toggleCollapsed = (): void => {
|
||||
this.setState(({ isCollapsed }: State): Pick<State, never> => {
|
||||
return {
|
||||
isCollapsed: !isCollapsed
|
||||
};
|
||||
});
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
classes(
|
||||
'ui--Inset',
|
||||
href && 'as-link',
|
||||
isCollapsible && 'collapsible',
|
||||
(isError && !isSuccess) && 'error',
|
||||
(!isError && isSuccess) && 'success',
|
||||
withBottomMargin && 'bottom-margin',
|
||||
withTopMargin && 'top-margin',
|
||||
className
|
||||
)
|
||||
}
|
||||
>
|
||||
{isCollapsible && (
|
||||
<div
|
||||
className='header'
|
||||
onClick={_toggleCollapsed}
|
||||
>
|
||||
<h3>{header}</h3>
|
||||
<Icon
|
||||
className={classes(isCollapsed && 'collapsed')}
|
||||
name='angle up'
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={classes('children', (isCollapsible && isCollapsed) && 'collapsed')}
|
||||
onClick={_onClick}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withRouter(
|
||||
styled(Inset as React.ComponentClass<InsetProps, State>)`
|
||||
styled(Inset)`
|
||||
& {
|
||||
box-shadow: 0 3px 3px rgba(0,0,0,.2);
|
||||
position: relative;
|
||||
|
||||
@ -34,8 +34,8 @@ const TYPES: Record<string, string> = {
|
||||
};
|
||||
|
||||
function LinkPolkascan ({ className, data, t, type }: Props): React.ReactElement<Props> | null {
|
||||
const { currentChain } = useContext(ApiContext);
|
||||
const extChain = CHAINS[currentChain];
|
||||
const { systemChain } = useContext(ApiContext);
|
||||
const extChain = CHAINS[systemChain];
|
||||
const extType = TYPES[type];
|
||||
|
||||
if (!extChain || !extType) {
|
||||
|
||||
@ -4,132 +4,94 @@
|
||||
|
||||
import { TreasuryProposal as TreasuryProposalType } from '@polkadot/types/interfaces';
|
||||
import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { ApiProps } from '@polkadot/react-api/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Option } from '@polkadot/types';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { InputAddress, Labelled, Static } from '@polkadot/react-components';
|
||||
import { withMulti, withApi } from '@polkadot/react-api';
|
||||
import { ApiContext } from '@polkadot/react-api';
|
||||
import { Option } from '@polkadot/types';
|
||||
import { formatBalance } from '@polkadot/util';
|
||||
|
||||
import Inset, { InsetProps } from './Inset';
|
||||
import translate from './translate';
|
||||
|
||||
interface Props extends I18nProps, ApiProps {
|
||||
interface Props extends I18nProps {
|
||||
className?: string;
|
||||
asInset?: boolean;
|
||||
insetProps?: Partial<InsetProps>;
|
||||
onClick?: () => void;
|
||||
proposalId: string;
|
||||
proposalId?: string;
|
||||
proposal?: TreasuryProposalType | null;
|
||||
withLink?: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
proposal?: TreasuryProposalType | null;
|
||||
}
|
||||
function TreasuryProposal ({ className, asInset, insetProps, onClick, proposal, proposalId, t }: Props): React.ReactElement<Props> | null {
|
||||
const [stateProposal, setProposal] = useState<TreasuryProposalType | null>(null);
|
||||
const { api } = useContext(ApiContext);
|
||||
|
||||
class TreasuryProposal extends React.PureComponent<Props, State> {
|
||||
public state: State = {};
|
||||
|
||||
public constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
if (!props.proposal) {
|
||||
this.fetchProposal();
|
||||
}
|
||||
}
|
||||
|
||||
public static getDerivedStateFromProps ({ proposal }: Props, state: State): State | null {
|
||||
if (!state.proposal && proposal) {
|
||||
return { proposal };
|
||||
useEffect((): void => {
|
||||
if (!proposal && proposalId) {
|
||||
api.query.treasury
|
||||
.proposals<Option<TreasuryProposalType>>(proposalId)
|
||||
.then((proposal): TreasuryProposalType | null => proposal.unwrapOr(null))
|
||||
.catch((): null => null)
|
||||
.then(setProposal);
|
||||
} else {
|
||||
setProposal(proposal || null);
|
||||
}
|
||||
}, [proposal, proposalId]);
|
||||
|
||||
if (!stateProposal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public componentDidUpdate (): void {
|
||||
this.fetchProposal();
|
||||
}
|
||||
const { bond, beneficiary, proposer, value } = stateProposal;
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, asInset, insetProps, onClick, t } = this.props;
|
||||
const { proposal } = this.state;
|
||||
|
||||
if (!proposal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { bond, beneficiary, proposer, value } = proposal;
|
||||
|
||||
const inner = (
|
||||
<>
|
||||
<Labelled label={t('proposed by')}>
|
||||
<InputAddress
|
||||
isDisabled
|
||||
defaultValue={proposer}
|
||||
value={proposer}
|
||||
withLabel={false}
|
||||
/>
|
||||
</Labelled>
|
||||
<Labelled label={t('beneficiary')}>
|
||||
<InputAddress
|
||||
isDisabled
|
||||
defaultValue={beneficiary}
|
||||
value={beneficiary}
|
||||
withLabel={false}
|
||||
/>
|
||||
</Labelled>
|
||||
<Static label={t('value')}>
|
||||
{formatBalance(value)}
|
||||
</Static>
|
||||
<Static label={t('bond')}>
|
||||
{formatBalance(bond)}
|
||||
</Static>
|
||||
</>
|
||||
);
|
||||
|
||||
if (asInset) {
|
||||
return (
|
||||
<Inset
|
||||
className={className}
|
||||
{...insetProps}
|
||||
>
|
||||
{inner}
|
||||
</Inset>
|
||||
);
|
||||
}
|
||||
const inner = (
|
||||
<>
|
||||
<Labelled label={t('proposed by')}>
|
||||
<InputAddress
|
||||
isDisabled
|
||||
defaultValue={proposer}
|
||||
value={proposer}
|
||||
withLabel={false}
|
||||
/>
|
||||
</Labelled>
|
||||
<Labelled label={t('beneficiary')}>
|
||||
<InputAddress
|
||||
isDisabled
|
||||
defaultValue={beneficiary}
|
||||
value={beneficiary}
|
||||
withLabel={false}
|
||||
/>
|
||||
</Labelled>
|
||||
<Static label={t('value')}>
|
||||
{formatBalance(value)}
|
||||
</Static>
|
||||
<Static label={t('bond')}>
|
||||
{formatBalance(bond)}
|
||||
</Static>
|
||||
</>
|
||||
);
|
||||
|
||||
if (asInset) {
|
||||
return (
|
||||
<div
|
||||
<Inset
|
||||
className={className}
|
||||
onClick={onClick && onClick}
|
||||
{...insetProps}
|
||||
>
|
||||
{inner}
|
||||
</div>
|
||||
</Inset>
|
||||
);
|
||||
}
|
||||
|
||||
private fetchProposal = (): void => {
|
||||
const { api, proposalId } = this.props;
|
||||
|
||||
if (!this.state.proposal && proposalId) {
|
||||
api.query.treasury
|
||||
.proposals<Option<TreasuryProposalType>>(proposalId)
|
||||
.then((proposal): void => {
|
||||
this.setState({
|
||||
proposal: proposal.unwrapOr(null)
|
||||
});
|
||||
})
|
||||
.catch((): void => {
|
||||
console.error('Error fetching proposal');
|
||||
});
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
onClick={onClick && onClick}
|
||||
>
|
||||
{inner}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
TreasuryProposal,
|
||||
translate,
|
||||
withApi
|
||||
);
|
||||
export default translate(TreasuryProposal);
|
||||
|
||||
@ -4,35 +4,37 @@
|
||||
|
||||
import { Props } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Compact } from '@polkadot/types';
|
||||
import { Button } from '@polkadot/react-components';
|
||||
|
||||
import BaseBytes from './BaseBytes';
|
||||
import File from './File';
|
||||
|
||||
interface State {
|
||||
isFileDrop: boolean;
|
||||
}
|
||||
export default function Bytes ({ className, defaultValue, isDisabled, isOptional, isError, label, name, onChange, onEnter, style, type, withLabel }: Props): React.ReactElement<Props> {
|
||||
const [isFileDrop, setIsFileDrop] = useState(false);
|
||||
|
||||
export default class Bytes extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
isFileDrop: false
|
||||
const _toggleFile = (): void => setIsFileDrop(true);
|
||||
const _onChangeFile = (value: Uint8Array): void => {
|
||||
onChange && onChange({
|
||||
isValid: value.length !== 0,
|
||||
value: Compact.addLengthPrefix(value)
|
||||
});
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { isDisabled } = this.props;
|
||||
const { isFileDrop } = this.state;
|
||||
|
||||
return !isDisabled && isFileDrop
|
||||
? this.renderFile()
|
||||
: this.renderInput();
|
||||
}
|
||||
|
||||
private renderInput (): React.ReactNode {
|
||||
const { className, defaultValue, isDisabled, isOptional, isError, label, name, onChange, onEnter, style, type, withLabel } = this.props;
|
||||
|
||||
return (
|
||||
return !isDisabled && isFileDrop
|
||||
? (
|
||||
<File
|
||||
className={className}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
label={label}
|
||||
onChange={_onChangeFile}
|
||||
style={style}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<BaseBytes
|
||||
className={className}
|
||||
defaultValue={defaultValue}
|
||||
@ -49,54 +51,12 @@ export default class Bytes extends React.PureComponent<Props, State> {
|
||||
withLabel={withLabel}
|
||||
withLength
|
||||
>
|
||||
{this.renderFileButton()}
|
||||
{!isDisabled && (
|
||||
<Button
|
||||
icon='file'
|
||||
onClick={_toggleFile}
|
||||
/>
|
||||
)}
|
||||
</BaseBytes>
|
||||
);
|
||||
}
|
||||
|
||||
private renderFileButton (): React.ReactNode {
|
||||
const { isDisabled } = this.props;
|
||||
|
||||
if (isDisabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
icon='file'
|
||||
onClick={this.toggleFile}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private renderFile (): React.ReactNode {
|
||||
const { className, isDisabled, isError, label, style, withLabel } = this.props;
|
||||
|
||||
return (
|
||||
<File
|
||||
className={className}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
label={label}
|
||||
onChange={this.onChangeFile}
|
||||
style={style}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private toggleFile = (): void => {
|
||||
this.setState(({ isFileDrop }: State): State => ({
|
||||
isFileDrop: !isFileDrop
|
||||
} as unknown as State));
|
||||
}
|
||||
|
||||
private onChangeFile = (value: Uint8Array): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
onChange && onChange({
|
||||
isValid: value.length !== 0,
|
||||
value: Compact.addLengthPrefix(value)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,19 +4,15 @@
|
||||
|
||||
import { Props } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default class Null extends React.PureComponent<Props> {
|
||||
public componentDidMount (): void {
|
||||
const { onChange } = this.props;
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
export default function Null ({ onChange }: Props): React.ReactElement<Props> | null {
|
||||
useEffect((): void => {
|
||||
onChange && onChange({
|
||||
isValid: true,
|
||||
value: null
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
public render (): React.ReactNode {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { BaseProps, Props as ComponentProps, ComponentMap } from '../types';
|
||||
import { BaseProps, Props as CProps, ComponentMap } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { classes } from '@polkadot/react-components/util';
|
||||
import translate from '@polkadot/react-components/translate';
|
||||
import { isUndefined } from '@polkadot/util';
|
||||
@ -17,52 +17,32 @@ interface Props extends I18nProps, BaseProps {
|
||||
overrides?: ComponentMap;
|
||||
}
|
||||
|
||||
interface State {
|
||||
Component: React.ComponentType<ComponentProps> | null;
|
||||
}
|
||||
function Param ({ className, defaultValue, isDisabled, isOptional, name, onChange, onEnter, overrides, style, type }: Props): React.ReactElement<Props> | null {
|
||||
const compRef = useRef<React.ComponentType<CProps> | null>(findComponent(type, overrides));
|
||||
|
||||
class ParamComponent extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
Component: null
|
||||
};
|
||||
|
||||
public static getDerivedStateFromProps ({ overrides, type }: Props): State {
|
||||
return {
|
||||
Component: !type
|
||||
? null
|
||||
: findComponent(type, overrides)
|
||||
};
|
||||
if (!compRef.current) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { Component } = this.state;
|
||||
|
||||
if (Component === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { className, defaultValue, isDisabled, isOptional, name, onChange, onEnter, style, type } = this.props;
|
||||
|
||||
return (
|
||||
<Component
|
||||
className={classes('ui--Param', className)}
|
||||
defaultValue={defaultValue}
|
||||
key={`${name}:${type}`}
|
||||
isDisabled={isDisabled}
|
||||
isOptional={isOptional}
|
||||
label={
|
||||
isUndefined(name)
|
||||
? type.type
|
||||
: `${name}: ${type.type}`
|
||||
}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
onEnter={onEnter}
|
||||
style={style}
|
||||
type={type}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<compRef.current
|
||||
className={classes('ui--Param', className)}
|
||||
defaultValue={defaultValue}
|
||||
key={`${name}:${type}`}
|
||||
isDisabled={isDisabled}
|
||||
isOptional={isOptional}
|
||||
label={
|
||||
isUndefined(name)
|
||||
? type.type
|
||||
: `${name}: ${type.type}`
|
||||
}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
onEnter={onEnter}
|
||||
style={style}
|
||||
type={type}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(ParamComponent);
|
||||
export default translate(Param);
|
||||
|
||||
@ -3,27 +3,25 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BareProps, CallProps } from '@polkadot/react-api/types';
|
||||
import { BareProps } from '@polkadot/react-api/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Text } from '@polkadot/types';
|
||||
import { withCalls } from '@polkadot/react-api';
|
||||
import React, { useContext } from 'react';
|
||||
import { ApiContext } from '@polkadot/react-api';
|
||||
|
||||
interface Props extends BareProps, CallProps {
|
||||
interface Props extends BareProps {
|
||||
children?: React.ReactNode;
|
||||
label?: React.ReactNode;
|
||||
system_chain?: Text;
|
||||
}
|
||||
|
||||
export function Chain ({ children, className, label = '', style, system_chain = new Text('unknown') }: Props): React.ReactElement<Props> {
|
||||
export default function Chain ({ children, className, label = '', style }: Props): React.ReactElement<Props> {
|
||||
const { systemChain } = useContext(ApiContext);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
{label}{system_chain.toString()}{children}
|
||||
{label}{systemChain}{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withCalls<Props>('rpc.system.chain')(Chain);
|
||||
|
||||
@ -3,27 +3,25 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BareProps, CallProps } from '@polkadot/react-api/types';
|
||||
import { BareProps } from '@polkadot/react-api/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Text } from '@polkadot/types';
|
||||
import { withCalls } from '@polkadot/react-api';
|
||||
import React, { useContext } from 'react';
|
||||
import { ApiContext } from '@polkadot/react-api';
|
||||
|
||||
interface Props extends BareProps, CallProps {
|
||||
interface Props extends BareProps {
|
||||
children?: React.ReactNode;
|
||||
label?: React.ReactNode;
|
||||
system_name?: Text;
|
||||
}
|
||||
|
||||
export function NodeName ({ children, className, label = '', style, system_name = new Text('unknown') }: Props): React.ReactElement<Props> {
|
||||
export default function NodeName ({ children, className, label = '', style }: Props): React.ReactElement<Props> {
|
||||
const { systemName } = useContext(ApiContext);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
{label}{system_name.toString()}{children}
|
||||
{label}{systemName}{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withCalls<Props>('rpc.system.name')(NodeName);
|
||||
|
||||
@ -3,27 +3,25 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BareProps, CallProps } from '@polkadot/react-api/types';
|
||||
import { BareProps } from '@polkadot/react-api/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Text } from '@polkadot/types';
|
||||
import { withCalls } from '@polkadot/react-api';
|
||||
import React, { useContext } from 'react';
|
||||
import { ApiContext } from '@polkadot/react-api';
|
||||
|
||||
interface Props extends BareProps, CallProps {
|
||||
interface Props extends BareProps {
|
||||
children?: React.ReactNode;
|
||||
label?: React.ReactNode;
|
||||
system_version?: Text;
|
||||
}
|
||||
|
||||
export function NodeVersion ({ children, className, label = '', style, system_version = new Text('-') }: Props): React.ReactElement<Props> {
|
||||
export default function NodeVersion ({ children, className, label = '', style }: Props): React.ReactElement<Props> {
|
||||
const { systemVersion } = useContext(ApiContext);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
{label}{system_version.toString()}{children}
|
||||
{label}{systemVersion}{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withCalls<Props>('rpc.system.version')(NodeVersion);
|
||||
|
||||
@ -8,7 +8,7 @@ import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { ExtraFees } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Compact, UInt } from '@polkadot/types';
|
||||
import { withCalls, withMulti } from '@polkadot/react-api';
|
||||
import { Icon } from '@polkadot/react-components';
|
||||
@ -27,65 +27,59 @@ interface State extends ExtraFees {
|
||||
isBelowMinimum: boolean;
|
||||
}
|
||||
|
||||
export class Proposal extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
extraFees: new BN(0),
|
||||
extraAmount: new BN(0),
|
||||
const ZERO = new BN(0);
|
||||
|
||||
export function Proposal ({ deposit, democracy_minimumDeposit = ZERO, onChange, t }: Props): React.ReactElement<Props> {
|
||||
const [{ extraAmount, isBelowMinimum }, setState] = useState<State>({
|
||||
extraFees: ZERO,
|
||||
extraAmount: ZERO,
|
||||
extraWarn: false,
|
||||
isBelowMinimum: false
|
||||
};
|
||||
});
|
||||
|
||||
public static getDerivedStateFromProps ({
|
||||
deposit,
|
||||
democracy_minimumDeposit = new BN(0),
|
||||
onChange
|
||||
}: Props): State {
|
||||
useEffect((): void => {
|
||||
const extraAmount = deposit instanceof Compact
|
||||
? deposit.toBn()
|
||||
: deposit;
|
||||
const isBelowMinimum = extraAmount.lt(democracy_minimumDeposit);
|
||||
|
||||
const update = {
|
||||
extraAmount,
|
||||
extraFees: new BN(0),
|
||||
extraFees: ZERO,
|
||||
extraWarn: isBelowMinimum
|
||||
};
|
||||
|
||||
onChange(update);
|
||||
|
||||
return {
|
||||
setState({
|
||||
...update,
|
||||
isBelowMinimum
|
||||
};
|
||||
}
|
||||
});
|
||||
}, [democracy_minimumDeposit]);
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { democracy_minimumDeposit = new BN(0), t } = this.props;
|
||||
const { extraAmount, isBelowMinimum } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
isBelowMinimum
|
||||
? <div><Icon name='warning sign' />{t('The deposit is below the {{minimum}} minimum required for the proposal to be evaluated', {
|
||||
replace: {
|
||||
minimum: formatBalance(democracy_minimumDeposit)
|
||||
}
|
||||
})}</div>
|
||||
: undefined
|
||||
}
|
||||
{
|
||||
extraAmount.isZero()
|
||||
? undefined
|
||||
: <div><Icon name='arrow right' />{t('The deposit of {{deposit}} will be reserved until the proposal is completed', {
|
||||
replace: {
|
||||
deposit: formatBalance(extraAmount)
|
||||
}
|
||||
})}</div>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{isBelowMinimum && (
|
||||
<div>
|
||||
<Icon name='warning sign' />
|
||||
{t('The deposit is below the {{minimum}} minimum required for the proposal to be evaluated', {
|
||||
replace: {
|
||||
minimum: formatBalance(democracy_minimumDeposit)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{!extraAmount.isZero() && (
|
||||
<div>
|
||||
<Icon name='arrow right' />
|
||||
{t('The deposit of {{deposit}} will be reserved until the proposal is completed', {
|
||||
replace: {
|
||||
deposit: formatBalance(extraAmount)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
|
||||
@ -8,7 +8,7 @@ import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { ExtraFees } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Compact, UInt } from '@polkadot/types';
|
||||
import { withCalls, withMulti } from '@polkadot/react-api';
|
||||
import { Icon } from '@polkadot/react-components';
|
||||
@ -30,16 +30,16 @@ interface State extends ExtraFees {
|
||||
isNoEffect: boolean;
|
||||
}
|
||||
|
||||
export class Transfer extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
export function Transfer ({ amount, balances_all = ZERO_BALANCE, fees, onChange, t }: Props): React.ReactElement<Props> {
|
||||
const [{ isCreation, isNoEffect }, setState] = useState<State>({
|
||||
extraFees: new BN(0),
|
||||
extraAmount: new BN(0),
|
||||
extraWarn: false,
|
||||
isCreation: false,
|
||||
isNoEffect: false
|
||||
};
|
||||
});
|
||||
|
||||
public static getDerivedStateFromProps ({ amount, balances_all = ZERO_BALANCE, fees, onChange }: Props): State {
|
||||
useEffect((): void => {
|
||||
let extraFees = new BN(fees.transferFee);
|
||||
|
||||
if (balances_all.votingBalance.isZero()) {
|
||||
@ -58,47 +58,37 @@ export class Transfer extends React.PureComponent<Props, State> {
|
||||
|
||||
onChange(update);
|
||||
|
||||
return {
|
||||
setState({
|
||||
...update,
|
||||
isCreation,
|
||||
isNoEffect
|
||||
};
|
||||
}
|
||||
});
|
||||
}, [balances_all, fees]);
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { fees, t } = this.props;
|
||||
const { isCreation, isNoEffect } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
isNoEffect
|
||||
? (
|
||||
<div>
|
||||
<Icon name='warning sign' />
|
||||
{t('The final recipient balance is less or equal to {{existentialDeposit}} (the existential amount) and will not be reflected',
|
||||
{
|
||||
replace: {
|
||||
existentialDeposit: formatBalance(fees.existentialDeposit)
|
||||
}
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
{
|
||||
isCreation
|
||||
? <div><Icon name='warning sign' />{t('A fee of {{creationFee}} will be deducted from the sender since the destination account does not exist', {
|
||||
replace: {
|
||||
creationFee: formatBalance(fees.creationFee)
|
||||
}
|
||||
})}</div>
|
||||
: undefined
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{isNoEffect && (
|
||||
<div>
|
||||
<Icon name='warning sign' />
|
||||
{t('The final recipient balance is less or equal to {{existentialDeposit}} (the existential amount) and will not be reflected', {
|
||||
replace: {
|
||||
existentialDeposit: formatBalance(fees.existentialDeposit)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{isCreation && (
|
||||
<div>
|
||||
<Icon name='warning sign' />
|
||||
{t('A fee of {{creationFee}} will be deducted from the sender since the destination account does not exist', {
|
||||
replace: {
|
||||
creationFee: formatBalance(fees.creationFee)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
|
||||
@ -51,6 +51,7 @@ const LENGTH_ADDRESS = 32 + 1; // publicKey + prefix
|
||||
const LENGTH_ERA = 2; // assuming mortals
|
||||
const LENGTH_SIGNATURE = 64; // assuming ed25519 or sr25519
|
||||
const LENGTH_VERSION = 1; // 0x80 & version
|
||||
const ZERO = new BN(0);
|
||||
|
||||
export const calcTxLength = (extrinsic?: IExtrinsic | null, nonce?: BN, tip?: BN): BN => {
|
||||
return new BN(
|
||||
@ -66,11 +67,11 @@ export const calcTxLength = (extrinsic?: IExtrinsic | null, nonce?: BN, tip?: BN
|
||||
|
||||
export class FeeDisplay extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
allFees: new BN(0),
|
||||
allTotal: new BN(0),
|
||||
allFees: ZERO,
|
||||
allTotal: ZERO,
|
||||
allWarn: false,
|
||||
extraAmount: new BN(0),
|
||||
extraFees: new BN(0),
|
||||
extraAmount: ZERO,
|
||||
extraFees: ZERO,
|
||||
extraWarn: false,
|
||||
hasAvailable: false,
|
||||
isRemovable: false,
|
||||
@ -78,7 +79,7 @@ export class FeeDisplay extends React.PureComponent<Props, State> {
|
||||
overLimit: false
|
||||
};
|
||||
|
||||
public static getDerivedStateFromProps ({ accountId, balances_all = ZERO_BALANCE, api, extrinsic, balances_fees = ZERO_FEES_BALANCES, system_accountNonce = new BN(0), tip }: Props, prevState: State): State | null {
|
||||
public static getDerivedStateFromProps ({ accountId, balances_all = ZERO_BALANCE, api, extrinsic, balances_fees = ZERO_FEES_BALANCES, system_accountNonce = ZERO, tip }: Props, prevState: State): State | null {
|
||||
if (!accountId || !extrinsic) {
|
||||
return null;
|
||||
}
|
||||
@ -91,10 +92,10 @@ export class FeeDisplay extends React.PureComponent<Props, State> {
|
||||
const isSameExtrinsic = prevState.extMethod === extMethod && prevState.extSection === extSection;
|
||||
const extraAmount = isSameExtrinsic
|
||||
? prevState.extraAmount
|
||||
: new BN(0);
|
||||
: ZERO;
|
||||
const extraFees = isSameExtrinsic
|
||||
? prevState.extraFees
|
||||
: new BN(0);
|
||||
: ZERO;
|
||||
const extraWarn = isSameExtrinsic
|
||||
? prevState.extraWarn
|
||||
: false;
|
||||
@ -142,11 +143,9 @@ export class FeeDisplay extends React.PureComponent<Props, State> {
|
||||
|
||||
const feeClass = !hasAvailable || overLimit || isRemovable
|
||||
? 'error'
|
||||
: (
|
||||
allWarn
|
||||
? 'warning'
|
||||
: 'normal'
|
||||
);
|
||||
: allWarn
|
||||
? 'warning'
|
||||
: 'normal';
|
||||
|
||||
// display all the errors, warning and information messages (in that order)
|
||||
return (
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { KeyringPair } from '@polkadot/keyring/types';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Password } from '@polkadot/react-components';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
|
||||
@ -21,58 +21,34 @@ interface Props extends I18nProps {
|
||||
value?: string | null;
|
||||
}
|
||||
|
||||
interface State {
|
||||
isError: boolean;
|
||||
isInjected?: boolean;
|
||||
isLocked: boolean;
|
||||
pair?: KeyringPair;
|
||||
function getPair (address?: string | null): KeyringPair | null {
|
||||
try {
|
||||
return keyring.getPair(address as string);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class Unlock extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
isError: false,
|
||||
isLocked: false
|
||||
};
|
||||
function Unlock ({ autoFocus, error, onChange, onEnter, password, t, tabIndex, value }: Props): React.ReactElement<Props> | null {
|
||||
const [pair] = useState<KeyringPair | null>(getPair(value));
|
||||
|
||||
public static getDerivedStateFromProps ({ error, value }: Props): State | null {
|
||||
const pair = keyring.getPair(value as string);
|
||||
|
||||
if (!pair) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { isLocked, meta: { isInjected = false } } = pair;
|
||||
|
||||
return {
|
||||
isError: !!error,
|
||||
isInjected,
|
||||
isLocked,
|
||||
pair
|
||||
};
|
||||
if (!pair || !(pair.isLocked) || pair.meta.isInjected) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { autoFocus, onChange, onEnter, password, t, tabIndex } = this.props;
|
||||
const { isError, isInjected, isLocked } = this.state;
|
||||
|
||||
if (isInjected || !isLocked) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='ui--signer-Signer-Unlock'>
|
||||
<Password
|
||||
autoFocus={autoFocus}
|
||||
isError={isError}
|
||||
label={t('unlock account with password')}
|
||||
onChange={onChange}
|
||||
onEnter={onEnter}
|
||||
tabIndex={tabIndex}
|
||||
value={password}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className='ui--signer-Signer-Unlock'>
|
||||
<Password
|
||||
autoFocus={autoFocus}
|
||||
isError={!!error}
|
||||
label={t('unlock account with password')}
|
||||
onChange={onChange}
|
||||
onEnter={onEnter}
|
||||
tabIndex={tabIndex}
|
||||
value={password}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(Unlock);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user