From d45642e3f6459dfaa41522e0b40c78fd434cd1f0 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 30 May 2018 12:07:45 +0200 Subject: [PATCH] Fix address defaultValue/value, hide testing accounts (#66) * Fix address defaultValue/value * @flow cleanups * Remove debug info * Properly hide dev chain addresses (default) * Close https://github.com/polkadot-js/apps/issues/68 * Don't allow pre-existing accounts and addr * Failsafe on conversion check * Don't try to encode arrays * Display decoded Proposals as text --- packages/app-accounts/src/Editor.js | 16 ++--- packages/app-addresses/src/Creator.js | 1 + packages/app-addresses/src/Editor.js | 16 ++--- packages/app-explorer/src/index.css | 13 +--- packages/app-explorer/src/index.js | 2 - packages/app-toolbox/src/Sign.js | 32 ++++++--- packages/app-toolbox/src/Unlock.js | 25 ++----- packages/apps/src/App/index.js | 34 --------- .../{app-explorer => apps}/src/NodeInfo.js | 21 ++++-- packages/apps/src/SideBar/index.js | 9 ++- packages/apps/src/{App/App.css => index.css} | 8 +++ packages/apps/src/index.js | 34 ++++++++- packages/ui-app/src/InputAddress/index.js | 30 ++++++-- packages/ui-app/src/Params/valueToText.js | 69 ++++++++++++------- packages/ui-app/src/Params/values.js | 10 +-- packages/ui-keyring/src/index.js | 13 +++- packages/ui-keyring/src/isAvailable.js | 20 ++++++ packages/ui-keyring/src/options/index.js | 8 +-- packages/ui-keyring/src/setTestMode.js | 14 ++++ packages/ui-keyring/src/types.js | 5 +- 20 files changed, 230 insertions(+), 150 deletions(-) delete mode 100644 packages/apps/src/App/index.js rename packages/{app-explorer => apps}/src/NodeInfo.js (62%) rename packages/apps/src/{App/App.css => index.css} (75%) create mode 100644 packages/ui-keyring/src/isAvailable.js create mode 100644 packages/ui-keyring/src/setTestMode.js diff --git a/packages/app-accounts/src/Editor.js b/packages/app-accounts/src/Editor.js index 4055088716..6a5d8b9991 100644 --- a/packages/app-accounts/src/Editor.js +++ b/packages/app-accounts/src/Editor.js @@ -23,7 +23,7 @@ type Props = I18nProps & { type State = { currentPair: KeyringPair | null, - defaultPublicKey?: Uint8Array, + defaultValue?: string, editedName: string, isEdited: boolean } @@ -38,8 +38,8 @@ class Editor extends React.PureComponent { const currentPair = pairs[pairs.length - 1] || null; this.state = this.createState(currentPair); - this.state.defaultPublicKey = currentPair - ? currentPair.publicKey() + this.state.defaultValue = currentPair + ? currentPair.address() : void 0; } @@ -96,7 +96,7 @@ class Editor extends React.PureComponent { renderData (): React$Node { const { t } = this.props; - const { currentPair, defaultPublicKey, editedName } = this.state; + const { currentPair, defaultValue, editedName } = this.state; if (!currentPair) { return t('editor.none', { @@ -116,7 +116,7 @@ class Editor extends React.PureComponent {
{ item.address() !== address ); const nextPair = pairs[pairs.length - 1] || null; - const defaultPublicKey = nextPair - ? nextPair.publicKey() + const defaultValue = nextPair + ? nextPair.address() : void 0; keyring.forgetAccount(address); this.nextState({ currentPair: nextPair, - defaultPublicKey + defaultValue }); } } diff --git a/packages/app-addresses/src/Creator.js b/packages/app-addresses/src/Creator.js index ed8fdce6f0..e5d9ea9196 100644 --- a/packages/app-addresses/src/Creator.js +++ b/packages/app-addresses/src/Creator.js @@ -137,6 +137,7 @@ class Creator extends React.PureComponent { newAddress = addressEncode( addressDecode(address) ); + isAddressValid = keyring.isAvailable(newAddress); } catch (error) { isAddressValid = false; } diff --git a/packages/app-addresses/src/Editor.js b/packages/app-addresses/src/Editor.js index 24197302a7..853037df7e 100644 --- a/packages/app-addresses/src/Editor.js +++ b/packages/app-addresses/src/Editor.js @@ -23,7 +23,7 @@ type Props = I18nProps & { type State = { currentAddress: KeyringAddress | null, - defaultPublicKey?: Uint8Array, + defaultValue?: string, editedName: string, isEdited: boolean } @@ -38,8 +38,8 @@ class Editor extends React.PureComponent { const currentAddress = addresses[addresses.length - 1] || null; this.state = this.createState(currentAddress); - this.state.defaultPublicKey = currentAddress - ? currentAddress.publicKey() + this.state.defaultValue = currentAddress + ? currentAddress.address() : void 0; } @@ -96,7 +96,7 @@ class Editor extends React.PureComponent { renderData (): React$Node { const { t } = this.props; - const { currentAddress, defaultPublicKey, editedName } = this.state; + const { currentAddress, defaultValue, editedName } = this.state; if (!currentAddress) { return t('editor.none', { @@ -116,7 +116,7 @@ class Editor extends React.PureComponent {
{ item.address() !== address ); const nextAddress = addresses[0] || null; - const defaultPublicKey = nextAddress - ? nextAddress.publicKey() + const defaultValue = nextAddress + ? nextAddress.address() : void 0; keyring.forgetAddress(address); this.nextState({ currentAddress: nextAddress, - defaultPublicKey + defaultValue }); } } diff --git a/packages/app-explorer/src/index.css b/packages/app-explorer/src/index.css index a72121ec7b..9a769496bd 100644 --- a/packages/app-explorer/src/index.css +++ b/packages/app-explorer/src/index.css @@ -10,18 +10,6 @@ background: transparent !important; } -.explorer--NodeInfo { - font-size: 0.75rem; - margin-bottom: 1.5rem; - opacity: 0.5; - text-align: right; -} - -.explorer--NodeInfo div { - display: inline-block; - margin-left: 0.5rem; -} - .explorer--BestHash, .explorer--BestNumber { font-weight: 100; @@ -37,6 +25,7 @@ .explorer--BestHash { font-size: 1.5em; line-height: 1.2; + margin-bottom: 0.75em; } .explorer--BestNumber { diff --git a/packages/app-explorer/src/index.js b/packages/app-explorer/src/index.js index f78b35a0bf..28961a456c 100644 --- a/packages/app-explorer/src/index.js +++ b/packages/app-explorer/src/index.js @@ -14,7 +14,6 @@ import BestNumber from '@polkadot/ui-react-rx/BestNumber'; import BestHash from './BestHash'; import BlockHeaders from './BlockHeaders'; -import NodeInfo from './NodeInfo'; import translate from './translate'; type Props = I18nProps & {}; @@ -32,7 +31,6 @@ function ExplorerApp ({ className, style, t }: Props): React$Node { })} /> -
); diff --git a/packages/app-toolbox/src/Sign.js b/packages/app-toolbox/src/Sign.js index 2a7fe5eb16..f1acd9a624 100644 --- a/packages/app-toolbox/src/Sign.js +++ b/packages/app-toolbox/src/Sign.js @@ -25,7 +25,7 @@ import translate from './translate'; type State = { currentPair: KeyringPair | null, - defaultPublicKey?: Uint8Array, + defaultValue?: string, data: string, isHexData: boolean, isLocked: boolean, @@ -44,8 +44,8 @@ class Sign extends React.PureComponent { this.state = { currentPair, - defaultPublicKey: currentPair - ? currentPair.publicKey() + defaultValue: currentPair + ? currentPair.address() : void 0, data: '', isHexData: false, @@ -59,7 +59,6 @@ class Sign extends React.PureComponent { render (): React$Node { const { className, style } = this.props; - const { currentPair, isUnlockVisible } = this.state; return (
{ {this.renderAccount()} {this.renderInput()} {this.renderSignature()} - + {this.renderUnlock()} {this.renderButtons()}
); @@ -81,13 +76,13 @@ class Sign extends React.PureComponent { renderAccount (): React$Node { const { t } = this.props; - const { defaultPublicKey } = this.state; + const { defaultValue } = this.state; return (
{ ); } + renderUnlock (): React$Node { + const { currentPair, isUnlockVisible } = this.state; + + if (!isUnlockVisible) { + return null; + } + + return ( + + ); + } + nextState = (newState: $Shape): void => { this.setState( (prevState: State): $Shape => { diff --git a/packages/app-toolbox/src/Unlock.js b/packages/app-toolbox/src/Unlock.js index 90daa80fa7..83df7a3085 100644 --- a/packages/app-toolbox/src/Unlock.js +++ b/packages/app-toolbox/src/Unlock.js @@ -18,7 +18,6 @@ import IdentityIcon from '@polkadot/ui-react/IdentityIcon'; import translate from './translate'; type Props = I18nProps & { - isVisible: boolean, onClose: () => void, pair: KeyringPair }; @@ -41,30 +40,16 @@ class Unlock extends React.PureComponent { unlockError: null }; - static getDerivedStateFromProps ({ pair }: Props, prevState: State): State { - const address = pair - ? pair.address() - : ''; - - const isSame = address !== prevState.address; - + static getDerivedStateFromProps ({ pair }: Props, prevState: State): $Shape { return { - address, - password: isSame - ? prevState.password - : '', - unlockError: isSame - ? prevState.unlockError - : null + address: pair + ? pair.address() + : '' }; } render (): React$Node { - const { className, isVisible, style, t } = this.props; - - if (!isVisible) { - return null; - } + const { className, style, t } = this.props; return ( - - - - - -
- ); -} diff --git a/packages/app-explorer/src/NodeInfo.js b/packages/apps/src/NodeInfo.js similarity index 62% rename from packages/app-explorer/src/NodeInfo.js rename to packages/apps/src/NodeInfo.js index eed38db716..c1194cede1 100644 --- a/packages/app-explorer/src/NodeInfo.js +++ b/packages/apps/src/NodeInfo.js @@ -8,6 +8,7 @@ import type { I18nProps } from '@polkadot/ui-app/types'; import React from 'react'; import classes from '@polkadot/ui-app/util/classes'; +import keyring from '@polkadot/ui-keyring'; import Chain from '@polkadot/ui-react-rx/Chain'; import NodeName from '@polkadot/ui-react-rx/NodeName'; import NodeVersion from '@polkadot/ui-react-rx/NodeVersion'; @@ -16,17 +17,25 @@ import translate from './translate'; type Props = I18nProps & {}; +function updateTestInfo (chain?: string) { + console.log('updateTestInfo:chain', chain); + keyring.setTestMode(chain === 'dev'); +} + function NodeInfo ({ className, style, t }: Props): React$Node { return ( -
- - + + -
diff --git a/packages/apps/src/SideBar/index.js b/packages/apps/src/SideBar/index.js index 60639009cb..09d1512433 100644 --- a/packages/apps/src/SideBar/index.js +++ b/packages/apps/src/SideBar/index.js @@ -17,9 +17,11 @@ import routing from '../routing'; import translate from '../translate'; import Item from './Item'; -type Props = I18nProps & {}; +type Props = I18nProps & { + children?: React$Node +}; -function SideBar ({ className, style, t }: Props): React$Node { +function SideBar ({ children, className, style, t }: Props): React$Node { return (
GitHub +
); } diff --git a/packages/apps/src/App/App.css b/packages/apps/src/index.css similarity index 75% rename from packages/apps/src/App/App.css rename to packages/apps/src/index.css index 8ee11ae115..c89869633d 100644 --- a/packages/apps/src/App/App.css +++ b/packages/apps/src/index.css @@ -16,3 +16,11 @@ flex-grow: 1; overflow-y: auto; } + +.apps--NodeInfo { + background: transparent; + font-size: 0.75rem; + padding-right: 1em; + opacity: 0.5; + text-align: right; +} diff --git a/packages/apps/src/index.js b/packages/apps/src/index.js index f0131fc335..d021af2a96 100644 --- a/packages/apps/src/index.js +++ b/packages/apps/src/index.js @@ -3,9 +3,39 @@ // of the ISC license. See the LICENSE file for details. // @flow -import createApp from '@polkadot/ui-app'; +import type { BareProps } from '@polkadot/ui-app/types'; -import App from './App'; +import './index.css'; + +import React from 'react'; + +import createApp from '@polkadot/ui-app'; +import classes from '@polkadot/ui-app/util/classes'; +import Signer from '@polkadot/ui-signer'; + +import Connecting from './Connecting'; +import Content from './Content'; +import NodeInfo from './NodeInfo'; +import SideBar from './SideBar'; + +type Props = BareProps & {}; + +function App ({ className, style }: Props): React$Node { + return ( +
+ + + + + + + +
+ ); +} createApp(App, { url: process.env.WS_URL === null || process.env.WS_URL === '' diff --git a/packages/ui-app/src/InputAddress/index.js b/packages/ui-app/src/InputAddress/index.js index f2dd7bd2be..e38c31d150 100644 --- a/packages/ui-app/src/InputAddress/index.js +++ b/packages/ui-app/src/InputAddress/index.js @@ -26,11 +26,14 @@ type Props = BareProps & { label?: string, onChange: (value: Uint8Array) => void, type?: KeyringOption$Type, + value?: string | Uint8Array, withLabel?: boolean }; type State = { - defaultValue: ?string; + defaultValue: ?string, + options: KeyringOptions, + value?: string } const RECENT_KEY = 'header-recent'; @@ -48,20 +51,34 @@ export default class InputAddress extends React.Component { constructor (props: Props) { super(props); - this.state = { + this.state = ({ defaultValue: addressToAddress(props.defaultValue) - }; + }: $Shape); + } + + static getDerivedStateFromProps ({ value }: Props): $Shape | null { + try { + return { + value: addressToAddress(value) || undefined + }; + } catch (error) { + return null; + } } render (): React$Node { - const { className, hideAddress = false, isError, label, onChange, style, type = 'all', withLabel } = this.props; - const { defaultValue } = this.state; + const { className, hideAddress = false, isError, label, onChange, type = 'all', style, withLabel } = this.props; + const { defaultValue, value } = this.state; const options = keyring.getOptions(type); return ( { options={options} style={style} transform={transform} + value={value} withLabel={withLabel} /> ); diff --git a/packages/ui-app/src/Params/valueToText.js b/packages/ui-app/src/Params/valueToText.js index f6ae3bb836..ca91173ddc 100644 --- a/packages/ui-app/src/Params/valueToText.js +++ b/packages/ui-app/src/Params/valueToText.js @@ -4,54 +4,77 @@ // @flow import type BN from 'bn.js'; -import type { Param$Types, Param$Type$Array } from '@polkadot/params/types'; +import type { Param$Types, Param$Type$Array, ExtrinsicDecoded } from '@polkadot/params/types'; -// import IdentityIcon from '@polkadot/ui-react/IdentityIcon'; import numberFormat from '@polkadot/ui-react-rx/util/numberFormat'; import addressEncode from '@polkadot/util-keyring/address/encode'; import u8aToHex from '@polkadot/util/u8a/toHex'; import isBn from '@polkadot/util/is/bn'; import isU8a from '@polkadot/util/is/u8a'; +function proposalToText ({ extrinsic, params }: ExtrinsicDecoded): string { + if (!extrinsic) { + return ''; + } + + const inputs = extrinsic.params.map(({ name, type }, index) => + `${name}=${valueToText(type, params[index])}` + ).join(', '); + + return `${extrinsic.section}.${extrinsic.name}(${inputs})`; +} + // flowlint-next-line unclear-type:off -function arrayToText (type: Param$Type$Array, value: Array): string { +function arrayToText (type: Param$Type$Array, value: Array, withBound: boolean = true): string { if (value.length === 0) { return 'empty'; } const values = type.map((_type, index) => - valueToText(_type, value[index]) + valueToText(_type, value[index], false) ).join(', '); - return type.length === 1 + return withBound && 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'; - } +function valueToText (type: Param$Types, value: any, swallowError: boolean = true): string { + try { + if (type === 'bool') { + return value ? 'Yes' : 'No'; + } - if (!value) { - return 'unknown'; - } + if (!value) { + return 'unknown'; + } - if (Array.isArray(type)) { - return arrayToText(type, value); - } + if (Array.isArray(type)) { + return arrayToText(type, value); + } - if (type === 'AccountId') { - return addressEncode((value: Uint8Array)); - } + if (type === 'AccountId') { + return addressEncode((value: Uint8Array)); + } - if (isU8a(value)) { - return u8aToHex((value: Uint8Array), 256); - } + if (type === 'Proposal') { + return proposalToText((value: ExtrinsicDecoded)); + } - if (isBn(value)) { - return numberFormat((value: BN)); + if (isU8a(value)) { + return u8aToHex((value: Uint8Array), 256); + } + + if (isBn(value)) { + return numberFormat((value: BN)); + } + } catch (error) { + if (!swallowError) { + throw error; + } else { + console.log('valueToText', type, value, error); + } } return value.toString(); diff --git a/packages/ui-app/src/Params/values.js b/packages/ui-app/src/Params/values.js index 2b1f23ccb7..973c9ee5a1 100644 --- a/packages/ui-app/src/Params/values.js +++ b/packages/ui-app/src/Params/values.js @@ -15,13 +15,6 @@ export default function values (params: Params): Array { 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 - // }; - if (type.length !== 1) { console.error('Unable to determine default values for tuple type', type); @@ -44,7 +37,8 @@ export default function values (params: Params): Array { type, value }; - }, { isValid: true, type, value }); + // FIXME Arrays are currently not valid as inputs, no rendered + }, { isValid: false, type, value }); } const value = getInitValue(type); diff --git a/packages/ui-keyring/src/index.js b/packages/ui-keyring/src/index.js index ebb61380b6..23d7fd44ca 100644 --- a/packages/ui-keyring/src/index.js +++ b/packages/ui-keyring/src/index.js @@ -11,6 +11,7 @@ import testKeyring from '@polkadot/util-keyring/testing'; import loadAll from './loadAll'; import createAccount from './account/create'; import forgetAccount from './account/forget'; +import isAvailable from './isAvailable'; import saveAccount from './account/save'; import saveAccountMeta from './account/meta'; import forgetAddress from './address/forget'; @@ -18,8 +19,10 @@ import getAddress from './address/get'; import getAddresses from './address/all'; import saveAddress from './address/meta'; import saveRecent from './address/metaRecent'; +import setTestMode from './setTestMode'; const state: State = { + isTestMode: false, available: { account: {}, address: {} @@ -37,6 +40,8 @@ export default ({ forgetAccount(state, address), forgetAddress: (address: string): void => forgetAddress(state, address), + isAvailable: (address: string | Uint8Array): boolean => + isAvailable(state, address), getAddress: (address: string | Uint8Array): KeyringAddress => getAddress(state, address), getAddresses: (): Array => @@ -46,7 +51,9 @@ export default ({ getPair: (address: string | Uint8Array): KeyringPair => state.keyring.getPair(address), getPairs: (): Array => - state.keyring.getPairs(), + state.keyring.getPairs().filter((pair) => + state.isTestMode || pair.getMeta().isTesting !== true + ), loadAll: (): void => loadAll(state), saveAccount: (pair: KeyringPair, password?: string): void => @@ -56,5 +63,7 @@ export default ({ saveAddress: (address: string, meta: KeyringPair$Meta): void => saveAddress(state, address, meta), saveRecent: (address: string): KeyringOption => - saveRecent(state, address) + saveRecent(state, address), + setTestMode: (isTest: boolean): void => + setTestMode(state, isTest) }: KeyringInstance); diff --git a/packages/ui-keyring/src/isAvailable.js b/packages/ui-keyring/src/isAvailable.js new file mode 100644 index 0000000000..ef59e9dd6b --- /dev/null +++ b/packages/ui-keyring/src/isAvailable.js @@ -0,0 +1,20 @@ +// 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 { State } from './types'; + +import isString from '@polkadot/util/is/string'; +import addressEncode from '@polkadot/util-keyring/address/encode'; + +export default function isAvailable (state: State, _address: Uint8Array | string): boolean { + const address = isString(_address) + // flowlint-next-line unclear-type:off + ? ((_address: any): string) + : addressEncode(_address); + + console.log('isAvailable', address, state.available.account[address], state.available.address[address]); + + return !state.available.account[address] && !state.available.address[address]; +} diff --git a/packages/ui-keyring/src/options/index.js b/packages/ui-keyring/src/options/index.js index a1e431a7e5..e0b33ce564 100644 --- a/packages/ui-keyring/src/options/index.js +++ b/packages/ui-keyring/src/options/index.js @@ -21,17 +21,17 @@ function addPairs ({ available, keyring }: State): void { }); } -function addAccounts ({ available, options }: State): void { +function addAccounts ({ available, isTestMode, options }: State): void { Object .keys(available.account) .map((address) => available.account[address]) .forEach(({ address, meta: { name, isTesting = false } }: KeyringJson) => { const option = createItem(address, name); - if (isTesting) { - options.testing.push(option); - } else { + if (!isTesting) { options.account.push(option); + } else if (isTestMode) { + options.testing.push(option); } }); } diff --git a/packages/ui-keyring/src/setTestMode.js b/packages/ui-keyring/src/setTestMode.js new file mode 100644 index 0000000000..56afcfe1d6 --- /dev/null +++ b/packages/ui-keyring/src/setTestMode.js @@ -0,0 +1,14 @@ +// 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 { State } from './types'; + +import createOptions from './options'; + +export default function setTestMode (state: State, isTest: boolean): void { + state.isTestMode = isTest; + + createOptions(state); +} diff --git a/packages/ui-keyring/src/types.js b/packages/ui-keyring/src/types.js index 2036ee2091..fe0cfcfd1c 100644 --- a/packages/ui-keyring/src/types.js +++ b/packages/ui-keyring/src/types.js @@ -41,6 +41,7 @@ export type KeyringAddress = { } export type State = { + isTestMode: boolean, available: { account: { [string]: KeyringJson }, address: { [string]: KeyringJson } @@ -60,9 +61,11 @@ export type KeyringInstance = { getOptions: (type: KeyringOption$Type) => KeyringOptions, getPair: (address: string | Uint8Array) => KeyringPair, getPairs: () => Array, + isAvailable: (address: string | Uint8Array) => boolean, loadAll: () => void, saveAccount: (pair: KeyringPair, password?: string) => void, saveAccountMeta: (pair: KeyringPair, meta: KeyringPair$Meta) => void, saveAddress: (address: string, meta: KeyringPair$Meta) => void, - saveRecent: (address: string) => KeyringOption + saveRecent: (address: string) => KeyringOption, + setTestMode: (isTest: boolean) => void };