Update jsonrpc dependency tree (#54)
* Use tyopeToString from @polkadot/params * Upgrades... before checks * Cleanups... WIP. * more WIP. * Expand defaultValue & handlers * Cleanups * CodeClimate cleanups * Additional CodeClimate cleanups * Base for app-rpc * Initial split into ui-signer * Signer operational (extrinsics) * Decode extrinsic method * RPC progress... * RPC class works again * @flow cleanups
This commit is contained in:
parent
578555ce1a
commit
397dd0c983
@ -15,4 +15,4 @@ all=warn
|
||||
include_warnings=true
|
||||
module.name_mapper='^@polkadot/app-\(accounts\|addresses\|explorer\|extrinsics\|home\|rpc\|settings\|storage\|toolbox\|vanitygen\)\(.*\)$' -> '<PROJECT_ROOT>/packages/app-\1/src\2'
|
||||
module.name_mapper='^@polkadot/apps\(.*\)$' -> '<PROJECT_ROOT>/packages/apps/src\1'
|
||||
module.name_mapper='^@polkadot/ui-\(app\|keyring\)\(.*\)$' -> '<PROJECT_ROOT>/packages/ui-\1/src\2'
|
||||
module.name_mapper='^@polkadot/ui-\(app\|keyring\|signer\)\(.*\)$' -> '<PROJECT_ROOT>/packages/ui-\1/src\2'
|
||||
|
||||
@ -55,7 +55,7 @@ export default class Address extends React.PureComponent<Props, State> {
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style } = this.props;
|
||||
const { address, isValid, publicKey, shortValue } = this.state;
|
||||
const { address, isValid, shortValue } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -73,26 +73,32 @@ export default class Address extends React.PureComponent<Props, State> {
|
||||
</div>
|
||||
<CopyButton value={address} />
|
||||
</div>
|
||||
{
|
||||
!isValid
|
||||
? null
|
||||
: [
|
||||
<Balance
|
||||
className='accounts--Address-balance'
|
||||
key='balance'
|
||||
label='balance '
|
||||
params={publicKey}
|
||||
/>,
|
||||
<Nonce
|
||||
className='accounts--Address-nonce'
|
||||
key='nonce'
|
||||
params={publicKey}
|
||||
>
|
||||
{' transactions'}
|
||||
</Nonce>
|
||||
]
|
||||
}
|
||||
{this.renderBalance()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderBalance (): React$Node {
|
||||
const { isValid, publicKey } = this.state;
|
||||
|
||||
if (!isValid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
<Balance
|
||||
className='accounts--Address-balance'
|
||||
key='balance'
|
||||
label='balance '
|
||||
params={publicKey}
|
||||
/>,
|
||||
<Nonce
|
||||
className='accounts--Address-nonce'
|
||||
key='nonce'
|
||||
params={publicKey}
|
||||
>
|
||||
{' transactions'}
|
||||
</Nonce>
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,8 +62,8 @@ class Creator extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { address, isNameValid, isPassValid, isSeedValid, isValid, name, password, seed } = this.state;
|
||||
const { className, style } = this.props;
|
||||
const { address, isSeedValid } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -79,58 +79,76 @@ class Creator extends React.PureComponent<Props, State> {
|
||||
: null
|
||||
}
|
||||
/>
|
||||
<div className='grow'>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
isError={!isSeedValid}
|
||||
label={t('creator.seed', {
|
||||
defaultValue: 'create from the following seed (hex or string)'
|
||||
})}
|
||||
onChange={this.onChangeSeed}
|
||||
value={seed}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
isError={!isNameValid}
|
||||
label={t('creator.name', {
|
||||
defaultValue: 'name the account'
|
||||
})}
|
||||
onChange={this.onChangeName}
|
||||
value={name}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Password
|
||||
className='full'
|
||||
isError={!isPassValid}
|
||||
label={t('creator.password', {
|
||||
defaultValue: 'encrypt it using the password'
|
||||
})}
|
||||
onChange={this.onChangePass}
|
||||
value={password}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{this.renderInput()}
|
||||
</div>
|
||||
<Button.Group>
|
||||
<Button
|
||||
onClick={this.onDiscard}
|
||||
text={t('creator.discard', {
|
||||
defaultValue: 'Reset'
|
||||
{this.renderButtons()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderButtons (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { isValid } = this.state;
|
||||
|
||||
return (
|
||||
<Button.Group>
|
||||
<Button
|
||||
onClick={this.onDiscard}
|
||||
text={t('creator.discard', {
|
||||
defaultValue: 'Reset'
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
isDisabled={!isValid}
|
||||
isPrimary
|
||||
onClick={this.onCommit}
|
||||
text={t('creator.save', {
|
||||
defaultValue: 'Save'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
renderInput (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { isNameValid, isPassValid, isSeedValid, name, password, seed } = this.state;
|
||||
|
||||
return (
|
||||
<div className='grow'>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
isError={!isSeedValid}
|
||||
label={t('creator.seed', {
|
||||
defaultValue: 'create from the following seed (hex or string)'
|
||||
})}
|
||||
onChange={this.onChangeSeed}
|
||||
value={seed}
|
||||
/>
|
||||
<Button
|
||||
isDisabled={!isValid}
|
||||
isPrimary
|
||||
onClick={this.onCommit}
|
||||
text={t('creator.save', {
|
||||
defaultValue: 'Save'
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
isError={!isNameValid}
|
||||
label={t('creator.name', {
|
||||
defaultValue: 'name the account'
|
||||
})}
|
||||
onChange={this.onChangeName}
|
||||
value={name}
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Password
|
||||
className='full'
|
||||
isError={!isPassValid}
|
||||
label={t('creator.password', {
|
||||
defaultValue: 'encrypt it using the password'
|
||||
})}
|
||||
onChange={this.onChangePass}
|
||||
value={password}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -44,86 +44,100 @@ class Editor extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { defaultPublicKey, currentPair, editedName, isEdited } = this.state;
|
||||
|
||||
if (!currentPair) {
|
||||
return (
|
||||
<div
|
||||
className={classes('accounts--Editor', className)}
|
||||
style={style}
|
||||
>
|
||||
{t('editor.none', {
|
||||
defaultValue: 'There are no saved accounts. Add some first.'
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const address = currentPair.address();
|
||||
const { className, style } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('accounts--Editor', className)}
|
||||
style={style}
|
||||
>
|
||||
<div className='ui--grid'>
|
||||
<Address
|
||||
className='shrink'
|
||||
value={address}
|
||||
/>
|
||||
<div className='grow'>
|
||||
<div className='ui--row'>
|
||||
<InputAddress
|
||||
className='full'
|
||||
defaultValue={defaultPublicKey}
|
||||
hideAddress
|
||||
isInput={false}
|
||||
label={t('editor.select', {
|
||||
defaultValue: 'using my account'
|
||||
})}
|
||||
onChange={this.onChangeAccount}
|
||||
type='account'
|
||||
value={address}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
label={t('editor.name', {
|
||||
defaultValue: 'identified by the name'
|
||||
})}
|
||||
onChange={this.onChangeName}
|
||||
value={editedName}
|
||||
/>
|
||||
</div>
|
||||
{this.renderData()}
|
||||
{this.renderButtons()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderButtons (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { currentPair, isEdited } = this.state;
|
||||
|
||||
if (!currentPair) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.onForget}
|
||||
text={t('editor.forget', {
|
||||
defaultValue: 'Forget'
|
||||
})}
|
||||
/>
|
||||
<Button.Group.Divider />
|
||||
<Button
|
||||
isDisabled={!isEdited}
|
||||
onClick={this.onDiscard}
|
||||
text={t('editor.reset', {
|
||||
defaultValue: 'Reset'
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
isDisabled={!isEdited}
|
||||
isPrimary
|
||||
onClick={this.onCommit}
|
||||
text={t('editor.save', {
|
||||
defaultValue: 'Save'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
renderData (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { currentPair, defaultPublicKey, editedName } = this.state;
|
||||
|
||||
if (!currentPair) {
|
||||
return t('editor.none', {
|
||||
defaultValue: 'There are no saved accounts. Add some first.'
|
||||
});
|
||||
}
|
||||
|
||||
const address = currentPair.address();
|
||||
|
||||
return (
|
||||
<div className='ui--grid'>
|
||||
<Address
|
||||
className='shrink'
|
||||
value={address}
|
||||
/>
|
||||
<div className='grow'>
|
||||
<div className='ui--row'>
|
||||
<InputAddress
|
||||
className='full'
|
||||
defaultValue={defaultPublicKey}
|
||||
hideAddress
|
||||
isInput={false}
|
||||
label={t('editor.select', {
|
||||
defaultValue: 'using my account'
|
||||
})}
|
||||
onChange={this.onChangeAccount}
|
||||
type='account'
|
||||
value={address}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
label={t('editor.name', {
|
||||
defaultValue: 'identified by the name'
|
||||
})}
|
||||
onChange={this.onChangeName}
|
||||
value={editedName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.onForget}
|
||||
text={t('editor.forget', {
|
||||
defaultValue: 'Forget'
|
||||
})}
|
||||
/>
|
||||
<Button.Group.Divider />
|
||||
<Button
|
||||
isDisabled={!isEdited}
|
||||
onClick={this.onDiscard}
|
||||
text={t('editor.reset', {
|
||||
defaultValue: 'Reset'
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
isDisabled={!isEdited}
|
||||
isPrimary
|
||||
onClick={this.onCommit}
|
||||
text={t('editor.save', {
|
||||
defaultValue: 'Save'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ const Components: { [Actions]: React$ComponentType<any> } = {
|
||||
'edit': Editor
|
||||
};
|
||||
|
||||
class App extends React.PureComponent<Props, State> {
|
||||
class AccountsApp extends React.PureComponent<Props, State> {
|
||||
state: State = { action: 'edit' };
|
||||
|
||||
render (): React$Node {
|
||||
@ -75,4 +75,4 @@ class App extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
export default translate(AccountsApp);
|
||||
|
||||
@ -39,8 +39,8 @@ class Creator extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { address, isAddressValid, isNameValid, isValid, name } = this.state;
|
||||
const { className, style } = this.props;
|
||||
const { address } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -52,47 +52,65 @@ class Creator extends React.PureComponent<Props, State> {
|
||||
className='shrink'
|
||||
value={address}
|
||||
/>
|
||||
<div className='grow'>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
isError={!isAddressValid}
|
||||
label={t('creator.address', {
|
||||
defaultValue: 'add the following address'
|
||||
})}
|
||||
onChange={this.onChangeAddress}
|
||||
value={address}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
isError={!isNameValid}
|
||||
label={t('creator.name', {
|
||||
defaultValue: 'name the entry'
|
||||
})}
|
||||
onChange={this.onChangeName}
|
||||
value={name}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{this.renderInput()}
|
||||
</div>
|
||||
<Button.Group>
|
||||
<Button
|
||||
onClick={this.onDiscard}
|
||||
text={t('creator.discard', {
|
||||
defaultValue: 'Reset'
|
||||
{this.renderButtons()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderButtons (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { isValid } = this.state;
|
||||
|
||||
return (
|
||||
<Button.Group>
|
||||
<Button
|
||||
onClick={this.onDiscard}
|
||||
text={t('creator.discard', {
|
||||
defaultValue: 'Reset'
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
isDisabled={!isValid}
|
||||
isPrimary
|
||||
onClick={this.onCommit}
|
||||
text={t('creator.save', {
|
||||
defaultValue: 'Save'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
renderInput (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { address, isAddressValid, isNameValid, name } = this.state;
|
||||
|
||||
return (
|
||||
<div className='grow'>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
isError={!isAddressValid}
|
||||
label={t('creator.address', {
|
||||
defaultValue: 'add the following address'
|
||||
})}
|
||||
onChange={this.onChangeAddress}
|
||||
value={address}
|
||||
/>
|
||||
<Button
|
||||
isDisabled={!isValid}
|
||||
isPrimary
|
||||
onClick={this.onCommit}
|
||||
text={t('creator.save', {
|
||||
defaultValue: 'Save'
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
isError={!isNameValid}
|
||||
label={t('creator.name', {
|
||||
defaultValue: 'name the entry'
|
||||
})}
|
||||
onChange={this.onChangeName}
|
||||
value={name}
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -45,86 +45,100 @@ class Editor extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { currentAddress, defaultPublicKey, editedName, isEdited } = this.state;
|
||||
|
||||
if (!currentAddress) {
|
||||
return (
|
||||
<div
|
||||
className={classes('addresses--Editor', className)}
|
||||
style={style}
|
||||
>
|
||||
{t('editor.none', {
|
||||
defaultValue: 'There are no saved addresses. Add some first.'
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const address = currentAddress.address();
|
||||
const { className, style } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('addresses--Editor', className)}
|
||||
style={style}
|
||||
>
|
||||
<div className='ui--grid'>
|
||||
<Address
|
||||
className='shrink'
|
||||
value={address}
|
||||
/>
|
||||
<div className='grow'>
|
||||
<div className='ui--row'>
|
||||
<InputAddress
|
||||
className='full'
|
||||
defaultValue={defaultPublicKey}
|
||||
hideAddress
|
||||
isInput={false}
|
||||
label={t('editor.select', {
|
||||
defaultValue: 'edit the selected address'
|
||||
})}
|
||||
onChange={this.onChangeAddress}
|
||||
type='address'
|
||||
value={address}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
label={t('editor.name', {
|
||||
defaultValue: 'identified by the name'
|
||||
})}
|
||||
onChange={this.onChangeName}
|
||||
value={editedName}
|
||||
/>
|
||||
</div>
|
||||
{this.renderData()}
|
||||
{this.renderButtons()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderButtons (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { currentAddress, isEdited } = this.state;
|
||||
|
||||
if (!currentAddress) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.onForget}
|
||||
text={t('editor.forget', {
|
||||
defaultValue: 'Forget'
|
||||
})}
|
||||
/>
|
||||
<Button.Group.Divider />
|
||||
<Button
|
||||
isDisabled={!isEdited}
|
||||
onClick={this.onDiscard}
|
||||
text={t('editor.reset', {
|
||||
defaultValue: 'Reset'
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
isDisabled={!isEdited}
|
||||
isPrimary
|
||||
onClick={this.onCommit}
|
||||
text={t('editor.save', {
|
||||
defaultValue: 'Save'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
renderData (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { currentAddress, defaultPublicKey, editedName } = this.state;
|
||||
|
||||
if (!currentAddress) {
|
||||
return t('editor.none', {
|
||||
defaultValue: 'There are no saved addresses. Add some first.'
|
||||
});
|
||||
}
|
||||
|
||||
const address = currentAddress.address();
|
||||
|
||||
return (
|
||||
<div className='ui--grid'>
|
||||
<Address
|
||||
className='shrink'
|
||||
value={address}
|
||||
/>
|
||||
<div className='grow'>
|
||||
<div className='ui--row'>
|
||||
<InputAddress
|
||||
className='full'
|
||||
defaultValue={defaultPublicKey}
|
||||
hideAddress
|
||||
isInput={false}
|
||||
label={t('editor.select', {
|
||||
defaultValue: 'edit the selected address'
|
||||
})}
|
||||
onChange={this.onChangeAddress}
|
||||
type='address'
|
||||
value={address}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
label={t('editor.name', {
|
||||
defaultValue: 'identified by the name'
|
||||
})}
|
||||
onChange={this.onChangeName}
|
||||
value={editedName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.onForget}
|
||||
text={t('editor.forget', {
|
||||
defaultValue: 'Forget'
|
||||
})}
|
||||
/>
|
||||
<Button.Group.Divider />
|
||||
<Button
|
||||
isDisabled={!isEdited}
|
||||
onClick={this.onDiscard}
|
||||
text={t('editor.reset', {
|
||||
defaultValue: 'Reset'
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
isDisabled={!isEdited}
|
||||
isPrimary
|
||||
onClick={this.onCommit}
|
||||
text={t('editor.save', {
|
||||
defaultValue: 'Save'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ const Components: { [Actions]: React$ComponentType<any> } = {
|
||||
'edit': Editor
|
||||
};
|
||||
|
||||
class App extends React.PureComponent<Props, State> {
|
||||
class AddressesApp extends React.PureComponent<Props, State> {
|
||||
state: State = { action: 'edit' };
|
||||
|
||||
render (): React$Node {
|
||||
@ -75,4 +75,4 @@ class App extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
export default translate(AddressesApp);
|
||||
|
||||
@ -12,10 +12,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/primitives": "^0.14.3",
|
||||
"@polkadot/primitives-codec": "^0.14.3",
|
||||
"@polkadot/primitives-json": "^0.14.3",
|
||||
"@polkadot/primitives": "^0.15.8",
|
||||
"@polkadot/primitives-codec": "^0.15.8",
|
||||
"@polkadot/primitives-json": "^0.15.8",
|
||||
"@polkadot/ui-app": "^0.6.2",
|
||||
"@polkadot/util-crypto": "^0.20.5"
|
||||
"@polkadot/util-crypto": "^0.21.3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
|
||||
function App ({ className, style, t }: Props): React$Node {
|
||||
function ExplorerApp ({ className, style, t }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={classes('explorer--App', className)}
|
||||
@ -36,4 +36,4 @@ function App ({ className, style, t }: Props): React$Node {
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
export default translate(ExplorerApp);
|
||||
|
||||
@ -12,9 +12,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/extrinsics": "^0.14.3",
|
||||
"@polkadot/extrinsics-codec": "^0.14.3",
|
||||
"@polkadot/extrinsics": "^0.15.8",
|
||||
"@polkadot/extrinsics-codec": "^0.15.8",
|
||||
"@polkadot/ui-app": "^0.6.2",
|
||||
"@polkadot/ui-signer": "^0.6.2",
|
||||
"react-dropzone": "^4.2.9"
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
import type { Extrinsic } from '@polkadot/extrinsics/types';
|
||||
import type { BareProps } from '@polkadot/ui-app/types';
|
||||
import type { RawParam } from '@polkadot/ui-app/Params/types';
|
||||
import type { EncodedMessage } from './types';
|
||||
import type { EncodedMessage } from '@polkadot/ui-signer/types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
@ -24,7 +24,7 @@ type Props = BareProps & {
|
||||
isPrivate?: boolean,
|
||||
labelMethod?: string,
|
||||
labelSection?: string,
|
||||
onChange: (values: EncodedMessage) => void
|
||||
onChange: (encoded: EncodedMessage) => void
|
||||
};
|
||||
|
||||
type State = {
|
||||
@ -87,7 +87,7 @@ export default class ExtrinsicDisplay extends React.PureComponent<Props, State>
|
||||
onChange({
|
||||
extrinsic,
|
||||
isValid,
|
||||
value
|
||||
values: [value]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -3,19 +3,17 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Props as BaseProps } from '@polkadot/ui-app/Params/types';
|
||||
import type { Props } from '@polkadot/ui-app/Params/types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import BaseAccount from '../Account';
|
||||
|
||||
type Props = BaseProps & {
|
||||
defaultValue?: Uint8Array
|
||||
};
|
||||
|
||||
export default class Account extends React.PureComponent<Props> {
|
||||
render (): React$Node {
|
||||
const { className, defaultValue, isDisabled, isError, label, style, withLabel } = this.props;
|
||||
const { className, defaultValue: { value }, isDisabled, isError, label, style, withLabel } = this.props;
|
||||
// flowlint-next-line unclear-type:off
|
||||
const defaultValue = ((value: any): Uint8Array);
|
||||
|
||||
return (
|
||||
<BaseAccount
|
||||
@ -33,9 +31,9 @@ export default class Account extends React.PureComponent<Props> {
|
||||
}
|
||||
|
||||
onChange = (value?: Uint8Array): void => {
|
||||
const { index, onChange } = this.props;
|
||||
const { onChange } = this.props;
|
||||
|
||||
onChange(index, {
|
||||
onChange({
|
||||
isValid: !!value && value.length === 32,
|
||||
value
|
||||
});
|
||||
|
||||
@ -13,7 +13,7 @@ import Extrinsic from './Extrinsic';
|
||||
|
||||
const defaultValue = extrinsics.staking.methods.public.transfer;
|
||||
|
||||
export default function Call ({ className, isDisabled, isError, index, label, onChange, style, withLabel }: Props): React$Node {
|
||||
export default function Call ({ className, isDisabled, isError, label, onChange, style, withLabel }: Props): React$Node {
|
||||
return (
|
||||
<Extrinsic
|
||||
className={className}
|
||||
@ -21,7 +21,6 @@ export default function Call ({ className, isDisabled, isError, index, label, on
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
isPrivate={false}
|
||||
index={index}
|
||||
label={label}
|
||||
onChange={onChange}
|
||||
style={style}
|
||||
|
||||
@ -4,18 +4,22 @@
|
||||
// @flow
|
||||
|
||||
import type { Extrinsic } from '@polkadot/extrinsics/types';
|
||||
import type { Props as BaseProps } from '@polkadot/ui-app/Params/types';
|
||||
import type { I18nProps } from '@polkadot/ui-app/types';
|
||||
import type { EncodedMessage } from '../types';
|
||||
import type { RawParam$OnChange } from '@polkadot/ui-app/Params/types';
|
||||
import type { EncodedMessage } from '@polkadot/ui-signer/types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import BaseExtrinsic from '../Extrinsic';
|
||||
import translate from '../translate';
|
||||
|
||||
type Props = BaseProps & I18nProps & {
|
||||
type Props = I18nProps & {
|
||||
defaultValue: Extrinsic,
|
||||
isDisabled: boolean,
|
||||
isError: boolean,
|
||||
isPrivate: boolean,
|
||||
label: string,
|
||||
onChange: RawParam$OnChange,
|
||||
withLabel?: boolean,
|
||||
};
|
||||
|
||||
@ -49,12 +53,13 @@ class CExtrinsic extends React.PureComponent<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
onChange = ({ isValid, value }: EncodedMessage): void => {
|
||||
const { index, onChange } = this.props;
|
||||
onChange = ({ isValid, values }: EncodedMessage): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
onChange(index, {
|
||||
onChange({
|
||||
isValid,
|
||||
value
|
||||
// flowlint-next-line unclear-type:off
|
||||
value: ((values[0]: any): Uint8Array)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import Extrinsic from './Extrinsic';
|
||||
|
||||
const defaultValue = extrinsics.consensus.methods.private.setCode;
|
||||
|
||||
export default function Proposal ({ className, isDisabled, isError, index, label, onChange, style, withLabel }: Props): React$Node {
|
||||
export default function Proposal ({ className, isDisabled, isError, label, onChange, style, withLabel }: Props): React$Node {
|
||||
return (
|
||||
<Extrinsic
|
||||
className={className}
|
||||
@ -21,7 +21,6 @@ export default function Proposal ({ className, isDisabled, isError, index, label
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
isPrivate
|
||||
index={index}
|
||||
label={label}
|
||||
onChange={onChange}
|
||||
style={style}
|
||||
|
||||
@ -5,11 +5,12 @@
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { I18nProps } from '@polkadot/ui-app/types';
|
||||
import type { EncodedMessage, QueueTx } from './types';
|
||||
import type { EncodedMessage, QueueTx$MessageAdd } from '@polkadot/ui-signer/types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import extrinsics from '@polkadot/extrinsics-substrate';
|
||||
import rpc from '@polkadot/jsonrpc';
|
||||
import Button from '@polkadot/ui-app/Button';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
|
||||
@ -19,7 +20,7 @@ import Nonce from './Nonce';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
onQueue: (value: QueueTx) => void
|
||||
queueAdd: QueueTx$MessageAdd
|
||||
};
|
||||
|
||||
type State = {
|
||||
@ -30,8 +31,7 @@ type State = {
|
||||
};
|
||||
|
||||
const defaultExtrinsic = extrinsics.staking.methods.public.transfer;
|
||||
|
||||
let id = 0;
|
||||
const defaultRpc = rpc.author.methods.submitExtrinsic;
|
||||
|
||||
class Selection extends React.PureComponent<Props, State> {
|
||||
state: State = ({
|
||||
@ -117,17 +117,15 @@ class Selection extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
onQueue = (): void => {
|
||||
const { onQueue } = this.props;
|
||||
const { encoded: { extrinsic, isValid, value }, nonce, publicKey } = this.state;
|
||||
const { queueAdd } = this.props;
|
||||
const { encoded: { isValid, values }, nonce, publicKey } = this.state;
|
||||
|
||||
onQueue({
|
||||
extrinsic,
|
||||
id: ++id,
|
||||
queueAdd({
|
||||
isValid,
|
||||
nonce,
|
||||
publicKey,
|
||||
status: 'queued',
|
||||
value
|
||||
rpc: defaultRpc,
|
||||
values
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,55 +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-app/types';
|
||||
import type { QueueTx } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
|
||||
import u8aToHexShort from '@polkadot/util/u8a/toHexShort';
|
||||
import addressEncode from '@polkadot/util-keyring/address/encode';
|
||||
|
||||
import translate from '../translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
value: QueueTx
|
||||
};
|
||||
|
||||
function Decoded ({ className, style, t, value: { extrinsic: { name, section }, nonce = new BN(0), publicKey, value } }: Props): React$Node {
|
||||
const from = addressEncode(publicKey);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('extrinsics--Signer-Decoded', className)}
|
||||
style={style}
|
||||
>
|
||||
<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'>{section}.{name}</span> with an index of <span className='code'>{nonce.toString()}</span>
|
||||
</Trans>
|
||||
</p>
|
||||
<p>
|
||||
{t('decoded.data', {
|
||||
defaultValue: 'The encoded parameters contains the data'
|
||||
})}
|
||||
</p>
|
||||
<p className='code'>
|
||||
{u8aToHexShort(value, 512)}
|
||||
</p>
|
||||
</div>
|
||||
<IdentityIcon
|
||||
className='icon'
|
||||
value={from}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(Decoded);
|
||||
@ -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. */
|
||||
|
||||
.extrinsics--Signer {}
|
||||
|
||||
.extrinsics--Signer-Decoded,
|
||||
.extrinsics--Signer-Unlock {
|
||||
align-content: center;
|
||||
display: flex;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.extrinsics--Signer-Decoded {}
|
||||
|
||||
.extrinsics--Signer-Unlock {
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.extrinsics--Signer-Content {}
|
||||
|
||||
.extrinsics--Signer-Content .expanded {
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
.extrinsics--Signer-Content .expanded .code {
|
||||
background: #f5f5f5;
|
||||
font-family: monospace;
|
||||
margin: 0 0.25em;
|
||||
overflow-wrap: break-word;
|
||||
padding: 0.25em 0.5em;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
@ -1,179 +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 { ApiProps } from '@polkadot/ui-react-rx/types';
|
||||
import type { I18nProps } from '@polkadot/ui-app/types';
|
||||
import type { QueueTx } from '../types';
|
||||
|
||||
import './Signer.css';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Button from '@polkadot/ui-app/Button';
|
||||
import Modal from '@polkadot/ui-app/Modal';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import withApi from '@polkadot/ui-react-rx/with/api';
|
||||
|
||||
import translate from '../translate';
|
||||
import Decoded from './Decoded';
|
||||
import Unlock from './Unlock';
|
||||
import submitExtrinsic from './submit';
|
||||
|
||||
type Props = I18nProps & ApiProps & {
|
||||
onSetStatus: (id: number, status: string) => void,
|
||||
queue: Array<QueueTx>
|
||||
};
|
||||
|
||||
type UnlockI18n = {
|
||||
key: string,
|
||||
value: I18Next$Translate$Config
|
||||
}
|
||||
|
||||
type State = {
|
||||
currentItem?: QueueTx,
|
||||
password: string,
|
||||
unlockError: UnlockI18n | null
|
||||
};
|
||||
|
||||
class Signer extends React.PureComponent<Props, State> {
|
||||
state: State = {
|
||||
password: '',
|
||||
unlockError: null
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps ({ queue }: Props, { currentItem, password, unlockError }: State): $Shape<State> {
|
||||
const nextItem = queue.find(({ status }) =>
|
||||
status === 'queued'
|
||||
);
|
||||
const isSame =
|
||||
!!nextItem &&
|
||||
!!currentItem &&
|
||||
nextItem.publicKey.toString() === currentItem.publicKey.toString();
|
||||
|
||||
return {
|
||||
currentItem: nextItem,
|
||||
password: isSame ? password : '',
|
||||
unlockError: isSame ? unlockError : null
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { currentItem, password, unlockError } = this.state;
|
||||
|
||||
if (!currentItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={classes('extrinsics--Signer', className)}
|
||||
dimmer='inverted'
|
||||
open
|
||||
style={style}
|
||||
>
|
||||
<Modal.Header>
|
||||
{t('signer.header', {
|
||||
defaultValue: 'Extrinsic submission'
|
||||
})}
|
||||
</Modal.Header>
|
||||
<Modal.Content className='extrinsics--Signer-Content'>
|
||||
<Decoded value={currentItem} />
|
||||
<Unlock
|
||||
error={unlockError && t(unlockError.key, unlockError.value)}
|
||||
onChange={this.onChangePassword}
|
||||
password={password}
|
||||
value={currentItem.publicKey}
|
||||
/>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.onCancel}
|
||||
text={t('signer.cancel', {
|
||||
defaultValue: 'Cancel'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isPrimary
|
||||
onClick={this.onSign}
|
||||
text={t('signer.send', {
|
||||
defaultValue: 'Sign and Submit'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
unlockAccount (publicKey: Uint8Array, password?: string): ?UnlockI18n {
|
||||
const pair = keyring.getPair(publicKey);
|
||||
|
||||
if (pair.hasSecretKey()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// $FlowFixMe typo in underlying type, fixed at base (upgrades)
|
||||
pair.decodePkcs8(void 0, password);
|
||||
} catch (error) {
|
||||
return {
|
||||
key: 'signer.unlock.generic',
|
||||
value: {
|
||||
defaultValue: error.message
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
onChangePassword = (password: string): void => {
|
||||
this.setState({
|
||||
password,
|
||||
unlockError: null
|
||||
});
|
||||
}
|
||||
|
||||
onCancel = (): void => {
|
||||
const { onSetStatus } = this.props;
|
||||
const { currentItem } = this.state;
|
||||
|
||||
// This should never be executed
|
||||
if (!currentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
onSetStatus(currentItem.id, 'cancelled');
|
||||
}
|
||||
|
||||
onSign = async (): Promise<void> => {
|
||||
const { api, onSetStatus } = this.props;
|
||||
const { currentItem, password } = this.state;
|
||||
|
||||
// This should never be executed
|
||||
if (!currentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unlockError = this.unlockAccount(currentItem.publicKey, password);
|
||||
|
||||
if (unlockError) {
|
||||
this.setState({ unlockError });
|
||||
return;
|
||||
}
|
||||
|
||||
onSetStatus(currentItem.id, 'sending');
|
||||
onSetStatus(currentItem.id, await submitExtrinsic(api, currentItem));
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(
|
||||
withApi(Signer)
|
||||
);
|
||||
@ -1,34 +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 { RxApiInterface } from '@polkadot/api-rx/types';
|
||||
import type { QueueTx } from '../types';
|
||||
|
||||
import encodeCall from '@polkadot/extrinsics-codec/encode/call';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
import u8aToHex from '@polkadot/util/u8a/toHex';
|
||||
|
||||
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));
|
||||
|
||||
return api.author
|
||||
.submitExtrinsic(u8aConcat(message, signature))
|
||||
.toPromise()
|
||||
.then((result) => {
|
||||
console.log('submitExtrinsic: result=', result);
|
||||
|
||||
return 'sent';
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('submitExtrinsic: error=', error);
|
||||
|
||||
return 'error';
|
||||
});
|
||||
}
|
||||
@ -5,27 +5,3 @@
|
||||
.extrinsics--App {
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
.extrinsics--Status {
|
||||
display: inline-block;
|
||||
position: fixed;
|
||||
right: 0.25rem;
|
||||
top: 0.125rem;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.extrinsics--Status-Item {
|
||||
background: #bcdaf0;
|
||||
margin: 0.125rem;
|
||||
padding: 0.5rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.extrinsics--Status-Item .status {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.extrinsics--Status-Item.error {
|
||||
background: red;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@ -4,69 +4,30 @@
|
||||
// @flow
|
||||
|
||||
import type { BareProps } from '@polkadot/ui-app/types';
|
||||
import type { QueueTx, QueueTx$Status } from './types';
|
||||
import type { QueueProps } from '@polkadot/ui-signer/types';
|
||||
|
||||
import './index.css';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import Signer from '@polkadot/ui-signer';
|
||||
|
||||
import Selection from './Selection';
|
||||
import Signer from './Signer';
|
||||
import Status from './Status';
|
||||
|
||||
type Props = BareProps & {};
|
||||
type Props = BareProps;
|
||||
|
||||
type State = {
|
||||
queue: Array<QueueTx>
|
||||
};
|
||||
|
||||
export default class App extends React.PureComponent<Props, State> {
|
||||
state: State = {
|
||||
queue: []
|
||||
};
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style } = this.props;
|
||||
const { queue } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('extrinsics--App', className)}
|
||||
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)) {
|
||||
setTimeout(() => this.setStatus(id, 'completed'), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
onQueue = (value: QueueTx): void => {
|
||||
this.setState(
|
||||
(prevState: State): State => ({
|
||||
queue: prevState.queue.concat([value])
|
||||
})
|
||||
);
|
||||
}
|
||||
export default function ExtrinsicsApp ({ className, style }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={classes('extrinsics--App', className)}
|
||||
style={style}
|
||||
>
|
||||
<Signer.Queue.Consumer>
|
||||
{({ queueAdd }: QueueProps = {}) => (
|
||||
<Selection queueAdd={queueAdd} />
|
||||
)}
|
||||
</Signer.Queue.Consumer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,22 +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 { Extrinsic } from '@polkadot/extrinsics/types';
|
||||
|
||||
export type EncodedMessage = {
|
||||
extrinsic: Extrinsic,
|
||||
isValid: boolean,
|
||||
value: Uint8Array
|
||||
};
|
||||
|
||||
export type QueueTx$Status = 'cancelled' | 'completed' | 'error' | 'incomplete' | 'queued' | 'sending' | 'sent';
|
||||
|
||||
export type QueueTx = EncodedMessage & {
|
||||
id: number,
|
||||
nonce: BN,
|
||||
publicKey: Uint8Array,
|
||||
status: QueueTx$Status
|
||||
};
|
||||
15
packages/app-rpc/LICENSE
Normal file
15
packages/app-rpc/LICENSE
Normal file
@ -0,0 +1,15 @@
|
||||
ISC License (ISC)
|
||||
|
||||
Copyright 2017-2018 Jaco Greeff <jacogr@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
1
packages/app-rpc/README.md
Normal file
1
packages/app-rpc/README.md
Normal file
@ -0,0 +1 @@
|
||||
# @polkadot/app-rpc
|
||||
19
packages/app-rpc/package.json
Normal file
19
packages/app-rpc/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@polkadot/app-rpc",
|
||||
"version": "0.6.2",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"build": "polkadot-dev-build-babel",
|
||||
"check": "stylelint 'src/**/*.css' && eslint src && flow check",
|
||||
"test": "jest --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/ui-app": "^0.6.2",
|
||||
"@polkadot/ui-keyring": "^0.6.2",
|
||||
"@polkadot/ui-signer": "^0.6.2"
|
||||
}
|
||||
}
|
||||
95
packages/app-rpc/src/Account.js
Normal file
95
packages/app-rpc/src/Account.js
Normal file
@ -0,0 +1,95 @@
|
||||
// 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-app/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
|
||||
import InputAddress from '@polkadot/ui-app/InputAddress';
|
||||
import Labelled from '@polkadot/ui-app/Labelled';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import Nonce from '@polkadot/ui-react-rx/Nonce';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
defaultValue?: Uint8Array,
|
||||
isError?: boolean,
|
||||
onChange: (publicKey?: Uint8Array, nonce: BN) => void
|
||||
};
|
||||
|
||||
type State = {
|
||||
nonce: BN,
|
||||
publicKey?: Uint8Array
|
||||
};
|
||||
|
||||
class Account extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
publicKey: props.defaultValue,
|
||||
nonce: new BN(0)
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, defaultValue, isError, style, t } = this.props;
|
||||
const { publicKey } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('rpc--Account', 'ui--row', className)}
|
||||
style={style}
|
||||
>
|
||||
<div className='large'>
|
||||
<InputAddress
|
||||
defaultValue={defaultValue}
|
||||
isError={isError}
|
||||
label={t('account.address', {
|
||||
defaultValue: 'sign data from account'
|
||||
})}
|
||||
onChange={this.onChangeAccount}
|
||||
placeholder='0x...'
|
||||
type='account'
|
||||
/>
|
||||
</div>
|
||||
<Labelled
|
||||
className='small'
|
||||
label={t('account.nonce', {
|
||||
defaultValue: 'with an index of'
|
||||
})}
|
||||
>
|
||||
<Nonce
|
||||
className='ui disabled dropdown selection'
|
||||
onChange={this.onChangeNonce}
|
||||
params={publicKey}
|
||||
/>
|
||||
</Labelled>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeAccount = (publicKey: Uint8Array): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
this.setState({ publicKey }, () =>
|
||||
onChange(publicKey, this.state.nonce)
|
||||
);
|
||||
};
|
||||
|
||||
onChangeNonce = (nonce: BN): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
this.setState({ nonce }, () =>
|
||||
onChange(this.state.publicKey, nonce)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(Account);
|
||||
49
packages/app-rpc/src/Results.js
Normal file
49
packages/app-rpc/src/Results.js
Normal file
@ -0,0 +1,49 @@
|
||||
// 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-app/types';
|
||||
import type { QueueTx } from '@polkadot/ui-signer/types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Output from '@polkadot/ui-app/Output';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
|
||||
type Props = BareProps & {
|
||||
queue: Array<QueueTx>
|
||||
};
|
||||
|
||||
export default function Results ({ className, queue = [], style }: Props): React$Node {
|
||||
const filtered = queue
|
||||
.filter(({ error, result }) =>
|
||||
!isUndefined(error) || !isUndefined(result)
|
||||
)
|
||||
.reverse();
|
||||
|
||||
if (!filtered.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('rpc--Results', className)}
|
||||
style={style}
|
||||
>
|
||||
{filtered.map(({ error, id, result, rpc: { section, name } }) => (
|
||||
<Output
|
||||
isError={!!error}
|
||||
key={id}
|
||||
label={`${id}: ${section}.${name}`}
|
||||
value={
|
||||
error
|
||||
? error.message
|
||||
: result
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
141
packages/app-rpc/src/Selection.js
Normal file
141
packages/app-rpc/src/Selection.js
Normal file
@ -0,0 +1,141 @@
|
||||
// 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 { InterfaceMethodDefinition } from '@polkadot/jsonrpc/types';
|
||||
import type { RawParam } from '@polkadot/ui-app/Params/types';
|
||||
import type { I18nProps } from '@polkadot/ui-app/types';
|
||||
import type { QueueTx$MessageAdd } from '@polkadot/ui-signer/types';
|
||||
|
||||
import './index.css';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
|
||||
import rpc from '@polkadot/jsonrpc';
|
||||
import Button from '@polkadot/ui-app/Button';
|
||||
import InputRpc from '@polkadot/ui-app/InputRpc';
|
||||
import Params from '@polkadot/ui-app/Params';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import rawToValues from '@polkadot/ui-signer/rawToValues';
|
||||
|
||||
import Account from './Account';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
queueAdd: QueueTx$MessageAdd
|
||||
};
|
||||
|
||||
type State = {
|
||||
isValid: boolean,
|
||||
nonce: BN,
|
||||
publicKey?: Uint8Array | null,
|
||||
rpc: InterfaceMethodDefinition,
|
||||
values: Array<RawParam>
|
||||
}
|
||||
|
||||
const defaultMethod = rpc.author.methods.submitExtrinsic;
|
||||
|
||||
class Selection extends React.PureComponent<Props, State> {
|
||||
state: State = {
|
||||
isValid: false,
|
||||
nonce: new BN(0),
|
||||
publicKey: null,
|
||||
rpc: defaultMethod,
|
||||
values: []
|
||||
};
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { isValid, rpc } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('rpc--Selection', className)}
|
||||
style={style}
|
||||
>
|
||||
<InputRpc
|
||||
defaultValue={defaultMethod}
|
||||
onChange={this.onChangeMethod}
|
||||
/>
|
||||
{this.renderAccount()}
|
||||
<Params
|
||||
item={rpc}
|
||||
onChange={this.onChangeValues}
|
||||
/>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isDisabled={!isValid}
|
||||
isPrimary
|
||||
onClick={this.onSubmit}
|
||||
text={t('submit', {
|
||||
defaultValue: 'Submit RPC call'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAccount (): React$Node {
|
||||
const { rpc: { isSigned = false }, publicKey } = this.state;
|
||||
|
||||
if (!isSigned) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Account
|
||||
defaultValue={publicKey}
|
||||
onChange={this.onChangeAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
nextState (newState: $Shape<State>): void {
|
||||
this.setState(
|
||||
(prevState: State): State => {
|
||||
const { rpc = prevState.rpc, nonce = prevState.nonce, publicKey = prevState.publicKey, values = prevState.values } = newState;
|
||||
const isValid = values.reduce((isValid, value) => {
|
||||
return isValid && value.isValid === true;
|
||||
}, rpc.isSigned !== true || (!!publicKey && publicKey.length === 32));
|
||||
|
||||
return {
|
||||
isValid,
|
||||
rpc,
|
||||
nonce,
|
||||
publicKey,
|
||||
values
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onChangeAccount = (publicKey?: Uint8Array, nonce: BN): void => {
|
||||
this.nextState({ nonce, publicKey });
|
||||
}
|
||||
|
||||
onChangeMethod = (rpc: InterfaceMethodDefinition): void => {
|
||||
this.nextState({ rpc });
|
||||
}
|
||||
|
||||
onChangeValues = (values: Array<RawParam>): void => {
|
||||
this.nextState({ values });
|
||||
}
|
||||
|
||||
onSubmit = (): void => {
|
||||
const { queueAdd } = this.props;
|
||||
const { isValid, nonce, publicKey, rpc, values } = this.state;
|
||||
|
||||
queueAdd({
|
||||
isValid,
|
||||
nonce,
|
||||
publicKey,
|
||||
rpc,
|
||||
values: rawToValues(values)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(Selection);
|
||||
12
packages/app-rpc/src/index.css
Normal file
12
packages/app-rpc/src/index.css
Normal file
@ -0,0 +1,12 @@
|
||||
/* 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. */
|
||||
|
||||
.rpc--App {
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
.rpc--Account {
|
||||
box-sizing: border-box;
|
||||
padding-left: 2em;
|
||||
}
|
||||
41
packages/app-rpc/src/index.js
Normal file
41
packages/app-rpc/src/index.js
Normal file
@ -0,0 +1,41 @@
|
||||
// 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-app/types';
|
||||
import type { QueueProps } from '@polkadot/ui-signer/types';
|
||||
|
||||
import './index.css';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import Signer from '@polkadot/ui-signer';
|
||||
|
||||
import Results from './Results';
|
||||
import Selection from './Selection';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
|
||||
export default function RpcApp ({ className, style }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={classes('rpc--App', className)}
|
||||
style={style}
|
||||
>
|
||||
<Signer.Queue.Consumer>
|
||||
{({ queue, queueAdd }: QueueProps = {}) => [
|
||||
<Selection
|
||||
key='add'
|
||||
queueAdd={queueAdd}
|
||||
/>,
|
||||
<Results
|
||||
key='results'
|
||||
queue={queue}
|
||||
/>
|
||||
]}
|
||||
</Signer.Queue.Consumer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
8
packages/app-rpc/src/translate.js
Normal file
8
packages/app-rpc/src/translate.js
Normal file
@ -0,0 +1,8 @@
|
||||
// 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 { translate } from 'react-i18next';
|
||||
|
||||
export default translate(['rpc', 'ui']);
|
||||
@ -8,9 +8,9 @@ import type { StorageQuery } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import typeToString from '@polkadot/params/typeToString';
|
||||
import Button from '@polkadot/ui-app/Button';
|
||||
import Labelled from '@polkadot/ui-app/Labelled';
|
||||
import typeToText from '@polkadot/ui-app/Params/typeToText';
|
||||
import valueToText from '@polkadot/ui-app/Params/valueToText';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import withStorageDiv from '@polkadot/ui-react-rx/with/storageDiv';
|
||||
@ -22,10 +22,11 @@ type Props = I18nProps & {
|
||||
value: StorageQuery
|
||||
};
|
||||
|
||||
type ComponentProps = {};
|
||||
|
||||
type State = {
|
||||
inputs: Array<React$Node>,
|
||||
// flowlint-next-line unclear-type:off
|
||||
Component: React$ComponentType<any>;
|
||||
Component: React$ComponentType<ComponentProps>;
|
||||
};
|
||||
|
||||
const cache = [];
|
||||
@ -33,21 +34,23 @@ const cache = [];
|
||||
class Query extends React.PureComponent<Props, State> {
|
||||
state: State = ({}: $Shape<State>);
|
||||
|
||||
static getDerivedStateFromProps ({ value: { id, key, params } }: Props, prevState: State): State | null {
|
||||
// flowlint-next-line unclear-type:off
|
||||
const Component = ((): React$ComponentType<any> => {
|
||||
if (!cache[id]) {
|
||||
const values = params.map(({ value }) => value);
|
||||
static getCachedComponent ({ id, key, params }: StorageQuery): React$ComponentType<ComponentProps> {
|
||||
if (!cache[id]) {
|
||||
const values = params.map(({ value }) => value);
|
||||
|
||||
cache[id] = withStorageDiv(key, { params: values })(
|
||||
(value) =>
|
||||
valueToText(key.type, value),
|
||||
{ className: 'ui--output' }
|
||||
);
|
||||
}
|
||||
cache[id] = withStorageDiv(key, { params: values })(
|
||||
(value) =>
|
||||
valueToText(key.type, value),
|
||||
{ className: 'ui--output' }
|
||||
);
|
||||
}
|
||||
|
||||
return cache[id];
|
||||
})();
|
||||
return cache[id];
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps ({ value }: Props, prevState: State): State | null {
|
||||
const Component = Query.getCachedComponent(value);
|
||||
const { key, params } = value;
|
||||
const inputs = Object
|
||||
.keys(key.params)
|
||||
.map((name, index) => {
|
||||
@ -76,7 +79,7 @@ class Query extends React.PureComponent<Props, State> {
|
||||
<Labelled
|
||||
className='storage--actionrow-value'
|
||||
label={
|
||||
<div>{key.section}.{key.name}({inputs}): {typeToText(key.type)}</div>
|
||||
<div>{key.section}.{key.name}({inputs}): {typeToString(key.type)}</div>
|
||||
}
|
||||
>
|
||||
<Component />
|
||||
|
||||
@ -10,7 +10,7 @@ import type { StorageQuery } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import storage from '@polkadot/storage-substrate/keys';
|
||||
import storage from '@polkadot/storage-substrate';
|
||||
import Button from '@polkadot/ui-app/Button';
|
||||
import InputStorage from '@polkadot/ui-app/InputStorage';
|
||||
import Labelled from '@polkadot/ui-app/Labelled';
|
||||
|
||||
@ -22,7 +22,7 @@ type State = {
|
||||
queue: Array<StorageQuery>
|
||||
}
|
||||
|
||||
class App extends React.PureComponent<Props, State> {
|
||||
class StorageApp extends React.PureComponent<Props, State> {
|
||||
state: State = {
|
||||
queue: []
|
||||
};
|
||||
@ -62,4 +62,4 @@ class App extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
export default translate(StorageApp);
|
||||
|
||||
@ -14,7 +14,7 @@ import classes from '@polkadot/ui-app/util/classes';
|
||||
import hexToU8a from '@polkadot/util/hex/toU8a';
|
||||
import isHex from '@polkadot/util/is/hex';
|
||||
import u8aFromString from '@polkadot/util/u8a/fromString';
|
||||
import blake2AsHex256 from '@polkadot/util-crypto/blake2/asHex256';
|
||||
import blake2AsHex from '@polkadot/util-crypto/blake2/asHex';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
@ -27,65 +27,83 @@ type State = {
|
||||
class Hash extends React.PureComponent<Props, State> {
|
||||
state: State = {
|
||||
data: '',
|
||||
hash: blake2AsHex256(u8aFromString('')),
|
||||
hash: blake2AsHex(u8aFromString(''), 256),
|
||||
isHexData: false
|
||||
};
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { data, hash, isHexData } = this.state;
|
||||
const { className, style } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('toolbox--Hash', className)}
|
||||
style={style}
|
||||
>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='large'
|
||||
label={t('hash.data', {
|
||||
defaultValue: 'from the following data (hex or string)'
|
||||
})}
|
||||
onChange={this.onChangeData}
|
||||
value={data}
|
||||
/>
|
||||
<Static
|
||||
className='small'
|
||||
label={t('hash.isHex', {
|
||||
defaultValue: 'hex input data'
|
||||
})}
|
||||
value={
|
||||
isHexData
|
||||
? t('hash.isHex.yes', {
|
||||
defaultValue: 'Yes'
|
||||
})
|
||||
: t('hash.isHex.no', {
|
||||
defaultValue: 'No'
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Output
|
||||
className='full toolbox--hex'
|
||||
isHidden={hash.length === 0}
|
||||
label={t('hash.output', {
|
||||
defaultValue: 'the resulting hash is'
|
||||
})}
|
||||
value={hash}
|
||||
withCopy
|
||||
/>
|
||||
</div>
|
||||
{this.renderInput()}
|
||||
{this.renderOutput()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderInput (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { data, isHexData } = this.state;
|
||||
|
||||
return (
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='large'
|
||||
label={t('hash.data', {
|
||||
defaultValue: 'from the following data (hex or string)'
|
||||
})}
|
||||
onChange={this.onChangeData}
|
||||
value={data}
|
||||
/>
|
||||
<Static
|
||||
className='small'
|
||||
label={t('hash.isHex', {
|
||||
defaultValue: 'hex input data'
|
||||
})}
|
||||
value={
|
||||
isHexData
|
||||
? t('hash.isHex.yes', {
|
||||
defaultValue: 'Yes'
|
||||
})
|
||||
: t('hash.isHex.no', {
|
||||
defaultValue: 'No'
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderOutput (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { hash } = this.state;
|
||||
|
||||
return (
|
||||
<div className='ui--row'>
|
||||
<Output
|
||||
className='full toolbox--hex'
|
||||
isHidden={hash.length === 0}
|
||||
label={t('hash.output', {
|
||||
defaultValue: 'the resulting hash is'
|
||||
})}
|
||||
value={hash}
|
||||
withCopy
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeData = (data: string): void => {
|
||||
const isHexData = isHex(data);
|
||||
const hash = blake2AsHex256(
|
||||
const hash = blake2AsHex(
|
||||
isHexData
|
||||
? hexToU8a(data)
|
||||
: u8aFromString(data)
|
||||
: u8aFromString(data),
|
||||
256
|
||||
);
|
||||
|
||||
this.setState({ data, hash, isHexData });
|
||||
|
||||
@ -58,82 +58,116 @@ class Sign extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { currentPair, data, defaultPublicKey, isHexData, isLocked, isUnlockVisible, signature } = this.state;
|
||||
const { className, style } = this.props;
|
||||
const { currentPair, isUnlockVisible } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('toolbox--Sign', className)}
|
||||
style={style}
|
||||
>
|
||||
<div className='ui--row'>
|
||||
<InputAddress
|
||||
className='full'
|
||||
defaultValue={defaultPublicKey}
|
||||
isInput={false}
|
||||
label={t('sign.account', {
|
||||
defaultValue: 'using my account'
|
||||
})}
|
||||
onChange={this.onChangeAccount}
|
||||
type='account'
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='large'
|
||||
label={t('sign.data', {
|
||||
defaultValue: 'sign the following data (hex or string)'
|
||||
})}
|
||||
onChange={this.onChangeData}
|
||||
value={data}
|
||||
/>
|
||||
<Static
|
||||
className='small'
|
||||
label={t('sign.isHex', {
|
||||
defaultValue: 'hex input data'
|
||||
})}
|
||||
value={
|
||||
isHexData
|
||||
? t('sign.isHex.yes', {
|
||||
defaultValue: 'Yes'
|
||||
})
|
||||
: t('sign.isHex.no', {
|
||||
defaultValue: 'No'
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Output
|
||||
className='full toolbox--hex'
|
||||
isHidden={signature.length === 0}
|
||||
label={t('sign.signed', {
|
||||
defaultValue: 'signature of supplied data'
|
||||
})}
|
||||
value={signature}
|
||||
withCopy
|
||||
/>
|
||||
</div>
|
||||
{this.renderAccount()}
|
||||
{this.renderInput()}
|
||||
{this.renderSignature()}
|
||||
<Unlock
|
||||
isVisible={isUnlockVisible}
|
||||
onClose={this.toggleUnlock}
|
||||
pair={currentPair}
|
||||
/>
|
||||
{
|
||||
isLocked
|
||||
? (
|
||||
<Button.Group>
|
||||
<Button
|
||||
isPrimary
|
||||
onClick={this.toggleUnlock}
|
||||
text={t('sign.unlock', {
|
||||
defaultValue: 'Unlock account'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
)
|
||||
: null
|
||||
}
|
||||
{this.renderButtons()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAccount (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { defaultPublicKey } = this.state;
|
||||
|
||||
return (
|
||||
<div className='ui--row'>
|
||||
<InputAddress
|
||||
className='full'
|
||||
defaultValue={defaultPublicKey}
|
||||
isInput={false}
|
||||
label={t('sign.account', {
|
||||
defaultValue: 'using my account'
|
||||
})}
|
||||
onChange={this.onChangeAccount}
|
||||
type='account'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderButtons (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { isLocked } = this.state;
|
||||
|
||||
if (!isLocked) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button.Group>
|
||||
<Button
|
||||
isPrimary
|
||||
onClick={this.toggleUnlock}
|
||||
text={t('sign.unlock', {
|
||||
defaultValue: 'Unlock account'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
renderInput (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { data, isHexData } = this.state;
|
||||
|
||||
return (
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='large'
|
||||
label={t('sign.data', {
|
||||
defaultValue: 'sign the following data (hex or string)'
|
||||
})}
|
||||
onChange={this.onChangeData}
|
||||
value={data}
|
||||
/>
|
||||
<Static
|
||||
className='small'
|
||||
label={t('sign.isHex', {
|
||||
defaultValue: 'hex input data'
|
||||
})}
|
||||
value={
|
||||
isHexData
|
||||
? t('sign.isHex.yes', {
|
||||
defaultValue: 'Yes'
|
||||
})
|
||||
: t('sign.isHex.no', {
|
||||
defaultValue: 'No'
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSignature (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { signature } = this.state;
|
||||
|
||||
return (
|
||||
<div className='ui--row'>
|
||||
<Output
|
||||
className='full toolbox--hex'
|
||||
isHidden={signature.length === 0}
|
||||
label={t('sign.signed', {
|
||||
defaultValue: 'signature of supplied data'
|
||||
})}
|
||||
value={signature}
|
||||
withCopy
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -61,7 +61,6 @@ class Unlock extends React.PureComponent<Props, State> {
|
||||
|
||||
render (): React$Node {
|
||||
const { className, isVisible, style, t } = this.props;
|
||||
const { address, password, unlockError } = this.state;
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
@ -80,56 +79,73 @@ class Unlock extends React.PureComponent<Props, State> {
|
||||
})}
|
||||
</Modal.Header>
|
||||
<Modal.Content>
|
||||
<div className='toolbox--Unlock-Content'>
|
||||
<div className='expanded'>
|
||||
<p>
|
||||
<Trans i18nkey='unlock.info'>
|
||||
You are about to unlock your account <span className='code'>{address}</span> to allow for the signing of messages.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
<IdentityIcon
|
||||
className='icon'
|
||||
value={address}
|
||||
/>
|
||||
</div>
|
||||
<div className='toolbox--Unlock-Entry'>
|
||||
<div className='ui--row'>
|
||||
<Password
|
||||
className='medium'
|
||||
isError={!!unlockError}
|
||||
label={t('unlock.password', {
|
||||
defaultValue: 'unlock account using'
|
||||
})}
|
||||
onChange={this.onChangePassword}
|
||||
value={password}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{this.renderContent()}
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.onCancel}
|
||||
text={t('unlock.cancel', {
|
||||
defaultValue: 'Cancel'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isPrimary
|
||||
onClick={this.onUnlock}
|
||||
text={t('unlock.doit', {
|
||||
defaultValue: 'Unlock'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
{this.renderActions()}
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderActions (): React$Node {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.onCancel}
|
||||
text={t('unlock.cancel', {
|
||||
defaultValue: 'Cancel'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isPrimary
|
||||
onClick={this.onUnlock}
|
||||
text={t('unlock.doit', {
|
||||
defaultValue: 'Unlock'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
renderContent (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { address, password, unlockError } = this.state;
|
||||
|
||||
return [
|
||||
<div className='toolbox--Unlock-Content' key='content'>
|
||||
<div className='expanded'>
|
||||
<p>
|
||||
<Trans i18nkey='unlock.info'>
|
||||
You are about to unlock your account <span className='code'>{address}</span> to allow for the signing of messages.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
<IdentityIcon
|
||||
className='icon'
|
||||
value={address}
|
||||
/>
|
||||
</div>,
|
||||
<div className='toolbox--Unlock-Entry' key='entry'>
|
||||
<div className='ui--row'>
|
||||
<Password
|
||||
className='medium'
|
||||
isError={!!unlockError}
|
||||
label={t('unlock.password', {
|
||||
defaultValue: 'unlock account using'
|
||||
})}
|
||||
onChange={this.onChangePassword}
|
||||
value={password}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
];
|
||||
}
|
||||
|
||||
unlockAccount (password?: string): ?UnlockI18n {
|
||||
const { pair } = this.props;
|
||||
|
||||
@ -138,8 +154,7 @@ class Unlock extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
try {
|
||||
// $FlowFixMe typo in underlying type, fixed at base (upgrades)
|
||||
pair.decodePkcs8(void 0, password);
|
||||
pair.decodePkcs8(password);
|
||||
} catch (error) {
|
||||
return {
|
||||
key: 'unlock.generic',
|
||||
|
||||
@ -56,69 +56,95 @@ class Verify extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { defaultPublicKey, data, isHexData, isValid, isValidAddress, isValidSignature, signature } = this.state;
|
||||
const { className, style } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('toolbox--Verify', className)}
|
||||
style={style}
|
||||
>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='large'
|
||||
label={t('verify.data', {
|
||||
defaultValue: 'using the following data (hex or string)'
|
||||
})}
|
||||
onChange={this.onChangeData}
|
||||
value={data}
|
||||
/>
|
||||
<Static
|
||||
className='small'
|
||||
label={t('verify.isHex', {
|
||||
defaultValue: 'hex input data'
|
||||
})}
|
||||
value={
|
||||
isHexData
|
||||
? t('verify.isHex.yes', {
|
||||
defaultValue: 'Yes'
|
||||
})
|
||||
: t('verify.isHex.no', {
|
||||
defaultValue: 'No'
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<InputAddress
|
||||
className='full'
|
||||
defaultValue={defaultPublicKey}
|
||||
isError={!isValidAddress}
|
||||
isInput
|
||||
label={t('verify.account', {
|
||||
defaultValue: 'verify using address'
|
||||
})}
|
||||
onChange={this.onChangeAddress}
|
||||
/>
|
||||
</div>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
icon={
|
||||
<Icon
|
||||
color={isValid ? 'green' : (isValidSignature ? 'red' : void 0)}
|
||||
name={isValid ? 'check circle' : (isValidSignature ? 'exclamation circle' : 'help circle')}
|
||||
size='big'
|
||||
/>
|
||||
}
|
||||
isError={!isValidSignature}
|
||||
label={t('verify.signature', {
|
||||
defaultValue: 'checking the supplied signature'
|
||||
})}
|
||||
onChange={this.onChangeSignature}
|
||||
value={signature}
|
||||
/>
|
||||
</div>
|
||||
{this.renderInput()}
|
||||
{this.renderAddress()}
|
||||
{this.renderSignature()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAddress (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { defaultPublicKey, isValidAddress } = this.state;
|
||||
|
||||
return (
|
||||
<div className='ui--row'>
|
||||
<InputAddress
|
||||
className='full'
|
||||
defaultValue={defaultPublicKey}
|
||||
isError={!isValidAddress}
|
||||
isInput
|
||||
label={t('verify.account', {
|
||||
defaultValue: 'verify using address'
|
||||
})}
|
||||
onChange={this.onChangeAddress}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderInput (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { data, isHexData } = this.state;
|
||||
|
||||
return (
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='large'
|
||||
label={t('verify.data', {
|
||||
defaultValue: 'using the following data (hex or string)'
|
||||
})}
|
||||
onChange={this.onChangeData}
|
||||
value={data}
|
||||
/>
|
||||
<Static
|
||||
className='small'
|
||||
label={t('verify.isHex', {
|
||||
defaultValue: 'hex input data'
|
||||
})}
|
||||
value={
|
||||
isHexData
|
||||
? t('verify.isHex.yes', {
|
||||
defaultValue: 'Yes'
|
||||
})
|
||||
: t('verify.isHex.no', {
|
||||
defaultValue: 'No'
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSignature (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { isValid, isValidSignature, signature } = this.state;
|
||||
|
||||
return (
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='full'
|
||||
icon={
|
||||
<Icon
|
||||
color={isValid ? 'green' : (isValidSignature ? 'red' : void 0)}
|
||||
name={isValid ? 'check circle' : (isValidSignature ? 'exclamation circle' : 'help circle')}
|
||||
size='big'
|
||||
/>
|
||||
}
|
||||
isError={!isValidSignature}
|
||||
label={t('verify.signature', {
|
||||
defaultValue: 'checking the supplied signature'
|
||||
})}
|
||||
onChange={this.onChangeSignature}
|
||||
value={signature}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -160,8 +186,6 @@ class Verify extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
onChangeSignature = (signature: string): void => {
|
||||
console.log('onChangeSig', isHex(signature), signature.length);
|
||||
|
||||
const isValidSignature = isHex(signature) && signature.length === 130;
|
||||
|
||||
this.nextState({ signature, isValidSignature });
|
||||
|
||||
@ -33,13 +33,13 @@ const Components: { [Actions]: React$ComponentType<any> } = {
|
||||
'verify': Verify
|
||||
};
|
||||
|
||||
class App extends React.PureComponent<Props, State> {
|
||||
class ToolboxApp extends React.PureComponent<Props, State> {
|
||||
state: State = {
|
||||
action: 'hash'
|
||||
};
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { className, style } = this.props;
|
||||
const { action } = this.state;
|
||||
const Component = Components[action];
|
||||
|
||||
@ -48,36 +48,45 @@ class App extends React.PureComponent<Props, State> {
|
||||
className={classes('toolbox--App', className)}
|
||||
style={style}
|
||||
>
|
||||
<Button.Group className='accounts--App-navigation'>
|
||||
<Button
|
||||
isPrimary={action === 'hash'}
|
||||
onClick={this.selectHash}
|
||||
text={t('app.hash', {
|
||||
defaultValue: 'Hash data'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isPrimary={action === 'sign'}
|
||||
onClick={this.selectSign}
|
||||
text={t('app.sign', {
|
||||
defaultValue: 'Sign message'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isPrimary={action === 'verify'}
|
||||
onClick={this.selectVerify}
|
||||
text={t('app.verify', {
|
||||
defaultValue: 'Verify signature'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
{this.renderButtons()}
|
||||
<Component />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderButtons (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { action } = this.state;
|
||||
|
||||
return (
|
||||
<Button.Group className='accounts--App-navigation'>
|
||||
<Button
|
||||
isPrimary={action === 'hash'}
|
||||
onClick={this.selectHash}
|
||||
text={t('app.hash', {
|
||||
defaultValue: 'Hash data'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isPrimary={action === 'sign'}
|
||||
onClick={this.selectSign}
|
||||
text={t('app.sign', {
|
||||
defaultValue: 'Sign message'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isPrimary={action === 'verify'}
|
||||
onClick={this.selectVerify}
|
||||
text={t('app.verify', {
|
||||
defaultValue: 'Verify signature'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
selectHash = (): void => {
|
||||
this.setState({ action: 'hash' });
|
||||
}
|
||||
@ -91,4 +100,4 @@ class App extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
export default translate(ToolboxApp);
|
||||
|
||||
@ -5,26 +5,26 @@
|
||||
|
||||
import type { Generator$Calculation, Generator$Options } from './types';
|
||||
|
||||
function calculateAt (atOffset: number, test: Array<string>, address: string): Generator$Calculation {
|
||||
return {
|
||||
count: test.reduce((count, c, index) => {
|
||||
if (index === count) {
|
||||
count += (c === '?' || c === address.charAt(index + atOffset)) ? 1 : 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}, 0),
|
||||
offset: atOffset
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function calculate (test: Array<string>, _address: string, { atOffset = -1, withCase = false }: Generator$Options): Generator$Calculation {
|
||||
const address = withCase
|
||||
? _address
|
||||
: _address.toLowerCase();
|
||||
|
||||
const calculateAt = (atOffset: number): Generator$Calculation => {
|
||||
return {
|
||||
count: test.reduce((count, c, index) => {
|
||||
if (index === count) {
|
||||
count += (c === '?' || c === address.charAt(index + atOffset)) ? 1 : 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}, 0),
|
||||
offset: atOffset
|
||||
};
|
||||
};
|
||||
|
||||
if (atOffset > 0) {
|
||||
return calculateAt(atOffset);
|
||||
return calculateAt(atOffset, test, address);
|
||||
}
|
||||
|
||||
const maxOffset = address.length - test.length - 1;
|
||||
@ -32,7 +32,7 @@ module.exports = function calculate (test: Array<string>, _address: string, { at
|
||||
let bestOffset = 1;
|
||||
|
||||
for (let index = 1; index < maxOffset; index++) {
|
||||
const { count, offset } = calculateAt(index);
|
||||
const { count, offset } = calculateAt(index, test, address);
|
||||
|
||||
if (count > bestCount) {
|
||||
bestCount = count;
|
||||
|
||||
@ -42,7 +42,7 @@ const BOOL_OPTIONS = [
|
||||
{ text: 'Yes', value: true }
|
||||
];
|
||||
|
||||
class App extends React.PureComponent<Props, State> {
|
||||
class VanityApp extends React.PureComponent<Props, State> {
|
||||
results: Array<Generator$Result> = [];
|
||||
state: State = {
|
||||
elapsed: 0,
|
||||
@ -57,82 +57,114 @@ class App extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { elapsed, isMatchValid, isRunning, match, matches, keyCount, withCase } = this.state;
|
||||
const { className, style } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('vanity--App', className)}
|
||||
style={style}
|
||||
>
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='medium'
|
||||
isDisabled={isRunning}
|
||||
isError={!isMatchValid}
|
||||
label={t('vanity.matching', {
|
||||
defaultValue: 'generate address containing (? wildcard)'
|
||||
})}
|
||||
onChange={this.onChangeMatch}
|
||||
value={match}
|
||||
{this.renderOptions()}
|
||||
{this.renderButtons()}
|
||||
{this.renderStats()}
|
||||
{this.renderMatches()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderButtons (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { isMatchValid, isRunning } = this.state;
|
||||
|
||||
return (
|
||||
<Button.Group>
|
||||
<Button
|
||||
isDisabled={!isMatchValid}
|
||||
isPrimary={!isRunning}
|
||||
onClick={this.toggleStart}
|
||||
text={
|
||||
isRunning
|
||||
? t('vanity.stop', { defaultValue: 'Stop generation' })
|
||||
: t('vanity.start', { defaultValue: 'Start generation' })
|
||||
}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
renderMatches (): React$Node {
|
||||
const { matches } = this.state;
|
||||
|
||||
return (
|
||||
<div className='vanity--App-matches'>
|
||||
{matches.map((match) => (
|
||||
<Match
|
||||
{...match}
|
||||
key={match.address}
|
||||
onRemove={this.onRemove}
|
||||
/>
|
||||
<Dropdown
|
||||
className='small'
|
||||
isDisabled={isRunning}
|
||||
label={t('vanity.case', {
|
||||
defaultValue: 'case sensitive match'
|
||||
})}
|
||||
options={BOOL_OPTIONS}
|
||||
onChange={this.onChangeCase}
|
||||
value={withCase}
|
||||
/>
|
||||
<Static
|
||||
className='small'
|
||||
label={t('vanity.offset', {
|
||||
defaultValue: 'exact offset'
|
||||
})}
|
||||
value={t('vanity.offset.off', {
|
||||
defaultValue: 'No'
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isDisabled={!isMatchValid}
|
||||
isPrimary={!isRunning}
|
||||
onClick={this.toggleStart}
|
||||
text={
|
||||
isRunning
|
||||
? t('vanity.stop', { defaultValue: 'Stop generation' })
|
||||
: t('vanity.start', { defaultValue: 'Start generation' })
|
||||
}
|
||||
/>
|
||||
</Button.Group>
|
||||
{
|
||||
keyCount
|
||||
? (
|
||||
<div className='vanity--App-stats'>
|
||||
{t('vanity.stats', {
|
||||
defaultValue: 'Evaluated {{count}} keys in {{elapsed}}s ({{avg}}ms/key)',
|
||||
replace: {
|
||||
avg: (elapsed / keyCount).toFixed(3),
|
||||
count: keyCount,
|
||||
elapsed: (elapsed / 1000).toFixed(2)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
<div className='vanity--App-matches'>
|
||||
{matches.map((match) => (
|
||||
<Match
|
||||
{...match}
|
||||
key={match.address}
|
||||
onRemove={this.onRemove}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderOptions (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { isMatchValid, isRunning, match, withCase } = this.state;
|
||||
|
||||
return (
|
||||
<div className='ui--row'>
|
||||
<Input
|
||||
className='medium'
|
||||
isDisabled={isRunning}
|
||||
isError={!isMatchValid}
|
||||
label={t('vanity.matching', {
|
||||
defaultValue: 'generate address containing (? wildcard)'
|
||||
})}
|
||||
onChange={this.onChangeMatch}
|
||||
value={match}
|
||||
/>
|
||||
<Dropdown
|
||||
className='small'
|
||||
isDisabled={isRunning}
|
||||
label={t('vanity.case', {
|
||||
defaultValue: 'case sensitive match'
|
||||
})}
|
||||
options={BOOL_OPTIONS}
|
||||
onChange={this.onChangeCase}
|
||||
value={withCase}
|
||||
/>
|
||||
<Static
|
||||
className='small'
|
||||
label={t('vanity.offset', {
|
||||
defaultValue: 'exact offset'
|
||||
})}
|
||||
value={t('vanity.offset.off', {
|
||||
defaultValue: 'No'
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderStats (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { elapsed, keyCount } = this.state;
|
||||
|
||||
if (!keyCount) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='vanity--App-stats'>
|
||||
{t('vanity.stats', {
|
||||
defaultValue: 'Evaluated {{count}} keys in {{elapsed}}s ({{avg}}ms/key)',
|
||||
replace: {
|
||||
avg: (elapsed / keyCount).toFixed(3),
|
||||
count: keyCount,
|
||||
elapsed: (elapsed / 1000).toFixed(2)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -237,4 +269,4 @@ class App extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
export default translate(VanityApp);
|
||||
|
||||
@ -19,10 +19,12 @@
|
||||
"@polkadot/app-addresses": "^0.6.2",
|
||||
"@polkadot/app-explorer": "^0.6.2",
|
||||
"@polkadot/app-extrinsics": "^0.6.2",
|
||||
"@polkadot/app-rpc": "^0.6.2",
|
||||
"@polkadot/app-storage": "^0.6.2",
|
||||
"@polkadot/app-toolbox": "^0.6.2",
|
||||
"@polkadot/app-vanitygen": "^0.6.2",
|
||||
"@polkadot/ui-app": "^0.6.2"
|
||||
"@polkadot/ui-app": "^0.6.2",
|
||||
"@polkadot/ui-signer": "^0.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-loader": "^8.0.0-beta.2",
|
||||
|
||||
@ -3,32 +3,32 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-app/types';
|
||||
import type { BareProps } from '@polkadot/ui-app/types';
|
||||
|
||||
import './App.css';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import Signer from '@polkadot/ui-signer';
|
||||
|
||||
import Connecting from '../Connecting';
|
||||
import Content from '../Content';
|
||||
import SideBar from '../SideBar';
|
||||
import translate from '../translate';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
type Props = BareProps & {};
|
||||
|
||||
function App ({ className, style }: Props): React$Node {
|
||||
export default function Apps ({ className, style }: Props): React$Node {
|
||||
return (
|
||||
<div
|
||||
className={classes('apps--App', className)}
|
||||
style={style}
|
||||
>
|
||||
<SideBar />
|
||||
<Content />
|
||||
<Signer>
|
||||
<Content />
|
||||
</Signer>
|
||||
<Connecting />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
|
||||
@ -10,6 +10,7 @@ import addresses from './addresses';
|
||||
import explorer from './explorer';
|
||||
import extrinsics from './extrinsics';
|
||||
// import home from './home';
|
||||
import rpc from './rpc';
|
||||
// import settings from './settings';
|
||||
import storage from './storage';
|
||||
import toolbox from './toolbox';
|
||||
@ -19,7 +20,7 @@ import vanitygen from './vanitygen';
|
||||
export default ({
|
||||
default: 'explorer',
|
||||
routes: [].concat(
|
||||
explorer, extrinsics, storage, toolbox, null, accounts, addresses, vanitygen
|
||||
explorer, extrinsics, storage, null, accounts, addresses, vanitygen, null, rpc, toolbox
|
||||
),
|
||||
unknown
|
||||
}: Routing);
|
||||
|
||||
21
packages/apps/src/routing/rpc.js
Normal file
21
packages/apps/src/routing/rpc.js
Normal file
@ -0,0 +1,21 @@
|
||||
// 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 { Routes } from '../types';
|
||||
|
||||
import Rpc from '@polkadot/app-rpc';
|
||||
|
||||
export default ([
|
||||
{
|
||||
Component: Rpc,
|
||||
i18n: {
|
||||
defaultValue: 'Raw RPC'
|
||||
},
|
||||
icon: 'code',
|
||||
isExact: false,
|
||||
isHidden: false,
|
||||
name: 'rpc'
|
||||
}
|
||||
]: Routes);
|
||||
@ -10,7 +10,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
const packages = ['app-accounts', 'app-addresses', 'app-explorer', 'app-extrinsics', 'app-storage', 'app-toolbox', 'app-vanitygen', 'ui-app', 'ui-keyring'];
|
||||
const packages = ['app-accounts', 'app-addresses', 'app-explorer', 'app-extrinsics', 'app-rpc', 'app-storage', 'app-toolbox', 'app-vanitygen', 'ui-app', 'ui-keyring', 'ui-signer'];
|
||||
|
||||
function createWebpack ({ alias = {}, context, name = 'index' }) {
|
||||
const pkgJson = require(path.join(context, 'package.json'));
|
||||
|
||||
@ -12,14 +12,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/extrinsics-substrate": "^0.14.3",
|
||||
"@polkadot/params": "^0.14.3",
|
||||
"@polkadot/primitives": "^0.14.3",
|
||||
"@polkadot/storage-substrate": "^0.14.3",
|
||||
"@polkadot/extrinsics-substrate": "^0.15.8",
|
||||
"@polkadot/params": "^0.15.8",
|
||||
"@polkadot/primitives": "^0.15.8",
|
||||
"@polkadot/storage-substrate": "^0.15.8",
|
||||
"@polkadot/ui-keyring": "^0.6.2",
|
||||
"@polkadot/ui-react": "^0.12.3",
|
||||
"@polkadot/ui-react-rx": "^0.12.3",
|
||||
"@polkadot/util": "^0.20.5",
|
||||
"@polkadot/ui-react": "^0.13.4",
|
||||
"@polkadot/ui-react-rx": "^0.13.4",
|
||||
"@polkadot/util": "^0.21.3",
|
||||
"i18next": "^11.1.1",
|
||||
"i18next-browser-languagedetector": "^2.2.0",
|
||||
"i18next-xhr-backend": "^1.5.1",
|
||||
|
||||
@ -58,8 +58,7 @@ export default class Dropdown extends React.PureComponent<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
onChange = (event: SyntheticEvent<any>, { value }: SUIEvent): void => {
|
||||
onChange = (event: SyntheticEvent<Element>, { value }: SUIEvent): void => {
|
||||
const { onChange, transform } = this.props;
|
||||
|
||||
onChange(
|
||||
|
||||
@ -94,8 +94,7 @@ export default class Input extends React.PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
onChange = (event: SyntheticEvent<any>, { value }: SUIEvent): void => {
|
||||
onChange = (event: SyntheticEvent<Element>, { value }: SUIEvent): void => {
|
||||
this.props.onChange(value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
import addressDecode from '@polkadot/util-keyring/address/decode';
|
||||
import addressEncode from '@polkadot/util-keyring/address/encode';
|
||||
|
||||
export default function addressToAddress (value?: string | Uint8Array): ?string {
|
||||
export default function addressToAddress (value?: string | Uint8Array | null): ?string {
|
||||
// flowlint-next-line sketchy-null-string:off
|
||||
if (value) {
|
||||
try {
|
||||
|
||||
@ -19,7 +19,7 @@ import classes from '../util/classes';
|
||||
import addressToAddress from './addressToAddress';
|
||||
|
||||
type Props = BareProps & {
|
||||
defaultValue?: string | Uint8Array,
|
||||
defaultValue?: string | Uint8Array | null,
|
||||
hideAddress?: boolean;
|
||||
isError?: boolean,
|
||||
isInput?: boolean,
|
||||
|
||||
53
packages/ui-app/src/InputRpc/SelectMethod.js
Normal file
53
packages/ui-app/src/InputRpc/SelectMethod.js
Normal file
@ -0,0 +1,53 @@
|
||||
// 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 { InterfaceMethodDefinition } from '@polkadot/jsonrpc/types';
|
||||
import type { I18nProps } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import map from '@polkadot/jsonrpc';
|
||||
|
||||
import Dropdown from '../Dropdown';
|
||||
import classes from '../util/classes';
|
||||
import translate from '../translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
isError?: boolean,
|
||||
label?: string,
|
||||
onChange: (value: InterfaceMethodDefinition) => void,
|
||||
// flowlint-next-line unclear-type:off
|
||||
options: Array<any>,
|
||||
value: InterfaceMethodDefinition,
|
||||
withLabel?: boolean
|
||||
};
|
||||
|
||||
function SelectMethod ({ className, isError, label = '', onChange, options, style, t, value: { name, section }, withLabel }: Props): React$Node {
|
||||
if (!options.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const transform = (name: string): InterfaceMethodDefinition =>
|
||||
// $FlowFixMe ... string to type :(
|
||||
map[section].methods[name];
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
className={classes('ui--DropdownLinked-Items', className)}
|
||||
isError={isError}
|
||||
label={label || t('input.rpc.method', {
|
||||
defaultValue: 'with method name'
|
||||
})}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
style={style}
|
||||
transform={transform}
|
||||
value={name}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(SelectMethod);
|
||||
44
packages/ui-app/src/InputRpc/SelectSection.js
Normal file
44
packages/ui-app/src/InputRpc/SelectSection.js
Normal file
@ -0,0 +1,44 @@
|
||||
// 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 { InterfaceMethodDefinition, InterfaceTypes } from '@polkadot/jsonrpc/types';
|
||||
import type { I18nProps } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Dropdown from '../Dropdown';
|
||||
import classes from '../util/classes';
|
||||
import translate from '../translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
defaultValue?: InterfaceTypes,
|
||||
isError?: boolean,
|
||||
label: string,
|
||||
onChange: (value: InterfaceTypes) => void,
|
||||
// flowlint-next-line unclear-type:off
|
||||
options: Array<any>,
|
||||
value: InterfaceMethodDefinition,
|
||||
withLabel?: boolean
|
||||
};
|
||||
|
||||
function SelectSection ({ className, defaultValue, isError, label, onChange, options, style, t, value: { section }, withLabel }: Props): React$Node {
|
||||
return (
|
||||
<Dropdown
|
||||
className={classes('ui--DropdownLinked-Sections', className)}
|
||||
defaultValue={defaultValue}
|
||||
isError={isError}
|
||||
label={label || t('input.rpc.section', {
|
||||
defaultValue: 'rpc area'
|
||||
})}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
style={style}
|
||||
value={section}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(SelectSection);
|
||||
114
packages/ui-app/src/InputRpc/index.js
Normal file
114
packages/ui-app/src/InputRpc/index.js
Normal file
@ -0,0 +1,114 @@
|
||||
// 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
|
||||
|
||||
// TODO: We have a lot shared between this and InputExtrinsic & InputStorage
|
||||
|
||||
import type { InterfaceMethodDefinition, InterfaceTypes } from '@polkadot/jsonrpc/types';
|
||||
import type { I18nProps } from '../types';
|
||||
|
||||
import '../InputExtrinsic/InputExtrinsic.css';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import map from '@polkadot/jsonrpc';
|
||||
|
||||
import classes from '../util/classes';
|
||||
import translate from '../translate';
|
||||
import SelectMethod from './SelectMethod';
|
||||
import SelectSection from './SelectSection';
|
||||
import methodOptions from './options/method';
|
||||
import sectionOptions from './options/section';
|
||||
|
||||
type Props = I18nProps & {
|
||||
defaultValue: InterfaceMethodDefinition,
|
||||
isError?: boolean,
|
||||
labelMethod?: string,
|
||||
labelSection?: string,
|
||||
onChange: (value: InterfaceMethodDefinition) => void,
|
||||
withLabel?: boolean
|
||||
};
|
||||
|
||||
type State = {
|
||||
// flowlint-next-line unclear-type:off
|
||||
optionsMethod: Array<any>,
|
||||
// flowlint-next-line unclear-type:off
|
||||
optionsSection: Array<any>,
|
||||
value: InterfaceMethodDefinition
|
||||
};
|
||||
|
||||
class InputRpc extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
const { section } = this.props.defaultValue;
|
||||
|
||||
this.state = {
|
||||
optionsMethod: methodOptions(section),
|
||||
optionsSection: sectionOptions(),
|
||||
value: this.props.defaultValue
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, labelMethod, labelSection, style, withLabel } = this.props;
|
||||
const { optionsMethod, optionsSection, value } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('ui--DropdownLinked', 'ui--row', className)}
|
||||
style={style}
|
||||
>
|
||||
<SelectSection
|
||||
className='small'
|
||||
label={labelSection}
|
||||
onChange={this.onSectionChange}
|
||||
options={optionsSection}
|
||||
value={value}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
<SelectMethod
|
||||
className='large'
|
||||
label={labelMethod}
|
||||
onChange={this.onMethodChange}
|
||||
options={optionsMethod}
|
||||
value={value}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onMethodChange = (value: InterfaceMethodDefinition): void => {
|
||||
const { onChange } = this.props;
|
||||
const { value: { name, section } } = this.state;
|
||||
|
||||
if (value.section === section && value.name === name) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ value }, () =>
|
||||
onChange(value)
|
||||
);
|
||||
}
|
||||
|
||||
onSectionChange = (newSection: InterfaceTypes): void => {
|
||||
const { value: { section } } = this.state;
|
||||
|
||||
if (newSection === section) {
|
||||
return;
|
||||
}
|
||||
|
||||
const optionsMethod = methodOptions(newSection);
|
||||
const value = map[newSection].methods[optionsMethod[0].value];
|
||||
|
||||
this.setState({ optionsMethod }, () =>
|
||||
this.onMethodChange(value)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(InputRpc);
|
||||
52
packages/ui-app/src/InputRpc/options/method.js
Normal file
52
packages/ui-app/src/InputRpc/options/method.js
Normal file
@ -0,0 +1,52 @@
|
||||
// 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 { InterfaceTypes } from '@polkadot/jsonrpc/types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import map from '@polkadot/jsonrpc';
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export default function createOptions (sectionName: any): Array<any> {
|
||||
const section = map[(sectionName: InterfaceTypes)];
|
||||
|
||||
if (!section) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object
|
||||
.keys(section.methods)
|
||||
.sort()
|
||||
.filter((name) => {
|
||||
const { isDeprecated = false, isHidden = false, isSubscription = false } = section.methods[name];
|
||||
|
||||
return !isDeprecated && !isHidden && !isSubscription;
|
||||
})
|
||||
.map((name) => {
|
||||
const { description = '', params = {} } = section.methods[name];
|
||||
const inputs = Object.keys(params).join(', ');
|
||||
|
||||
return {
|
||||
className: 'ui--DropdownLinked-Item',
|
||||
key: `${sectionName}_${name}`,
|
||||
text: [
|
||||
<div
|
||||
className='ui--DropdownLinked-Item-call'
|
||||
key={`${sectionName}_${name}:call`}
|
||||
>
|
||||
{name}({inputs})
|
||||
</div>,
|
||||
<div
|
||||
className='ui--DropdownLinked-Item-text'
|
||||
key={`${sectionName}_${name}:text`}
|
||||
>
|
||||
{description || name}
|
||||
</div>
|
||||
],
|
||||
value: name
|
||||
};
|
||||
});
|
||||
}
|
||||
29
packages/ui-app/src/InputRpc/options/section.js
Normal file
29
packages/ui-app/src/InputRpc/options/section.js
Normal file
@ -0,0 +1,29 @@
|
||||
// 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 map from '@polkadot/jsonrpc';
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export default function createOptions (): Array<any> {
|
||||
return Object
|
||||
.keys(map)
|
||||
.sort()
|
||||
.filter((name) => {
|
||||
const { isDeprecated = false, methods } = map[name];
|
||||
const available = Object
|
||||
.keys(methods)
|
||||
.filter((name) => {
|
||||
const { isDeprecated = false, isHidden = false } = methods[name];
|
||||
|
||||
return !isDeprecated && !isHidden;
|
||||
});
|
||||
|
||||
return !isDeprecated && available.length !== 0;
|
||||
})
|
||||
.map((name) => ({
|
||||
text: name,
|
||||
value: name
|
||||
}));
|
||||
}
|
||||
@ -8,7 +8,7 @@ import type { I18nProps } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import map from '@polkadot/storage-substrate/keys';
|
||||
import map from '@polkadot/storage-substrate';
|
||||
|
||||
import Dropdown from '../Dropdown';
|
||||
import classes from '../util/classes';
|
||||
|
||||
@ -12,7 +12,7 @@ import '../InputExtrinsic/InputExtrinsic.css';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import map from '@polkadot/storage-substrate/keys';
|
||||
import map from '@polkadot/storage-substrate';
|
||||
|
||||
import classes from '../util/classes';
|
||||
import translate from '../translate';
|
||||
|
||||
@ -7,7 +7,7 @@ import type { StateDb$SectionNames } from '@polkadot/storage/types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import map from '@polkadot/storage-substrate/keys';
|
||||
import map from '@polkadot/storage-substrate';
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export default function createOptions (sectionName: any): Array<any> {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import map from '@polkadot/storage-substrate/keys';
|
||||
import map from '@polkadot/storage-substrate';
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export default function createOptions (): Array<any> {
|
||||
|
||||
@ -9,9 +9,11 @@ import React from 'react';
|
||||
|
||||
import CopyButton from './CopyButton';
|
||||
import Labelled from './Labelled';
|
||||
import classes from './util/classes';
|
||||
|
||||
type Props = BareProps & {
|
||||
children?: React$Node,
|
||||
isError?: boolean,
|
||||
isHidden?: boolean,
|
||||
label?: React$Node,
|
||||
value?: mixed,
|
||||
@ -19,7 +21,7 @@ type Props = BareProps & {
|
||||
withLabel?: boolean
|
||||
};
|
||||
|
||||
export default function Output ({ className, children, isHidden, label, style, value, withCopy = false, withLabel }: Props): React$Node {
|
||||
export default function Output ({ className, children, isError = false, isHidden, label, style, value, withCopy = false, withLabel }: Props): React$Node {
|
||||
return (
|
||||
<Labelled
|
||||
className={className}
|
||||
@ -28,7 +30,7 @@ export default function Output ({ className, children, isHidden, label, style, v
|
||||
style={style}
|
||||
withLabel={withLabel}
|
||||
>
|
||||
<div className='ui--output'>
|
||||
<div className={classes('ui--output', isError && 'error')}>
|
||||
{
|
||||
// flowlint-next-line unclear-type:off
|
||||
((value: any): React$Node)
|
||||
|
||||
@ -12,10 +12,9 @@ import Bare from './Bare';
|
||||
|
||||
export default class Account extends React.PureComponent<Props> {
|
||||
render (): React$Node {
|
||||
const { className, isDisabled, isError, label, style, value: { options: { initValue } = {} }, withLabel } = this.props;
|
||||
|
||||
const { className, defaultValue: { value }, isDisabled, isError, label, style, withLabel } = this.props;
|
||||
// flowlint-next-line unclear-type:off
|
||||
const defaultValue = ((initValue: any): Uint8Array);
|
||||
const defaultValue = ((value: any): Uint8Array);
|
||||
|
||||
return (
|
||||
<Bare
|
||||
@ -38,9 +37,9 @@ export default class Account extends React.PureComponent<Props> {
|
||||
}
|
||||
|
||||
onChange = (value?: Uint8Array): void => {
|
||||
const { index, onChange } = this.props;
|
||||
const { onChange } = this.props;
|
||||
|
||||
onChange(index, {
|
||||
onChange({
|
||||
isValid: !!value && value.length === 32,
|
||||
value
|
||||
});
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
// flowlint sketchy-null-mixed:off,sketchy-null-number:off
|
||||
|
||||
import type { Props } from '../types';
|
||||
|
||||
@ -14,7 +13,9 @@ import Bare from './Bare';
|
||||
|
||||
export default class Amount extends React.PureComponent<Props> {
|
||||
render (): React$Node {
|
||||
const { className, isDisabled, isError, label, style, value: { options: { initValue = 0, maxValue, minValue = 0 } = {} }, withLabel } = this.props;
|
||||
const { className, defaultValue: { value }, isDisabled, isError, label, style, withLabel } = this.props;
|
||||
// flowlint-next-line unclear-type:off
|
||||
const defaultValue = new BN(((value: any): BN) || 0).toNumber();
|
||||
|
||||
return (
|
||||
<Bare
|
||||
@ -23,12 +24,11 @@ export default class Amount extends React.PureComponent<Props> {
|
||||
>
|
||||
<Input
|
||||
className='small'
|
||||
defaultValue={initValue || minValue}
|
||||
defaultValue={defaultValue || 0}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
label={label}
|
||||
max={maxValue}
|
||||
min={minValue}
|
||||
min={0}
|
||||
onChange={this.onChange}
|
||||
type='number'
|
||||
withLabel={withLabel}
|
||||
@ -38,11 +38,11 @@ export default class Amount extends React.PureComponent<Props> {
|
||||
}
|
||||
|
||||
onChange = (value: number): void => {
|
||||
const { index, onChange, value: { options: { minValue = 0 } = {} } } = this.props;
|
||||
const { onChange } = this.props;
|
||||
|
||||
onChange(index, {
|
||||
onChange({
|
||||
isValid: true,
|
||||
value: new BN(value || minValue || 0)
|
||||
value: new BN(value || 0)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
67
packages/ui-app/src/Params/Param/BaseBytes.js
Normal file
67
packages/ui-app/src/Params/Param/BaseBytes.js
Normal file
@ -0,0 +1,67 @@
|
||||
// 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 { Props as BaseProps, Size } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import hexToU8a from '@polkadot/util/hex/toU8a';
|
||||
|
||||
import Input from '../../Input';
|
||||
import Bare from './Bare';
|
||||
|
||||
type Props = BaseProps & {
|
||||
length?: number,
|
||||
size?: Size,
|
||||
validate?: (u8a: Uint8Array) => boolean
|
||||
};
|
||||
|
||||
const defaultValidate = (u8a: Uint8Array): boolean =>
|
||||
true;
|
||||
|
||||
export default class BaseBytes extends React.PureComponent<Props> {
|
||||
render (): React$Node {
|
||||
const { className, isDisabled, isError, label, size = 'full', style, withLabel } = this.props;
|
||||
|
||||
return (
|
||||
<Bare
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
<Input
|
||||
className={size}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
label={label}
|
||||
onChange={this.onChange}
|
||||
placeholder='0x...'
|
||||
type='text'
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
</Bare>
|
||||
);
|
||||
}
|
||||
|
||||
onChange = (hex: string): void => {
|
||||
const { length = -1, onChange, validate = defaultValidate } = this.props;
|
||||
|
||||
let u8a;
|
||||
|
||||
try {
|
||||
u8a = hexToU8a(hex);
|
||||
} catch (error) {
|
||||
u8a = new Uint8Array([]);
|
||||
}
|
||||
|
||||
const isValidLength = length !== -1
|
||||
? u8a.length === length
|
||||
: u8a.length !== 0;
|
||||
|
||||
onChange({
|
||||
isValid: isValidLength && validate(u8a),
|
||||
value: u8a
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -17,7 +17,9 @@ const options = [
|
||||
|
||||
export default class Bool extends React.PureComponent<Props> {
|
||||
render (): React$Node {
|
||||
const { className, isDisabled, isError, label, style, value: { options: { initValue = false } = {} }, withLabel } = this.props;
|
||||
const { className, defaultValue: { value }, isDisabled, isError, label, style, withLabel } = this.props;
|
||||
// flowlint-next-line unclear-type:off
|
||||
const defaultValue = ((value: any): boolean);
|
||||
|
||||
return (
|
||||
<Bare
|
||||
@ -26,9 +28,9 @@ export default class Bool extends React.PureComponent<Props> {
|
||||
>
|
||||
<Dropdown
|
||||
className='small'
|
||||
defaultValue={defaultValue}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
defaultValue={initValue}
|
||||
label={label}
|
||||
options={options}
|
||||
onChange={this.onChange}
|
||||
@ -39,9 +41,9 @@ export default class Bool extends React.PureComponent<Props> {
|
||||
}
|
||||
|
||||
onChange = (value: boolean): void => {
|
||||
const { index, onChange } = this.props;
|
||||
const { onChange } = this.props;
|
||||
|
||||
onChange(index, {
|
||||
onChange({
|
||||
isValid: true,
|
||||
value
|
||||
});
|
||||
|
||||
@ -3,65 +3,26 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Props as BaseProps, Size } from '../types';
|
||||
import type { Props } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import hexToU8a from '@polkadot/util/hex/toU8a';
|
||||
import BaseBytes from './BaseBytes';
|
||||
|
||||
import Input from '../../Input';
|
||||
import Bare from './Bare';
|
||||
|
||||
type Props = BaseProps & {
|
||||
length?: number,
|
||||
size?: Size,
|
||||
validate?: (u8a: Uint8Array) => boolean
|
||||
}
|
||||
|
||||
const defaultValidate = (u8a: Uint8Array): boolean =>
|
||||
true;
|
||||
|
||||
export default class Bytes extends React.PureComponent<Props> {
|
||||
render (): React$Node {
|
||||
const { className, isDisabled, isError, label, size = 'full', style, withLabel } = this.props;
|
||||
|
||||
return (
|
||||
<Bare
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
<Input
|
||||
className={size}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
label={label}
|
||||
onChange={this.onChange}
|
||||
placeholder='0x...'
|
||||
type='text'
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
</Bare>
|
||||
);
|
||||
}
|
||||
|
||||
onChange = (hex: string): void => {
|
||||
const { index, length = -1, onChange, validate = defaultValidate } = this.props;
|
||||
|
||||
let u8a;
|
||||
|
||||
try {
|
||||
u8a = hexToU8a(hex);
|
||||
} catch (error) {
|
||||
u8a = new Uint8Array([]);
|
||||
}
|
||||
|
||||
const isValidLength = length !== -1
|
||||
? u8a.length === length
|
||||
: u8a.length !== 0;
|
||||
|
||||
onChange(index, {
|
||||
isValid: isValidLength && validate(u8a),
|
||||
value: u8a
|
||||
});
|
||||
};
|
||||
export default function Bytes ({ className, defaultValue, isDisabled, isError, label, name, onChange, style, withLabel }: Props): React$Node {
|
||||
return (
|
||||
<BaseBytes
|
||||
className={className}
|
||||
defaultValue={defaultValue}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
label={label}
|
||||
length={-1}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
size='full'
|
||||
style={style}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -31,9 +31,9 @@ export default class Code extends React.PureComponent<Props> {
|
||||
|
||||
// TODO: Validate that we have actual proper WASM code
|
||||
onChange = (value: Uint8Array): void => {
|
||||
const { index, onChange } = this.props;
|
||||
const { onChange } = this.props;
|
||||
|
||||
onChange(index, {
|
||||
onChange({
|
||||
isValid: value.length !== 0,
|
||||
value: u8aConcat(bnToU8a(value.length, 32, true), value)
|
||||
});
|
||||
|
||||
@ -7,19 +7,19 @@ import type { Props } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Bytes from './Bytes';
|
||||
import BaseBytes from './BaseBytes';
|
||||
|
||||
export default function Hash ({ index, isDisabled, isError, label, onChange, value, withLabel }: Props): React$Node {
|
||||
export default function Hash ({ defaultValue, isDisabled, isError, label, name, onChange, withLabel }: Props): React$Node {
|
||||
return (
|
||||
<Bytes
|
||||
index={index}
|
||||
<BaseBytes
|
||||
defaultValue={defaultValue}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
label={label}
|
||||
length={32}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
size='medium'
|
||||
value={value}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -89,10 +89,10 @@ export default class KeyValue extends React.PureComponent<Props, State> {
|
||||
|
||||
nextState (newState: $Shape<State>): void {
|
||||
this.setState(
|
||||
(prevState: State, { index, onChange }: Props) => {
|
||||
(prevState: State, { onChange }: Props) => {
|
||||
const { key = prevState.key, value = prevState.value } = newState;
|
||||
|
||||
onChange(index, {
|
||||
onChange({
|
||||
isValid: key.isValid && value.isValid,
|
||||
value: u8aConcat(
|
||||
u8aConcat(bnToU8a(key.u8a.length, 32, true), key.u8a),
|
||||
|
||||
@ -3,24 +3,22 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Param$Types } from '@polkadot/params/types';
|
||||
import type { Props as BareProps } from '../types';
|
||||
import type { Props as BareProps, RawParam } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import typeToString from '@polkadot/params/typeToString';
|
||||
|
||||
import translate from '../../translate';
|
||||
import Base from './Base';
|
||||
import typeToText from '../typeToText';
|
||||
|
||||
type Props = BareProps & {
|
||||
defaultValue: RawParam,
|
||||
t: I18Next$Translate,
|
||||
value: {
|
||||
type: Param$Types
|
||||
},
|
||||
withLabel?: boolean
|
||||
};
|
||||
|
||||
function Unknown ({ label, t, value: { type }, withLabel }: Props): React$Node {
|
||||
function Unknown ({ defaultValue: { type }, label, t, withLabel }: Props): React$Node {
|
||||
return (
|
||||
<Base
|
||||
size='full'
|
||||
@ -31,7 +29,7 @@ function Unknown ({ label, t, value: { type }, withLabel }: Props): React$Node {
|
||||
{t('param.unknown', {
|
||||
defaultValue: `ERROR: Unimplemented type '{{type}}' requested. No renderer exists`,
|
||||
replace: {
|
||||
type: typeToText(type)
|
||||
type: typeToString(type)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
// flowlint sketchy-null-mixed:off
|
||||
|
||||
import type { Props } from '../types';
|
||||
|
||||
@ -19,8 +18,9 @@ const options = [
|
||||
|
||||
export default class VoteThreshold extends React.PureComponent<Props> {
|
||||
render (): React$Node {
|
||||
const { className, isDisabled, isError, label, style, value: { options: { initValue = 0 } = {} }, withLabel } = this.props;
|
||||
const defaultValue = initValue || 0;
|
||||
const { className, defaultValue: { value }, isDisabled, isError, label, style, withLabel } = this.props;
|
||||
// flowlint-next-line unclear-type:off
|
||||
const defaultValue = ((value: any): number);
|
||||
|
||||
return (
|
||||
<Bare
|
||||
@ -42,9 +42,9 @@ export default class VoteThreshold extends React.PureComponent<Props> {
|
||||
}
|
||||
|
||||
onChange = (value: number): void => {
|
||||
const { index, onChange } = this.props;
|
||||
const { onChange } = this.props;
|
||||
|
||||
onChange(index, {
|
||||
onChange({
|
||||
isValid: true,
|
||||
value
|
||||
});
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
// @flow
|
||||
|
||||
import type { Param$Types } from '@polkadot/params/types';
|
||||
import type { ComponentMap } from '../types';
|
||||
import type { Props, ComponentMap } from '../types';
|
||||
|
||||
import Account from './Account';
|
||||
import Amount from './Amount';
|
||||
@ -34,6 +34,7 @@ const components: ComponentMap = {
|
||||
'ReferendumIndex': Amount,
|
||||
'SessionKey': Amount,
|
||||
'Signature': Hash,
|
||||
'String': Unknown,
|
||||
'Timestamp': Amount,
|
||||
'u32': Amount,
|
||||
'u64': Amount,
|
||||
@ -41,14 +42,9 @@ const components: ComponentMap = {
|
||||
'VoteThreshold': VoteThreshold
|
||||
};
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export default function findComponent (type: Param$Types, overrides?: ComponentMap = {}): React$ComponentType<any> | Array<React$ComponentType<any>> {
|
||||
export default function findComponent (type: Param$Types, overrides?: ComponentMap = {}): React$ComponentType<Props> {
|
||||
if (Array.isArray(type)) {
|
||||
return type
|
||||
.map((type) =>
|
||||
// $FlowFixMe running out of options to check the embedded arrays
|
||||
findComponent(type, overrides)
|
||||
);
|
||||
return Unknown;
|
||||
}
|
||||
|
||||
return overrides[type] || components[type] || Unknown;
|
||||
|
||||
@ -3,137 +3,61 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Param } from '@polkadot/params/types';
|
||||
import type { I18nProps } from '../../types';
|
||||
import type { BaseProps, ComponentMap, RawParam } from '../types';
|
||||
import type { Props as BaseProps, ComponentMap } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import typeToString from '@polkadot/params/typeToString';
|
||||
|
||||
import classes from '../../util/classes';
|
||||
import translate from '../../translate';
|
||||
import typeToText from '../typeToText';
|
||||
import findComponent from './findComponent';
|
||||
import Unknown from './Unknown';
|
||||
|
||||
type Props = I18nProps & {
|
||||
type Props = I18nProps & BaseProps & {
|
||||
index: number,
|
||||
overrides?: ComponentMap,
|
||||
onChange: (index: number, value: RawParam) => void,
|
||||
value: Param & {
|
||||
name: string
|
||||
};
|
||||
};
|
||||
|
||||
type ComponentProps = BaseProps & {
|
||||
index: number
|
||||
overrides?: ComponentMap
|
||||
};
|
||||
|
||||
type State = {
|
||||
// flowlint-next-line unclear-type:off
|
||||
Components: React$ComponentType<any> | Array<React$ComponentType<any>>
|
||||
| null
|
||||
Component: React$ComponentType<BaseProps> | null
|
||||
}
|
||||
|
||||
class ParamComponent extends React.PureComponent<Props, State> {
|
||||
state: State = {
|
||||
Components: []
|
||||
Component: null
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps ({ overrides, value: { type } = {} }: Props): State {
|
||||
static getDerivedStateFromProps ({ defaultValue: { type }, overrides }: Props): State {
|
||||
return {
|
||||
Components: !type
|
||||
Component: !type
|
||||
? null
|
||||
: findComponent(type, overrides)
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { Components } = this.state;
|
||||
const { Component } = this.state;
|
||||
|
||||
if (Components === null) {
|
||||
if (Component === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { value: { type } } = this.props;
|
||||
|
||||
// FIXME We don't handle array or tuple inputs atm
|
||||
return Array.isArray(type)
|
||||
? this.renderUnknown()
|
||||
: this.renderComponents();
|
||||
}
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
renderComponents = (_Components: React$ComponentType<any> | Array<React$ComponentType<any>> | null = this.state.Components, startIndex: string = '0', { name, type, options = {} } = this.props.value) => {
|
||||
if (!_Components) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { className, index, onChange, style } = this.props;
|
||||
|
||||
if (!Array.isArray(type)) {
|
||||
// flowlint-next-line unclear-type:off
|
||||
const Component = ((_Components: any): React$ComponentType<any>);
|
||||
|
||||
return this.renderComponent(Component, startIndex, {
|
||||
className,
|
||||
index,
|
||||
onChange,
|
||||
style,
|
||||
value: {
|
||||
name,
|
||||
type,
|
||||
options
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
const Components = ((_Components: any): Array<React$ComponentType<any>>);
|
||||
|
||||
return Components.map((Component, index) => {
|
||||
return this.renderComponents(Component, `${startIndex}-${index}`, {
|
||||
name,
|
||||
type: type[index],
|
||||
options: {}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
renderComponent = (Component: React$ComponentType<any>, sub: string, props: ComponentProps): React$Node => {
|
||||
const { className, index, onChange, style, value: { name, type, options = {} } } = props;
|
||||
const text = typeToText(type);
|
||||
const { className, defaultValue, index, name, onChange, style } = this.props;
|
||||
const type = typeToString(defaultValue.type);
|
||||
|
||||
return (
|
||||
<Component
|
||||
className={classes('ui--Param', className)}
|
||||
index={index}
|
||||
key={`${name}:${text}:${index}}`}
|
||||
label={`${name}: ${text} (${index})`}
|
||||
defaultValue={defaultValue}
|
||||
key={`${name}:${type}:${index}}`}
|
||||
label={`${name}: ${type} (${index})`}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
style={style}
|
||||
value={{
|
||||
options,
|
||||
type
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderUnknown = (): React$Node => {
|
||||
const { className, index, onChange, style, value: { name, type } = {} } = this.props;
|
||||
|
||||
return this.renderComponent(Unknown, '0', {
|
||||
className,
|
||||
index,
|
||||
onChange,
|
||||
style,
|
||||
value: {
|
||||
name,
|
||||
type
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(ParamComponent);
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
import type { Section$Item } from '@polkadot/params/types';
|
||||
import type { I18nProps } from '../types';
|
||||
import type { ComponentMap, RawParam, RawParams } from './types';
|
||||
import type { ComponentMap, RawParam, RawParams, RawParam$OnChange } from './types';
|
||||
|
||||
import './Params.css';
|
||||
|
||||
@ -24,40 +24,50 @@ type Props = I18nProps & {
|
||||
|
||||
type State = {
|
||||
item: Section$Item,
|
||||
handlers: Array<RawParam$OnChange>,
|
||||
onChangeParam: (at: number, next: RawParam) => void,
|
||||
values: RawParams
|
||||
};
|
||||
|
||||
class Params extends React.PureComponent<Props, State> {
|
||||
state: State = ({}: $Shape<State>);
|
||||
state: State;
|
||||
|
||||
static getDerivedStateFromProps ({ item, onChange }: Props, { item: { name, section } = {} }: State): $Shape<State> | null {
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = ({
|
||||
onChangeParam: this.onChangeParam
|
||||
}: $Shape<State>);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps ({ item, onChange }: Props, { item: { name, section } = {}, onChangeParam }: State): $Shape<State> | null {
|
||||
if (name === item.name && section === item.section) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { params = {} } = item;
|
||||
const values = createValues(params);
|
||||
const handlers = values.map(
|
||||
(value, index): RawParam$OnChange =>
|
||||
(value: RawParam): void =>
|
||||
onChangeParam(index, value)
|
||||
);
|
||||
|
||||
onChange(values);
|
||||
|
||||
return {
|
||||
item,
|
||||
values: createValues(params)
|
||||
handlers,
|
||||
values
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, item, overrides, style } = this.props;
|
||||
const { values } = this.state;
|
||||
|
||||
if (!values.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { name, params = {} } = item;
|
||||
const { className, item: { name, params = {} }, overrides, style } = this.props;
|
||||
const { handlers, values } = this.state;
|
||||
const paramNames = Object.keys(params);
|
||||
|
||||
if (paramNames.length === 0) {
|
||||
if (values.length === 0 || paramNames.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -67,45 +77,45 @@ class Params extends React.PureComponent<Props, State> {
|
||||
style={style}
|
||||
>
|
||||
<div className='ui--Params-Content'>
|
||||
{paramNames.map((paramName, index) => {
|
||||
const { options, type } = params[paramName];
|
||||
|
||||
return (
|
||||
<Param
|
||||
index={index}
|
||||
key={`${name}:${paramName}:${index}`}
|
||||
onChange={this.onChangeParam}
|
||||
overrides={overrides}
|
||||
value={{
|
||||
name: paramName,
|
||||
options,
|
||||
type
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{paramNames.map((paramName, index) => (
|
||||
<Param
|
||||
defaultValue={values[index]}
|
||||
index={index}
|
||||
key={`${name}:${paramName}:${index}`}
|
||||
name={paramName}
|
||||
onChange={handlers[index]}
|
||||
overrides={overrides}
|
||||
type={params[paramName].type}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeParam = (at: number, next: RawParam): void => {
|
||||
onChangeParam = (at: number, { isValid = false, value }: RawParam): void => {
|
||||
this.setState(
|
||||
(prevState: State): $Shape<State> => ({
|
||||
values: prevState.values.map((value, index) =>
|
||||
index === at
|
||||
? next
|
||||
: value
|
||||
values: prevState.values.map((prev, index) =>
|
||||
index !== at
|
||||
? prev
|
||||
: {
|
||||
isValid,
|
||||
type: prev.type,
|
||||
value
|
||||
}
|
||||
)
|
||||
}),
|
||||
(): void => {
|
||||
const { onChange } = this.props;
|
||||
const { values } = this.state;
|
||||
|
||||
onChange(values);
|
||||
}
|
||||
this.notifyChange
|
||||
);
|
||||
}
|
||||
|
||||
notifyChange = (): void => {
|
||||
const { onChange } = this.props;
|
||||
const { values } = this.state;
|
||||
|
||||
onChange(values);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(Params);
|
||||
|
||||
@ -2,20 +2,19 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
// flowlint sketchy-null-mixed:off
|
||||
|
||||
import type { Param$Type } from '@polkadot/params/types';
|
||||
import type { Param$Types } from '@polkadot/params/types';
|
||||
import type { RawParam$Values } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
type Options = {
|
||||
initValue?: mixed,
|
||||
minValue?: mixed
|
||||
};
|
||||
import typeToString from '@polkadot/params/typeToString';
|
||||
|
||||
export default function getInitValue (type: Param$Type, { initValue, minValue }: Options = {}): ?mixed {
|
||||
export default function getInitValue (type: Param$Types): RawParam$Values {
|
||||
switch (type) {
|
||||
case 'Balance':
|
||||
return new BN(1);
|
||||
|
||||
case 'BlockNumber':
|
||||
case 'Index':
|
||||
case 'SessionKey':
|
||||
@ -24,15 +23,43 @@ export default function getInitValue (type: Param$Type, { initValue, minValue }:
|
||||
case 'ReferendumIndex':
|
||||
case 'u32':
|
||||
case 'VoteIndex':
|
||||
return new BN(initValue || minValue || 0);
|
||||
return new BN(0);
|
||||
|
||||
case 'bool':
|
||||
return initValue || false;
|
||||
return false;
|
||||
|
||||
case 'String':
|
||||
return '';
|
||||
|
||||
case 'Timestamp':
|
||||
return new Date(0);
|
||||
|
||||
case 'VoteThreshold':
|
||||
return initValue || 0;
|
||||
return 0;
|
||||
|
||||
case 'AccountId':
|
||||
case 'Bytes':
|
||||
case 'Call':
|
||||
case 'Code':
|
||||
case 'Digest':
|
||||
case 'Hash':
|
||||
case 'Header':
|
||||
case 'KeyValue':
|
||||
case 'KeyValueStorage':
|
||||
case 'MisbehaviorReport':
|
||||
case 'Proposal':
|
||||
case 'Signature':
|
||||
return void 0;
|
||||
|
||||
default:
|
||||
return initValue;
|
||||
if (Array.isArray(type)) {
|
||||
return type.map((subtype) =>
|
||||
getInitValue(subtype)
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE for @flow, if it fails here, we know what is missing
|
||||
(type: empty); // eslint-disable-line no-unused-expressions
|
||||
throw new Error(`Unable to determine default type for ${typeToString(type)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 { Param$Types } from '@polkadot/params/types';
|
||||
|
||||
export default function typeToText (type: Param$Types): string {
|
||||
if (!Array.isArray(type)) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// $FlowFixMe hate doing this, but it _looks_ ok
|
||||
const text = type.map(typeToText).join(', ');
|
||||
|
||||
return type.length !== 1
|
||||
? `(${text})`
|
||||
: `Array<${text}>`;
|
||||
}
|
||||
@ -3,33 +3,34 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Param$Type, Param$Types } from '@polkadot/params/types';
|
||||
import type { Param$Type, Param$Types, Param$Value } from '@polkadot/params/types';
|
||||
import type { BareProps } from '../types';
|
||||
|
||||
export type RawParam$Value = ?Param$Value;
|
||||
|
||||
export type RawParam$ValueArray = Array<?Param$Value | RawParam$ValueArray>;
|
||||
|
||||
export type RawParam$Values = RawParam$Value | RawParam$ValueArray;
|
||||
|
||||
export type RawParam = {
|
||||
isValid: boolean,
|
||||
value: mixed,
|
||||
type: Param$Types,
|
||||
value: RawParam$Values,
|
||||
}
|
||||
|
||||
export type RawParam$OnChange = (value: $Shape<RawParam>) => void;
|
||||
|
||||
export type RawParams = Array<RawParam>;
|
||||
|
||||
export type BaseProps = BareProps & {
|
||||
onChange: (index: number, value: RawParam) => void,
|
||||
value: {
|
||||
name: string,
|
||||
options?: {
|
||||
initValue?: mixed,
|
||||
minValue?: mixed,
|
||||
maxValue?: mixed
|
||||
},
|
||||
type: Param$Types
|
||||
}
|
||||
defaultValue: RawParam,
|
||||
name: string,
|
||||
onChange: RawParam$OnChange
|
||||
};
|
||||
|
||||
export type Props = BaseProps & {
|
||||
isDisabled?: boolean,
|
||||
isError?: boolean,
|
||||
index: number,
|
||||
label: string,
|
||||
withLabel?: boolean
|
||||
};
|
||||
@ -37,6 +38,5 @@ export type Props = BaseProps & {
|
||||
export type Size = 'full' | 'large' | 'medium' | 'small';
|
||||
|
||||
export type ComponentMap = {
|
||||
// flowlint-next-line unclear-type:off
|
||||
[Param$Type]: React$ComponentType<any>
|
||||
[Param$Type]: React$ComponentType<Props>
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
// @flow
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { Param$Types } from '@polkadot/params/types';
|
||||
import type { Param$Types, Param$Type$Array } from '@polkadot/params/types';
|
||||
|
||||
// import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
|
||||
import numberFormat from '@polkadot/ui-react-rx/util/numberFormat';
|
||||
@ -14,7 +14,22 @@ import isBn from '@polkadot/util/is/bn';
|
||||
import isU8a from '@polkadot/util/is/u8a';
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
export default function valueToText (type: Param$Types, value: any): string {
|
||||
function arrayToText (type: Param$Type$Array, value: Array<any>): string {
|
||||
if (value.length === 0) {
|
||||
return 'empty';
|
||||
}
|
||||
|
||||
const values = type.map((_type, index) =>
|
||||
valueToText(_type, value[index])
|
||||
).join(', ');
|
||||
|
||||
return type.length === 1
|
||||
? `(${values})`
|
||||
: values;
|
||||
}
|
||||
|
||||
// flowlint-next-line unclear-type:off
|
||||
function valueToText (type: Param$Types, value: any): string {
|
||||
if (type === 'bool') {
|
||||
return value ? 'Yes' : 'No';
|
||||
}
|
||||
@ -24,18 +39,7 @@ export default function valueToText (type: Param$Types, value: any): string {
|
||||
}
|
||||
|
||||
if (Array.isArray(type)) {
|
||||
if (value.length === 0) {
|
||||
return 'empty';
|
||||
}
|
||||
|
||||
const values = type.map((_type, index) =>
|
||||
// $FlowFixMe hate doing this, but it _looks_ ok
|
||||
valueToText(_type, value[index])
|
||||
).join(', ');
|
||||
|
||||
return type.length === 1
|
||||
? `(${values})`
|
||||
: values;
|
||||
return arrayToText(type, value);
|
||||
}
|
||||
|
||||
if (type === 'AccountId') {
|
||||
@ -52,3 +56,5 @@ export default function valueToText (type: Param$Types, value: any): string {
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
export default valueToText;
|
||||
|
||||
@ -3,55 +3,56 @@
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Param, Params } from '@polkadot/params/types';
|
||||
import type { RawParam } from './types';
|
||||
import type { Params } from '@polkadot/params/types';
|
||||
import type { RawParam, RawParam$ValueArray } from './types';
|
||||
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
|
||||
import getInitValue from './initValue';
|
||||
|
||||
export default function values (params: Params): Array<RawParam> {
|
||||
return Object
|
||||
.values(params)
|
||||
// $FlowFixMe yes, we are sure, the type is correct
|
||||
.map(({ type, options }: Param): RawParam => {
|
||||
if (Array.isArray(type)) {
|
||||
// console.error('Unable to determine default values for array/tuple type', type);
|
||||
//
|
||||
// return {
|
||||
// isValid: false,
|
||||
// value: void 0
|
||||
// };
|
||||
const types = Object.keys(params).map((key) => params[key].type);
|
||||
|
||||
if (type.length !== 1) {
|
||||
console.error('Unable to determine default values for tuple type', type);
|
||||
return types.map((type): RawParam => {
|
||||
if (Array.isArray(type)) {
|
||||
// console.error('Unable to determine default values for array/tuple type', type);
|
||||
//
|
||||
// return {
|
||||
// isValid: false,
|
||||
// value: void 0
|
||||
// };
|
||||
|
||||
return {
|
||||
isValid: false,
|
||||
value: void 0
|
||||
};
|
||||
}
|
||||
if (type.length !== 1) {
|
||||
console.error('Unable to determine default values for tuple type', type);
|
||||
|
||||
return type.reduce(({ isValid, value }, type) => {
|
||||
// $FlowFixMe still not sure how to iterate through these
|
||||
const avalue = getInitValue(type, {});
|
||||
|
||||
value.push(avalue);
|
||||
|
||||
return {
|
||||
isValid: isValid && !isUndefined(avalue),
|
||||
value
|
||||
};
|
||||
}, { isValid: true, value: [] });
|
||||
return {
|
||||
isValid: false,
|
||||
type,
|
||||
value: void 0
|
||||
};
|
||||
}
|
||||
|
||||
const value = getInitValue(type, options);
|
||||
const value: RawParam$ValueArray = [];
|
||||
|
||||
return {
|
||||
isValid: !isUndefined(value),
|
||||
value
|
||||
};
|
||||
});
|
||||
return type.reduce(({ isValid, type }, subtype) => {
|
||||
const avalue = getInitValue(subtype);
|
||||
|
||||
value.push(avalue);
|
||||
|
||||
return {
|
||||
isValid: isValid && !isUndefined(avalue),
|
||||
type,
|
||||
value
|
||||
};
|
||||
}, { isValid: true, type, value });
|
||||
}
|
||||
|
||||
const value = getInitValue(type);
|
||||
|
||||
return {
|
||||
isValid: !isUndefined(value),
|
||||
type,
|
||||
value
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 0x050000000600000055555555550ae803000000000000
|
||||
|
||||
@ -1,3 +1,26 @@
|
||||
/* 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--output {
|
||||
background: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #f5f5f5;
|
||||
box-sizing: border-box;
|
||||
line-height: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0.75em 1em;
|
||||
position: relative;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.ui--output .ui--output-button {
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
right: 0.25rem;
|
||||
}
|
||||
|
||||
.ui--output.error {
|
||||
background: #fff6f6;
|
||||
border-color: #e0b4b4;
|
||||
}
|
||||
|
||||
@ -76,21 +76,3 @@
|
||||
.ui--row > div.thirty3 {
|
||||
padding: 0 0.25rem;
|
||||
}
|
||||
|
||||
.ui--output {
|
||||
background: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #f9f9f9;
|
||||
box-sizing: border-box;
|
||||
line-height: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0.75em 1em;
|
||||
position: relative;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.ui--output .ui--output-button {
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
right: 0.25rem;
|
||||
}
|
||||
|
||||
@ -2,6 +2,10 @@
|
||||
/* This software may be modified and distributed under the terms
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.ui.hidden.divider {
|
||||
margin: 0.75rem 0;
|
||||
}
|
||||
|
||||
.ui.dropdown {
|
||||
/* from .ui.fluid.dropdown */
|
||||
display: block;
|
||||
|
||||
@ -12,9 +12,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/ui-react": "^0.12.3",
|
||||
"@polkadot/util": "^0.20.5",
|
||||
"@polkadot/util-keyring": "^0.20.5",
|
||||
"@polkadot/ui-react": "^0.13.4",
|
||||
"@polkadot/util": "^0.21.3",
|
||||
"@polkadot/util-keyring": "^0.21.3",
|
||||
"store": "^2.0.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@ -11,7 +11,6 @@ import { accountKey } from '../defaults';
|
||||
import createOptions from '../options';
|
||||
|
||||
export default function forgetAccount (state: State, address: string): void {
|
||||
// $FlowFixMe oops, types not updated upstream
|
||||
state.keyring.removePair(address);
|
||||
|
||||
store.remove(accountKey(address));
|
||||
|
||||
@ -8,17 +8,7 @@ import type { State, KeyringJson } from '../types';
|
||||
import createItem from './item';
|
||||
import createHeader from './header';
|
||||
|
||||
export default function createOptions (state: State): void {
|
||||
state.options = {
|
||||
account: [],
|
||||
address: [],
|
||||
all: [],
|
||||
recent: [],
|
||||
testing: []
|
||||
};
|
||||
|
||||
const { available, keyring, options } = state;
|
||||
|
||||
function addPairs ({ available, keyring }: State): void {
|
||||
keyring
|
||||
.getPairs()
|
||||
.forEach((pair) => {
|
||||
@ -29,10 +19,12 @@ export default function createOptions (state: State): void {
|
||||
meta: pair.getMeta()
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function addAccounts ({ available, options }: State): void {
|
||||
Object
|
||||
.values(available.account)
|
||||
// $FlowFixMe values() -> mixed, this is an object
|
||||
.keys(available.account)
|
||||
.map((address) => available.account[address])
|
||||
.forEach(({ address, meta: { name, isTesting = false } }: KeyringJson) => {
|
||||
const option = createItem(address, name);
|
||||
|
||||
@ -42,10 +34,12 @@ export default function createOptions (state: State): void {
|
||||
options.account.push(option);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addAddresses ({ available, options }: State): void {
|
||||
Object
|
||||
.values(available.address)
|
||||
// $FlowFixMe values() -> mixed, this is an object
|
||||
.keys(available.address)
|
||||
.map((address) => available.address[address])
|
||||
.forEach(({ address, meta: { name, isRecent = false } }: KeyringJson) => {
|
||||
if (available.account[address]) {
|
||||
return;
|
||||
@ -59,6 +53,22 @@ export default function createOptions (state: State): void {
|
||||
options.address.push(option);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default function createOptions (state: State): void {
|
||||
state.options = {
|
||||
account: [],
|
||||
address: [],
|
||||
all: [],
|
||||
recent: [],
|
||||
testing: []
|
||||
};
|
||||
|
||||
addPairs(state);
|
||||
addAccounts(state);
|
||||
addAddresses(state);
|
||||
|
||||
const { options } = state;
|
||||
|
||||
options.address = [].concat(
|
||||
options.address.length ? [ createHeader('Addresses') ] : [],
|
||||
|
||||
15
packages/ui-signer/LICENSE
Normal file
15
packages/ui-signer/LICENSE
Normal file
@ -0,0 +1,15 @@
|
||||
ISC License (ISC)
|
||||
|
||||
Copyright 2017-2018 Jaco Greeff <jacogr@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
1
packages/ui-signer/README.md
Normal file
1
packages/ui-signer/README.md
Normal file
@ -0,0 +1 @@
|
||||
# @polkadot/ui-signer
|
||||
24
packages/ui-signer/package.json
Normal file
24
packages/ui-signer/package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "@polkadot/ui-signer",
|
||||
"version": "0.6.2",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"build": "polkadot-dev-build-babel",
|
||||
"check": "stylelint 'src/**/*.css' && eslint src && flow check",
|
||||
"test": "jest --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.47",
|
||||
"@polkadot/extrinsics": "^0.15.8",
|
||||
"@polkadot/ui-app": "^0.6.2",
|
||||
"@polkadot/ui-keyring": "^0.6.2",
|
||||
"@polkadot/util": "^0.21.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.3.1",
|
||||
"semantic-ui-react": "^0.80.0"
|
||||
}
|
||||
}
|
||||
86
packages/ui-signer/src/Extrinsic.js
Normal file
86
packages/ui-signer/src/Extrinsic.js
Normal file
@ -0,0 +1,86 @@
|
||||
// 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-app/types';
|
||||
import type { QueueTx } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import extrinsics from '@polkadot/extrinsics-substrate';
|
||||
import Modal from '@polkadot/ui-app/Modal';
|
||||
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
|
||||
import u8aToHexShort from '@polkadot/util/u8a/toHexShort';
|
||||
import addressEncode from '@polkadot/util-keyring/address/encode';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
children?: React$Node,
|
||||
value: QueueTx
|
||||
};
|
||||
|
||||
function findExtrinsic (sectionId: number, methodId: number): { method: ?string, section: ?string } {
|
||||
const section = Object.keys(extrinsics).find((section) =>
|
||||
extrinsics[section].index[0] === sectionId
|
||||
);
|
||||
const methods = section
|
||||
? extrinsics[section].methods.public
|
||||
: {};
|
||||
const method = Object.keys(methods).find((method) =>
|
||||
methods[method].index[1] === methodId
|
||||
);
|
||||
|
||||
return {
|
||||
method,
|
||||
section
|
||||
};
|
||||
}
|
||||
|
||||
function Extrinsic ({ children, className, style, t, value: { nonce = new BN(0), publicKey, values: [_value] } }: Props): React$Node {
|
||||
const unknown = t('decoded.unknown', {
|
||||
defaultValue: 'unknown'
|
||||
});
|
||||
// flowlint-next-line unclear-type:off
|
||||
const value = ((_value: any): Uint8Array);
|
||||
const { method = unknown, section = unknown } = findExtrinsic(value[0], value[1]);
|
||||
// flowlint-next-line unclear-type:off
|
||||
const from = addressEncode(((publicKey: any): Uint8Array));
|
||||
|
||||
return [
|
||||
<Modal.Header key='header'>
|
||||
{t('extrinsic.header', {
|
||||
defaultValue: 'Extrinsic submission'
|
||||
})}
|
||||
</Modal.Header>,
|
||||
<Modal.Content className='ui--signer-Signer-Content' key='content'>
|
||||
<div className='ui--signer-Signer-Decoded'>
|
||||
<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'>{section}.{method}</span> with an index of <span className='code'>{nonce.toString()}</span>
|
||||
</Trans>
|
||||
</p>
|
||||
<p>
|
||||
{t('decoded.data', {
|
||||
defaultValue: 'The encoded parameters contains the data'
|
||||
})}
|
||||
</p>
|
||||
<p className='code'>
|
||||
{u8aToHexShort(value, 512)}
|
||||
</p>
|
||||
</div>
|
||||
<IdentityIcon
|
||||
className='icon'
|
||||
value={from}
|
||||
/>
|
||||
</div>
|
||||
{children}
|
||||
</Modal.Content>
|
||||
];
|
||||
}
|
||||
|
||||
export default translate(Extrinsic);
|
||||
245
packages/ui-signer/src/Modal.js
Normal file
245
packages/ui-signer/src/Modal.js
Normal file
@ -0,0 +1,245 @@
|
||||
// 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 { ApiProps } from '@polkadot/ui-react-rx/types';
|
||||
import type { I18nProps } from '@polkadot/ui-app/types';
|
||||
import type { QueueTx, QueueTx$MessageSetStatus } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Button from '@polkadot/ui-app/Button';
|
||||
import Modal from '@polkadot/ui-app/Modal';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import withApi from '@polkadot/ui-react-rx/with/api';
|
||||
|
||||
import Extrinsic from './Extrinsic';
|
||||
import Unlock from './Unlock';
|
||||
import signMessage from './sign';
|
||||
import submitMessage from './submit';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & ApiProps & {
|
||||
queue: Array<QueueTx>,
|
||||
queueSetStatus: QueueTx$MessageSetStatus
|
||||
};
|
||||
|
||||
type UnlockI18n = {
|
||||
key: string,
|
||||
value: I18Next$Translate$Config
|
||||
}
|
||||
|
||||
type State = {
|
||||
currentItem?: QueueTx,
|
||||
password: string,
|
||||
sendItem: (item: QueueTx) => Promise<void>,
|
||||
unlockError: UnlockI18n | null
|
||||
};
|
||||
|
||||
class Signer extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
password: '',
|
||||
sendItem: this.sendItem,
|
||||
unlockError: null
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps ({ queue }: Props, { currentItem, password, sendItem, unlockError }: State): $Shape<State> {
|
||||
const nextItem = queue.find(({ status }) =>
|
||||
status === 'queued'
|
||||
);
|
||||
const isSame =
|
||||
!!nextItem &&
|
||||
!!currentItem &&
|
||||
(
|
||||
(!nextItem.publicKey && !currentItem.publicKey) ||
|
||||
(
|
||||
(nextItem.publicKey && nextItem.publicKey.toString()) === (currentItem.publicKey && currentItem.publicKey.toString())
|
||||
)
|
||||
);
|
||||
|
||||
if (nextItem && nextItem.rpc.isSigned !== true) {
|
||||
sendItem(nextItem);
|
||||
}
|
||||
|
||||
return {
|
||||
currentItem: nextItem,
|
||||
password: isSame ? password : '',
|
||||
unlockError: isSame ? unlockError : null
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style } = this.props;
|
||||
const { currentItem } = this.state;
|
||||
|
||||
if (!currentItem || currentItem.rpc.isSigned !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={classes('ui--signer-Signer', className)}
|
||||
dimmer='inverted'
|
||||
open
|
||||
style={style}
|
||||
>
|
||||
{this.renderContent()}
|
||||
{this.renderButtons()}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
renderButtons (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { currentItem: { rpc: { isSigned = false } = {} } = {} } = this.state;
|
||||
|
||||
return (
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.onCancel}
|
||||
text={t('extrinsic.cancel', {
|
||||
defaultValue: 'Cancel'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isPrimary
|
||||
onClick={this.onSend}
|
||||
text={
|
||||
isSigned
|
||||
? t('extrinsic.signedSend', {
|
||||
defaultValue: 'Sign and Submit'
|
||||
})
|
||||
: t('extrinsic.send', {
|
||||
defaultValue: 'Submit'
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Button.Group>
|
||||
</Modal.Actions>
|
||||
);
|
||||
}
|
||||
|
||||
renderContent (): React$Node {
|
||||
const { currentItem } = this.state;
|
||||
|
||||
if (!currentItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Extrinsic value={currentItem}>
|
||||
{this.renderUnlock()}
|
||||
</Extrinsic>
|
||||
);
|
||||
}
|
||||
|
||||
renderUnlock (): React$Node {
|
||||
const { t } = this.props;
|
||||
const { currentItem, password, unlockError } = this.state;
|
||||
|
||||
if (!currentItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Unlock
|
||||
error={unlockError && t(unlockError.key, unlockError.value)}
|
||||
onChange={this.onChangePassword}
|
||||
password={password}
|
||||
value={currentItem.publicKey}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
unlockAccount (publicKey: Uint8Array, password?: string): ?UnlockI18n {
|
||||
const pair = keyring.getPair(publicKey);
|
||||
|
||||
if (pair.hasSecretKey()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
pair.decodePkcs8(password);
|
||||
} catch (error) {
|
||||
return {
|
||||
key: 'signer.unlock.generic',
|
||||
value: {
|
||||
defaultValue: error.message
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
onChangePassword = (password: string): void => {
|
||||
this.setState({
|
||||
password,
|
||||
unlockError: null
|
||||
});
|
||||
}
|
||||
|
||||
onCancel = (): void => {
|
||||
const { queueSetStatus } = this.props;
|
||||
const { currentItem } = this.state;
|
||||
|
||||
// This should never be executed
|
||||
if (!currentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
queueSetStatus(currentItem.id, 'cancelled');
|
||||
}
|
||||
|
||||
onSend = async (): Promise<void> => {
|
||||
const { currentItem, password } = this.state;
|
||||
|
||||
// This should never be executed
|
||||
if (!currentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendItem(currentItem, password);
|
||||
};
|
||||
|
||||
sendItem = async ({ id, nonce, publicKey, rpc, values }: QueueTx, password?: string): Promise<void> => {
|
||||
if (rpc.isSigned === true && publicKey) {
|
||||
const unlockError = this.unlockAccount(publicKey, password);
|
||||
|
||||
if (unlockError) {
|
||||
this.setState({ unlockError });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const { api, queueSetStatus } = this.props;
|
||||
|
||||
queueSetStatus(id, 'sending');
|
||||
|
||||
let data = values;
|
||||
|
||||
if (rpc.isSigned === true && publicKey) {
|
||||
// flowlint-next-line unclear-type:off
|
||||
data = [signMessage(publicKey, nonce, ((data[0]: any): Uint8Array)).data];
|
||||
}
|
||||
|
||||
const { error, result, status } = await submitMessage(api, data, rpc);
|
||||
|
||||
queueSetStatus(id, status, result, error);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(
|
||||
withApi(Signer)
|
||||
);
|
||||
83
packages/ui-signer/src/Queue.js
Normal file
83
packages/ui-signer/src/Queue.js
Normal file
@ -0,0 +1,83 @@
|
||||
// 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-app/types';
|
||||
import type { QueueProps, QueueTx$Base, QueueTx$Id, QueueTx$Status } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type Props = BareProps & {
|
||||
children: React$Node
|
||||
};
|
||||
|
||||
type State = QueueProps;
|
||||
|
||||
const { Consumer, Provider } = React.createContext();
|
||||
let nextId: QueueTx$Id = 0;
|
||||
|
||||
export default class Queue extends React.Component<Props, State> {
|
||||
static Provider = Provider;
|
||||
static Consumer = Consumer
|
||||
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
queue: [],
|
||||
queueAdd: this.queueAdd,
|
||||
queueSetStatus: this.queueSetStatus
|
||||
};
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<Provider value={this.state}>
|
||||
{this.props.children}
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
queueSetStatus = (id: QueueTx$Id, status: QueueTx$Status, result?: mixed, error?: Error): void => {
|
||||
this.setState(
|
||||
(prevState: State): $Shape<State> => ({
|
||||
queue: prevState.queue.map((item) =>
|
||||
item.id === id
|
||||
? {
|
||||
...item,
|
||||
error: error || item.error,
|
||||
// flowlint-next-line sketchy-null-mixed:off
|
||||
result: result || item.result,
|
||||
status
|
||||
}
|
||||
: item
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
if (['cancelled', 'error', 'sent'].includes(status)) {
|
||||
setTimeout(() => {
|
||||
this.queueSetStatus(id, 'completed');
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
queueAdd = (value: QueueTx$Base): QueueTx$Id => {
|
||||
const id: QueueTx$Id = ++nextId;
|
||||
|
||||
this.setState(
|
||||
(prevState: State): $Shape<State> => ({
|
||||
queue: prevState.queue.concat([{
|
||||
...value,
|
||||
id,
|
||||
status: 'queued'
|
||||
}])
|
||||
})
|
||||
);
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@ -25,16 +25,16 @@ export default function Status ({ className, queue, style }: Props): React$Node
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('extrinsics--Status', className)}
|
||||
className={classes('ui--signer-Status', className)}
|
||||
style={style}
|
||||
>
|
||||
{available.map(({ extrinsic: { name, section }, id, status }) =>
|
||||
{available.map(({ rpc: { name, section }, id, status }) =>
|
||||
<div
|
||||
className={classes('extrinsics--Status-Item', status)}
|
||||
className={classes('ui--signer-Status-Item', status)}
|
||||
key={id}
|
||||
>
|
||||
<div className='header'>
|
||||
{section}_{name}
|
||||
{section}.{name}
|
||||
</div>
|
||||
<div className='status'>
|
||||
{status}
|
||||
@ -12,7 +12,7 @@ import Password from '@polkadot/ui-app/Password';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
|
||||
import translate from '../translate';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
error?: string,
|
||||
@ -52,7 +52,7 @@ class Unlock extends React.PureComponent<Props, State> {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('extrinsics--Signer-Unlock', className)}
|
||||
className={classes('ui--signer-Signer-Unlock', className)}
|
||||
style={style}
|
||||
>
|
||||
<div className='ui--row'>
|
||||
59
packages/ui-signer/src/index.css
Normal file
59
packages/ui-signer/src/index.css
Normal file
@ -0,0 +1,59 @@
|
||||
/* 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--signer-Signer {}
|
||||
|
||||
.ui--signer-Signer-Decoded,
|
||||
.ui--signer-Signer-Unlock {
|
||||
align-content: center;
|
||||
display: flex;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.ui--signer-Signer-Decoded {}
|
||||
|
||||
.ui--signer-Signer-Unlock {
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.ui--signer-Signer-Content {}
|
||||
|
||||
.ui--signer-Signer-Content .expanded {
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
.ui--signer-Signer-Content .expanded .code {
|
||||
background: #f5f5f5;
|
||||
font-family: monospace;
|
||||
margin: 0 0.25em;
|
||||
overflow-wrap: break-word;
|
||||
padding: 0.25em 0.5em;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.ui--signer-Status {
|
||||
display: inline-block;
|
||||
position: fixed;
|
||||
right: 0.25rem;
|
||||
top: 0.125rem;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.ui--signer-Status-Item {
|
||||
background: #bcdaf0;
|
||||
margin: 0.125rem;
|
||||
padding: 0.5rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ui--signer-Status-Item .status {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.ui--signer-Status-Item.error {
|
||||
background: red;
|
||||
color: white;
|
||||
}
|
||||
46
packages/ui-signer/src/index.js
Normal file
46
packages/ui-signer/src/index.js
Normal file
@ -0,0 +1,46 @@
|
||||
// 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-app/types';
|
||||
import type { QueueProps } from './types';
|
||||
|
||||
import './index.css';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Modal from './Modal';
|
||||
import Status from './Status';
|
||||
import Queue from './Queue';
|
||||
|
||||
type Props = BareProps & {
|
||||
children: React$Node
|
||||
};
|
||||
|
||||
function Signer ({ children, className, style }: Props): React$Node {
|
||||
return (
|
||||
<Queue>
|
||||
{children}
|
||||
<Queue.Consumer>
|
||||
{({ queue, queueSetStatus }: QueueProps = {}) => [
|
||||
<Modal
|
||||
className={className}
|
||||
key='signer-modal'
|
||||
queue={queue}
|
||||
queueSetStatus={queueSetStatus}
|
||||
style={style}
|
||||
/>,
|
||||
<Status
|
||||
key='signer-status'
|
||||
queue={queue}
|
||||
/>
|
||||
]}
|
||||
</Queue.Consumer>
|
||||
</Queue>
|
||||
);
|
||||
}
|
||||
|
||||
Signer.Queue = Queue;
|
||||
|
||||
export default Signer;
|
||||
16
packages/ui-signer/src/rawToValues.js
Normal file
16
packages/ui-signer/src/rawToValues.js
Normal file
@ -0,0 +1,16 @@
|
||||
// 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 { Param$Value, Param$Values } from '@polkadot/params/types';
|
||||
import type { RawParam } from '@polkadot/ui-app/Params/types';
|
||||
|
||||
function rawToValues (raw: Array<RawParam>): Array<Param$Values> {
|
||||
return raw.map(({ value }) =>
|
||||
// flowlint-next-line unclear-type:off
|
||||
((value: any): Param$Value)
|
||||
);
|
||||
}
|
||||
|
||||
export default rawToValues;
|
||||
26
packages/ui-signer/src/sign.js
Normal file
26
packages/ui-signer/src/sign.js
Normal file
@ -0,0 +1,26 @@
|
||||
// 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 { Signed } from './types';
|
||||
|
||||
import encodeCall from '@polkadot/extrinsics-codec/encode/call';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
import u8aToHex from '@polkadot/util/u8a/toHex';
|
||||
|
||||
export default function signMessage (publicKey: Uint8Array, nonce: BN | number, value: Uint8Array): Signed {
|
||||
const message = encodeCall(publicKey, nonce, value);
|
||||
const signature = keyring.getPair(publicKey).sign(message);
|
||||
|
||||
console.log(` message :: ${u8aToHex(message)}`);
|
||||
console.log(`signature :: ${u8aToHex(signature)}`);
|
||||
|
||||
return {
|
||||
data: u8aConcat(message, signature),
|
||||
message,
|
||||
signature
|
||||
};
|
||||
}
|
||||
34
packages/ui-signer/src/submit.js
Normal file
34
packages/ui-signer/src/submit.js
Normal file
@ -0,0 +1,34 @@
|
||||
// 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 { RxApiInterface } from '@polkadot/api-rx/types';
|
||||
import type { InterfaceMethodDefinition } from '@polkadot/jsonrpc/types';
|
||||
import type { Param$Values } from '@polkadot/params/types';
|
||||
import type { QueueTx$Result } from './types';
|
||||
|
||||
export default function submitMessage (api: RxApiInterface, params: Array<Param$Values>, rpc: InterfaceMethodDefinition): Promise<QueueTx$Result> {
|
||||
const { name, section } = rpc;
|
||||
|
||||
// $FlowFixMe This should not be an issue...
|
||||
return api[section][name]
|
||||
.apply(null, params)
|
||||
.toPromise()
|
||||
.then((result) => {
|
||||
console.log(`${section}.${name}: result ::`, result);
|
||||
|
||||
return {
|
||||
result,
|
||||
status: 'sent'
|
||||
};
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
|
||||
return {
|
||||
error,
|
||||
status: 'error'
|
||||
};
|
||||
});
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user