Import AddresBook from exported JSON (#3433)
* Adds error handler in Importer // Import AddressBook #2885 * More robust import #2885
This commit is contained in:
commit
604be3e463
@ -29,6 +29,8 @@ const initialState = {
|
|||||||
show: false,
|
show: false,
|
||||||
validate: false,
|
validate: false,
|
||||||
validationBody: null,
|
validationBody: null,
|
||||||
|
error: false,
|
||||||
|
errorText: '',
|
||||||
content: ''
|
content: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,7 +67,7 @@ export default class ActionbarImport extends Component {
|
|||||||
|
|
||||||
renderModal () {
|
renderModal () {
|
||||||
const { title, renderValidation } = this.props;
|
const { title, renderValidation } = this.props;
|
||||||
const { show, step } = this.state;
|
const { show, step, error } = this.state;
|
||||||
|
|
||||||
if (!show) {
|
if (!show) {
|
||||||
return null;
|
return null;
|
||||||
@ -73,7 +75,7 @@ export default class ActionbarImport extends Component {
|
|||||||
|
|
||||||
const hasSteps = typeof renderValidation === 'function';
|
const hasSteps = typeof renderValidation === 'function';
|
||||||
|
|
||||||
const steps = hasSteps ? [ 'select a file', 'validate' ] : null;
|
const steps = hasSteps ? [ 'select a file', error ? 'error' : 'validate' ] : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@ -89,7 +91,7 @@ export default class ActionbarImport extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderActions () {
|
renderActions () {
|
||||||
const { validate } = this.state;
|
const { validate, error } = this.state;
|
||||||
|
|
||||||
const cancelBtn = (
|
const cancelBtn = (
|
||||||
<Button
|
<Button
|
||||||
@ -100,6 +102,10 @@ export default class ActionbarImport extends Component {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return [ cancelBtn ];
|
||||||
|
}
|
||||||
|
|
||||||
if (validate) {
|
if (validate) {
|
||||||
const confirmBtn = (
|
const confirmBtn = (
|
||||||
<Button
|
<Button
|
||||||
@ -117,7 +123,15 @@ export default class ActionbarImport extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderBody () {
|
renderBody () {
|
||||||
const { validate } = this.state;
|
const { validate, errorText, error } = this.state;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>An error occured: { errorText }</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (validate) {
|
if (validate) {
|
||||||
return this.renderValidation();
|
return this.renderValidation();
|
||||||
@ -169,10 +183,20 @@ export default class ActionbarImport extends Component {
|
|||||||
return this.onCloseModal();
|
return this.onCloseModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validationBody = renderValidation(content);
|
||||||
|
|
||||||
|
if (validationBody && validationBody.error) {
|
||||||
|
return this.setState({
|
||||||
|
step: 1,
|
||||||
|
error: true,
|
||||||
|
errorText: validationBody.error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
step: 1,
|
step: 1,
|
||||||
validate: true,
|
validate: true,
|
||||||
validationBody: renderValidation(content),
|
validationBody,
|
||||||
content
|
content
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -28,11 +28,12 @@ class IdentityName extends Component {
|
|||||||
tokens: PropTypes.object,
|
tokens: PropTypes.object,
|
||||||
empty: PropTypes.bool,
|
empty: PropTypes.bool,
|
||||||
shorten: PropTypes.bool,
|
shorten: PropTypes.bool,
|
||||||
unknown: PropTypes.bool
|
unknown: PropTypes.bool,
|
||||||
|
name: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { address, accountsInfo, tokens, empty, shorten, unknown, className } = this.props;
|
const { address, accountsInfo, tokens, empty, name, shorten, unknown, className } = this.props;
|
||||||
const account = accountsInfo[address] || tokens[address];
|
const account = accountsInfo[address] || tokens[address];
|
||||||
const hasAccount = account && (!account.meta || !account.meta.deleted);
|
const hasAccount = account && (!account.meta || !account.meta.deleted);
|
||||||
|
|
||||||
@ -43,13 +44,14 @@ class IdentityName extends Component {
|
|||||||
const addressFallback = shorten ? this.formatHash(address) : address;
|
const addressFallback = shorten ? this.formatHash(address) : address;
|
||||||
const fallback = unknown ? defaultName : addressFallback;
|
const fallback = unknown ? defaultName : addressFallback;
|
||||||
const isUuid = hasAccount && account.name === account.uuid;
|
const isUuid = hasAccount && account.name === account.uuid;
|
||||||
const name = hasAccount && !isUuid
|
const displayName = (name && name.toUpperCase().trim()) ||
|
||||||
|
(hasAccount && !isUuid
|
||||||
? account.name.toUpperCase().trim()
|
? account.name.toUpperCase().trim()
|
||||||
: fallback;
|
: fallback);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={ className }>
|
<span className={ className }>
|
||||||
{ name && name.length ? name : fallback }
|
{ displayName && displayName.length ? displayName : fallback }
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -22,22 +22,28 @@ import { Balance, Container, ContainerTitle, IdentityIcon, IdentityName, Tags, I
|
|||||||
export default class Summary extends Component {
|
export default class Summary extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
api: React.PropTypes.object
|
api: React.PropTypes.object
|
||||||
}
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
account: PropTypes.object.isRequired,
|
account: PropTypes.object.isRequired,
|
||||||
balance: PropTypes.object.isRequired,
|
balance: PropTypes.object,
|
||||||
link: PropTypes.string,
|
link: PropTypes.string,
|
||||||
|
name: PropTypes.string,
|
||||||
|
noLink: PropTypes.bool,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
handleAddSearchToken: PropTypes.func
|
handleAddSearchToken: PropTypes.func
|
||||||
}
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
noLink: false
|
||||||
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
name: 'Unnamed'
|
name: 'Unnamed'
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, balance, children, link, handleAddSearchToken } = this.props;
|
const { account, children, handleAddSearchToken } = this.props;
|
||||||
const { tags } = account.meta;
|
const { tags } = account.meta;
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
@ -45,7 +51,6 @@ export default class Summary extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { address } = account;
|
const { address } = account;
|
||||||
const viewLink = `/${link || 'account'}/${address}`;
|
|
||||||
|
|
||||||
const addressComponent = (
|
const addressComponent = (
|
||||||
<Input
|
<Input
|
||||||
@ -62,12 +67,45 @@ export default class Summary extends Component {
|
|||||||
<IdentityIcon
|
<IdentityIcon
|
||||||
address={ address } />
|
address={ address } />
|
||||||
<ContainerTitle
|
<ContainerTitle
|
||||||
title={ <Link to={ viewLink }>{ <IdentityName address={ address } unknown /> }</Link> }
|
title={ this.renderLink() }
|
||||||
byline={ addressComponent } />
|
byline={ addressComponent } />
|
||||||
<Balance
|
|
||||||
balance={ balance } />
|
{ this.renderBalance() }
|
||||||
{ children }
|
{ children }
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderLink () {
|
||||||
|
const { link, noLink, account, name } = this.props;
|
||||||
|
|
||||||
|
const { address } = account;
|
||||||
|
const viewLink = `/${link || 'account'}/${address}`;
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<IdentityName address={ address } name={ name } unknown />
|
||||||
|
);
|
||||||
|
|
||||||
|
if (noLink) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link to={ viewLink }>
|
||||||
|
{ content }
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBalance () {
|
||||||
|
const { balance } = this.props;
|
||||||
|
|
||||||
|
if (!balance) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Balance balance={ balance } />
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,9 @@ import ContentAdd from 'material-ui/svg-icons/content/add';
|
|||||||
import { uniq } from 'lodash';
|
import { uniq } from 'lodash';
|
||||||
|
|
||||||
import List from '../Accounts/List';
|
import List from '../Accounts/List';
|
||||||
|
import Summary from '../Accounts/Summary';
|
||||||
import { AddAddress } from '../../modals';
|
import { AddAddress } from '../../modals';
|
||||||
import { Actionbar, ActionbarExport, ActionbarSearch, ActionbarSort, Button, Page } from '../../ui';
|
import { Actionbar, ActionbarExport, ActionbarImport, ActionbarSearch, ActionbarSort, Button, Page } from '../../ui';
|
||||||
|
|
||||||
import styles from './addresses.css';
|
import styles from './addresses.css';
|
||||||
|
|
||||||
@ -107,6 +108,12 @@ class Addresses extends Component {
|
|||||||
content={ contacts }
|
content={ contacts }
|
||||||
filename='addressbook.json' />,
|
filename='addressbook.json' />,
|
||||||
|
|
||||||
|
<ActionbarImport
|
||||||
|
key='importAddressbook'
|
||||||
|
onConfirm={ this.onImport }
|
||||||
|
renderValidation={ this.renderValidation }
|
||||||
|
/>,
|
||||||
|
|
||||||
this.renderSearchButton(),
|
this.renderSearchButton(),
|
||||||
this.renderSortButton()
|
this.renderSortButton()
|
||||||
];
|
];
|
||||||
@ -134,6 +141,66 @@ class Addresses extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderValidation = (content) => {
|
||||||
|
const error = {
|
||||||
|
error: 'The provided file is invalid...'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const addresses = JSON.parse(content);
|
||||||
|
|
||||||
|
if (!addresses || Object.keys(addresses).length === 0) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = Object
|
||||||
|
.values(addresses)
|
||||||
|
.filter((account) => account && account.address)
|
||||||
|
.map((account, index) => (
|
||||||
|
<Summary
|
||||||
|
key={ index }
|
||||||
|
account={ account }
|
||||||
|
name={ account.name }
|
||||||
|
noLink
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{ body }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} catch (e) { return error; }
|
||||||
|
}
|
||||||
|
|
||||||
|
onImport = (content) => {
|
||||||
|
try {
|
||||||
|
const addresses = JSON.parse(content);
|
||||||
|
|
||||||
|
Object.values(addresses).forEach((account) => {
|
||||||
|
this.onAddAccount(account);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('onImport', content, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddAccount = (account) => {
|
||||||
|
const { api } = this.context;
|
||||||
|
const { address, name, meta } = account;
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
api.parity.setAccountName(address, name),
|
||||||
|
api.parity.setAccountMeta(address, {
|
||||||
|
...meta,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
deleted: false
|
||||||
|
})
|
||||||
|
]).catch((error) => {
|
||||||
|
console.error('onAddAccount', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onAddSearchToken = (token) => {
|
onAddSearchToken = (token) => {
|
||||||
const { searchTokens } = this.state;
|
const { searchTokens } = this.state;
|
||||||
const newSearchTokens = uniq([].concat(searchTokens, token));
|
const newSearchTokens = uniq([].concat(searchTokens, token));
|
||||||
|
Loading…
Reference in New Issue
Block a user