Simplify app using setState (#23)
* Remove extra subjects from app-explorer * Use bs58 addresses * Base upgrades subject -> onChange * app-storage & app-explorer works * Remove useless placeholder * Progres... * Rename Submission -> Selection * Baby steps... things display now... * setState operational * Explicit onChange * Work around eslint issues (!!!) * @flow fixes * Small styling updates * Small app content styling updates * Simplify
This commit is contained in:
parent
351af88ab2
commit
072c5d73f1
2
flow-typed/react-i18next.js
vendored
2
flow-typed/react-i18next.js
vendored
@ -3,7 +3,7 @@
|
||||
declare type I18Next$Translate$Config = {
|
||||
defaultValue: string,
|
||||
replace?: {
|
||||
[string]: string
|
||||
[string]: any
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
|
||||
import React from 'react';
|
||||
import BestNumber from '@polkadot/rx-react/BestNumber';
|
||||
|
||||
import BestHash from './BestHash';
|
||||
import BlockHeaders from './BlockHeaders';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
|
||||
function App ({ className, style, t }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={['explorer--App', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<BestNumber
|
||||
className='explorer--BestNumber'
|
||||
label={t('app.bestNumber', {
|
||||
defaultValue: 'best #'
|
||||
})}
|
||||
/>
|
||||
<BestHash />
|
||||
<BlockHeaders />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
@ -6,21 +6,14 @@
|
||||
import type { Header } from '@polkadot/primitives/header';
|
||||
|
||||
import headerHash from '@polkadot/primitives-codec/header/hash';
|
||||
import Div from '@polkadot/rx-react/Div';
|
||||
import withApiCall from '@polkadot/rx-react/with/apiCall';
|
||||
import withApiDiv from '@polkadot/rx-react/with/apiDiv';
|
||||
import u8aToHexShort from '@polkadot/util/u8a/toHexShort';
|
||||
|
||||
const apiMethod = {
|
||||
method: 'newHead',
|
||||
section: 'chain'
|
||||
};
|
||||
|
||||
const props = {
|
||||
className: 'explorer--BestHash',
|
||||
format: (value?: Header): ?string =>
|
||||
value
|
||||
export default withApiDiv({ name: 'newHead', section: 'chain' })(
|
||||
(value?: Header): ?string => {
|
||||
return value
|
||||
? u8aToHexShort(headerHash(value))
|
||||
: value
|
||||
};
|
||||
|
||||
export default withApiCall(apiMethod)(Div, props);
|
||||
: value;
|
||||
},
|
||||
{ className: 'explorer--BestHash' }
|
||||
);
|
||||
|
||||
@ -6,15 +6,14 @@
|
||||
import type { Header } from '@polkadot/primitives/header';
|
||||
|
||||
import React from 'react';
|
||||
import Div from '@polkadot/rx-react/Div';
|
||||
import withApiCall from '@polkadot/rx-react/with/apiCall';
|
||||
import withApiDiv from '@polkadot/rx-react/with/apiDiv';
|
||||
|
||||
import BlockHeader from './BlockHeader';
|
||||
|
||||
let blockHeaders: Array<Header> = [];
|
||||
|
||||
const apiMethod = {
|
||||
method: 'newHead',
|
||||
name: 'newHead',
|
||||
section: 'chain'
|
||||
};
|
||||
|
||||
@ -36,15 +35,14 @@ const apiOptions = {
|
||||
}
|
||||
};
|
||||
|
||||
const props = {
|
||||
className: 'explorer--BlockHeaders',
|
||||
format: (value?: Array<Header> = []): React$Node =>
|
||||
value.map((value) => (
|
||||
export default withApiDiv(apiMethod, apiOptions)(
|
||||
(value?: Array<Header> = []): Array<React$Node> => {
|
||||
return value.map((value) => (
|
||||
<BlockHeader
|
||||
key={value.number.toString()}
|
||||
value={value}
|
||||
/>
|
||||
))
|
||||
};
|
||||
|
||||
export default withApiCall(apiMethod, apiOptions)(Div, props);
|
||||
));
|
||||
},
|
||||
{ className: 'explorer--BlockHeaders' }
|
||||
);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.explorer--App {
|
||||
padding: 1em;
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
.explorer--App .rx--updated {
|
||||
|
||||
@ -3,8 +3,35 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
|
||||
import './index.css';
|
||||
|
||||
import App from './App';
|
||||
import React from 'react';
|
||||
import BestNumber from '@polkadot/rx-react/BestNumber';
|
||||
|
||||
export default App;
|
||||
import BestHash from './BestHash';
|
||||
import BlockHeaders from './BlockHeaders';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
|
||||
function App ({ className, style, t }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={['explorer--App', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<BestNumber
|
||||
className='explorer--BestNumber'
|
||||
label={t('app.bestNumber', {
|
||||
defaultValue: 'best #'
|
||||
})}
|
||||
/>
|
||||
<BestHash />
|
||||
<BlockHeaders />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
|
||||
@ -8,31 +8,35 @@ import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import React from 'react';
|
||||
import Label from 'semantic-ui-react/dist/es/elements/Label';
|
||||
import Balance from '@polkadot/rx-react/Balance';
|
||||
import withObservableParams from '@polkadot/rx-react/with/observableParams';
|
||||
import InputAddress from '@polkadot/ui-react-app/src/InputAddress';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
defaultValue?: Uint8Array,
|
||||
isError?: boolean,
|
||||
label: string,
|
||||
subject: rxjs$BehaviorSubject<Uint8Array>
|
||||
onChange: (publicKey: Uint8Array) => void
|
||||
};
|
||||
|
||||
class Account extends React.PureComponent<Props> {
|
||||
Balance: React$ComponentType<*>;
|
||||
type State = {
|
||||
publicKey?: Uint8Array
|
||||
};
|
||||
|
||||
class Account extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.Balance = withObservableParams(props.subject)(Balance, {
|
||||
className: 'ui disabled dropdown selection'
|
||||
});
|
||||
this.state = {
|
||||
publicKey: props.defaultValue
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, label, subject, style, t } = this.props;
|
||||
const Balance = this.Balance;
|
||||
const { className, defaultValue, label, style, t } = this.props;
|
||||
const { publicKey } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -41,9 +45,10 @@ class Account extends React.PureComponent<Props> {
|
||||
>
|
||||
<div className='large'>
|
||||
<InputAddress
|
||||
defaultValue={defaultValue}
|
||||
label={label}
|
||||
onChange={this.onChange}
|
||||
placeholder='0x...'
|
||||
subject={subject}
|
||||
/>
|
||||
</div>
|
||||
<div className='small'>
|
||||
@ -52,11 +57,22 @@ class Account extends React.PureComponent<Props> {
|
||||
defaultValue: 'with an available balance of'
|
||||
})}
|
||||
</Label>
|
||||
<Balance />
|
||||
<Balance
|
||||
className='ui disabled dropdown selection'
|
||||
params={publicKey}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChange = (publicKey: Uint8Array): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
this.setState({ publicKey }, () =>
|
||||
onChange(publicKey)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(Account);
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { QueueTx } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import withObservable from '@polkadot/rx-react/with/observable';
|
||||
|
||||
import Signer from './Signer';
|
||||
import Submission from './Submission';
|
||||
import Status from './Status';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
|
||||
const CLOSE_STATUS = ['cancelled', 'error', 'sent'];
|
||||
|
||||
const queue: rxjs$BehaviorSubject<QueueTx> = new BehaviorSubject(({
|
||||
isValid: false,
|
||||
status: 'incomplete'
|
||||
}: $Shape<QueueTx>));
|
||||
|
||||
const QueuedSigner = withObservable(queue)(Signer);
|
||||
const QueuedStatus = withObservable(queue)(Status);
|
||||
|
||||
queue.subscribe(({ status }) => {
|
||||
if (CLOSE_STATUS.includes(status)) {
|
||||
setTimeout(() => {
|
||||
const tx = queue.getValue();
|
||||
|
||||
if (CLOSE_STATUS.includes(tx.status)) {
|
||||
queue.next({
|
||||
...tx,
|
||||
status: 'incomplete'
|
||||
});
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
function App ({ className, style }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={['extrinsics--App', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<Submission subject={queue} />
|
||||
<QueuedSigner subject={queue} />
|
||||
<QueuedStatus />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
102
packages/app-extrinsics/src/Extrinsic.js
Normal file
102
packages/app-extrinsics/src/Extrinsic.js
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Extrinsic } from '@polkadot/extrinsics/types';
|
||||
import type { BareProps } from '@polkadot/ui-react-app/types';
|
||||
import type { RawParam } from '@polkadot/ui-react-app/Params/types';
|
||||
import type { EncodedMessage } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import encode from '@polkadot/extrinsics-codec/encode/extrinsic';
|
||||
import InputExtrinsic from '@polkadot/ui-react-app/src/InputExtrinsic';
|
||||
import Params from '@polkadot/ui-react-app/src/Params';
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
|
||||
import paramComponents from './Params';
|
||||
|
||||
type Props = BareProps & {
|
||||
defaultValue: Extrinsic,
|
||||
isError?: boolean,
|
||||
isPrivate?: boolean,
|
||||
labelMethod?: string,
|
||||
labelSection?: string,
|
||||
onChange: (values: EncodedMessage) => void
|
||||
};
|
||||
|
||||
type State = {
|
||||
extrinsic: Extrinsic,
|
||||
values: Array<RawParam>
|
||||
};
|
||||
|
||||
export default class ExtrinsicDisplay extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = ({
|
||||
extrinsic: props.defaultValue
|
||||
}: $Shape<State>);
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, defaultValue, isError, isPrivate, labelMethod, labelSection, style } = this.props;
|
||||
const { extrinsic } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['extrinsics--Extrinsic', 'ui--form', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='full'>
|
||||
<InputExtrinsic
|
||||
defaultValue={defaultValue}
|
||||
isError={isError}
|
||||
isPrivate={isPrivate}
|
||||
labelMethod={labelMethod}
|
||||
labelSection={labelSection}
|
||||
onChange={this.onChangeExtrinsic}
|
||||
/>
|
||||
</div>
|
||||
<Params
|
||||
extrinsic={extrinsic}
|
||||
onChange={this.onChangeValues}
|
||||
overrides={paramComponents}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
nextState (newState: $Shape<State>): void {
|
||||
this.setState(newState, () => {
|
||||
const { onChange } = this.props;
|
||||
const { extrinsic, values } = this.state;
|
||||
const params = Object.values(extrinsic.params || {});
|
||||
const isValid = values.length === params.length &&
|
||||
params.reduce((isValid, param, index) =>
|
||||
isValid &&
|
||||
!isUndefined(values[index]) &&
|
||||
!isUndefined(values[index].value) &&
|
||||
values[index].isValid, true);
|
||||
const value = isValid && extrinsic.params
|
||||
? encode(extrinsic, values.map((p) => p.value))
|
||||
: new Uint8Array([]);
|
||||
|
||||
onChange({
|
||||
extrinsic,
|
||||
isValid,
|
||||
value
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onChangeExtrinsic = (extrinsic: Extrinsic): void => {
|
||||
this.nextState({ extrinsic });
|
||||
};
|
||||
|
||||
onChangeValues = (values: Array<RawParam>): void => {
|
||||
this.nextState({ values });
|
||||
}
|
||||
}
|
||||
@ -1,103 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { BareProps } from '@polkadot/ui-react-app/types';
|
||||
import type { EncodedParams } from '../types';
|
||||
import type { Subjects } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import encodeExtrinsic from '@polkadot/extrinsics-codec/encode/extrinsic';
|
||||
import withObservable from '@polkadot/rx-react/with/observable';
|
||||
import InputExtrinsic from '@polkadot/ui-react-app/src/InputExtrinsic';
|
||||
import Params from '@polkadot/ui-react-app/src/Params';
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
|
||||
import paramComponents from '../Params';
|
||||
import createSubjects from './subjects';
|
||||
|
||||
type Props = BareProps & {
|
||||
isError?: boolean,
|
||||
isPrivate?: boolean,
|
||||
labelMethod?: string,
|
||||
labelSection?: string,
|
||||
subject: rxjs$BehaviorSubject<EncodedParams>
|
||||
};
|
||||
|
||||
export default class Extrinsic extends React.PureComponent<Props> {
|
||||
subjects: Subjects;
|
||||
subscriptions: Array<rxjs$ISubscription>;
|
||||
Params: React$ComponentType<*>;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.subjects = createSubjects(props.subject);
|
||||
this.Params = withObservable(this.subjects.method)(Params);
|
||||
this.subscriptions = [];
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.subscriptions = [
|
||||
this.subjects.params.subscribe(this.onChange),
|
||||
this.subjects.method.subscribe(this.onChange)
|
||||
];
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.subscriptions.forEach((s) =>
|
||||
s.unsubscribe()
|
||||
);
|
||||
}
|
||||
|
||||
onChange = (): void => {
|
||||
const extrinsic = this.subjects.method.getValue();
|
||||
const values = this.subjects.params.getValue();
|
||||
const isValid = !!extrinsic.params &&
|
||||
Object
|
||||
.values(extrinsic.params)
|
||||
.reduce((isValid, param, index) => {
|
||||
return isValid &&
|
||||
!isUndefined(values[index]) &&
|
||||
!isUndefined(values[index].value) &&
|
||||
values[index].isValid;
|
||||
}, !!values);
|
||||
const raw = values.map((param) => param && param.value);
|
||||
const data = extrinsic.params
|
||||
? encodeExtrinsic(extrinsic, raw)
|
||||
: new Uint8Array([]);
|
||||
|
||||
this.props.subject.next({
|
||||
data,
|
||||
extrinsic,
|
||||
isValid
|
||||
});
|
||||
};
|
||||
|
||||
render (): React$Node {
|
||||
const { className, isError, isPrivate, labelMethod, labelSection, style } = this.props;
|
||||
const { Params, subjects } = this;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['extrinsics--Extrinsic', 'ui--form', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='full'>
|
||||
<InputExtrinsic
|
||||
isError={isError}
|
||||
isPrivate={isPrivate}
|
||||
labelMethod={labelMethod}
|
||||
labelSection={labelSection}
|
||||
subject={subjects.method}
|
||||
/>
|
||||
</div>
|
||||
<Params
|
||||
overrides={paramComponents}
|
||||
subject={subjects.params}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Extrinsic } from '@polkadot/extrinsics/types';
|
||||
import type { EncodedParams } from '../types';
|
||||
import type { Subjects } from './types';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
export default function subjects (subject: rxjs$BehaviorSubject<EncodedParams>): Subjects {
|
||||
const extrinsic = subject.getValue().extrinsic || ({}: $Shape<Extrinsic>);
|
||||
|
||||
return {
|
||||
method: new BehaviorSubject(extrinsic),
|
||||
params: new BehaviorSubject([])
|
||||
};
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Extrinsic } from '@polkadot/extrinsics/types';
|
||||
import type { RawParam } from '@polkadot/ui-react-app/Params/types';
|
||||
|
||||
export type Subjects = {
|
||||
method: rxjs$BehaviorSubject<Extrinsic>,
|
||||
params: rxjs$BehaviorSubject<Array<RawParam>>
|
||||
};
|
||||
@ -3,49 +3,31 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Props } from '@polkadot/ui-react-app/Params/types';
|
||||
import type { Props as BaseProps } from '@polkadot/ui-react-app/Params/types';
|
||||
|
||||
import React from 'react';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
import BaseAccount from '../Account';
|
||||
|
||||
export default class Account extends React.PureComponent<Props> {
|
||||
account: rxjs$BehaviorSubject<Uint8Array>;
|
||||
subscriptions: Array<rxjs$ISubscription>
|
||||
type Props = BaseProps & {
|
||||
defaultValue?: Uint8Array
|
||||
};
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
export default function Account ({ className, defaultValue, index, isError, onChange, label, style }: Props): React$Node {
|
||||
const _onChange = (value?: Uint8Array): void =>
|
||||
onChange(index, {
|
||||
isValid: !!value && value.length === 32,
|
||||
value
|
||||
});
|
||||
|
||||
this.account = new BehaviorSubject(new Uint8Array([]));
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.subscriptions = [
|
||||
this.account.subscribe((value: Uint8Array) =>
|
||||
this.props.subject.next({
|
||||
isValid: !!value && value.length === 32,
|
||||
value
|
||||
})
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.subscriptions.forEach((s) =>
|
||||
s.unsubscribe()
|
||||
);
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { isError, label } = this.props;
|
||||
|
||||
return (
|
||||
<BaseAccount
|
||||
isError={isError}
|
||||
label={label}
|
||||
subject={this.account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<BaseAccount
|
||||
className={className}
|
||||
defaultValue={defaultValue}
|
||||
isError={isError}
|
||||
label={label}
|
||||
onChange={_onChange}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,66 +5,47 @@
|
||||
|
||||
import type { Props as BaseProps } from '@polkadot/ui-react-app/Params/types';
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { EncodedParams } from '../types';
|
||||
import type { EncodedMessage } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import extrinsics from '@polkadot/extrinsics-substrate';
|
||||
|
||||
import Extrinsic from '../Extrinsic';
|
||||
import translate from '../translate';
|
||||
|
||||
type Props = BaseProps & I18nProps;
|
||||
|
||||
class Proposal extends React.PureComponent<Props> {
|
||||
extrinsic: rxjs$BehaviorSubject<EncodedParams>;
|
||||
subscriptions: Array<rxjs$ISubscription>
|
||||
const defaultValue = extrinsics.consensus.methods.private.setCode;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
function Proposal ({ className, index, isError, onChange, label, style, t }: Props): React$Node {
|
||||
const _onChange = ({ isValid, value }: EncodedMessage): void =>
|
||||
onChange(index, {
|
||||
isValid,
|
||||
value
|
||||
});
|
||||
|
||||
this.extrinsic = new BehaviorSubject(({ isValid: false }: $Shape<EncodedParams>));
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.subscriptions = [
|
||||
this.extrinsic.subscribe(({ data, isValid }: EncodedParams) =>
|
||||
this.props.subject.next({
|
||||
isValid,
|
||||
value: data
|
||||
})
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.subscriptions.forEach((s) =>
|
||||
s.unsubscribe()
|
||||
);
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { isError, label, t } = this.props;
|
||||
|
||||
return (
|
||||
<Extrinsic
|
||||
isError={isError}
|
||||
isPrivate
|
||||
labelMethod={t('proposal.method', {
|
||||
defaultValue: '{{label}} (extrinsic)',
|
||||
replace: {
|
||||
label
|
||||
}
|
||||
})}
|
||||
labelSection={t('proposal.method', {
|
||||
defaultValue: '{{label}} (section)',
|
||||
replace: {
|
||||
label
|
||||
}
|
||||
})}
|
||||
subject={this.extrinsic}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Extrinsic
|
||||
className={className}
|
||||
defaultValue={defaultValue}
|
||||
isError={isError}
|
||||
isPrivate
|
||||
labelMethod={t('proposal.method', {
|
||||
defaultValue: '{{label}} (extrinsic)',
|
||||
replace: {
|
||||
label
|
||||
}
|
||||
})}
|
||||
labelSection={t('proposal.method', {
|
||||
defaultValue: '{{label}} (section)',
|
||||
replace: {
|
||||
label
|
||||
}
|
||||
})}
|
||||
onChange={_onChange}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(Proposal);
|
||||
|
||||
35
packages/app-extrinsics/src/Selection/Nonce.js
Normal file
35
packages/app-extrinsics/src/Selection/Nonce.js
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { BareProps } from '@polkadot/ui-react-app/types';
|
||||
|
||||
import React from 'react';
|
||||
import Label from 'semantic-ui-react/dist/es/elements/Label';
|
||||
import RxNonce from '@polkadot/rx-react/Nonce';
|
||||
|
||||
type Props = BareProps & {
|
||||
label: string,
|
||||
onChange: (value: BN) => void,
|
||||
value?: Uint8Array
|
||||
};
|
||||
|
||||
export default function Nonce ({ className, label, style, onChange, value }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={['ui--form', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='small'>
|
||||
<Label>{label}</Label>
|
||||
<RxNonce
|
||||
className='ui disabled dropdown selection'
|
||||
onChange={onChange}
|
||||
params={value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -2,14 +2,14 @@
|
||||
/* This software may be modified and distributed under the terms
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.extrinsics--Submission {}
|
||||
.extrinsics--Selection {}
|
||||
|
||||
.extrinsics--Submission-ButtonRow {
|
||||
.extrinsics--Selection-ButtonRow {
|
||||
margin: 1em 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.extrinsics--Submission-ButtonRow .button {
|
||||
margin-left: 0.5rem;
|
||||
.extrinsics--Selection-ButtonRow .button {
|
||||
margin: 0 0 0 0.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
140
packages/app-extrinsics/src/Selection/index.js
Normal file
140
packages/app-extrinsics/src/Selection/index.js
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { EncodedMessage, QueueTx } from '../types';
|
||||
|
||||
import './Selection.css';
|
||||
|
||||
import React from 'react';
|
||||
import Button from 'semantic-ui-react/dist/es/elements/Button';
|
||||
import extrinsics from '@polkadot/extrinsics-substrate';
|
||||
|
||||
import Account from '../Account';
|
||||
import Extrinsic from '../Extrinsic';
|
||||
import translate from '../translate';
|
||||
import Nonce from './Nonce';
|
||||
|
||||
type Props = I18nProps & {
|
||||
onQueue: (value: QueueTx) => void
|
||||
};
|
||||
|
||||
type State = {
|
||||
isValid: boolean,
|
||||
encoded: EncodedMessage,
|
||||
nonce: BN,
|
||||
publicKey: Uint8Array
|
||||
};
|
||||
|
||||
const defaultExtrinsic = extrinsics.staking.methods.public.transfer;
|
||||
|
||||
let id = 0;
|
||||
|
||||
class Selection extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = ({
|
||||
isValid: false
|
||||
}: $Shape<State>);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, style, t } = this.props;
|
||||
const { publicKey, isValid } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['extrinsics--Selection', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<Account
|
||||
label={t('display.sender', {
|
||||
defaultValue: 'using the selected account'
|
||||
})}
|
||||
onChange={this.onChangeSender}
|
||||
/>
|
||||
<Extrinsic
|
||||
defaultValue={defaultExtrinsic}
|
||||
labelMethod={t('display.method', {
|
||||
defaultValue: 'submit the following extrinsic'
|
||||
})}
|
||||
onChange={this.onChangeMessage}
|
||||
/>
|
||||
<Nonce
|
||||
label={t('display.nonce', {
|
||||
defaultValue: 'with an index'
|
||||
})}
|
||||
onChange={this.onChangeNonce}
|
||||
value={publicKey}
|
||||
/>
|
||||
<div className='extrinsics--Selection-ButtonRow'>
|
||||
<Button
|
||||
disabled={!isValid}
|
||||
onClick={this.onQueue}
|
||||
primary
|
||||
>
|
||||
{t('submit.label', {
|
||||
defaultValue: 'Submit Extrinsic'
|
||||
})}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
nextState (newState: $Shape<State>): void {
|
||||
this.setState(
|
||||
(prevState: State): State => {
|
||||
const { encoded = prevState.encoded, nonce = prevState.nonce, publicKey = prevState.publicKey } = newState;
|
||||
const isValid = !!(
|
||||
publicKey &&
|
||||
publicKey.length &&
|
||||
encoded &&
|
||||
encoded.isValid
|
||||
);
|
||||
|
||||
return {
|
||||
encoded,
|
||||
isValid,
|
||||
nonce,
|
||||
publicKey
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onChangeMessage = (encoded: EncodedMessage): void => {
|
||||
this.nextState({ encoded });
|
||||
}
|
||||
|
||||
onChangeNonce = (nonce: BN): void => {
|
||||
this.nextState({ nonce });
|
||||
}
|
||||
|
||||
onChangeSender = (publicKey: Uint8Array): void => {
|
||||
this.nextState({ publicKey });
|
||||
}
|
||||
|
||||
onQueue = (): void => {
|
||||
const { onQueue } = this.props;
|
||||
const { encoded: { extrinsic, isValid, value }, nonce, publicKey } = this.state;
|
||||
|
||||
onQueue({
|
||||
extrinsic,
|
||||
id: ++id,
|
||||
isValid,
|
||||
nonce,
|
||||
publicKey,
|
||||
status: 'queued',
|
||||
value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(Selection);
|
||||
@ -19,7 +19,7 @@ type Props = I18nProps & {
|
||||
value: QueueTx
|
||||
};
|
||||
|
||||
function Decoded ({ className, style, t, value: { data, extrinsic, index, publicKey } }: Props): React$Node {
|
||||
function Decoded ({ className, style, t, value: { extrinsic: { name, section }, nonce, publicKey, value } }: Props): React$Node {
|
||||
const from = addressEncode(publicKey);
|
||||
|
||||
return (
|
||||
@ -35,7 +35,7 @@ function Decoded ({ className, style, t, value: { data, extrinsic, index, public
|
||||
<div className='expanded'>
|
||||
<p>
|
||||
<Trans i18nkey='decoded.short'>
|
||||
You are about to sign a message from <span className='code'>{from}</span> calling <span className='code'>{extrinsic.section}_{extrinsic.name}</span> with an index of <span className='code'>{index.toString()}</span>
|
||||
You are about to sign a message from <span className='code'>{from}</span> calling <span className='code'>{section}_{name}</span> with an index of <span className='code'>{nonce.toString()}</span>
|
||||
</Trans>
|
||||
</p>
|
||||
<p>
|
||||
@ -43,7 +43,9 @@ function Decoded ({ className, style, t, value: { data, extrinsic, index, public
|
||||
defaultValue: 'The encoded parameters contains the data'
|
||||
})}
|
||||
</p>
|
||||
<p className='code'>{u8aToHexShort(data, 512)}</p>
|
||||
<p className='code'>
|
||||
{u8aToHexShort(value, 512)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
|
||||
@ -19,23 +19,23 @@ import Decoded from './Decoded';
|
||||
import submitExtrinsic from './submit';
|
||||
|
||||
type Props = I18nProps & ApiProps & {
|
||||
subject: rxjs$BehaviorSubject<QueueTx>,
|
||||
value: QueueTx
|
||||
onSetStatus: (id: number, status: string) => void,
|
||||
queue: Array<QueueTx>
|
||||
};
|
||||
|
||||
function Signer ({ api, className, subject, style, t, value }: Props): React$Node {
|
||||
if (value.status !== 'queued') {
|
||||
function Signer ({ api, className, onSetStatus, queue, style, t }: Props): React$Node {
|
||||
const first = queue.find(({ status }) => status === 'queued');
|
||||
|
||||
if (!first) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const onClose = (): void => {
|
||||
subject.next({
|
||||
...value,
|
||||
status: 'cancelled'
|
||||
});
|
||||
};
|
||||
const onSign = (): void => {
|
||||
submitExtrinsic(api, value, subject);
|
||||
const onClose = (): void =>
|
||||
onSetStatus(first.id, 'cancelled');
|
||||
const onSign = async (): Promise<void> => {
|
||||
const status = await submitExtrinsic(api, first);
|
||||
|
||||
onSetStatus(first.id, status);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -50,7 +50,7 @@ function Signer ({ api, className, subject, style, t, value }: Props): React$Nod
|
||||
defaultValue: 'Sign and submit'
|
||||
})}
|
||||
</Modal.Header>
|
||||
<Decoded value={value} />
|
||||
<Decoded value={first} />
|
||||
<Modal.Actions>
|
||||
<Button onClick={onClose}>
|
||||
{t('signer.cancel', {
|
||||
|
||||
@ -11,9 +11,9 @@ import keyring from '@polkadot/ui-react-app/src/keyring';
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
import u8aToHex from '@polkadot/util/u8a/toHex';
|
||||
|
||||
export default function submit (api: RxApiInterface, tx: QueueTx, subject: rxjs$BehaviorSubject<QueueTx>): Promise<void> {
|
||||
const message = encodeCall(tx.publicKey, tx.index, tx.data);
|
||||
const signature = keyring.getPair(tx.publicKey).sign(message);
|
||||
export default function submit (api: RxApiInterface, { nonce, publicKey, value }: QueueTx): Promise<string> {
|
||||
const message = encodeCall(publicKey, nonce, value);
|
||||
const signature = keyring.getPair(publicKey).sign(message);
|
||||
|
||||
console.log('submitExtrinsic: message =', u8aToHex(message));
|
||||
console.log('submitExtrinsic: signature =', u8aToHex(signature));
|
||||
@ -24,17 +24,11 @@ export default function submit (api: RxApiInterface, tx: QueueTx, subject: rxjs$
|
||||
.then((result) => {
|
||||
console.log('submitExtrinsic: result=', result);
|
||||
|
||||
subject.next({
|
||||
...tx,
|
||||
status: 'sent'
|
||||
});
|
||||
return 'sent';
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('submitExtrinsic: error=', error);
|
||||
|
||||
subject.next({
|
||||
...tx,
|
||||
status: 'error'
|
||||
});
|
||||
return 'error';
|
||||
});
|
||||
}
|
||||
|
||||
@ -3,35 +3,42 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { BareProps } from '@polkadot/ui-react-app/types';
|
||||
import type { QueueTx } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
value: QueueTx
|
||||
type Props = BareProps & {
|
||||
queue: Array<QueueTx>
|
||||
}
|
||||
|
||||
function Status ({ className, style, value: { extrinsic, isValid, status } }: Props): React$Node {
|
||||
if (!isValid || ['incomplete'].includes(status)) {
|
||||
export default function Status ({ className, queue, style }: Props): React$Node {
|
||||
const available = queue.filter(({ isValid, status }) =>
|
||||
!['completed', 'incomplete'].includes(status)
|
||||
);
|
||||
|
||||
if (!available.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['extrinsics--Status', status, className].join(' ')}
|
||||
className={['extrinsics--Status', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='header'>
|
||||
{extrinsic.section}_{extrinsic.name}
|
||||
</div>
|
||||
<div className='status'>
|
||||
{status}
|
||||
</div>
|
||||
{available.map(({ extrinsic: { name, section }, id, status }) =>
|
||||
<div
|
||||
className={['extrinsics--Status-Item', status].join(' ')}
|
||||
key={id}
|
||||
>
|
||||
<div className='header'>
|
||||
{section}_{name}
|
||||
</div>
|
||||
<div className='status'>
|
||||
{status}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(Status);
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { BareProps } from '@polkadot/ui-react-app/types';
|
||||
|
||||
import React from 'react';
|
||||
import Label from 'semantic-ui-react/dist/es/elements/Label';
|
||||
import RxNonce from '@polkadot/rx-react/Nonce';
|
||||
import withObservableParams from '@polkadot/rx-react/with/observableParams';
|
||||
|
||||
type Props = BareProps & {
|
||||
label: string,
|
||||
subject: rxjs$BehaviorSubject<BN>,
|
||||
value: rxjs$BehaviorSubject<Uint8Array>
|
||||
};
|
||||
|
||||
export default class Nonce extends React.PureComponent<Props> {
|
||||
Nonce: React$ComponentType<*>;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
// NOTE we basically get away with not checking updates since the parent is only created once
|
||||
this.Nonce = withObservableParams(props.value)(RxNonce);
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, label, style, subject } = this.props;
|
||||
const Nonce = this.Nonce;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['ui--form', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='small'>
|
||||
<Label>{label}</Label>
|
||||
<Nonce
|
||||
className='ui disabled dropdown selection'
|
||||
subject={subject}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { QueueTx } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import Button from 'semantic-ui-react/dist/es/elements/Button';
|
||||
|
||||
import translate from '../translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
subject: rxjs$BehaviorSubject<QueueTx>,
|
||||
value: QueueTx;
|
||||
};
|
||||
|
||||
function Submit ({ className, subject, style, t, value }: Props): React$Node {
|
||||
const onSubmit = (): void => {
|
||||
subject.next({
|
||||
...value,
|
||||
status: 'queued'
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
disabled={!value.isValid}
|
||||
onClick={onSubmit}
|
||||
primary
|
||||
>
|
||||
{t('submit.label', {
|
||||
defaultValue: 'Submit Extrinsic'
|
||||
})}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(Submit);
|
||||
@ -1,95 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { QueueTx } from '../types';
|
||||
import type { Subjects } from './types';
|
||||
|
||||
import './Submission.css';
|
||||
|
||||
import React from 'react';
|
||||
import withObservable from '@polkadot/rx-react/with/observable';
|
||||
|
||||
import Account from '../Account';
|
||||
import Extrinsic from '../Extrinsic';
|
||||
import translate from '../translate';
|
||||
import Nonce from './Nonce';
|
||||
import Submit from './Submit';
|
||||
import createSubjects from './subjects';
|
||||
|
||||
type Props = I18nProps & {
|
||||
subject: rxjs$BehaviorSubject<QueueTx>
|
||||
};
|
||||
|
||||
class Submission extends React.PureComponent<Props> {
|
||||
Submit: React$ComponentType<*>;
|
||||
subjects: Subjects;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.subjects = createSubjects();
|
||||
this.Submit = withObservable(this.subjects.call)(Submit);
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.subjects.index.subscribe(this.onChange);
|
||||
this.subjects.method.subscribe(this.onChange);
|
||||
this.subjects.sender.subscribe(this.onChange);
|
||||
}
|
||||
|
||||
onChange = (): void => {
|
||||
const index = this.subjects.index.getValue();
|
||||
const message = this.subjects.method.getValue();
|
||||
const publicKey = this.subjects.sender.getValue();
|
||||
const isValid = !!(publicKey && publicKey.length && message && message.isValid);
|
||||
|
||||
this.subjects.call.next({
|
||||
data: message.data,
|
||||
extrinsic: message.extrinsic,
|
||||
isValid,
|
||||
index,
|
||||
publicKey,
|
||||
status: 'incomplete'
|
||||
});
|
||||
};
|
||||
|
||||
render (): React$Node {
|
||||
const { className, subject, style, t } = this.props;
|
||||
const { Submit, subjects } = this;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['extrinsics--Submission', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<Account
|
||||
label={t('display.sender', {
|
||||
defaultValue: 'using the selected account'
|
||||
})}
|
||||
subject={subjects.sender}
|
||||
/>
|
||||
<Extrinsic
|
||||
labelMethod={t('display.method', {
|
||||
defaultValue: 'submit the following extrinsic'
|
||||
})}
|
||||
subject={subjects.method}
|
||||
/>
|
||||
<Nonce
|
||||
label={t('display.nonce', {
|
||||
defaultValue: 'with an index'
|
||||
})}
|
||||
subject={subjects.index}
|
||||
value={subjects.sender}
|
||||
/>
|
||||
<div className='extrinsics--Submission-ButtonRow'>
|
||||
<Submit subject={subject} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(Submission);
|
||||
@ -1,25 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { EncodedParams, QueueTx } from '../types';
|
||||
import type { Subjects } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import map from '@polkadot/extrinsics-substrate';
|
||||
|
||||
export default function createSubjects (): Subjects {
|
||||
return {
|
||||
index: new BehaviorSubject(new BN(0)),
|
||||
call: new BehaviorSubject(({
|
||||
isValid: false
|
||||
}: $Shape<QueueTx>)),
|
||||
method: new BehaviorSubject(({
|
||||
extrinsic: map.staking.methods.public.transfer,
|
||||
isValid: false
|
||||
}: $Shape<EncodedParams>)),
|
||||
sender: new BehaviorSubject(new Uint8Array([]))
|
||||
};
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { EncodedParams, QueueTx } from '../types';
|
||||
|
||||
export type Subjects = {
|
||||
call: rxjs$BehaviorSubject<QueueTx>,
|
||||
index: rxjs$BehaviorSubject<BN>,
|
||||
method: rxjs$BehaviorSubject<EncodedParams>,
|
||||
sender: rxjs$BehaviorSubject<Uint8Array>
|
||||
};
|
||||
@ -3,25 +3,29 @@
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.extrinsics--App {
|
||||
padding: 1em;
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
.extrinsics--Status {
|
||||
background: #bcdaf0;
|
||||
bottom: 0;
|
||||
display: inline-block;
|
||||
padding: 0.5rem 1rem;
|
||||
position: fixed;
|
||||
bottom: 0.125rem;
|
||||
right: 0.125rem;
|
||||
text-align: center;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.extrinsics--Status .status {
|
||||
.extrinsics--Status-Item {
|
||||
background: #bcdaf0;
|
||||
margin-bottom: 0.125rem;
|
||||
padding: 0.5rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.extrinsics--Status-Item .status {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.extrinsics--Status.error {
|
||||
.extrinsics--Status-Item.error {
|
||||
background: red;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@ -3,8 +3,76 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { BareProps } from '@polkadot/ui-react-app/types';
|
||||
import type { QueueTx, QueueTx$Status } from './types';
|
||||
|
||||
import './index.css';
|
||||
|
||||
import App from './App';
|
||||
import React from 'react';
|
||||
|
||||
export default App;
|
||||
import Selection from './Selection';
|
||||
import Signer from './Signer';
|
||||
import Status from './Status';
|
||||
|
||||
type Props = BareProps & {};
|
||||
|
||||
type State = {
|
||||
queue: Array<QueueTx>
|
||||
};
|
||||
|
||||
export default class App extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
queue: []
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style } = this.props;
|
||||
const { queue } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['extrinsics--App', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<Selection onQueue={this.onQueue} />
|
||||
<Signer
|
||||
onSetStatus={this.setStatus}
|
||||
queue={queue}
|
||||
/>
|
||||
<Status queue={queue} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
setStatus = (id: number, status: QueueTx$Status): void => {
|
||||
this.setState(
|
||||
(prevState: State): State => ({
|
||||
queue: prevState.queue.map((item) =>
|
||||
item.id === id
|
||||
? { ...item, status }
|
||||
: item
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
if (!['cancelled', 'error', 'sent'].includes(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => this.setStatus(id, 'completed'), 5000);
|
||||
}
|
||||
|
||||
onQueue = (value: QueueTx): void => {
|
||||
this.setState(
|
||||
(prevState: State): State => ({
|
||||
queue: prevState.queue.concat([value])
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,16 +6,17 @@
|
||||
import type BN from 'bn.js';
|
||||
import type { Extrinsic } from '@polkadot/extrinsics/types';
|
||||
|
||||
export type EncodedParams = {
|
||||
data: Uint8Array,
|
||||
export type EncodedMessage = {
|
||||
extrinsic: Extrinsic,
|
||||
isValid: boolean
|
||||
isValid: boolean,
|
||||
value: Uint8Array
|
||||
};
|
||||
|
||||
export type QueueTx$Status = 'incomplete' | 'cancelled' | 'error' | 'queued' | 'sent';
|
||||
export type QueueTx$Status = 'cancelled' | 'completed' | 'error' | 'incomplete' | 'queued' | 'sent';
|
||||
|
||||
export type QueueTx = EncodedParams & {
|
||||
index: BN,
|
||||
export type QueueTx = EncodedMessage & {
|
||||
id: number,
|
||||
nonce: BN,
|
||||
publicKey: Uint8Array,
|
||||
status: QueueTx$Status
|
||||
};
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Queries from './Queries';
|
||||
import Selection from './Selection';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
|
||||
function App ({ className, style, t }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={['storage--App', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<Selection />
|
||||
<Queries />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
@ -4,20 +4,19 @@
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { StorageQuery } from '../types';
|
||||
import type { StorageQuery } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import withObservable from '@polkadot/rx-react/with/observable';
|
||||
|
||||
import subjects from '../subjects';
|
||||
import translate from '../translate';
|
||||
import translate from './translate';
|
||||
import Query from './Query';
|
||||
|
||||
type Props = I18nProps & {
|
||||
onRemove: (id: number) => void,
|
||||
value?: Array<StorageQuery>
|
||||
};
|
||||
|
||||
function Queries ({ className, style, value }: Props): React$Node {
|
||||
function Queries ({ className, onRemove, style, value }: Props): React$Node {
|
||||
if (!value || !value.length) {
|
||||
return null;
|
||||
}
|
||||
@ -27,16 +26,15 @@ function Queries ({ className, style, value }: Props): React$Node {
|
||||
className={['storage--Queries', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
{value.map((query) => (
|
||||
{value.map((query) =>
|
||||
<Query
|
||||
key={query.id}
|
||||
onRemove={onRemove}
|
||||
value={query}
|
||||
/>
|
||||
))}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(
|
||||
withObservable(subjects.queries)(Queries)
|
||||
);
|
||||
export default translate(Queries);
|
||||
@ -1,46 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
|
||||
import React from 'react';
|
||||
import Button from 'semantic-ui-react/dist/es/elements/Button';
|
||||
|
||||
import subjects from '../subjects';
|
||||
import translate from '../translate';
|
||||
import cache from './cache';
|
||||
|
||||
type Props = I18nProps & {
|
||||
value: number;
|
||||
};
|
||||
|
||||
function Remove ({ className, style, value }: Props): React$Node {
|
||||
const onRemove = (): void => {
|
||||
const current = subjects.queries.getValue();
|
||||
|
||||
delete cache[value];
|
||||
|
||||
subjects.queries.next(
|
||||
current
|
||||
.filter(({ id }) => id !== value)
|
||||
.reduce((next, item) => {
|
||||
next.push(item);
|
||||
|
||||
return next;
|
||||
}, [])
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
disabled={!value}
|
||||
icon='close'
|
||||
negative
|
||||
onClick={onRemove}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(Remove);
|
||||
@ -1,8 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
const cache = {};
|
||||
|
||||
export default cache;
|
||||
@ -1,33 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { StorageDef$Key } from '@polkadot/storage/types';
|
||||
|
||||
import u8aToHexShort from '@polkadot/util/u8a/toHexShort';
|
||||
import isU8a from '@polkadot/util/is/u8a';
|
||||
|
||||
type Formatter = (value: boolean | BN | Date | Uint8Array) => string;
|
||||
|
||||
export default function transform ({ type }: StorageDef$Key): Formatter {
|
||||
return (value: boolean | BN | Date | Uint8Array): string => {
|
||||
if (typeof value === typeof true) {
|
||||
return value
|
||||
? 'Yes'
|
||||
: 'No';
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
if (isU8a(value)) {
|
||||
// $FlowFixMe type has been determined
|
||||
return u8aToHexShort(value, 256);
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
};
|
||||
}
|
||||
37
packages/app-storage/src/Query/cached.js
Normal file
37
packages/app-storage/src/Query/cached.js
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { StorageDef$Key } from '@polkadot/storage/types';
|
||||
|
||||
import withStorageDiv from '@polkadot/rx-react/with/storageDiv';
|
||||
import u8aToHexShort from '@polkadot/util/u8a/toHexShort';
|
||||
import isU8a from '@polkadot/util/is/u8a';
|
||||
|
||||
export default function cached (key: StorageDef$Key, params?: Array<*> = []): React$ComponentType<*> {
|
||||
return withStorageDiv(key, { params })(
|
||||
(value?: boolean | BN | Date | Uint8Array): string => {
|
||||
if (typeof value === typeof true) {
|
||||
// flowlint-next-line sketchy-null-bool:off
|
||||
return value
|
||||
? 'Yes'
|
||||
: 'No';
|
||||
}
|
||||
|
||||
// flowlint-next-line sketchy-null-bool:off
|
||||
if (!value) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
if (isU8a(value)) {
|
||||
// $FlowFixMe type has been determined
|
||||
return u8aToHexShort(value, 256);
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
},
|
||||
{ className: 'ui disabled dropdown selection' }
|
||||
);
|
||||
}
|
||||
@ -7,32 +7,34 @@ import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { StorageQuery } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import Button from 'semantic-ui-react/dist/es/elements/Button';
|
||||
import Label from 'semantic-ui-react/dist/es/elements/Label';
|
||||
import Div from '@polkadot/rx-react/Div';
|
||||
import withStorage from '@polkadot/rx-react/with/storage';
|
||||
|
||||
import translate from '../translate';
|
||||
import Remove from './Remove';
|
||||
import cache from './cache';
|
||||
import createTransform from './transform';
|
||||
import createCached from './cached';
|
||||
|
||||
type Props = I18nProps & {
|
||||
onRemove: (id: number) => void,
|
||||
value: StorageQuery
|
||||
};
|
||||
|
||||
function Query ({ className, style, value: { id, key, params } }: Props): React$Node {
|
||||
const cache = [];
|
||||
|
||||
function Query ({ className, onRemove, style, value: { id, key, params } }: Props): React$Node {
|
||||
const CachedQuery = (() => {
|
||||
if (!cache[id]) {
|
||||
const transform = createTransform(key);
|
||||
|
||||
cache[id] = withStorage(key, { params, transform })(
|
||||
Div, { className: 'ui disabled dropdown selection' }
|
||||
);
|
||||
cache[id] = createCached(key, params);
|
||||
}
|
||||
|
||||
return cache[id];
|
||||
})();
|
||||
|
||||
const _onRemove = (): void => {
|
||||
delete cache[id];
|
||||
|
||||
onRemove(id);
|
||||
};
|
||||
|
||||
const inputs = Object
|
||||
.keys(key.params || {})
|
||||
// $FlowFixMe key.params exists
|
||||
@ -52,7 +54,11 @@ function Query ({ className, style, value: { id, key, params } }: Props): React$
|
||||
</div>
|
||||
<div className='storage--actionrow-button'>
|
||||
<Label> </Label>
|
||||
<Remove value={id} />
|
||||
<Button
|
||||
icon='close'
|
||||
negative
|
||||
onClick={_onRemove}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
85
packages/app-storage/src/Selection.js
Normal file
85
packages/app-storage/src/Selection.js
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { StorageDef$Key } from '@polkadot/storage/types';
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { StorageQuery } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import Button from 'semantic-ui-react/dist/es/elements/Button';
|
||||
import Label from 'semantic-ui-react/dist/es/elements/Label';
|
||||
import storage from '@polkadot/storage-substrate/keys';
|
||||
import InputStorage from '@polkadot/ui-react-app/src/InputStorage';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
onAdd: (query: StorageQuery) => void
|
||||
};
|
||||
|
||||
type State = {
|
||||
key: StorageDef$Key
|
||||
};
|
||||
|
||||
const defaultValue = storage.timestamp.keys.current;
|
||||
let id = -1;
|
||||
|
||||
class Selection extends React.PureComponent<Props, State> {
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
key: defaultValue
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { key } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['storage--Selection', 'storage--actionrow', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='storage--actionrow-value'>
|
||||
<InputStorage
|
||||
defaultValue={defaultValue}
|
||||
labelSection={t('selection.section', {
|
||||
defaultValue: 'query storage area'
|
||||
})}
|
||||
onChange={this.onChangeStorage}
|
||||
/>
|
||||
</div>
|
||||
<div className='storage--actionrow-button'>
|
||||
<Label> </Label>
|
||||
<Button
|
||||
disabled={!key}
|
||||
icon='plus'
|
||||
onClick={this.onAdd}
|
||||
primary
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onAdd = (): void => {
|
||||
const { onAdd } = this.props;
|
||||
const { key } = this.state;
|
||||
|
||||
onAdd({
|
||||
id: ++id,
|
||||
key,
|
||||
params: []
|
||||
});
|
||||
}
|
||||
|
||||
onChangeStorage = (key: StorageDef$Key): void => {
|
||||
this.setState({ key });
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(Selection);
|
||||
@ -1,56 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { StorageDef$Key } from '@polkadot/storage/types';
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
|
||||
import React from 'react';
|
||||
import Button from 'semantic-ui-react/dist/es/elements/Button';
|
||||
import withObservable from '@polkadot/rx-react/with/observable';
|
||||
|
||||
import subjects from '../subjects';
|
||||
import translate from '../translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
value: StorageDef$Key;
|
||||
};
|
||||
|
||||
let id = 0;
|
||||
|
||||
function Add ({ className, style, value }: Props): React$Node {
|
||||
const onAdd = (): void => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const current = subjects.queries.getValue();
|
||||
const next = [{
|
||||
id: ++id,
|
||||
key: value,
|
||||
params: subjects.params.getValue().map((s) => s.getValue())
|
||||
}];
|
||||
|
||||
subjects.queries.next(
|
||||
current.reduce((next, item) => {
|
||||
next.push(item);
|
||||
|
||||
return next;
|
||||
}, next)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
disabled={!value}
|
||||
icon='plus'
|
||||
onClick={onAdd}
|
||||
primary
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(
|
||||
withObservable(subjects.next)(Add)
|
||||
);
|
||||
@ -1,44 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
|
||||
import React from 'react';
|
||||
import Label from 'semantic-ui-react/dist/es/elements/Label';
|
||||
import InputStorage from '@polkadot/ui-react-app/src/InputStorage';
|
||||
|
||||
import subjects from '../subjects';
|
||||
import translate from '../translate';
|
||||
import Add from './Add';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
|
||||
function Selection ({ className, style, t }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={['storage--Selection', 'storage--actionrow', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='storage--actionrow-value'>
|
||||
<div className='ui--form'>
|
||||
<div className='full'>
|
||||
<InputStorage
|
||||
labelSection={t('selection.section', {
|
||||
defaultValue: 'query storage area'
|
||||
})}
|
||||
subject={subjects.next}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='storage--actionrow-button'>
|
||||
<Label> </Label>
|
||||
<Add />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(Selection);
|
||||
@ -3,7 +3,7 @@
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.storage--App {
|
||||
padding: 1em;
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
.storage--Queries {
|
||||
|
||||
@ -3,8 +3,67 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { StorageQuery } from './types';
|
||||
|
||||
import './index.css';
|
||||
|
||||
import App from './App';
|
||||
import React from 'react';
|
||||
|
||||
export default App;
|
||||
import Queries from './Queries';
|
||||
import Selection from './Selection';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
|
||||
type State = {
|
||||
queue: Array<StorageQuery>
|
||||
}
|
||||
|
||||
class App extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
queue: []
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style } = this.props;
|
||||
const { queue } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['storage--App', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<Selection onAdd={this.onAdd} />
|
||||
<Queries
|
||||
onRemove={this.onRemove}
|
||||
value={queue}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onAdd = (query: StorageQuery): void => {
|
||||
this.setState(
|
||||
(prevState: State): State => ({
|
||||
queue: [query].concat(prevState.queue)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
onRemove = (id: number): void => {
|
||||
this.setState(
|
||||
(prevState: State): State => ({
|
||||
queue: prevState.queue.filter((item) => item.id !== id)
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { StorageDef$Key } from '@polkadot/storage/types';
|
||||
import type { StorageQuery } from './types';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import storage from '@polkadot/storage-substrate/keys';
|
||||
|
||||
type ValueSubject = rxjs$BehaviorSubject<*>;
|
||||
type ValueSubjects = Array<ValueSubject>;
|
||||
|
||||
type Subjects = {
|
||||
next: rxjs$BehaviorSubject<StorageDef$Key>,
|
||||
params: rxjs$BehaviorSubject<ValueSubjects>,
|
||||
queries: rxjs$BehaviorSubject<Array<StorageQuery>>
|
||||
};
|
||||
|
||||
function createSubjects (): Subjects {
|
||||
return {
|
||||
next: new BehaviorSubject(storage.timestamp.keys.current),
|
||||
params: new BehaviorSubject([]),
|
||||
queries: new BehaviorSubject([])
|
||||
};
|
||||
}
|
||||
|
||||
export default createSubjects();
|
||||
@ -37,7 +37,7 @@ function Connecting ({ className, style, value, t }: Props): React$Node {
|
||||
}
|
||||
|
||||
export default translate(
|
||||
withApiCall({ method: 'isConnected' })(
|
||||
withApiCall({ name: 'isConnected' })(
|
||||
Connecting
|
||||
)
|
||||
);
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.apps--Content {
|
||||
background: white;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@ -2,12 +2,18 @@
|
||||
/* This software may be modified and distributed under the terms
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.apps--SideBar-Item {}
|
||||
.apps--SideBar-Item {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.apps--SideBar-Item-NavLink {
|
||||
display: block;
|
||||
padding: 0.75em 0.75em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.apps--SideBar-Item-NavLink-active {
|
||||
background: white;
|
||||
border-radius: .28571429rem 0 0 .28571429rem;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@ -3,8 +3,13 @@
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.apps--SideBar {
|
||||
background: #f9f9f9;
|
||||
flex-shrink: 0;
|
||||
min-width: 12em;
|
||||
padding: 0.75em;
|
||||
padding: 0.75em 0 0 0.75em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.apps--SideBar .ui.vertical.menu {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
// @flow
|
||||
|
||||
export type Route = {
|
||||
component: React$StatelessFunctionalComponent<*>,
|
||||
component: React$ComponentType<*>,
|
||||
i18n: I18Next$Translate$Config,
|
||||
icon: React$StatelessFunctionalComponent<*> | string,
|
||||
icon: React$ComponentType<*> | string,
|
||||
isExact: boolean,
|
||||
isHidden: boolean,
|
||||
name: string,
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@polkadot/extrinsics-substrate": "^0.12.7",
|
||||
"@polkadot/rx-react": "^0.5.5",
|
||||
"@polkadot/rx-react": "^0.6.1",
|
||||
"@polkadot/storage-substrate": "^0.12.7",
|
||||
"@polkadot/ui-react": "^0.10.3",
|
||||
"@polkadot/util": "^0.19.5",
|
||||
|
||||
@ -22,8 +22,6 @@ export default function PairDisplay ({ className, pair, style }: Props): React$N
|
||||
const address = pair.address();
|
||||
const { name } = pair.getMeta();
|
||||
|
||||
console.log('address', address);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['ui--InputyAddress-PairDisplay', className].join(' ')}
|
||||
|
||||
@ -9,6 +9,7 @@ import './InputAddress.css';
|
||||
|
||||
import React from 'react';
|
||||
import addressDecode from '@polkadot/util-keyring/address/decode';
|
||||
import addressEncode from '@polkadot/util-keyring/address/encode';
|
||||
|
||||
// TODO: We need to actually pass the keyring in, this is the testing keyring
|
||||
import keyring from '../keyring';
|
||||
@ -16,10 +17,10 @@ import RxDropdown from '../RxDropdown';
|
||||
import PairDisplay from './PairDisplay';
|
||||
|
||||
type Props = BareProps & {
|
||||
defaultValue?: Uint8Array,
|
||||
isError?: boolean,
|
||||
label?: string,
|
||||
onChange?: (event: SyntheticEvent<*>, value: Uint8Array) => void,
|
||||
subject?: rxjs$Subject<*>
|
||||
onChange: (value: Uint8Array) => void
|
||||
};
|
||||
|
||||
const options = keyring.getPairs().map((pair) => ({
|
||||
@ -32,15 +33,21 @@ const options = keyring.getPairs().map((pair) => ({
|
||||
const transform = (value: string): Uint8Array =>
|
||||
addressDecode(value);
|
||||
|
||||
export default function InputAddress ({ className, isError, label, onChange, style, subject }: Props): React$Node {
|
||||
export default function InputAddress ({ className, defaultValue, isError, label, onChange, style }: Props): React$Node {
|
||||
let _defaultValue;
|
||||
|
||||
if (defaultValue) {
|
||||
_defaultValue = addressEncode(defaultValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<RxDropdown
|
||||
className={['ui--InputAddress', className].join(' ')}
|
||||
defaultValue={_defaultValue}
|
||||
isError={isError}
|
||||
label={label}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
subject={subject}
|
||||
transform={transform}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -2,9 +2,11 @@
|
||||
/* This software may be modified and distributed under the terms
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.ui--InputExtrinsic {}
|
||||
.ui--RxDropdownLinked {}
|
||||
|
||||
.ui--InputExtrinsic-SelectMethod .text {
|
||||
.ui--RxDropdownLinked-Items {}
|
||||
|
||||
.ui--RxDropdownLinked-Items .text {
|
||||
box-sizing: border-box;
|
||||
display: flex !important;
|
||||
flex-wrap: nowrap;
|
||||
@ -14,16 +16,16 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ui--InputExtrinsic-SelectMethod-Item > .text {
|
||||
.ui--RxDropdownLinked-Item > .text {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.ui--InputExtrinsic-SelectMethod-Item-text,
|
||||
.ui--InputExtrinsic-SelectMethod-Item-call {
|
||||
.ui--RxDropdownLinked-Item-text,
|
||||
.ui--RxDropdownLinked-Item-call {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.ui--InputExtrinsic-SelectMethod-Item-call {
|
||||
.ui--RxDropdownLinked-Item-call {
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
margin-left: 1rem;
|
||||
@ -33,6 +35,8 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ui--InputExtrinsic-SelectMethod-Item-text {
|
||||
.ui--RxDropdownLinked-Item-text {
|
||||
flex: 1 0;
|
||||
}
|
||||
|
||||
.ui--RxDropdownLinked-Sections {}
|
||||
|
||||
@ -16,26 +16,25 @@ import createOptions from './options/method';
|
||||
type Props = I18nProps & {
|
||||
isError?: boolean,
|
||||
label?: string,
|
||||
onChange?: (event: SyntheticEvent<*>, value: Extrinsic) => void,
|
||||
subject?: rxjs$Subject<Extrinsic>,
|
||||
onChange: (value: Extrinsic) => void,
|
||||
type: 'private' | 'public',
|
||||
value?: Extrinsic
|
||||
value: Extrinsic
|
||||
};
|
||||
|
||||
function SelectMethod ({ className, isError, label = '', onChange, style, subject, t, type, value }: Props): React$Node {
|
||||
function SelectMethod ({ className, isError, label = '', onChange, style, t, type, value: { name, section } }: Props): React$Node {
|
||||
// $FlowFixMe string vs ...
|
||||
if (!value || !extrinsics[value.section]) {
|
||||
if (!extrinsics[section]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const methods = extrinsics[value.section].methods[type];
|
||||
const methods = extrinsics[section].methods[type];
|
||||
const transform = (name: string): Extrinsic =>
|
||||
methods[name];
|
||||
const options = createOptions(value.section, type);
|
||||
const options = createOptions(section, type);
|
||||
|
||||
return (
|
||||
<RxDropdown
|
||||
className={['ui--InputExtrinsic-SelectMethod', className].join(' ')}
|
||||
className={['ui--RxDropdownLinked-Items', className].join(' ')}
|
||||
isError={isError}
|
||||
label={label || t('input.extrinsic.method', {
|
||||
defaultValue: 'with the extrinsic'
|
||||
@ -43,9 +42,8 @@ function SelectMethod ({ className, isError, label = '', onChange, style, subjec
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
style={style}
|
||||
subject={subject}
|
||||
transform={transform}
|
||||
value={value.name}
|
||||
value={name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { StateDb$SectionNames } from '@polkadot/storage/types';
|
||||
import type { Extrinsic, ExtrinsicSectionName } from '@polkadot/extrinsics/types';
|
||||
import type { I18nProps } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
@ -13,26 +13,29 @@ import translate from '../translate';
|
||||
import createOptions from './options/section';
|
||||
|
||||
type Props = I18nProps & {
|
||||
defaultValue?: ExtrinsicSectionName,
|
||||
isError?: boolean,
|
||||
label?: string,
|
||||
subject: rxjs$BehaviorSubject<StateDb$SectionNames>,
|
||||
type: 'private' | 'public'
|
||||
onChange: (value: ExtrinsicSectionName) => void,
|
||||
type: 'private' | 'public',
|
||||
value: Extrinsic
|
||||
};
|
||||
|
||||
function SelectSection ({ className, isError, label = '', subject, style, t, type }: Props): React$Node {
|
||||
function SelectSection ({ className, defaultValue, isError, label = '', onChange, style, t, type, value: { section } }: Props): React$Node {
|
||||
const options = createOptions(type);
|
||||
|
||||
return (
|
||||
<RxDropdown
|
||||
className={['ui--InputExtrinsic-SelectSection', className].join(' ')}
|
||||
defaultValue={subject.getValue()}
|
||||
className={['ui--RxDropdownLinked-Sections', className].join(' ')}
|
||||
defaultValue={defaultValue}
|
||||
isError={isError}
|
||||
label={label || t('input.extrinsic.section', {
|
||||
defaultValue: 'from extrinsic section'
|
||||
})}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
style={style}
|
||||
subject={subject}
|
||||
value={section}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,17 +3,15 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
// TODO: We have a lot shared between this and InputExtrinsic
|
||||
// TODO: We have a lot shared between this and InputStorage
|
||||
|
||||
import type { Extrinsic } from '@polkadot/extrinsics/types';
|
||||
import type { Extrinsic, ExtrinsicSectionName } from '@polkadot/extrinsics/types';
|
||||
import type { I18nProps } from '../types';
|
||||
|
||||
import './InputExtrinsic.css';
|
||||
|
||||
import React from 'react';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import map from '@polkadot/extrinsics-substrate';
|
||||
import withObservable from '@polkadot/rx-react/with/observable';
|
||||
|
||||
import translate from '../translate';
|
||||
import SelectMethod from './SelectMethod';
|
||||
@ -21,81 +19,80 @@ import SelectSection from './SelectSection';
|
||||
import methodOptions from './options/method';
|
||||
|
||||
type Props = I18nProps & {
|
||||
defaultValue: Extrinsic,
|
||||
isError?: boolean,
|
||||
isPrivate?: boolean,
|
||||
labelMethod?: string,
|
||||
labelSection?: string,
|
||||
onChange?: (event: SyntheticEvent<*>, value: Extrinsic) => void,
|
||||
subject: rxjs$BehaviorSubject<Extrinsic>
|
||||
onChange: (value: Extrinsic) => void
|
||||
};
|
||||
|
||||
class InputExtrinsic extends React.PureComponent<Props> {
|
||||
sectionSubject: rxjs$Subject<string>;
|
||||
SelectMethod: React$ComponentType<*>;
|
||||
subscriptions: Array<rxjs$ISubscription>
|
||||
type State = {
|
||||
value: Extrinsic
|
||||
};
|
||||
|
||||
class InputExtrinsic extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.sectionSubject = new BehaviorSubject(props.subject.getValue().section);
|
||||
this.SelectMethod = withObservable(props.subject)(SelectMethod);
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.subscriptions = [
|
||||
this.sectionSubject.subscribe((section) => {
|
||||
const { isPrivate = false, subject } = this.props;
|
||||
const current = subject.getValue();
|
||||
|
||||
if (current.section === section) {
|
||||
return;
|
||||
}
|
||||
|
||||
const type = isPrivate ? 'private' : 'public';
|
||||
const options = methodOptions(section, type);
|
||||
|
||||
subject.next(
|
||||
// $FlowFixMe we have string to be generix, but...
|
||||
map[section].methods[type][options[0].value]
|
||||
);
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.subscriptions.forEach((s) =>
|
||||
s.unsubscribe()
|
||||
);
|
||||
this.state = {
|
||||
value: this.props.defaultValue
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, isPrivate = false, labelMethod, labelSection, onChange, style, subject } = this.props;
|
||||
const { className, isPrivate = false, labelMethod, labelSection, style } = this.props;
|
||||
const { value } = this.state;
|
||||
const type = isPrivate ? 'private' : 'public';
|
||||
const SelectMethod = this.SelectMethod;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['ui--InputExtrinsic', 'ui--form', className].join(' ')}
|
||||
className={['ui--RxDropdownLinked', 'ui--form', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='small'>
|
||||
<SelectSection
|
||||
label={labelSection}
|
||||
subject={this.sectionSubject}
|
||||
onChange={this.onSectionChange}
|
||||
type={type}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
<div className='large'>
|
||||
<SelectMethod
|
||||
label={labelMethod}
|
||||
onChange={onChange}
|
||||
subject={subject}
|
||||
onChange={this.onKeyChange}
|
||||
value={value}
|
||||
type={type}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onKeyChange = (value: Extrinsic): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
this.setState({ value }, () =>
|
||||
onChange(value)
|
||||
);
|
||||
}
|
||||
|
||||
onSectionChange = (section: ExtrinsicSectionName): void => {
|
||||
const { isPrivate = false } = this.props;
|
||||
|
||||
if (this.state.value.section === section) {
|
||||
return;
|
||||
}
|
||||
|
||||
const type = isPrivate ? 'private' : 'public';
|
||||
const options = methodOptions(section, type);
|
||||
const value = map[section].methods[type][options[0].value];
|
||||
|
||||
this.onKeyChange(value);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(InputExtrinsic);
|
||||
|
||||
@ -26,17 +26,17 @@ export default function createOptions (sectionName: any, type: 'private' | 'publ
|
||||
const inputs = Object.keys(params).join(', ');
|
||||
|
||||
return {
|
||||
className: 'ui--InputExtrinsic-SelectMethod-Item',
|
||||
className: 'ui--RxDropdownLinked-Item',
|
||||
key: `${sectionName}_${name}`,
|
||||
text: [
|
||||
<div
|
||||
className='ui--InputExtrinsic-SelectMethod-Item-text'
|
||||
className='ui--RxDropdownLinked-Item-text'
|
||||
key={`${sectionName}_${name}:text`}
|
||||
>
|
||||
{description || name}
|
||||
</div>,
|
||||
<div
|
||||
className='ui--InputExtrinsic-SelectMethod-Item-call'
|
||||
className='ui--RxDropdownLinked-Item-call'
|
||||
key={`${sectionName}_${name}:call`}
|
||||
>
|
||||
{name}({inputs})
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
/* Copyright 2017-2018 Jaco Greeff
|
||||
/* This software may be modified and distributed under the terms
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.ui--InputStorage {}
|
||||
|
||||
.ui--InputStorage-SelectKey .text {
|
||||
box-sizing: border-box;
|
||||
display: flex !important;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ui--InputStorage-SelectKey-Item > .text {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.ui--InputStorage-SelectKey-Item-text,
|
||||
.ui--InputStorage-SelectKey-Item-call {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.ui--InputStorage-SelectKey-Item-call {
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
margin-left: 1rem;
|
||||
opacity: 0.5;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ui--InputStorage-SelectKey-Item-text {
|
||||
flex: 1 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@ -16,25 +16,24 @@ import createOptions from './options/key';
|
||||
type Props = I18nProps & {
|
||||
isError?: boolean,
|
||||
label?: string,
|
||||
onChange?: (event: SyntheticEvent<*>, value: StorageDef$Key) => void,
|
||||
subject?: rxjs$Subject<StorageDef$Key>,
|
||||
value?: StorageDef$Key
|
||||
onChange: (value: StorageDef$Key) => void,
|
||||
value: StorageDef$Key
|
||||
};
|
||||
|
||||
function SelectKey ({ className, isError, label = '', onChange, style, subject, t, value }: Props): React$Node {
|
||||
function SelectKey ({ className, isError, label = '', onChange, style, t, value: { name, section } }: Props): React$Node {
|
||||
// $FlowFixMe string vs ...
|
||||
if (!value || !map[value.section]) {
|
||||
if (!map[section]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const keys = map[value.section].keys;
|
||||
const keys = map[section].keys;
|
||||
const transform = (name: string): StorageDef$Key =>
|
||||
keys[name];
|
||||
const options = createOptions(value.section);
|
||||
const options = createOptions(section);
|
||||
|
||||
return (
|
||||
<RxDropdown
|
||||
className={['ui--InputStorage-SelectKey', className].join(' ')}
|
||||
className={['ui--RxDropdownLinked-Items', className].join(' ')}
|
||||
isError={isError}
|
||||
label={label || t('input.storage.key', {
|
||||
defaultValue: 'with storage key'
|
||||
@ -42,9 +41,8 @@ function SelectKey ({ className, isError, label = '', onChange, style, subject,
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
style={style}
|
||||
subject={subject}
|
||||
transform={transform}
|
||||
value={value.name}
|
||||
value={name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { StateDb$SectionNames } from '@polkadot/storage/types';
|
||||
import type { StorageDef$Key, StateDb$SectionNames } from '@polkadot/storage/types';
|
||||
import type { I18nProps } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
@ -13,25 +13,28 @@ import translate from '../translate';
|
||||
import createOptions from './options/section';
|
||||
|
||||
type Props = I18nProps & {
|
||||
defaultValue?: StateDb$SectionNames,
|
||||
isError?: boolean,
|
||||
label: string,
|
||||
subject: rxjs$BehaviorSubject<StateDb$SectionNames>
|
||||
onChange: (value: StateDb$SectionNames) => void,
|
||||
value: StorageDef$Key
|
||||
};
|
||||
|
||||
const options = createOptions();
|
||||
|
||||
function SelectSection ({ className, isError, label, subject, style, t }: Props): React$Node {
|
||||
function SelectSection ({ className, defaultValue, isError, label, onChange, style, t, value: { section } }: Props): React$Node {
|
||||
return (
|
||||
<RxDropdown
|
||||
className={['ui--InputStorage-SelectSection', className].join(' ')}
|
||||
defaultValue={subject.getValue()}
|
||||
className={['ui--RxDropdownLinked-Sections', className].join(' ')}
|
||||
defaultValue={defaultValue}
|
||||
isError={isError}
|
||||
label={label || t('input.storage.section', {
|
||||
defaultValue: 'storage area'
|
||||
})}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
style={style}
|
||||
subject={subject}
|
||||
value={section}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,14 +5,12 @@
|
||||
|
||||
// TODO: We have a lot shared between this and InputExtrinsic
|
||||
|
||||
import type { StorageDef$Key } from '@polkadot/storage/types';
|
||||
import type { StorageDef$Key, StateDb$SectionNames } from '@polkadot/storage/types';
|
||||
import type { I18nProps } from '../types';
|
||||
|
||||
import './InputStorage.css';
|
||||
import '../InputExtrinsic/InputExtrinsic.css';
|
||||
|
||||
import React from 'react';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import withObservable from '@polkadot/rx-react/with/observable';
|
||||
import map from '@polkadot/storage-substrate/keys';
|
||||
|
||||
import translate from '../translate';
|
||||
@ -21,76 +19,73 @@ import SelectSection from './SelectSection';
|
||||
import keyOptions from './options/key';
|
||||
|
||||
type Props = I18nProps & {
|
||||
defaultValue: StorageDef$Key,
|
||||
isError?: boolean,
|
||||
labelMethod?: string,
|
||||
labelSection?: string,
|
||||
onChange?: (event: SyntheticEvent<*>, value: StorageDef$Key) => void,
|
||||
subject: rxjs$BehaviorSubject<StorageDef$Key>
|
||||
onChange: (value: StorageDef$Key) => void,
|
||||
};
|
||||
|
||||
class InputStorage extends React.PureComponent<Props> {
|
||||
sectionSubject: rxjs$Subject<string>;
|
||||
subscriptions: Array<rxjs$ISubscription>
|
||||
SelectKey: React$ComponentType<*>;
|
||||
type State = {
|
||||
value: StorageDef$Key
|
||||
};
|
||||
|
||||
class InputStorage extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.sectionSubject = new BehaviorSubject(props.subject.getValue().section);
|
||||
this.SelectKey = withObservable(props.subject)(SelectKey);
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.subscriptions = [
|
||||
this.sectionSubject.subscribe((section) => {
|
||||
const { subject } = this.props;
|
||||
const current = subject.getValue();
|
||||
|
||||
if (current.section === section) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = keyOptions(section);
|
||||
|
||||
subject.next(
|
||||
// $FlowFixMe we have string to be generix, but...
|
||||
map[section].keys[options[0].value]
|
||||
);
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.subscriptions.forEach((s) =>
|
||||
s.unsubscribe()
|
||||
);
|
||||
this.state = {
|
||||
value: this.props.defaultValue
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, labelMethod, labelSection, onChange, style, subject } = this.props;
|
||||
const SelectKey = this.SelectKey;
|
||||
const { className, labelMethod, labelSection, style } = this.props;
|
||||
const { value } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['ui--InputStorage', 'ui--form', className].join(' ')}
|
||||
className={['ui--RxDropdownLinked', 'ui--form', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='small'>
|
||||
<SelectSection
|
||||
label={labelSection}
|
||||
subject={this.sectionSubject}
|
||||
onChange={this.onSectionChange}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
<div className='large'>
|
||||
<SelectKey
|
||||
label={labelMethod}
|
||||
onChange={onChange}
|
||||
subject={subject}
|
||||
onChange={this.onKeyChange}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onKeyChange = (value: StorageDef$Key): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
this.setState({ value }, () =>
|
||||
onChange(value)
|
||||
);
|
||||
}
|
||||
|
||||
onSectionChange = (section: StateDb$SectionNames): void => {
|
||||
if (this.state.value.section === section) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = keyOptions(section);
|
||||
const value = map[section].keys[options[0].value];
|
||||
|
||||
this.onKeyChange(value);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(InputStorage);
|
||||
|
||||
@ -29,17 +29,17 @@ export default function createOptions (sectionName: any): Array<*> {
|
||||
const inputs = Object.keys(params).join(', ');
|
||||
|
||||
return {
|
||||
className: 'ui--InputStorage-SelectKey-Item',
|
||||
className: 'ui--RxDropdownLinked-Item',
|
||||
key: `${sectionName}_${name}`,
|
||||
text: [
|
||||
<div
|
||||
className='ui--InputStorage-SelectKey-Item-text'
|
||||
className='ui--RxDropdownLinked-Item-text'
|
||||
key={`${sectionName}_${name}:text`}
|
||||
>
|
||||
{description || name}
|
||||
</div>,
|
||||
<div
|
||||
className='ui--InputStorage-SelectKey-Item-call'
|
||||
className='ui--RxDropdownLinked-Item-call'
|
||||
key={`${sectionName}_${name}:call`}
|
||||
>
|
||||
{name}({inputs})
|
||||
|
||||
@ -10,15 +10,14 @@ import React from 'react';
|
||||
import InputAddress from '../../InputAddress';
|
||||
import Base from './Base';
|
||||
|
||||
export default function Account ({ isError, label, subject, t, value: { options: { initValue } = {} } }: Props): React$Node {
|
||||
const defaultValue = initValue;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const onChange = (event: SyntheticEvent<*>, value?: Uint8Array): void => {
|
||||
subject.next({
|
||||
export default function Account ({ index, isError, label, onChange, t, value: { options: { initValue } = {} } }: Props): React$Node {
|
||||
// flowlint-next-line unclear-type:off
|
||||
const defaultValue = ((initValue: any): Uint8Array);
|
||||
const _onChange = (value?: Uint8Array): void =>
|
||||
onChange(index, {
|
||||
isValid: !!value && value.length === 32,
|
||||
value
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Base
|
||||
@ -28,7 +27,7 @@ export default function Account ({ isError, label, subject, t, value: { options:
|
||||
<InputAddress
|
||||
defaultValue={defaultValue}
|
||||
label={label}
|
||||
onChange={onChange}
|
||||
onChange={_onChange}
|
||||
placeholder='0x...'
|
||||
/>
|
||||
</Base>
|
||||
|
||||
@ -12,11 +12,10 @@ import Input from 'semantic-ui-react/dist/es/elements/Input';
|
||||
|
||||
import Base from './Base';
|
||||
|
||||
export default function Amount ({ isError, label, subject, t, value: { options: { initValue = 0, maxValue, minValue = 0 } = {} } }: Props): React$Node {
|
||||
const defaultValue = initValue || minValue;
|
||||
export default function Amount ({ index, isError, label, onChange, t, value: { options: { initValue = 0, maxValue, minValue = 0 } = {} } }: Props): React$Node {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const onChange = (event: SyntheticEvent<*>, { value }) =>
|
||||
subject.next({
|
||||
const _onChange = (event: SyntheticEvent<*>, { value }) =>
|
||||
onChange(index, {
|
||||
isValid: true,
|
||||
value: new BN(value || minValue || 0)
|
||||
});
|
||||
@ -27,11 +26,11 @@ export default function Amount ({ isError, label, subject, t, value: { options:
|
||||
size='small'
|
||||
>
|
||||
<Input
|
||||
defaultValue={defaultValue}
|
||||
defaultValue={initValue || minValue}
|
||||
error={isError}
|
||||
max={maxValue}
|
||||
min={minValue}
|
||||
onChange={onChange}
|
||||
onChange={_onChange}
|
||||
type='number'
|
||||
/>
|
||||
</Base>
|
||||
|
||||
@ -15,9 +15,9 @@ const options = [
|
||||
{ text: 'Yes', value: true }
|
||||
];
|
||||
|
||||
export default function Bool ({ isError, label, subject, t, value: { options: { initValue = false } = {} } }: Props): React$Node {
|
||||
const onChange = (event: SyntheticEvent<*>, { value }) =>
|
||||
subject.next({
|
||||
export default function Bool ({ index, isError, label, onChange, t, value: { options: { initValue = false } = {} } }: Props): React$Node {
|
||||
const _onChange = (event: SyntheticEvent<*>, { value }) =>
|
||||
onChange(index, {
|
||||
isValid: true,
|
||||
value
|
||||
});
|
||||
@ -32,7 +32,7 @@ export default function Bool ({ isError, label, subject, t, value: { options: {
|
||||
selection
|
||||
defaultValue={initValue}
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
onChange={_onChange}
|
||||
/>
|
||||
</Base>
|
||||
);
|
||||
|
||||
@ -20,9 +20,9 @@ type Props = BaseProps & {
|
||||
const defaultValidate = (u8a: Uint8Array): boolean =>
|
||||
true;
|
||||
|
||||
export default function Bytes ({ isError, length = -1, label, size = 'full', subject, validate = defaultValidate }: Props): React$Node {
|
||||
export default function Bytes ({ index, isError, length = -1, label, onChange, size = 'full', validate = defaultValidate }: Props): React$Node {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const onChange = (event: SyntheticEvent<*>, { value }) => {
|
||||
const _onChange = (event: SyntheticEvent<*>, { value }) => {
|
||||
let u8a;
|
||||
|
||||
try {
|
||||
@ -35,7 +35,7 @@ export default function Bytes ({ isError, length = -1, label, size = 'full', sub
|
||||
? u8a.length === length
|
||||
: u8a.length !== 0;
|
||||
|
||||
subject.next({
|
||||
onChange(index, {
|
||||
isValid: isValidLength && validate(u8a),
|
||||
value: u8a
|
||||
});
|
||||
@ -48,7 +48,7 @@ export default function Bytes ({ isError, length = -1, label, size = 'full', sub
|
||||
>
|
||||
<Input
|
||||
error={isError}
|
||||
onChange={onChange}
|
||||
onChange={_onChange}
|
||||
placeholder='0x...'
|
||||
type='text'
|
||||
/>
|
||||
|
||||
@ -4,13 +4,9 @@
|
||||
// @flow
|
||||
|
||||
import type { BareProps } from '@polkadot/ui-react-app/types';
|
||||
import type { RawParam } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import Dropzone from 'react-dropzone';
|
||||
import withObservable from '@polkadot/rx-react/with/observable';
|
||||
import bnToU8a from '@polkadot/util/bn/toU8a';
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
|
||||
import translate from '../../translate';
|
||||
import Base from './Base';
|
||||
@ -18,70 +14,82 @@ import Base from './Base';
|
||||
type Props = BareProps & {
|
||||
isError?: boolean,
|
||||
label: string,
|
||||
onChange: (file?: File, contents?: Uint8Array) => void,
|
||||
subject: rxjs$BehaviorSubject<RawParam>,
|
||||
onChange: (contents: Uint8Array) => void,
|
||||
t: I18Next$Translate
|
||||
}
|
||||
|
||||
type WrappedProps = Props & {
|
||||
value: RawParam
|
||||
}
|
||||
type State = {
|
||||
file?: {
|
||||
name: string,
|
||||
size: number
|
||||
}
|
||||
};
|
||||
|
||||
function BytesFile ({ className, isError = false, label, onChange, subject, t }: Props): React$Node {
|
||||
const onDrop = (files: Array<File>) => {
|
||||
files.forEach((file) => {
|
||||
const reader = new FileReader();
|
||||
class BytesFile extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
reader.onload = () => {
|
||||
// flowlint-next-line unclear-type:off
|
||||
const data = new Uint8Array((((reader.result): any): ArrayBuffer));
|
||||
const vec = u8aConcat(bnToU8a(data.length, 32, true), data);
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
onChange(file, vec);
|
||||
};
|
||||
reader.onabort = () =>
|
||||
onChange();
|
||||
reader.onerror = () =>
|
||||
onChange();
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
};
|
||||
render (): React$Node {
|
||||
const { className, isError = false, label, t } = this.props;
|
||||
const { file } = this.state;
|
||||
|
||||
const Component = withObservable(subject)(({ value: { data } = {} }: WrappedProps): React$Node => {
|
||||
return (
|
||||
<Base
|
||||
label={label}
|
||||
size='full'
|
||||
>
|
||||
<Dropzone
|
||||
className={['ui--Param-File', isError ? 'error' : '', !!data, className].join(' ')}
|
||||
className={['ui--Param-File', isError ? 'error' : '', className].join(' ')}
|
||||
multiple={false}
|
||||
onDrop={onDrop}
|
||||
onDrop={this.onDrop}
|
||||
>
|
||||
<div className='label'>
|
||||
{
|
||||
!data
|
||||
!file
|
||||
? t('file.dnd', {
|
||||
defaultValue: 'drag and drop the file here'
|
||||
})
|
||||
: t('file.description', {
|
||||
defaultValue: '{{name}} ({{size}} bytes)',
|
||||
replace: {
|
||||
name: data.name,
|
||||
size: data.size
|
||||
}
|
||||
replace: file
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</Dropzone>
|
||||
</Base>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Component />
|
||||
);
|
||||
onDrop = (files: Array<File>) => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
files.forEach((file) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onabort = () => {};
|
||||
reader.onerror = () => {};
|
||||
reader.onload = () => {
|
||||
// flowlint-next-line unclear-type:off
|
||||
const data = new Uint8Array((((reader.result): any): ArrayBuffer));
|
||||
|
||||
onChange(data);
|
||||
|
||||
this.setState({
|
||||
file: {
|
||||
name: file.name,
|
||||
size: data.length
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(BytesFile);
|
||||
|
||||
@ -9,14 +9,15 @@ import React from 'react';
|
||||
|
||||
import Bytes from './Bytes';
|
||||
|
||||
export default function Hash ({ isError, label, subject, value }: Props): React$Node {
|
||||
export default function Hash ({ index, isError, label, onChange, value }: Props): React$Node {
|
||||
return (
|
||||
<Bytes
|
||||
index={index}
|
||||
isError={isError}
|
||||
label={label}
|
||||
length={32}
|
||||
onChange={onChange}
|
||||
size='medium'
|
||||
subject={subject}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -15,12 +15,7 @@ type Props = BaseProps & {
|
||||
t: I18Next$Translate
|
||||
};
|
||||
|
||||
function Unknown ({ label, subject, t, value: { type } }: Props): React$Node {
|
||||
subject.next({
|
||||
isValid: false,
|
||||
value: new Uint8Array([])
|
||||
});
|
||||
|
||||
function Unknown ({ label, t, value: { type } }: Props): React$Node {
|
||||
return (
|
||||
<Base
|
||||
size='full'
|
||||
|
||||
@ -17,10 +17,10 @@ const options = [
|
||||
{ text: 'Simple majority', value: 2 }
|
||||
];
|
||||
|
||||
export default function VoteThreshold ({ label, subject, t, value: { options: { initValue = 0 } = {} } }: Props): React$Node {
|
||||
export default function VoteThreshold ({ index, label, onChange, t, value: { options: { initValue = 0 } = {} } }: Props): React$Node {
|
||||
const defaultValue = initValue || 0;
|
||||
const onChange = (event: SyntheticEvent<*>, { value }) =>
|
||||
subject.next({
|
||||
const _onChange = (event: SyntheticEvent<*>, { value }) =>
|
||||
onChange(index, {
|
||||
isValid: true,
|
||||
value
|
||||
});
|
||||
@ -34,7 +34,7 @@ export default function VoteThreshold ({ label, subject, t, value: { options: {
|
||||
selection
|
||||
defaultValue={defaultValue}
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
onChange={_onChange}
|
||||
/>
|
||||
</Base>
|
||||
);
|
||||
|
||||
@ -6,25 +6,24 @@
|
||||
import type { Props } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
import bnToU8a from '@polkadot/util/bn/toU8a';
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
|
||||
import BytesFile from './File';
|
||||
|
||||
export default function Wasm ({ isError, label, subject }: Props): React$Node {
|
||||
const onChange = (file?: File, value?: Uint8Array) => {
|
||||
// TODO: Validate that we have actual proper WASM code
|
||||
subject.next({
|
||||
data: file,
|
||||
isValid: !!value && value.length !== 0,
|
||||
value
|
||||
export default function Wasm ({ index, isError, label, onChange }: Props): React$Node {
|
||||
// TODO: Validate that we have actual proper WASM code
|
||||
const _onChange = (value: Uint8Array) =>
|
||||
onChange(index, {
|
||||
isValid: value.length !== 0,
|
||||
value: u8aConcat(bnToU8a(value.length, 32, true), value)
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<BytesFile
|
||||
isError={isError}
|
||||
label={label}
|
||||
onChange={onChange}
|
||||
subject={subject}
|
||||
onChange={_onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -14,35 +14,37 @@ import typeToText from '../typeToText';
|
||||
import findComponent from './findComponent';
|
||||
|
||||
type Props = I18nProps & {
|
||||
index: number,
|
||||
overrides?: ComponentMap,
|
||||
subject: rxjs$BehaviorSubject<RawParam>,
|
||||
onChange: (value: RawParam) => void,
|
||||
value: Param & {
|
||||
name: string
|
||||
};
|
||||
};
|
||||
|
||||
function ParamComponent ({ className, overrides, style, subject, value: { name, type, options = {} } = {} }: Props): React$Node {
|
||||
function ParamComponent ({ className, index, onChange, overrides, style, value: { name, type, options = {} } = {} }: Props): React$Node {
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const Component = findComponent(type, overrides);
|
||||
const renderComponent = (Component: React$ComponentType<*>, index: number = -1) => {
|
||||
const renderComponent = (Component: React$ComponentType<*>, sub: number = -1) => {
|
||||
const _type: Param$Type = Array.isArray(type)
|
||||
? type[index]
|
||||
? type[sub]
|
||||
: type;
|
||||
const text = typeToText(_type);
|
||||
const labelExtra = index === -1
|
||||
const labelExtra = sub === -1
|
||||
? ''
|
||||
: ` (${index})`;
|
||||
|
||||
return (
|
||||
<Component
|
||||
className='ui--Param'
|
||||
index={index}
|
||||
key={`${name}:${text}:${index}}`}
|
||||
label={`${name}: ${text}${labelExtra}`}
|
||||
// FIXME subjects are not for array components (as defined here)
|
||||
subject={subject}
|
||||
onChange={onChange}
|
||||
value={{
|
||||
options,
|
||||
type: _type
|
||||
|
||||
@ -13,17 +13,19 @@ import React from 'react';
|
||||
|
||||
import translate from '../translate';
|
||||
import Param from './Param';
|
||||
import createSubjects from './subjects';
|
||||
import createValues from './values';
|
||||
|
||||
type RawParams = Array<RawParam>;
|
||||
|
||||
type Props = I18nProps & {
|
||||
overrides?: ComponentMap,
|
||||
subject: rxjs$BehaviorSubject<Array<RawParam>>,
|
||||
value: Extrinsic;
|
||||
extrinsic: Extrinsic,
|
||||
onChange: (value: RawParams) => void,
|
||||
overrides?: ComponentMap
|
||||
};
|
||||
|
||||
type State = {
|
||||
subjects: Array<rxjs$BehaviorSubject<RawParam>>,
|
||||
subscriptions: Array<rxjs$ISubscription>
|
||||
extrinsic: Extrinsic,
|
||||
values: Array<RawParam>
|
||||
};
|
||||
|
||||
class Params extends React.PureComponent<Props, State> {
|
||||
@ -32,62 +34,35 @@ class Params extends React.PureComponent<Props, State> {
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
subjects: [],
|
||||
subscriptions: []
|
||||
};
|
||||
// wed ont' set anything, getDerivedStateFromProps will update
|
||||
this.state = ({}: $Shape<State>);
|
||||
}
|
||||
|
||||
static unsubscribe (subscriptions: Array<rxjs$ISubscription>): void {
|
||||
subscriptions.forEach((s) =>
|
||||
s.unsubscribe()
|
||||
);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps (props: Props, prevState: State): $Shape<State> | null {
|
||||
const { value: { params = {} } = {}, subject } = props;
|
||||
|
||||
if (Object.keys(params).length === 0) {
|
||||
Params.unsubscribe(prevState.subscriptions);
|
||||
|
||||
return {
|
||||
subjects: [],
|
||||
subscriptions: []
|
||||
};
|
||||
}
|
||||
|
||||
const subjects = createSubjects(params, subject);
|
||||
let subscriptions;
|
||||
|
||||
if (subject) {
|
||||
const onChange = (): void => {
|
||||
subject.next(
|
||||
subjects.map((s) => s.getValue())
|
||||
);
|
||||
};
|
||||
|
||||
subscriptions = subjects.map((s) => s.subscribe(onChange));
|
||||
}
|
||||
|
||||
return {
|
||||
subjects,
|
||||
subscriptions
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
Params.unsubscribe(this.state.subscriptions);
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, overrides, style, value } = this.props;
|
||||
const { subjects } = this.state;
|
||||
|
||||
if (!value || !subjects.length) {
|
||||
static getDerivedStateFromProps ({ extrinsic, onChange }: Props, { extrinsic: { name, section } = {} }: State): $Shape<State> | null {
|
||||
if (name === extrinsic.name && section === extrinsic.section) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { name, params = {} } = value;
|
||||
const { params = {} } = extrinsic;
|
||||
const values = createValues(params);
|
||||
|
||||
onChange(values);
|
||||
|
||||
return {
|
||||
extrinsic,
|
||||
values: createValues(params)
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, overrides, style, extrinsic } = this.props;
|
||||
const { values } = this.state;
|
||||
|
||||
if (!values.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { name, params = {} } = extrinsic;
|
||||
const paramNames = Object.keys(params);
|
||||
|
||||
if (paramNames.length === 0) {
|
||||
@ -105,9 +80,10 @@ class Params extends React.PureComponent<Props, State> {
|
||||
|
||||
return (
|
||||
<Param
|
||||
index={index}
|
||||
key={`${name}:${paramName}:${index}`}
|
||||
onChange={this.onChangeParam}
|
||||
overrides={overrides}
|
||||
subject={subjects[index]}
|
||||
value={{
|
||||
name: paramName,
|
||||
options,
|
||||
@ -120,6 +96,24 @@ class Params extends React.PureComponent<Props, State> {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeParam = (at: number, next: RawParam): void => {
|
||||
this.setState(
|
||||
(prevState: State): $Shape<State> => ({
|
||||
values: prevState.values.map((value, index) =>
|
||||
index === at
|
||||
? next
|
||||
: value
|
||||
)
|
||||
}),
|
||||
(): void => {
|
||||
const { onChange } = this.props;
|
||||
const { values } = this.state;
|
||||
|
||||
onChange(values);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(Params);
|
||||
|
||||
@ -7,19 +7,18 @@ import type { Param, Param$Type } from '@polkadot/primitives/param';
|
||||
import type { BareProps } from '../types';
|
||||
|
||||
export type RawParam = {
|
||||
// flowlint-next-line unclear-type:off
|
||||
data?: any,
|
||||
isValid: boolean,
|
||||
value: mixed,
|
||||
}
|
||||
|
||||
export type BaseProps = BareProps & {
|
||||
subject: rxjs$BehaviorSubject<RawParam>,
|
||||
onChange: (index: number, value: RawParam) => void,
|
||||
value: Param
|
||||
};
|
||||
|
||||
export type Props = BaseProps & {
|
||||
isError?: boolean,
|
||||
index: number,
|
||||
label: string
|
||||
};
|
||||
|
||||
|
||||
@ -3,24 +3,23 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Params } from '@polkadot/primitives/param';
|
||||
import type { Param, Params } from '@polkadot/primitives/param';
|
||||
import type { RawParam } from './types';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
|
||||
import getInitValue from './initValue';
|
||||
|
||||
export default function subjects (params: Params, subject: rxjs$BehaviorSubject<Array<RawParam>>): Array<rxjs$BehaviorSubject<RawParam>> {
|
||||
export default function values (params: Params): Array<RawParam> {
|
||||
return Object
|
||||
.keys(params)
|
||||
.map((name: string): rxjs$BehaviorSubject<RawParam> => {
|
||||
const param = params[name];
|
||||
.values(params)
|
||||
// $FlowFixMe yes, we are sure, the type is correct
|
||||
.map((param: Param): RawParam => {
|
||||
const value = getInitValue(param);
|
||||
|
||||
return new BehaviorSubject({
|
||||
return {
|
||||
isValid: !isUndefined(value),
|
||||
value
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -14,31 +14,24 @@ type Props = BareProps & {
|
||||
isError?: boolean,
|
||||
label?: string,
|
||||
// flowlint-next-line unclear-type:off
|
||||
onChange?: (event: SyntheticEvent<*>, value: any) => void,
|
||||
subject?: rxjs$Subject<*>,
|
||||
onChange: (value: any) => void,
|
||||
// flowlint-next-line unclear-type:off
|
||||
transform?: (value: any) => any
|
||||
};
|
||||
|
||||
export default function RxDropdown (props: Props): React$Node {
|
||||
const onChange = (event: SyntheticEvent<*>, { value }): void => {
|
||||
const _value = props.transform
|
||||
? props.transform(value)
|
||||
: value;
|
||||
|
||||
if (props.subject) {
|
||||
props.subject.next(_value);
|
||||
}
|
||||
|
||||
if (props.onChange) {
|
||||
props.onChange(event, _value);
|
||||
}
|
||||
};
|
||||
const _onChange = (event: SyntheticEvent<*>, { value }): void =>
|
||||
props.onChange(
|
||||
props.transform
|
||||
? props.transform(value)
|
||||
: value
|
||||
);
|
||||
|
||||
const _props = {...props};
|
||||
|
||||
delete _props.isError;
|
||||
delete _props.label;
|
||||
delete _props.onChange;
|
||||
delete _props.transform;
|
||||
|
||||
return [
|
||||
@ -51,7 +44,7 @@ export default function RxDropdown (props: Props): React$Node {
|
||||
{..._props}
|
||||
className={props.className}
|
||||
error={props.isError}
|
||||
onChange={onChange}
|
||||
onChange={_onChange}
|
||||
/>
|
||||
];
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ i18n
|
||||
fallbackLng: 'en',
|
||||
ns: ['ui'],
|
||||
defaultNS: 'ui',
|
||||
debug: true,
|
||||
debug: false,
|
||||
interpolation: {
|
||||
escapeValue: false
|
||||
},
|
||||
|
||||
14
yarn.lock
14
yarn.lock
@ -769,20 +769,20 @@
|
||||
"@polkadot/util" "^0.19.5"
|
||||
babel-runtime "^6.26.0"
|
||||
|
||||
"@polkadot/rx-api@^0.5.5":
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/rx-api/-/rx-api-0.5.5.tgz#9301b2e88c734f3882721a4d490524e74e37a88b"
|
||||
"@polkadot/rx-api@^0.6.1":
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/rx-api/-/rx-api-0.6.1.tgz#a99d167d7a5046d5cff7bdab07da3e147f1c44a7"
|
||||
dependencies:
|
||||
"@polkadot/api" "^0.9.16"
|
||||
"@polkadot/api-provider" "^0.9.16"
|
||||
rxjs "^5.5.10"
|
||||
|
||||
"@polkadot/rx-react@^0.5.5":
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/rx-react/-/rx-react-0.5.5.tgz#3910d45ab25979b83521cbc77e9bb392a409553e"
|
||||
"@polkadot/rx-react@^0.6.1":
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/rx-react/-/rx-react-0.6.1.tgz#805776ee938df1621153ec3e16e674b6eabdc102"
|
||||
dependencies:
|
||||
"@polkadot/primitives" "^0.12.7"
|
||||
"@polkadot/rx-api" "^0.5.5"
|
||||
"@polkadot/rx-api" "^0.6.1"
|
||||
"@polkadot/storage-substrate" "^0.12.7"
|
||||
"@polkadot/util" "^0.19.5"
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user