parent
48eac51c8a
commit
71f84067d9
@ -14,15 +14,13 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { Checkbox, MenuItem } from 'material-ui';
|
||||
import { isEqual } from 'lodash';
|
||||
import { Checkbox } from 'material-ui';
|
||||
|
||||
import Form, { Input, InputAddressSelect, AddressSelect, Select } from '~/ui/Form';
|
||||
import Form, { Input, InputAddressSelect, AddressSelect } from '~/ui/Form';
|
||||
import { nullableProptype } from '~/util/proptypes';
|
||||
|
||||
import imageUnknown from '../../../../assets/images/contracts/unknown-64x64.png';
|
||||
import TokenSelect from './tokenSelect';
|
||||
import styles from '../transfer.css';
|
||||
|
||||
const CHECK_STYLE = {
|
||||
@ -31,110 +29,12 @@ const CHECK_STYLE = {
|
||||
left: '1em'
|
||||
};
|
||||
|
||||
class TokenSelect extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
balance: PropTypes.object.isRequired,
|
||||
images: PropTypes.object.isRequired,
|
||||
tag: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
this.computeTokens();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
const prevTokens = this.props.balance.tokens.map((t) => `${t.token.tag}_${t.value.toNumber()}`);
|
||||
const nextTokens = nextProps.balance.tokens.map((t) => `${t.token.tag}_${t.value.toNumber()}`);
|
||||
|
||||
if (!isEqual(prevTokens, nextTokens)) {
|
||||
this.computeTokens(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
computeTokens (props = this.props) {
|
||||
const { api } = this.context;
|
||||
const { balance, images } = this.props;
|
||||
|
||||
const items = balance.tokens
|
||||
.filter((token, index) => !index || token.value.gt(0))
|
||||
.map((balance, index) => {
|
||||
const token = balance.token;
|
||||
const isEth = index === 0;
|
||||
let imagesrc = token.image;
|
||||
|
||||
if (!imagesrc) {
|
||||
imagesrc =
|
||||
images[token.address]
|
||||
? `${api.dappsUrl}${images[token.address]}`
|
||||
: imageUnknown;
|
||||
}
|
||||
let value = 0;
|
||||
|
||||
if (isEth) {
|
||||
value = api.util.fromWei(balance.value).toFormat(3);
|
||||
} else {
|
||||
const format = balance.token.format || 1;
|
||||
const decimals = format === 1 ? 0 : Math.min(3, Math.floor(format / 10));
|
||||
|
||||
value = new BigNumber(balance.value).div(format).toFormat(decimals);
|
||||
}
|
||||
|
||||
const label = (
|
||||
<div className={ styles.token }>
|
||||
<img src={ imagesrc } />
|
||||
<div className={ styles.tokenname }>
|
||||
{ token.name }
|
||||
</div>
|
||||
<div className={ styles.tokenbalance }>
|
||||
{ value }<small> { token.tag }</small>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={ `${index}_${token.tag}` }
|
||||
value={ token.tag }
|
||||
label={ label }
|
||||
>
|
||||
{ label }
|
||||
</MenuItem>
|
||||
);
|
||||
});
|
||||
|
||||
this.setState({ items });
|
||||
}
|
||||
|
||||
render () {
|
||||
const { tag, onChange } = this.props;
|
||||
const { items } = this.state;
|
||||
|
||||
return (
|
||||
<Select
|
||||
className={ styles.tokenSelect }
|
||||
label='type of token transfer'
|
||||
hint='type of token to transfer'
|
||||
value={ tag }
|
||||
onChange={ onChange }
|
||||
>
|
||||
{ items }
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class Details extends Component {
|
||||
static propTypes = {
|
||||
address: PropTypes.string,
|
||||
balance: PropTypes.object,
|
||||
all: PropTypes.bool,
|
||||
extras: PropTypes.bool,
|
||||
images: PropTypes.object.isRequired,
|
||||
sender: PropTypes.string,
|
||||
senderError: PropTypes.string,
|
||||
sendersBalances: PropTypes.object,
|
||||
@ -249,12 +149,11 @@ export default class Details extends Component {
|
||||
}
|
||||
|
||||
renderTokenSelect () {
|
||||
const { balance, images, tag } = this.props;
|
||||
const { balance, tag } = this.props;
|
||||
|
||||
return (
|
||||
<TokenSelect
|
||||
balance={ balance }
|
||||
images={ images }
|
||||
tag={ tag }
|
||||
onChange={ this.onChangeToken }
|
||||
/>
|
||||
|
114
js/src/modals/Transfer/Details/tokenSelect.js
Normal file
114
js/src/modals/Transfer/Details/tokenSelect.js
Normal file
@ -0,0 +1,114 @@
|
||||
// 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 BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { MenuItem } from 'material-ui';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { Select } from '~/ui/Form';
|
||||
import TokenImage from '~/ui/TokenImage';
|
||||
|
||||
import styles from '../transfer.css';
|
||||
|
||||
export default class TokenSelect extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
balance: PropTypes.object.isRequired,
|
||||
tag: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
this.computeTokens();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
const prevTokens = this.props.balance.tokens.map((t) => `${t.token.tag}_${t.value.toNumber()}`);
|
||||
const nextTokens = nextProps.balance.tokens.map((t) => `${t.token.tag}_${t.value.toNumber()}`);
|
||||
|
||||
if (!isEqual(prevTokens, nextTokens)) {
|
||||
this.computeTokens(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
computeTokens (props = this.props) {
|
||||
const { api } = this.context;
|
||||
const { balance } = this.props;
|
||||
|
||||
const items = balance.tokens
|
||||
.filter((token, index) => !index || token.value.gt(0))
|
||||
.map((balance, index) => {
|
||||
const token = balance.token;
|
||||
const isEth = index === 0;
|
||||
|
||||
let value = 0;
|
||||
|
||||
if (isEth) {
|
||||
value = api.util.fromWei(balance.value).toFormat(3);
|
||||
} else {
|
||||
const format = balance.token.format || 1;
|
||||
const decimals = format === 1 ? 0 : Math.min(3, Math.floor(format / 10));
|
||||
|
||||
value = new BigNumber(balance.value).div(format).toFormat(decimals);
|
||||
}
|
||||
|
||||
const label = (
|
||||
<div className={ styles.token }>
|
||||
<TokenImage token={ token } />
|
||||
<div className={ styles.tokenname }>
|
||||
{ token.name }
|
||||
</div>
|
||||
<div className={ styles.tokenbalance }>
|
||||
{ value }<small> { token.tag }</small>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={ `${index}_${token.tag}` }
|
||||
value={ token.tag }
|
||||
label={ label }
|
||||
>
|
||||
{ label }
|
||||
</MenuItem>
|
||||
);
|
||||
});
|
||||
|
||||
this.setState({ items });
|
||||
}
|
||||
|
||||
render () {
|
||||
const { tag, onChange } = this.props;
|
||||
const { items } = this.state;
|
||||
|
||||
return (
|
||||
<Select
|
||||
className={ styles.tokenSelect }
|
||||
label='type of token transfer'
|
||||
hint='type of token to transfer'
|
||||
value={ tag }
|
||||
onChange={ onChange }
|
||||
>
|
||||
{ items }
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
}
|
@ -44,7 +44,6 @@ class Transfer extends Component {
|
||||
static propTypes = {
|
||||
newError: PropTypes.func.isRequired,
|
||||
gasLimit: PropTypes.object.isRequired,
|
||||
images: PropTypes.object.isRequired,
|
||||
|
||||
senders: nullableProptype(PropTypes.object),
|
||||
sendersBalances: nullableProptype(PropTypes.object),
|
||||
@ -174,7 +173,7 @@ class Transfer extends Component {
|
||||
}
|
||||
|
||||
renderDetailsPage () {
|
||||
const { account, balance, images, senders } = this.props;
|
||||
const { account, balance, senders } = this.props;
|
||||
const { recipient, recipientError, sender, senderError, sendersBalances } = this.store;
|
||||
const { valueAll, extras, tag, total, totalError, value, valueError } = this.store;
|
||||
|
||||
@ -184,7 +183,6 @@ class Transfer extends Component {
|
||||
all={ valueAll }
|
||||
balance={ balance }
|
||||
extras={ extras }
|
||||
images={ images }
|
||||
onChange={ this.store.onUpdateDetails }
|
||||
recipient={ recipient }
|
||||
recipientError={ recipientError }
|
||||
|
@ -65,7 +65,7 @@ describe('ui/AccountCard', () => {
|
||||
let balance;
|
||||
|
||||
beforeEach(() => {
|
||||
balance = component.find('Connect(Balance)');
|
||||
balance = component.find('Balance');
|
||||
});
|
||||
|
||||
it('renders the balance', () => {
|
||||
|
@ -17,13 +17,12 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import unknownImage from '~/../assets/images/contracts/unknown-64x64.png';
|
||||
import TokenImage from '~/ui/TokenImage';
|
||||
|
||||
import styles from './balance.css';
|
||||
|
||||
class Balance extends Component {
|
||||
export default class Balance extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object
|
||||
};
|
||||
@ -31,7 +30,6 @@ class Balance extends Component {
|
||||
static propTypes = {
|
||||
balance: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
images: PropTypes.object.isRequired,
|
||||
showOnlyEth: PropTypes.bool,
|
||||
showZeroValues: PropTypes.bool
|
||||
};
|
||||
@ -43,7 +41,7 @@ class Balance extends Component {
|
||||
|
||||
render () {
|
||||
const { api } = this.context;
|
||||
const { balance, className, images, showZeroValues, showOnlyEth } = this.props;
|
||||
const { balance, className, showZeroValues, showOnlyEth } = this.props;
|
||||
|
||||
if (!balance || !balance.tokens) {
|
||||
return null;
|
||||
@ -79,26 +77,12 @@ class Balance extends Component {
|
||||
value = api.util.fromWei(balance.value).toFormat(3);
|
||||
}
|
||||
|
||||
const imageurl = token.image || images[token.address];
|
||||
let imagesrc = unknownImage;
|
||||
|
||||
if (imageurl) {
|
||||
const host = /^(\/)?api/.test(imageurl)
|
||||
? api.dappsUrl
|
||||
: '';
|
||||
|
||||
imagesrc = `${host}${imageurl}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ styles.balance }
|
||||
key={ `${index}_${token.tag}` }
|
||||
>
|
||||
<img
|
||||
src={ imagesrc }
|
||||
alt={ token.name }
|
||||
/>
|
||||
<TokenImage token={ token } />
|
||||
<div className={ styles.balanceValue }>
|
||||
<span title={ value }> { value } </span>
|
||||
</div>
|
||||
@ -125,14 +109,3 @@ class Balance extends Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { images } = state;
|
||||
|
||||
return { images };
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(Balance);
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import apiutil from '~/api/util';
|
||||
|
||||
@ -32,7 +31,6 @@ const BALANCE = {
|
||||
|
||||
let api;
|
||||
let component;
|
||||
let store;
|
||||
|
||||
function createApi () {
|
||||
api = {
|
||||
@ -43,36 +41,22 @@ function createApi () {
|
||||
return api;
|
||||
}
|
||||
|
||||
function createStore () {
|
||||
store = {
|
||||
dispatch: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
getState: () => {
|
||||
return {
|
||||
images: {}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
function render (props = {}) {
|
||||
if (!props.balance) {
|
||||
props.balance = BALANCE;
|
||||
}
|
||||
|
||||
const api = createApi();
|
||||
|
||||
component = shallow(
|
||||
<Balance
|
||||
className='testClass'
|
||||
{ ...props }
|
||||
/>,
|
||||
{
|
||||
context: {
|
||||
store: createStore()
|
||||
context: { api }
|
||||
}
|
||||
}
|
||||
).find('Balance').shallow({ context: { api: createApi() } });
|
||||
);
|
||||
|
||||
return component;
|
||||
}
|
||||
@ -91,18 +75,18 @@ describe('ui/Balance', () => {
|
||||
});
|
||||
|
||||
it('renders all the non-zero balances', () => {
|
||||
expect(component.find('img')).to.have.length(2);
|
||||
expect(component.find('Connect(TokenImage)')).to.have.length(2);
|
||||
});
|
||||
|
||||
describe('render specifiers', () => {
|
||||
it('renders only the single token with showOnlyEth', () => {
|
||||
render({ showOnlyEth: true });
|
||||
expect(component.find('img')).to.have.length(1);
|
||||
expect(component.find('Connect(TokenImage)')).to.have.length(1);
|
||||
});
|
||||
|
||||
it('renders all the tokens with showZeroValues', () => {
|
||||
render({ showZeroValues: true });
|
||||
expect(component.find('img')).to.have.length(3);
|
||||
expect(component.find('Connect(TokenImage)')).to.have.length(3);
|
||||
});
|
||||
|
||||
it('shows ETH with zero value with showOnlyEth & showZeroValues', () => {
|
||||
@ -116,7 +100,7 @@ describe('ui/Balance', () => {
|
||||
]
|
||||
}
|
||||
});
|
||||
expect(component.find('img')).to.have.length(1);
|
||||
expect(component.find('Connect(TokenImage)')).to.have.length(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
17
js/src/ui/TokenImage/index.js
Normal file
17
js/src/ui/TokenImage/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// 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/>.
|
||||
|
||||
export default from './tokenImage';
|
72
js/src/ui/TokenImage/tokenImage.js
Normal file
72
js/src/ui/TokenImage/tokenImage.js
Normal file
@ -0,0 +1,72 @@
|
||||
// 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 React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import unknownImage from '~/../assets/images/contracts/unknown-64x64.png';
|
||||
|
||||
class TokenImage extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
image: PropTypes.string,
|
||||
token: PropTypes.shape({
|
||||
image: PropTypes.string,
|
||||
address: PropTypes.string
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
render () {
|
||||
const { api } = this.context;
|
||||
const { image, token } = this.props;
|
||||
|
||||
const imageurl = token.image || image;
|
||||
let imagesrc = unknownImage;
|
||||
|
||||
if (imageurl) {
|
||||
const host = /^(\/)?api/.test(imageurl)
|
||||
? api.dappsUrl
|
||||
: '';
|
||||
|
||||
imagesrc = `${host}${imageurl}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<img
|
||||
src={ imagesrc }
|
||||
alt={ token.name }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (iniState) {
|
||||
const { images } = iniState;
|
||||
|
||||
return (_, props) => {
|
||||
const { token } = props;
|
||||
|
||||
return { image: images[token.address] };
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(TokenImage);
|
@ -73,7 +73,7 @@ describe('views/Account/Header', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
render({ balance: { balance: 'testing' } });
|
||||
balance = component.find('Connect(Balance)');
|
||||
balance = component.find('Balance');
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
|
@ -37,7 +37,6 @@ class Account extends Component {
|
||||
static propTypes = {
|
||||
fetchCertifiers: PropTypes.func.isRequired,
|
||||
fetchCertifications: PropTypes.func.isRequired,
|
||||
images: PropTypes.object.isRequired,
|
||||
setVisibleAccounts: PropTypes.func.isRequired,
|
||||
|
||||
accounts: PropTypes.object,
|
||||
@ -257,14 +256,13 @@ class Account extends Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { balances, images } = this.props;
|
||||
const { balances } = this.props;
|
||||
|
||||
return (
|
||||
<Transfer
|
||||
account={ account }
|
||||
balance={ balance }
|
||||
balances={ balances }
|
||||
images={ images }
|
||||
onClose={ this.store.toggleTransferDialog }
|
||||
/>
|
||||
);
|
||||
@ -289,12 +287,10 @@ class Account extends Component {
|
||||
function mapStateToProps (state) {
|
||||
const { accounts } = state.personal;
|
||||
const { balances } = state.balances;
|
||||
const { images } = state;
|
||||
|
||||
return {
|
||||
accounts,
|
||||
balances,
|
||||
images
|
||||
balances
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,6 @@ class Wallet extends Component {
|
||||
static propTypes = {
|
||||
address: PropTypes.string.isRequired,
|
||||
balance: nullableProptype(PropTypes.object.isRequired),
|
||||
images: PropTypes.object.isRequired,
|
||||
isTest: PropTypes.bool.isRequired,
|
||||
owned: PropTypes.bool.isRequired,
|
||||
setVisibleAccounts: PropTypes.func.isRequired,
|
||||
@ -315,13 +314,12 @@ class Wallet extends Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { walletAccount, balance, images } = this.props;
|
||||
const { walletAccount, balance } = this.props;
|
||||
|
||||
return (
|
||||
<Transfer
|
||||
account={ walletAccount }
|
||||
balance={ balance }
|
||||
images={ images }
|
||||
onClose={ this.onTransferClose }
|
||||
/>
|
||||
);
|
||||
@ -365,7 +363,6 @@ function mapStateToProps (_, initProps) {
|
||||
const { isTest } = state.nodeStatus;
|
||||
const { accountsInfo = {}, accounts = {} } = state.personal;
|
||||
const { balances } = state.balances;
|
||||
const { images } = state;
|
||||
const walletAccount = accounts[address] || accountsInfo[address] || null;
|
||||
|
||||
if (walletAccount) {
|
||||
@ -379,7 +376,6 @@ function mapStateToProps (_, initProps) {
|
||||
return {
|
||||
address,
|
||||
balance,
|
||||
images,
|
||||
isTest,
|
||||
owned,
|
||||
wallet,
|
||||
|
Loading…
Reference in New Issue
Block a user