diff --git a/js/src/ui/Actionbar/Import/import.js b/js/src/ui/Actionbar/Import/import.js index 776e5bb08..081fe1a17 100644 --- a/js/src/ui/Actionbar/Import/import.js +++ b/js/src/ui/Actionbar/Import/import.js @@ -29,6 +29,8 @@ const initialState = { show: false, validate: false, validationBody: null, + error: false, + errorText: '', content: '' }; @@ -65,7 +67,7 @@ export default class ActionbarImport extends Component { renderModal () { const { title, renderValidation } = this.props; - const { show, step } = this.state; + const { show, step, error } = this.state; if (!show) { return null; @@ -73,7 +75,7 @@ export default class ActionbarImport extends Component { const hasSteps = typeof renderValidation === 'function'; - const steps = hasSteps ? [ 'select a file', 'validate' ] : null; + const steps = hasSteps ? [ 'select a file', error ? 'error' : 'validate' ] : null; return ( ); + if (error) { + return [ cancelBtn ]; + } + if (validate) { const confirmBtn = ( + An error occured: { errorText } + + ); + } if (validate) { return this.renderValidation(); @@ -169,10 +183,20 @@ export default class ActionbarImport extends Component { return this.onCloseModal(); } + const validationBody = renderValidation(content); + + if (validationBody && validationBody.error) { + return this.setState({ + step: 1, + error: true, + errorText: validationBody.error + }); + } + this.setState({ step: 1, validate: true, - validationBody: renderValidation(content), + validationBody, content }); }; diff --git a/js/src/ui/IdentityName/identityName.js b/js/src/ui/IdentityName/identityName.js index 6b6c75be2..f19e2bb2e 100644 --- a/js/src/ui/IdentityName/identityName.js +++ b/js/src/ui/IdentityName/identityName.js @@ -28,11 +28,12 @@ class IdentityName extends Component { tokens: PropTypes.object, empty: PropTypes.bool, shorten: PropTypes.bool, - unknown: PropTypes.bool + unknown: PropTypes.bool, + name: PropTypes.string } 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 hasAccount = account && (!account.meta || !account.meta.deleted); @@ -43,13 +44,14 @@ class IdentityName extends Component { const addressFallback = shorten ? this.formatHash(address) : address; const fallback = unknown ? defaultName : addressFallback; const isUuid = hasAccount && account.name === account.uuid; - const name = hasAccount && !isUuid + const displayName = (name && name.toUpperCase().trim()) || + (hasAccount && !isUuid ? account.name.toUpperCase().trim() - : fallback; + : fallback); return ( - { name && name.length ? name : fallback } + { displayName && displayName.length ? displayName : fallback } ); } diff --git a/js/src/views/Accounts/Summary/summary.js b/js/src/views/Accounts/Summary/summary.js index 51bb81215..5a96f64ff 100644 --- a/js/src/views/Accounts/Summary/summary.js +++ b/js/src/views/Accounts/Summary/summary.js @@ -22,22 +22,28 @@ import { Balance, Container, ContainerTitle, IdentityIcon, IdentityName, Tags, I export default class Summary extends Component { static contextTypes = { api: React.PropTypes.object - } + }; static propTypes = { account: PropTypes.object.isRequired, - balance: PropTypes.object.isRequired, + balance: PropTypes.object, link: PropTypes.string, + name: PropTypes.string, + noLink: PropTypes.bool, children: PropTypes.node, handleAddSearchToken: PropTypes.func - } + }; + + static defaultProps = { + noLink: false + }; state = { name: 'Unnamed' - } + }; render () { - const { account, balance, children, link, handleAddSearchToken } = this.props; + const { account, children, handleAddSearchToken } = this.props; const { tags } = account.meta; if (!account) { @@ -45,7 +51,6 @@ export default class Summary extends Component { } const { address } = account; - const viewLink = `/${link || 'account'}/${address}`; const addressComponent = ( { } } + title={ this.renderLink() } byline={ addressComponent } /> - + + { this.renderBalance() } { children } ); } + + renderLink () { + const { link, noLink, account, name } = this.props; + + const { address } = account; + const viewLink = `/${link || 'account'}/${address}`; + + const content = ( + + ); + + if (noLink) { + return content; + } + + return ( + + { content } + + ); + } + + renderBalance () { + const { balance } = this.props; + + if (!balance) { + return null; + } + + return ( + + ); + } } diff --git a/js/src/views/Addresses/addresses.js b/js/src/views/Addresses/addresses.js index 13c512713..8b5fc0818 100644 --- a/js/src/views/Addresses/addresses.js +++ b/js/src/views/Addresses/addresses.js @@ -21,8 +21,9 @@ import ContentAdd from 'material-ui/svg-icons/content/add'; import { uniq } from 'lodash'; import List from '../Accounts/List'; +import Summary from '../Accounts/Summary'; 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'; @@ -107,6 +108,12 @@ class Addresses extends Component { content={ contacts } filename='addressbook.json' />, + , + this.renderSearchButton(), 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) => ( + + )); + + return ( + + { body } + + ); + } 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) => { const { searchTokens } = this.state; const newSearchTokens = uniq([].concat(searchTokens, token));
An error occured: { errorText }