Cleanup address default saving (#242)

This commit is contained in:
Jaco Greeff 2018-08-21 12:01:12 +02:00 committed by GitHub
parent 553d4b73a4
commit cf8f2064f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 200 additions and 158 deletions

View File

@ -12,17 +12,19 @@ import Input from '@polkadot/ui-app/Input';
import InputAddress from '@polkadot/ui-app/InputAddress';
import classes from '@polkadot/ui-app/util/classes';
import keyring from '@polkadot/ui-keyring/index';
import accountObservable from '@polkadot/ui-keyring/observable/accounts';
import withObservableBase from '@polkadot/ui-react-rx/with/observableBase';
import AddressSummary from '@polkadot/ui-app/AddressSummary';
import translate from './translate';
type Props = I18nProps & {
accountAll?: Array<any>,
onBack: () => void
};
type State = {
currentPair: KeyringPair | null,
defaultValue?: string,
current: KeyringPair | null,
editedName: string,
isEdited: boolean
};
@ -33,13 +35,7 @@ class Editor extends React.PureComponent<Props, State> {
constructor (props: Props) {
super(props);
const pairs = keyring.getPairs();
const currentPair = pairs[pairs.length - 1] || null;
this.state = this.createState(currentPair);
this.state.defaultValue = currentPair
? currentPair.address()
: void 0;
this.state = this.createState(null);
}
render () {
@ -58,9 +54,9 @@ class Editor extends React.PureComponent<Props, State> {
renderButtons () {
const { t } = this.props;
const { currentPair, isEdited } = this.state;
const { current, isEdited } = this.state;
if (!currentPair) {
if (!current) {
return null;
}
@ -94,28 +90,29 @@ class Editor extends React.PureComponent<Props, State> {
}
renderData () {
const { t } = this.props;
const { currentPair, defaultValue, editedName } = this.state;
const { accountAll, t } = this.props;
const { current, editedName } = this.state;
if (!currentPair) {
if (!accountAll || !Object.keys(accountAll).length) {
return t('editor.none', {
defaultValue: 'There are no saved accounts. Add some first.'
});
}
const address = currentPair.address();
const address = current
? current.address()
: undefined;
return (
<div className='ui--grid'>
<AddressSummary
className='shrink'
value={address}
value={address || ''}
/>
<div className='grow'>
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultValue}
hideAddress
isInput={false}
label={t('editor.select', {
@ -142,11 +139,11 @@ class Editor extends React.PureComponent<Props, State> {
);
}
createState (currentPair: KeyringPair | null): State {
createState (current: KeyringPair | null): State {
return {
currentPair,
editedName: currentPair
? currentPair.getMeta().name || ''
current,
editedName: current
? current.getMeta().name || ''
: '',
isEdited: false
};
@ -155,14 +152,14 @@ class Editor extends React.PureComponent<Props, State> {
nextState (newState: State = {} as State): void {
this.setState(
(prevState: State): State => {
let { currentPair = prevState.currentPair, editedName = prevState.editedName } = newState;
const previous = prevState.currentPair || { address: () => null };
let { current = prevState.current, editedName = prevState.editedName } = newState;
const previous = prevState.current || { address: () => undefined };
let isEdited = false;
if (currentPair) {
if (currentPair.address() !== previous.address()) {
editedName = currentPair.getMeta().name || '';
} else if (editedName !== currentPair.getMeta().name) {
if (current) {
if (current.address() !== previous.address()) {
editedName = current.getMeta().name || '';
} else if (editedName !== current.getMeta().name) {
isEdited = true;
}
} else {
@ -170,7 +167,7 @@ class Editor extends React.PureComponent<Props, State> {
}
return {
currentPair,
current,
editedName,
isEdited
};
@ -179,10 +176,12 @@ class Editor extends React.PureComponent<Props, State> {
}
onChangeAccount = (publicKey: Uint8Array): void => {
const currentPair = keyring.getPair(publicKey);
const current = publicKey && publicKey.length === 32
? keyring.getPair(publicKey)
: null;
this.nextState({
currentPair
current
} as State);
}
@ -191,13 +190,13 @@ class Editor extends React.PureComponent<Props, State> {
}
onCommit = (): void => {
const { currentPair, editedName } = this.state;
const { current, editedName } = this.state;
if (!currentPair) {
if (!current) {
return;
}
keyring.saveAccountMeta(currentPair, {
keyring.saveAccountMeta(current, {
name: editedName,
whenEdited: Date.now()
});
@ -206,40 +205,35 @@ class Editor extends React.PureComponent<Props, State> {
}
onDiscard = (): void => {
const { currentPair } = this.state;
const { current } = this.state;
if (!currentPair) {
if (!current) {
return;
}
this.nextState({
editedName: currentPair.getMeta().name
editedName: current.getMeta().name
} as State);
}
onForget = (): void => {
const { currentPair } = this.state;
const { current } = this.state;
if (!currentPair) {
if (!current) {
return;
}
const address = currentPair.address();
const pairs = keyring.getPairs().filter((item) =>
item.address() !== address
this.setState(
this.createState(null),
() => {
keyring.forgetAccount(
current.address()
);
}
);
const nextPair = pairs[pairs.length - 1] || null;
const defaultValue = nextPair
? nextPair.address()
: void 0;
keyring.forgetAccount(address);
this.nextState({
currentPair: nextPair,
defaultValue
} as State);
}
}
export default translate(Editor);
export default withObservableBase(
accountObservable.subject, { propName: 'accountAll' }
)(translate(Editor));

View File

@ -9,20 +9,22 @@ import React from 'react';
import Button from '@polkadot/ui-app/Button';
import Input from '@polkadot/ui-app/Input';
import AddressSummary from '@polkadot/ui-app/AddressSummary';
import InputAddress from '@polkadot/ui-app/InputAddress';
import classes from '@polkadot/ui-app/util/classes';
import keyring from '@polkadot/ui-keyring/index';
import addressObservable from '@polkadot/ui-keyring/observable/addresses';
import withObservableBase from '@polkadot/ui-react-rx/with/observableBase';
import AddressSummary from '@polkadot/ui-app/AddressSummary';
import translate from './translate';
type Props = I18nProps & {
addressAll?: Array<any>,
onBack: () => void
};
type State = {
currentAddress: KeyringAddress | null,
defaultValue?: string,
current: KeyringAddress | null,
editedName: string,
isEdited: boolean
};
@ -33,13 +35,7 @@ class Editor extends React.PureComponent<Props, State> {
constructor (props: Props) {
super(props);
const addresses = keyring.getAddresses();
const currentAddress = addresses[addresses.length - 1] || null;
this.state = this.createState(currentAddress);
this.state.defaultValue = currentAddress
? currentAddress.address()
: void 0;
this.state = this.createState(null);
}
render () {
@ -58,9 +54,9 @@ class Editor extends React.PureComponent<Props, State> {
renderButtons () {
const { t } = this.props;
const { currentAddress, isEdited } = this.state;
const { current, isEdited } = this.state;
if (!currentAddress) {
if (!current) {
return null;
}
@ -94,28 +90,29 @@ class Editor extends React.PureComponent<Props, State> {
}
renderData () {
const { t } = this.props;
const { currentAddress, defaultValue, editedName } = this.state;
const { addressAll, t } = this.props;
const { current, editedName } = this.state;
if (!currentAddress) {
if (!addressAll || !Object.keys(addressAll).length) {
return t('editor.none', {
defaultValue: 'There are no saved addresses. Add some first.'
});
}
const address = currentAddress.address();
const address = current
? current.address()
: undefined;
return (
<div className='ui--grid'>
<AddressSummary
className='shrink'
value={address}
value={address || ''}
/>
<div className='grow'>
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultValue}
hideAddress
isInput={false}
label={t('editor.select', {
@ -141,13 +138,13 @@ class Editor extends React.PureComponent<Props, State> {
);
}
createState (currentAddress: KeyringAddress | null): State {
const { name = '' } = currentAddress
? currentAddress.getMeta()
createState (current: KeyringAddress | null): State {
const { name = '' } = current
? current.getMeta()
: {};
return {
currentAddress,
current,
editedName: name,
isEdited: false
};
@ -156,14 +153,14 @@ class Editor extends React.PureComponent<Props, State> {
nextState (newState: State = {} as State): void {
this.setState(
(prevState: State): State => {
let { currentAddress = prevState.currentAddress, editedName = prevState.editedName } = newState;
const previous = prevState.currentAddress || { address: () => null };
let { current = prevState.current, editedName = prevState.editedName } = newState;
const previous = prevState.current || { address: () => null };
let isEdited = false;
if (currentAddress && currentAddress.isValid()) {
if (currentAddress.address() !== previous.address()) {
editedName = currentAddress.getMeta().name || '';
} else if (editedName !== currentAddress.getMeta().name) {
if (current && current.isValid()) {
if (current.address() !== previous.address()) {
editedName = current.getMeta().name || '';
} else if (editedName !== current.getMeta().name) {
isEdited = true;
}
} else {
@ -171,7 +168,7 @@ class Editor extends React.PureComponent<Props, State> {
}
return {
currentAddress,
current,
editedName,
isEdited
};
@ -180,9 +177,11 @@ class Editor extends React.PureComponent<Props, State> {
}
onChangeAddress = (publicKey: Uint8Array): void => {
const currentAddress = keyring.getAddress(publicKey) || null;
const current = publicKey && publicKey.length === 32
? (keyring.getAddress(publicKey) || null)
: null;
this.nextState({ currentAddress } as State);
this.nextState({ current } as State);
}
onChangeName = (editedName: string): void => {
@ -190,55 +189,48 @@ class Editor extends React.PureComponent<Props, State> {
}
onCommit = (): void => {
const { currentAddress, editedName } = this.state;
const { current, editedName } = this.state;
if (!currentAddress) {
if (!current) {
return;
}
keyring.saveAddress(currentAddress.address(), {
keyring.saveAddress(current.address(), {
name: editedName,
whenEdited: Date.now()
});
this.nextState({} as State);
}
onDiscard = (): void => {
const { currentAddress } = this.state;
const { current } = this.state;
if (!currentAddress) {
if (!current) {
return;
}
this.nextState({
editedName: currentAddress.getMeta().name
editedName: current.getMeta().name
} as State);
}
onForget = (): void => {
const { currentAddress } = this.state;
const { current } = this.state;
if (!currentAddress) {
if (!current) {
return;
}
const address = currentAddress.address();
const addresses = keyring.getAddresses().filter((item) =>
item.address() !== address
this.setState(
this.createState(null),
() => {
keyring.forgetAddress(
current.address()
);
}
);
const nextAddress = addresses[0] || null;
const defaultValue = nextAddress
? nextAddress.address()
: void 0;
keyring.forgetAddress(address);
this.nextState({
currentAddress: nextAddress,
defaultValue
} as State);
}
}
export default translate(Editor);
export default withObservableBase(
addressObservable.subject, { propName: 'addressAll' }
)(translate(Editor));

View File

@ -20,7 +20,7 @@ type Props = I18nProps & {};
const pkgJson = require('../package.json');
function updateTestInfo (chain?: string) {
keyring.setTestMode(isTestChain(chain));
keyring.setDevMode(isTestChain(chain));
}
class NodeInfo extends React.PureComponent<Props> {

View File

@ -31,13 +31,22 @@ type SUIEvent = {
export default class Dropdown<Option> extends React.PureComponent<Props<Option>> {
componentDidMount () {
this.componentDidUpdate({} as Props<Option>);
}
componentDidUpdate (prevProps: Props<Option>) {
const { defaultValue, value } = this.props;
const startValue = isUndefined(value)
? defaultValue
: value;
const prevStart = isUndefined(prevProps.value)
? prevProps.defaultValue
: prevProps.value;
if (startValue) {
this.onChange(null, { value: startValue });
if (startValue !== prevStart) {
this.onChange(null, {
value: startValue
});
}
}
@ -52,7 +61,6 @@ export default class Dropdown<Option> extends React.PureComponent<Props<Option>>
withLabel={withLabel}
>
<SUIDropdown
defaultValue={defaultValue}
disabled={isDisabled}
error={isError}
// @ts-ignore some mismatch here, look into it
@ -62,7 +70,11 @@ export default class Dropdown<Option> extends React.PureComponent<Props<Option>>
// @ts-ignore some mismatch here, look into it
search={onSearch}
selection
value={value}
value={
isUndefined(value)
? defaultValue
: value
}
/>
</Labelled>
);

View File

@ -2,7 +2,7 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { KeyringOptions, KeyringOptionsSection, KeyringOption$Type } from '@polkadot/ui-keyring/options/types';
import { KeyringOptions, KeyringSectionOption, KeyringSectionOptions, KeyringOption$Type } from '@polkadot/ui-keyring/options/types';
import { BareProps } from '../types';
import './InputAddress.css';
@ -96,17 +96,18 @@ class InputAddress extends React.PureComponent<Props, State> {
const { className, defaultValue, hideAddress = false, isDisabled = false, isError, label, optionsAll, type = DEFAULT_TYPE, style, withLabel } = this.props;
const { value } = this.state;
if (!optionsAll) {
if (!optionsAll || !Object.keys(optionsAll[type]).length) {
return null;
}
const lastValue = InputAddress.getLastValue(type);
const lastOption = this.getLastOptionValue();
const actualValue = isDisabled || (defaultValue && this.hasValue(defaultValue))
? defaultValue
: (
this.hasValue(lastValue)
? lastValue
: undefined
: (lastOption && lastOption.value)
);
return (
@ -134,19 +135,35 @@ class InputAddress extends React.PureComponent<Props, State> {
);
}
private hasValue (value?: string): boolean {
private getLastOptionValue (): KeyringSectionOption | undefined {
const { optionsAll, type = DEFAULT_TYPE } = this.props;
if (!optionsAll) {
return;
}
const available = optionsAll[type].filter(({ value }) =>
!!value
);
return available.length
? available[available.length - 1]
: undefined;
}
private hasValue (test?: string): boolean {
const { optionsAll, type = DEFAULT_TYPE } = this.props;
if (!optionsAll) {
return false;
}
return !!optionsAll[type].find(({ key }) =>
key === value
return !!optionsAll[type].find(({ value }) =>
test === value
);
}
onChange = (address: string) => {
private onChange = (address: string) => {
const { onChange, type } = this.props;
InputAddress.setLastValue(type, address);
@ -154,7 +171,7 @@ class InputAddress extends React.PureComponent<Props, State> {
onChange(transform(address));
}
onSearch = (filteredOptions: KeyringOptionsSection, query: string): KeyringOptionsSection => {
private onSearch = (filteredOptions: KeyringSectionOptions, query: string): KeyringSectionOptions => {
const { isInput = true } = this.props;
const queryLower = query.toLowerCase();
const matches = filteredOptions.filter((item) =>

View File

@ -10,6 +10,7 @@ import testKeyring from '@polkadot/util-keyring/testing';
import accounts from './observable/accounts';
import addresses from './observable/addresses';
import development from './observable/development';
import loadAll from './loadAll';
import createAccount from './account/create';
import forgetAccount from './account/forget';
@ -22,10 +23,8 @@ 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,
accounts,
addresses,
keyring: testKeyring()
@ -52,7 +51,7 @@ export default ({
state.keyring.getPair(address),
getPairs: (): Array<KeyringPair> =>
state.keyring.getPairs().filter((pair) =>
state.isTestMode || pair.getMeta().isTesting !== true
development.isDevelopment() || pair.getMeta().isTesting !== true
),
loadAll: (): void =>
loadAll(state),
@ -64,6 +63,6 @@ export default ({
saveAddress(state, address, meta),
saveRecent: (address: string): SingleAddress =>
saveRecent(state, address),
setTestMode: (isTest: boolean): void =>
setTestMode(state, isTest)
setDevMode: (isDevelopment: boolean): void =>
development.set(isDevelopment)
} as KeyringInstance);

View File

@ -34,8 +34,10 @@ export default function loadAll (state: State): void {
store.each((json: KeyringJson, key: string) => {
if (accountRegex.test(key)) {
keyring.addFromJson(json as KeyringPair$Json);
accounts.add(json.address, json);
if (!json.meta || !json.meta.isTesting) {
keyring.addFromJson(json as KeyringPair$Json);
accounts.add(json.address, json);
}
} else if (addressRegex.test(key)) {
const address = isHex(json.address)
? addressEncode(hexToU8a(json.address))

View File

@ -6,4 +6,4 @@ import { accountKey } from '../defaults';
import genericSubject from './genericSubject';
export default genericSubject(accountKey);
export default genericSubject(accountKey, true);

View File

@ -0,0 +1,16 @@
// Copyright 2017-2018 @polkadot/ui-keyring authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { BehaviorSubject } from 'rxjs';
const subject = new BehaviorSubject(false);
export default {
isDevelopment: (): boolean =>
subject.getValue(),
set: (isDevelopment: boolean): void => {
subject.next(isDevelopment);
},
subject
};

View File

@ -9,10 +9,30 @@ import { KeyringJson } from '../types';
import store from 'store';
import createOptionItem from '../options/item';
import development from './development';
export default function genericSubject (keyCreator: (address: string) => string): AddressSubject {
export default function genericSubject (keyCreator: (address: string) => string, withTest: boolean = false): AddressSubject {
let current: SubjectInfo = {};
const subject = new BehaviorSubject({});
const next = (): void => {
const isDevMode = development.isDevelopment();
subject.next(
Object
.keys(current)
.reduce((filtered, key) => {
const { json: { meta: { isTesting = false } = {} } = {} } = current[key];
if (!withTest || isDevMode || isTesting !== true) {
filtered[key] = current[key];
}
return filtered;
}, {} as SubjectInfo)
);
};
development.subject.subscribe(next);
return {
add: (address: string, json: KeyringJson): SingleAddress => {
@ -24,7 +44,7 @@ export default function genericSubject (keyCreator: (address: string) => string)
};
store.set(keyCreator(address), json);
subject.next(current);
next();
return current[address];
},
@ -34,7 +54,7 @@ export default function genericSubject (keyCreator: (address: string) => string)
delete current[address];
store.remove(keyCreator(address));
subject.next(current);
next();
},
subject
};

View File

@ -4,14 +4,14 @@
import { State } from '../types';
import { SingleAddress } from '../observable/types';
import { KeyringOptions, KeyringOptionsSection } from './types';
import { KeyringOptions, KeyringSectionOptions } from './types';
import { BehaviorSubject } from 'rxjs';
import observableAll from '../observable';
import createHeader from './header';
function addAccounts ({ accounts, isTestMode }: State, options: KeyringOptions): void {
function addAccounts ({ accounts }: State, options: KeyringOptions): void {
const available = accounts.subject.getValue();
Object
@ -22,7 +22,7 @@ function addAccounts ({ accounts, isTestMode }: State, options: KeyringOptions):
.forEach(({ json: { meta: { isTesting = false } }, option }: SingleAddress) => {
if (!isTesting) {
options.account.push(option);
} else if (isTestMode) {
} else {
options.testing.push(option);
}
});
@ -65,20 +65,20 @@ export default function initOptions (state: State): void {
addAccounts(state, options);
addAddresses(state, options);
options.address = ([] as KeyringOptionsSection).concat(
options.address = ([] as KeyringSectionOptions).concat(
options.address.length ? [ createHeader('Addresses') ] : [],
options.address,
options.recent.length ? [ createHeader('Recent') ] : [],
options.recent
);
options.account = ([] as KeyringOptionsSection).concat(
options.account = ([] as KeyringSectionOptions).concat(
options.account.length ? [ createHeader('Accounts') ] : [],
options.account,
options.testing.length ? [ createHeader('Testing') ] : [],
options.testing.length ? [ createHeader('Development') ] : [],
options.testing
);
options.all = ([] as KeyringOptionsSection).concat(
options.all = ([] as KeyringSectionOptions).concat(
options.account,
options.address
);

View File

@ -12,14 +12,14 @@ export type KeyringSectionOption = {
value: string | null
};
export type KeyringOptionsSection = Array<KeyringSectionOption>;
export type KeyringSectionOptions = Array<KeyringSectionOption>;
export type KeyringOptions = {
account: KeyringOptionsSection,
address: KeyringOptionsSection,
all: KeyringOptionsSection,
recent: KeyringOptionsSection,
testing: KeyringOptionsSection
account: KeyringSectionOptions,
address: KeyringSectionOptions,
all: KeyringSectionOptions,
recent: KeyringSectionOptions,
testing: KeyringSectionOptions
}
export type KeyringOption$Type = keyof KeyringOptions;

View File

@ -1,9 +0,0 @@
// Copyright 2017-2018 @polkadot/ui-keyring authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { State } from './types';
export default function setTestMode (state: State, isTest: boolean): void {
state.isTestMode = isTest;
}

View File

@ -29,7 +29,6 @@ export type KeyringAddress = {
}
export type State = {
isTestMode: boolean,
accounts: AddressSubject,
addresses: AddressSubject,
keyring: BaseKeyringInstance
@ -50,5 +49,5 @@ export type KeyringInstance = {
saveAccountMeta: (pair: KeyringPair, meta: KeyringPair$Meta) => void,
saveAddress: (address: string, meta: KeyringPair$Meta) => void,
saveRecent: (address: string) => SingleAddress,
setTestMode: (isTest: boolean) => void
setDevMode: (isDevelopment: boolean) => void
};

View File

@ -29,7 +29,7 @@ type State = {
class Unlock extends React.PureComponent<Props, State> {
state: State = {} as State;
static getDerivedStateFromProps ({ error, password, value }: Props): State {
static getDerivedStateFromProps ({ error, value }: Props): State {
const pair = keyring.getPair(value as Uint8Array);
const isLocked = pair.isLocked();