openethereum/js/packages/dapp-accounts/List/list.js
Jaco Greeff 49fdd23d58 Ui 2 move to packages/* (#6113)
* Move secureApi to shell

* Extract isTestnet test

* Use mobx + subscriptions for status

* Re-add status indicator

* Add lerna

* Move intial packages to js/packages

* Move 3rdparty/{email,sms}-verification to correct location

* Move package.json & README to library src

* Move tests for library packages

* Move views & dapps to packages

* Move i18n to root

* Move shell to actual src (main app)

* Remove ~ references

* Change ~ to root (explicit imports)

* Finalise convert of ~

* Move views into dapps as well

* Move dapps to packages/

* Fix references

* Update css

* Update test spec locations

* Update tests

* Case fix

* Skip flakey tests

* Update enzyme

* Skip previously ignored tests

* Allow empty api for hw

* Re-add theme for embed
2017-07-21 15:46:53 +02:00

279 lines
6.8 KiB
JavaScript

// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { pick } from 'lodash';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { ETH_TOKEN } from '@parity/shared/util/tokens';
import { fetchCertifiers, fetchCertifications } from '@parity/shared/redux/providers/certifications/actions';
import { Container, SectionList } from '@parity/ui';
import Summary from '../Summary';
import styles from './list.css';
class List extends Component {
static propTypes = {
balances: PropTypes.object.isRequired,
certifications: PropTypes.object.isRequired,
accounts: PropTypes.object,
disabled: PropTypes.object,
empty: PropTypes.bool,
link: PropTypes.string,
order: PropTypes.string,
orderFallback: PropTypes.string,
search: PropTypes.array,
fetchCertifiers: PropTypes.func.isRequired,
fetchCertifications: PropTypes.func.isRequired,
handleAddSearchToken: PropTypes.func
};
componentWillMount () {
const { accounts, fetchCertifiers, fetchCertifications } = this.props;
fetchCertifiers();
for (let address in accounts) {
fetchCertifications(address);
}
}
render () {
const { accounts, disabled, empty } = this.props;
if (empty) {
return (
<Container className={ styles.empty }>
<div>
There are currently no accounts or addresses to display.
</div>
</Container>
);
}
const addresses = this
.getAddresses()
.map((address) => {
const account = accounts[address] || {};
const isDisabled = disabled ? disabled[address] : false;
const owners = account.owners || null;
return {
account,
isDisabled,
owners
};
});
return (
<SectionList
items={ addresses }
renderItem={ this.renderSummary }
/>
);
}
renderSummary = (item) => {
const { account, isDisabled, owners } = item;
const { handleAddSearchToken, link } = this.props;
return (
<Summary
account={ account }
disabled={ isDisabled }
handleAddSearchToken={ handleAddSearchToken }
link={ link }
owners={ owners }
showCertifications
/>
);
}
getAddresses () {
const filteredAddresses = this.getFilteredAddresses();
return this.sortAddresses(filteredAddresses);
}
sortAddresses (addresses) {
const { order, orderFallback } = this.props;
if (!order) {
return addresses;
}
const { accounts } = this.props;
return addresses.sort((addressA, addressB) => {
const accountA = accounts[addressA];
const accountB = accounts[addressB];
const sort = this.compareAccounts(accountA, accountB, order);
if (sort === 0 && orderFallback) {
return this.compareAccounts(accountA, accountB, orderFallback);
}
return sort;
});
}
compareAccounts (accountA, accountB, key, _reverse = null) {
if (key && key.split(':')[1] === '-1') {
return this.compareAccounts(accountA, accountB, key.split(':')[0], true);
}
if (key === 'timestamp' && _reverse === null) {
return this.compareAccounts(accountA, accountB, key, true);
}
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;
} else if (balanceA && !balanceB) {
return -1;
} else if (!balanceA && balanceB) {
return 1;
}
const ethA = balanceA[ETH_TOKEN.id];
const ethB = balanceB[ETH_TOKEN.id];
if (!ethA && !ethB) {
return 0;
} else if (ethA && !ethB) {
return -1;
} else if (!ethA && ethB) {
return 1;
}
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;
} else if (tagsA && !tagsB) {
return -1;
} else if (!tagsA && tagsB) {
return 1;
}
return tagsA.localeCompare(tagsB);
}
const reverse = _reverse
? -1
: 1;
const metaA = accountA.meta[key];
const metaB = accountB.meta[key];
if (!metaA && !metaB) {
return 0;
}
if (metaA && !metaB) {
return -1;
}
if (metaA < metaB) {
return -1 * reverse;
}
if (!metaA && metaB) {
return 1;
}
if (metaA > metaB) {
return 1 * reverse;
}
return 0;
}
getFilteredAddresses () {
const { accounts, search } = this.props;
const searchValues = (search || []).map(v => v.toLowerCase());
if (searchValues.length === 0) {
return Object.keys(accounts);
}
return Object.keys(accounts)
.filter((address) => {
const account = accounts[address];
const tags = account.meta.tags || [];
const name = account.name || '';
const values = tags
.concat(name)
.map(v => v.toLowerCase());
return searchValues
.map(searchValue => {
return values
.some(value => value.indexOf(searchValue) >= 0);
})
// `current && truth, true` => use tokens as AND
// `current || truth, false` => use tokens as OR
.reduce((current, truth) => current && truth, true);
});
}
}
function mapStateToProps (state, props) {
const addresses = Object.keys(props.accounts);
const balances = pick(state.balances, addresses);
const { certifications } = state;
return { balances, certifications };
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
fetchCertifiers,
fetchCertifications
}, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(List);