openethereum/js/src/modals/Transfer/transfer.js
Gav Wood 8404edb656 Fix whitespace (#4299)
* Fix whitespace.

* Update copyright years/owner.

* Push release only for tags.
2017-01-25 18:51:41 +01:00

327 lines
8.2 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 React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { observer } from 'mobx-react';
import { pick } from 'lodash';
import { BusyStep, CompletedStep, Button, IdentityIcon, Input, Modal, TxHash, Warning } from '~/ui';
import { newError } from '~/ui/Errors/actions';
import { CancelIcon, DoneIcon, NextIcon, PrevIcon } from '~/ui/Icons';
import { nullableProptype } from '~/util/proptypes';
import Details from './Details';
import Extras from './Extras';
import TransferStore from './store';
import styles from './transfer.css';
const STEP_DETAILS = 0;
const STEP_ADVANCED_OR_BUSY = 1;
const STEP_BUSY = 2;
@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);
render () {
const { stage, extras, steps } = this.store;
return (
<Modal
actions={ this.renderDialogActions() }
current={ stage }
steps={ steps }
waiting={
extras
? [STEP_BUSY]
: [STEP_ADVANCED_OR_BUSY]
}
visible
>
{ this.renderExceptionWarning() }
{ this.renderPage() }
</Modal>
);
}
renderExceptionWarning () {
const { extras, stage } = this.store;
const { errorEstimated } = this.store.gasStore;
if (!errorEstimated || stage >= (extras ? STEP_BUSY : STEP_ADVANCED_OR_BUSY)) {
return null;
}
return (
<Warning warning={ errorEstimated } />
);
}
renderAccount () {
const { account } = this.props;
return (
<div className={ styles.hdraccount }>
<div className={ styles.hdrimage }>
<IdentityIcon
address={ account.address }
center
inline
/>
</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 === STEP_DETAILS) {
return this.renderDetailsPage();
} else if (stage === STEP_ADVANCED_OR_BUSY && 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 />
<div>
<p>This transaction needs confirmation from other owners.</p>
<Input
style={ { width: '50%', margin: '0 auto' } }
value={ this.store.operation }
label='operation hash'
readOnly
allowCopy
/>
</div>
</div>
)
: null
}
</CompletedStep>
);
}
renderDetailsPage () {
const { account, balance, images, senders } = this.props;
const { recipient, recipientError, sender, senderError, sendersBalances } = this.store;
const { valueAll, extras, tag, total, totalError, value, valueError } = this.store;
return (
<Details
address={ account.address }
all={ valueAll }
balance={ balance }
extras={ extras }
images={ images }
onChange={ this.store.onUpdateDetails }
recipient={ recipient }
recipientError={ recipientError }
sender={ sender }
senderError={ senderError }
senders={ senders }
sendersBalances={ sendersBalances }
tag={ tag }
total={ total }
totalError={ totalError }
value={ value }
valueError={ valueError }
wallet={ account.wallet && this.props.wallet }
/>
);
}
renderExtrasPage () {
if (!this.store.gasStore.histogram) {
return null;
}
const { isEth, data, dataError, minBlock, minBlockError, total, totalError } = this.store;
return (
<Extras
data={ data }
dataError={ dataError }
gasStore={ this.store.gasStore }
isEth={ isEth }
minBlock={ minBlock }
minBlockError={ minBlockError }
onChange={ this.store.onUpdateDetails }
total={ total }
totalError={ totalError }
/>
);
}
renderDialogActions () {
const { account } = this.props;
const { extras, sending, stage } = this.store;
const cancelBtn = (
<Button
icon={ <CancelIcon /> }
label='Cancel'
onClick={ this.handleClose }
/>
);
const nextBtn = (
<Button
disabled={ !this.store.isValid }
icon={ <NextIcon /> }
label='Next'
onClick={ this.store.onNext }
/>
);
const prevBtn = (
<Button
icon={ <PrevIcon /> }
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={ <DoneIcon /> }
label='Close'
onClick={ this.handleClose }
/>
);
switch (stage) {
case 0:
return extras
? [cancelBtn, nextBtn]
: [cancelBtn, sendBtn];
case 1:
return extras
? [cancelBtn, prevBtn, sendBtn]
: [doneBtn];
default:
return [doneBtn];
}
}
handleClose = () => {
const { onClose } = this.props;
this.store.handleClose();
typeof onClose === 'function' && onClose();
}
}
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);