openethereum/js/src/modals/Transfer/transfer.js
Nicolas Gotchac 8dbd56888d Add functionalities to multi-sig wallet (#3729)
* WIP Sending tokens in multi-sig wallet

* Working Token transfer for multi-sig wallet #3282

* Add operation hash to transfer modal

* Add existing wallet from address #3282

* Wallet delete redirect to Wallets/Accounts #3282

* Rightly check balance in Transfer // Get all accounts balances #3282

* Fix linting

* Better Header UI for Wallet

* Use the `~` webpack alias

* Use Webpack `~` alias
2016-12-07 12:47:44 +01:00

316 lines
8.1 KiB
JavaScript

// Copyright 2015, 2016 Ethcore (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 { bindActionCreators } from 'redux';
import { observer } from 'mobx-react';
import { pick } from 'lodash';
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
import ContentClear from 'material-ui/svg-icons/content/clear';
import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
import { newError } from '~/ui/Errors/actions';
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash, Input } from '~/ui';
import { nullableProptype } from '~/util/proptypes';
import Details from './Details';
import Extras from './Extras';
import TransferStore from './store';
import styles from './transfer.css';
@observer
class Transfer extends Component {
static contextTypes = {
api: PropTypes.object.isRequired
}
static propTypes = {
newError: PropTypes.func.isRequired,
gasLimit: PropTypes.object.isRequired,
images: PropTypes.object.isRequired,
senders: nullableProptype(PropTypes.object),
sendersBalances: nullableProptype(PropTypes.object),
account: PropTypes.object,
balance: PropTypes.object,
wallet: PropTypes.object,
onClose: PropTypes.func
}
store = new TransferStore(this.context.api, this.props);
componentDidMount () {
this.store.getDefaults();
}
render () {
const { stage, extras, steps } = this.store;
return (
<Modal
actions={ this.renderDialogActions() }
current={ stage }
steps={ steps }
waiting={ extras ? [2] : [1] }
visible
>
{ this.renderWarning() }
{ this.renderPage() }
</Modal>
);
}
renderAccount () {
const { account } = this.props;
return (
<div className={ styles.hdraccount }>
<div className={ styles.hdrimage }>
<IdentityIcon
inline center
address={ account.address } />
</div>
<div className={ styles.hdrdetails }>
<div className={ styles.hdrname }>
{ account.name || 'Unnamed' }
</div>
<div className={ styles.hdraddress }>
{ account.address }
</div>
</div>
</div>
);
}
renderPage () {
const { extras, stage } = this.store;
if (stage === 0) {
return this.renderDetailsPage();
} else if (stage === 1 && extras) {
return this.renderExtrasPage();
}
return this.renderCompletePage();
}
renderCompletePage () {
const { sending, txhash, busyState, rejected } = this.store;
if (rejected) {
return (
<BusyStep
title='The transaction has been rejected'
state='You can safely close this window, the transfer will not occur.'
/>
);
}
if (sending) {
return (
<BusyStep
title='The transaction is in progress'
state={ busyState } />
);
}
return (
<CompletedStep>
<TxHash hash={ txhash } />
{
this.store.operation
? (
<div>
<br />
<p>
This transaction needs confirmation from other owners.
<Input
style={ { width: '50%', margin: '0 auto' } }
value={ this.store.operation }
label='operation hash'
readOnly
allowCopy
/>
</p>
</div>
)
: null
}
</CompletedStep>
);
}
renderDetailsPage () {
const { account, balance, images, senders } = this.props;
const { valueAll, extras, recipient, recipientError, sender, senderError } = this.store;
const { tag, total, totalError, value, valueError } = this.store;
return (
<Details
address={ account.address }
all={ valueAll }
balance={ balance }
extras={ extras }
images={ images }
senders={ senders }
recipient={ recipient }
recipientError={ recipientError }
sender={ sender }
senderError={ senderError }
tag={ tag }
total={ total }
totalError={ totalError }
value={ value }
valueError={ valueError }
onChange={ this.store.onUpdateDetails }
wallet={ account.wallet && this.props.wallet }
/>
);
}
renderExtrasPage () {
if (!this.store.gasPriceHistogram) {
return null;
}
const { isEth, data, dataError, gas, gasEst, gasError, gasPrice } = this.store;
const { gasPriceDefault, gasPriceError, gasPriceHistogram, total, totalError } = this.store;
return (
<Extras
isEth={ isEth }
data={ data }
dataError={ dataError }
gas={ gas }
gasEst={ gasEst }
gasError={ gasError }
gasPrice={ gasPrice }
gasPriceDefault={ gasPriceDefault }
gasPriceError={ gasPriceError }
gasPriceHistogram={ gasPriceHistogram }
total={ total }
totalError={ totalError }
onChange={ this.store.onUpdateDetails } />
);
}
renderDialogActions () {
const { account } = this.props;
const { extras, sending, stage } = this.store;
const cancelBtn = (
<Button
icon={ <ContentClear /> }
label='Cancel'
onClick={ this.store.onClose } />
);
const nextBtn = (
<Button
disabled={ !this.store.isValid }
icon={ <NavigationArrowForward /> }
label='Next'
onClick={ this.store.onNext } />
);
const prevBtn = (
<Button
icon={ <NavigationArrowBack /> }
label='Back'
onClick={ this.store.onPrev } />
);
const sendBtn = (
<Button
disabled={ !this.store.isValid || sending }
icon={ <IdentityIcon address={ account.address } button /> }
label='Send'
onClick={ this.store.onSend } />
);
const doneBtn = (
<Button
icon={ <ActionDoneAll /> }
label='Close'
onClick={ this.store.onClose } />
);
switch (stage) {
case 0:
return extras
? [cancelBtn, nextBtn]
: [cancelBtn, sendBtn];
case 1:
return extras
? [cancelBtn, prevBtn, sendBtn]
: [doneBtn];
default:
return [doneBtn];
}
}
renderWarning () {
const { gasLimitError } = this.store;
if (!gasLimitError) {
return null;
}
return (
<div className={ styles.warning }>
{ gasLimitError }
</div>
);
}
}
function mapStateToProps (initState, initProps) {
const { address } = initProps.account;
const isWallet = initProps.account && initProps.account.wallet;
const wallet = isWallet
? initState.wallet.wallets[address]
: null;
const senders = isWallet
? Object
.values(initState.personal.accounts)
.filter((account) => wallet.owners.includes(account.address))
.reduce((accounts, account) => {
accounts[account.address] = account;
return accounts;
}, {})
: null;
return (state) => {
const { gasLimit } = state.nodeStatus;
const sendersBalances = senders ? pick(state.balances.balances, Object.keys(senders)) : null;
return { gasLimit, wallet, senders, sendersBalances };
};
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
newError
}, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Transfer);