diff --git a/js/src/modals/AddAddress/addAddress.js b/js/src/modals/AddAddress/addAddress.js index 5ab8bbe80..ebcf78815 100644 --- a/js/src/modals/AddAddress/addAddress.js +++ b/js/src/modals/AddAddress/addAddress.js @@ -136,6 +136,7 @@ export default class AddAddress extends Component { api.personal.setAccountName(address, name), api.personal.setAccountMeta(address, { description, + timestamp: Date.now(), deleted: false }) ]).catch((error) => { diff --git a/js/src/modals/AddContract/addContract.js b/js/src/modals/AddContract/addContract.js index ebad86807..ad7345430 100644 --- a/js/src/modals/AddContract/addContract.js +++ b/js/src/modals/AddContract/addContract.js @@ -145,6 +145,7 @@ export default class AddContract extends Component { api.personal.setAccountMeta(address, { contract: true, deleted: false, + timestamp: Date.now(), abi: abiParsed, description }) diff --git a/js/src/modals/CreateAccount/createAccount.js b/js/src/modals/CreateAccount/createAccount.js index e0808b47b..aacc91d5e 100644 --- a/js/src/modals/CreateAccount/createAccount.js +++ b/js/src/modals/CreateAccount/createAccount.js @@ -214,7 +214,10 @@ export default class CreateAccount extends Component { this.setState({ address }); return api.personal .setAccountName(address, this.state.name) - .then(() => api.personal.setAccountMeta(address, { passwordHint: this.state.passwordHint })); + .then(() => api.personal.setAccountMeta(address, { + timestamp: Date.now(), + passwordHint: this.state.passwordHint + })); }) .then(() => { this.onNext(); @@ -236,7 +239,10 @@ export default class CreateAccount extends Component { this.setState({ address }); return api.personal .setAccountName(address, this.state.name) - .then(() => api.personal.setAccountMeta(address, { passwordHint: this.state.passwordHint })); + .then(() => api.personal.setAccountMeta(address, { + timestamp: Date.now(), + passwordHint: this.state.passwordHint + })); }) .then(() => { this.onNext(); @@ -285,7 +291,10 @@ export default class CreateAccount extends Component { return api.personal .setAccountName(address, this.state.name) - .then(() => api.personal.setAccountMeta(address, { passwordHint: this.state.passwordHint })); + .then(() => api.personal.setAccountMeta(address, { + timestamp: Date.now(), + passwordHint: this.state.passwordHint + })); }) .then(() => { this.onNext(); diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index 3e1b10599..c62b968b5 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -216,6 +216,7 @@ export default class DeployContract extends Component { api.personal.setAccountMeta(address, { abi: abiParsed, contract: true, + timestamp: Date.now(), deleted: false, description }) diff --git a/js/src/ui/Actionbar/Sort/sort.js b/js/src/ui/Actionbar/Sort/sort.js index 35c51e5a1..474ef465b 100644 --- a/js/src/ui/Actionbar/Sort/sort.js +++ b/js/src/ui/Actionbar/Sort/sort.js @@ -27,14 +27,23 @@ import styles from './sort.css'; export default class ActionbarSort extends Component { static propTypes = { onChange: PropTypes.func.isRequired, - order: PropTypes.string + order: PropTypes.string, + showDefault: PropTypes.bool, + metas: PropTypes.array }; + static defaultProps = { + metas: [], + showDefault: true + } + state = { menuOpen: false } render () { + const { showDefault } = this.props; + return ( - - - + touchTapCloseDelay={ 0 } + > + { + showDefault + ? this.renderMenuItem('', 'Default') + : null + } + { this.renderMenuItem('tags', 'Sort by tags') } + { this.renderMenuItem('name', 'Sort by name') } + { this.renderMenuItem('eth', 'Sort by ETH') } + + { this.renderSortByMetas() } ); } + renderSortByMetas () { + const { metas } = this.props; + + return metas + .map((meta, index) => { + return this + .renderMenuItem(meta.key, `Sort by ${meta.label}`, index); + }); + } + + renderMenuItem (value, label, key = null) { + const { order } = this.props; + + const props = {}; + + if (key !== null) { + props.key = key; + } + + const checked = order === value; + + return ( + + ); + } + handleSortChange = (event, child) => { const order = child.props.value; this.props.onChange(order); diff --git a/js/src/ui/Container/container.css b/js/src/ui/Container/container.css index 1071125bd..d5c2ef336 100644 --- a/js/src/ui/Container/container.css +++ b/js/src/ui/Container/container.css @@ -18,6 +18,7 @@ flex: 1; padding: 0em; background: rgba(0, 0, 0, 0.8); + height: 100%; } .compact, diff --git a/js/src/views/Accounts/List/list.js b/js/src/views/Accounts/List/list.js index bcdf7b072..ab231a53a 100644 --- a/js/src/views/Accounts/List/list.js +++ b/js/src/views/Accounts/List/list.js @@ -29,6 +29,7 @@ export default class List extends Component { search: PropTypes.array, empty: PropTypes.bool, order: PropTypes.string, + orderFallback: PropTypes.string, handleAddSearchToken: PropTypes.func }; @@ -79,9 +80,9 @@ export default class List extends Component { } sortAddresses (addresses) { - const { order } = this.props; + const { order, orderFallback } = this.props; - if (!order || ['tags', 'name'].indexOf(order) === -1) { + if (!order) { return addresses; } @@ -91,26 +92,77 @@ export default class List extends Component { const accountA = accounts[addressA]; const accountB = accounts[addressB]; - if (order === 'name') { - return accountA.name.localeCompare(accountB.name); + const sort = this.compareAccounts(accountA, accountB, order); + + if (sort === 0 && orderFallback) { + return this.compareAccounts(accountA, accountB, orderFallback); } - if (order === 'tags') { - const tagsA = [].concat(accountA.meta.tags) - .filter(t => t) - .sort(); - const tagsB = [].concat(accountB.meta.tags) - .filter(t => t) - .sort(); - - if (tagsA.length === 0) return 1; - if (tagsB.length === 0) return -1; - - return tagsA.join('').localeCompare(tagsB.join('')); - } + return sort; }); } + compareAccounts (accountA, accountB, key) { + if (key === 'name') { + return accountA.name.localeCompare(accountB.name); + } + + if (key === 'eth') { + const { balances } = this.props; + + const balanceA = balances[accountA.address]; + const balanceB = balances[accountB.address]; + + if (!balanceA && !balanceB) return 0; + if (balanceA && !balanceB) return -1; + if (!balanceA && balanceB) return 1; + + const ethA = balanceA.tokens + .find(token => token.token.tag.toLowerCase() === 'eth') + .value; + const ethB = balanceB.tokens + .find(token => token.token.tag.toLowerCase() === 'eth') + .value; + + return -1 * ethA.comparedTo(ethB); + } + + if (key === 'tags') { + const tagsA = [].concat(accountA.meta.tags) + .filter(t => t) + .sort() + .join(''); + + const tagsB = [].concat(accountB.meta.tags) + .filter(t => t) + .sort() + .join(''); + + if (!tagsA && !tagsB) return 0; + if (tagsA && !tagsB) return -1; + if (!tagsA && tagsB) return 1; + + return tagsA.localeCompare(tagsB); + } + + const metaA = accountA.meta[key]; + const metaB = accountB.meta[key]; + + if (!metaA && !metaB) { + return 0; + } + + if ((metaA && !metaB) || (metaA < metaB)) { + return -1; + } + + if ((!metaA && metaB) || (metaA > metaB)) { + return 1; + } + + return 0; + } + getFilteredAddresses () { const { accounts, search } = this.props; const searchValues = (search || []).map(v => v.toLowerCase()); diff --git a/js/src/views/Contracts/contracts.js b/js/src/views/Contracts/contracts.js index f4cbc6dcc..ef408cae2 100644 --- a/js/src/views/Contracts/contracts.js +++ b/js/src/views/Contracts/contracts.js @@ -42,7 +42,7 @@ class Contracts extends Component { state = { addContract: false, deployContract: false, - sortOrder: '', + sortOrder: 'timestamp', searchValues: [], searchTokens: [] } @@ -65,6 +65,7 @@ class Contracts extends Component { balances={ balances } empty={ !hasContracts } order={ sortOrder } + orderFallback='name' handleAddSearchToken={ this.onAddSearchToken } /> @@ -80,6 +81,10 @@ class Contracts extends Component { ); }