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
This commit is contained in:
Jaco Greeff 2018-05-30 12:07:45 +02:00 committed by GitHub
parent bae287ad3c
commit d45642e3f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 230 additions and 150 deletions

View File

@ -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<Props, State> {
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<Props, State> {
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<Props, State> {
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
defaultValue={defaultValue}
hideAddress
isInput={false}
label={t('editor.select', {
@ -229,15 +229,15 @@ class Editor extends React.PureComponent<Props, State> {
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
});
}
}

View File

@ -137,6 +137,7 @@ class Creator extends React.PureComponent<Props, State> {
newAddress = addressEncode(
addressDecode(address)
);
isAddressValid = keyring.isAvailable(newAddress);
} catch (error) {
isAddressValid = false;
}

View File

@ -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<Props, State> {
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<Props, State> {
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<Props, State> {
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
defaultValue={defaultValue}
hideAddress
isInput={false}
label={t('editor.select', {
@ -231,15 +231,15 @@ class Editor extends React.PureComponent<Props, State> {
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
});
}
}

View File

@ -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 {

View File

@ -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 {
})}
/>
<BestHash />
<NodeInfo />
<BlockHeaders />
</div>
);

View File

@ -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<Props, State> {
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<Props, State> {
render (): React$Node {
const { className, style } = this.props;
const { currentPair, isUnlockVisible } = this.state;
return (
<div
@ -69,11 +68,7 @@ class Sign extends React.PureComponent<Props, State> {
{this.renderAccount()}
{this.renderInput()}
{this.renderSignature()}
<Unlock
isVisible={isUnlockVisible}
onClose={this.toggleUnlock}
pair={currentPair}
/>
{this.renderUnlock()}
{this.renderButtons()}
</div>
);
@ -81,13 +76,13 @@ class Sign extends React.PureComponent<Props, State> {
renderAccount (): React$Node {
const { t } = this.props;
const { defaultPublicKey } = this.state;
const { defaultValue } = this.state;
return (
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
defaultValue={defaultValue}
isInput={false}
label={t('sign.account', {
defaultValue: 'using my account'
@ -172,6 +167,21 @@ class Sign extends React.PureComponent<Props, State> {
);
}
renderUnlock (): React$Node {
const { currentPair, isUnlockVisible } = this.state;
if (!isUnlockVisible) {
return null;
}
return (
<Unlock
onClose={this.toggleUnlock}
pair={currentPair}
/>
);
}
nextState = (newState: $Shape<State>): void => {
this.setState(
(prevState: State): $Shape<State> => {

View File

@ -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<Props, State> {
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<State> {
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 (
<Modal

View File

@ -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 { 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';
type Props = BareProps & {};
export default function Apps ({ className, style }: Props): React$Node {
return (
<div
className={classes('apps--App', className)}
style={style}
>
<SideBar />
<Signer>
<Content />
</Signer>
<Connecting />
</div>
);
}

View File

@ -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 (
<div className={classes('explorer--NodeInfo', className)}>
<Chain label={t('app.chain', {
defaultValue: 'chain: '
})} />
<NodeName label={t('app.name', {
<div className={classes('apps--NodeInfo', className)}>
<Chain
label={t('info.chain', {
defaultValue: 'chain: '
})}
onChange={updateTestInfo}
/>
<NodeName label={t('info.name', {
defaultValue: 'client: '
})}
/>
<NodeVersion label={t('app.name', {
<NodeVersion label={t('info.name', {
defaultValue: 'version: '
})} />
</div>

View File

@ -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 (
<div
className={classes('apps--SideBar', className)}
@ -55,8 +57,9 @@ function SideBar ({ className, style, t }: Props): React$Node {
<Icon name='github' /> GitHub
</a>
</Menu.Item>
<Menu.Divider hidden />
{children}
</Menu>
</div>
);
}

View File

@ -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;
}

View File

@ -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 (
<div
className={classes('apps--App', className)}
style={style}
>
<SideBar>
<NodeInfo />
</SideBar>
<Signer>
<Content />
</Signer>
<Connecting />
</div>
);
}
createApp(App, {
url: process.env.WS_URL === null || process.env.WS_URL === ''

View File

@ -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<Props, State> {
constructor (props: Props) {
super(props);
this.state = {
this.state = ({
defaultValue: addressToAddress(props.defaultValue)
};
}: $Shape<State>);
}
static getDerivedStateFromProps ({ value }: Props): $Shape<State> | 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 (
<Dropdown
className={classes('ui--InputAddress', hideAddress ? 'flag--hideAddress' : '', className)}
defaultValue={defaultValue}
defaultValue={
value !== undefined
? undefined
: defaultValue
}
isError={isError}
label={label}
onChange={onChange}
@ -69,6 +86,7 @@ export default class InputAddress extends React.Component<Props, State> {
options={options}
style={style}
transform={transform}
value={value}
withLabel={withLabel}
/>
);

View File

@ -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 '<unknown>';
}
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<any>): string {
function arrayToText (type: Param$Type$Array, value: Array<any>, 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();

View File

@ -15,13 +15,6 @@ export default function values (params: Params): Array<RawParam> {
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<RawParam> {
type,
value
};
}, { isValid: true, type, value });
// FIXME Arrays are currently not valid as inputs, no rendered
}, { isValid: false, type, value });
}
const value = getInitValue(type);

View File

@ -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<KeyringAddress> =>
@ -46,7 +51,9 @@ export default ({
getPair: (address: string | Uint8Array): KeyringPair =>
state.keyring.getPair(address),
getPairs: (): Array<KeyringPair> =>
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);

View File

@ -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];
}

View File

@ -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);
}
});
}

View File

@ -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);
}

View File

@ -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<KeyringPair>,
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
};