Allow setting of minBlock on sending (#3921)
* minBlock value formatting * Allow Contract execute to specify minBock * Transfer allows minBlock * Cleanups * Check errors, verify via testing * Display Submitted/Submission block in MethodDecoding
This commit is contained in:
parent
74efb22230
commit
fc620d0d3e
@ -137,6 +137,10 @@ export function inOptions (options) {
|
|||||||
options[key] = inNumber16((new BigNumber(options[key])).round());
|
options[key] = inNumber16((new BigNumber(options[key])).round());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'minBlock':
|
||||||
|
options[key] = options[key] ? inNumber16(options[key]) : null;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'value':
|
case 'value':
|
||||||
case 'nonce':
|
case 'nonce':
|
||||||
options[key] = inNumber16(options[key]);
|
options[key] = inNumber16(options[key]);
|
||||||
|
@ -204,7 +204,7 @@ describe('api/format/input', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
['gas', 'gasPrice', 'value', 'nonce'].forEach((input) => {
|
['gas', 'gasPrice', 'value', 'minBlock', 'nonce'].forEach((input) => {
|
||||||
it(`formats ${input} number as hexnumber`, () => {
|
it(`formats ${input} number as hexnumber`, () => {
|
||||||
const block = {};
|
const block = {};
|
||||||
block[input] = 0x123;
|
block[input] = 0x123;
|
||||||
@ -214,6 +214,10 @@ describe('api/format/input', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('passes minBlock as null when specified as such', () => {
|
||||||
|
expect(inOptions({ minBlock: null })).to.deep.equal({ minBlock: null });
|
||||||
|
});
|
||||||
|
|
||||||
it('ignores and passes through unknown keys', () => {
|
it('ignores and passes through unknown keys', () => {
|
||||||
expect(inOptions({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
expect(inOptions({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||||
});
|
});
|
||||||
|
@ -205,6 +205,10 @@ export function outTransaction (tx) {
|
|||||||
tx[key] = outNumber(tx[key]);
|
tx[key] = outNumber(tx[key]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'minBlock':
|
||||||
|
tx[key] = tx[key] ? outNumber(tx[key]) : null;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'creates':
|
case 'creates':
|
||||||
case 'from':
|
case 'from':
|
||||||
case 'to':
|
case 'to':
|
||||||
|
@ -283,7 +283,7 @@ describe('api/format/output', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
['blockNumber', 'gasPrice', 'gas', 'nonce', 'transactionIndex', 'value'].forEach((input) => {
|
['blockNumber', 'gasPrice', 'gas', 'minBlock', 'nonce', 'transactionIndex', 'value'].forEach((input) => {
|
||||||
it(`formats ${input} number as hexnumber`, () => {
|
it(`formats ${input} number as hexnumber`, () => {
|
||||||
const block = {};
|
const block = {};
|
||||||
block[input] = 0x123;
|
block[input] = 0x123;
|
||||||
@ -294,6 +294,10 @@ describe('api/format/output', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('passes minBlock as null when null', () => {
|
||||||
|
expect(outTransaction({ minBlock: null })).to.deep.equal({ minBlock: null });
|
||||||
|
});
|
||||||
|
|
||||||
it('ignores and passes through unknown keys', () => {
|
it('ignores and passes through unknown keys', () => {
|
||||||
expect(outTransaction({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
expect(outTransaction({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||||
});
|
});
|
||||||
|
57
js/src/modals/ExecuteContract/AdvancedStep/advancedStep.js
Normal file
57
js/src/modals/ExecuteContract/AdvancedStep/advancedStep.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2015, 2016 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 { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { Input, GasPriceEditor } from '~/ui';
|
||||||
|
|
||||||
|
import styles from '../executeContract.css';
|
||||||
|
|
||||||
|
export default class AdvancedStep extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
gasStore: PropTypes.object.isRequired,
|
||||||
|
minBlock: PropTypes.string,
|
||||||
|
minBlockError: PropTypes.string,
|
||||||
|
onMinBlockChange: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { gasStore, minBlock, minBlockError, onMinBlockChange } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Input
|
||||||
|
error={ minBlockError }
|
||||||
|
hint={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.advanced.minBlock.hint'
|
||||||
|
defaultMessage='Only post the transaction after this block' />
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.advanced.minBlock.label'
|
||||||
|
defaultMessage='BlockNumber to send from' />
|
||||||
|
}
|
||||||
|
value={ minBlock }
|
||||||
|
onSubmit={ onMinBlockChange } />
|
||||||
|
<div className={ styles.gaseditor }>
|
||||||
|
<GasPriceEditor store={ gasStore } />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
17
js/src/modals/ExecuteContract/AdvancedStep/index.js
Normal file
17
js/src/modals/ExecuteContract/AdvancedStep/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 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 './advancedStep';
|
@ -14,8 +14,9 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
|
||||||
import { Checkbox, MenuItem } from 'material-ui';
|
import { Checkbox, MenuItem } from 'material-ui';
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { AddressSelect, Form, Input, Select, TypedInput } from '~/ui';
|
import { AddressSelect, Form, Input, Select, TypedInput } from '~/ui';
|
||||||
|
|
||||||
@ -29,29 +30,28 @@ const CHECK_STYLE = {
|
|||||||
|
|
||||||
export default class DetailsStep extends Component {
|
export default class DetailsStep extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
advancedOptions: PropTypes.bool,
|
||||||
accounts: PropTypes.object.isRequired,
|
accounts: PropTypes.object.isRequired,
|
||||||
contract: PropTypes.object.isRequired,
|
|
||||||
onAmountChange: PropTypes.func.isRequired,
|
|
||||||
onFromAddressChange: PropTypes.func.isRequired,
|
|
||||||
onValueChange: PropTypes.func.isRequired,
|
|
||||||
values: PropTypes.array.isRequired,
|
|
||||||
valuesError: PropTypes.array.isRequired,
|
|
||||||
|
|
||||||
amount: PropTypes.string,
|
amount: PropTypes.string,
|
||||||
amountError: PropTypes.string,
|
amountError: PropTypes.string,
|
||||||
balances: PropTypes.object,
|
balances: PropTypes.object,
|
||||||
|
contract: PropTypes.object.isRequired,
|
||||||
fromAddress: PropTypes.string,
|
fromAddress: PropTypes.string,
|
||||||
fromAddressError: PropTypes.string,
|
fromAddressError: PropTypes.string,
|
||||||
func: PropTypes.object,
|
func: PropTypes.object,
|
||||||
funcError: PropTypes.string,
|
funcError: PropTypes.string,
|
||||||
gasEdit: PropTypes.bool,
|
onAdvancedClick: PropTypes.func,
|
||||||
|
onAmountChange: PropTypes.func.isRequired,
|
||||||
|
onFromAddressChange: PropTypes.func.isRequired,
|
||||||
onFuncChange: PropTypes.func,
|
onFuncChange: PropTypes.func,
|
||||||
onGasEditClick: PropTypes.func,
|
onValueChange: PropTypes.func.isRequired,
|
||||||
|
values: PropTypes.array.isRequired,
|
||||||
|
valuesError: PropTypes.array.isRequired,
|
||||||
warning: PropTypes.string
|
warning: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { accounts, amount, amountError, balances, fromAddress, fromAddressError, gasEdit, onGasEditClick, onFromAddressChange, onAmountChange } = this.props;
|
const { accounts, advancedOptions, amount, amountError, balances, fromAddress, fromAddressError, onAdvancedClick, onAmountChange, onFromAddressChange } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
@ -60,8 +60,16 @@ export default class DetailsStep extends Component {
|
|||||||
accounts={ accounts }
|
accounts={ accounts }
|
||||||
balances={ balances }
|
balances={ balances }
|
||||||
error={ fromAddressError }
|
error={ fromAddressError }
|
||||||
hint='the account to transact with'
|
hint={
|
||||||
label='from account'
|
<FormattedMessage
|
||||||
|
id='executeContract.details.address.label'
|
||||||
|
defaultMessage='the account to transact with' />
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.details.address.hint'
|
||||||
|
defaultMessage='from account' />
|
||||||
|
}
|
||||||
onChange={ onFromAddressChange }
|
onChange={ onFromAddressChange }
|
||||||
value={ fromAddress } />
|
value={ fromAddress } />
|
||||||
{ this.renderFunctionSelect() }
|
{ this.renderFunctionSelect() }
|
||||||
@ -70,16 +78,28 @@ export default class DetailsStep extends Component {
|
|||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
error={ amountError }
|
error={ amountError }
|
||||||
hint='the amount to send to with the transaction'
|
hint={
|
||||||
label='transaction value (in ETH)'
|
<FormattedMessage
|
||||||
|
id='executeContract.details.amount.hint'
|
||||||
|
defaultMessage='the amount to send to with the transaction' />
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.details.amount.label'
|
||||||
|
defaultMessage='transaction value (in ETH)' />
|
||||||
|
}
|
||||||
onSubmit={ onAmountChange }
|
onSubmit={ onAmountChange }
|
||||||
value={ amount } />
|
value={ amount } />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={ gasEdit }
|
checked={ advancedOptions }
|
||||||
label='edit gas price or value'
|
label={
|
||||||
onCheck={ onGasEditClick }
|
<FormattedMessage
|
||||||
|
id='executeContract.details.advancedCheck.label'
|
||||||
|
defaultMessage='advanced sending options' />
|
||||||
|
}
|
||||||
|
onCheck={ onAdvancedClick }
|
||||||
style={ CHECK_STYLE } />
|
style={ CHECK_STYLE } />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -129,9 +149,17 @@ export default class DetailsStep extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
label='function to execute'
|
|
||||||
hint='the function to call on the contract'
|
|
||||||
error={ funcError }
|
error={ funcError }
|
||||||
|
hint={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.details.function.hint'
|
||||||
|
defaultMessage='the function to call on the contract' />
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.details.function.label'
|
||||||
|
defaultMessage='function to execute' />
|
||||||
|
}
|
||||||
onChange={ this.onFuncChange }
|
onChange={ this.onFuncChange }
|
||||||
value={ func.signature }>
|
value={ func.signature }>
|
||||||
{ functions }
|
{ functions }
|
||||||
|
@ -14,6 +14,19 @@
|
|||||||
/* You should have received a copy of the GNU General Public License
|
/* You should have received a copy of the GNU General Public License
|
||||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.funcparams {
|
||||||
|
padding-left: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gaseditor {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paramname {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
.modalbody,
|
.modalbody,
|
||||||
.modalcenter {
|
.modalcenter {
|
||||||
}
|
}
|
||||||
@ -22,14 +35,6 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.funcparams {
|
|
||||||
padding-left: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.paramname {
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.txhash {
|
.txhash {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
@ -14,40 +14,54 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { pick } from 'lodash';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators } from '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 { toWei } from '~/api/util/wei';
|
import { toWei } from '~/api/util/wei';
|
||||||
import { BusyStep, Button, CompletedStep, GasPriceEditor, IdentityIcon, Modal, TxHash } from '~/ui';
|
import { BusyStep, Button, CompletedStep, GasPriceEditor, IdentityIcon, Modal, TxHash } from '~/ui';
|
||||||
|
import { CancelIcon, DoneIcon, NextIcon, PrevIcon } from '~/ui/Icons';
|
||||||
import { MAX_GAS_ESTIMATION } from '~/util/constants';
|
import { MAX_GAS_ESTIMATION } from '~/util/constants';
|
||||||
import { validateAddress, validateUint } from '~/util/validation';
|
import { validateAddress, validateUint } from '~/util/validation';
|
||||||
import { parseAbiType } from '~/util/abi';
|
import { parseAbiType } from '~/util/abi';
|
||||||
|
|
||||||
|
import AdvancedStep from './AdvancedStep';
|
||||||
import DetailsStep from './DetailsStep';
|
import DetailsStep from './DetailsStep';
|
||||||
|
|
||||||
import { ERROR_CODES } from '~/api/transport/error';
|
import { ERROR_CODES } from '~/api/transport/error';
|
||||||
|
|
||||||
const STEP_DETAILS = 0;
|
const STEP_DETAILS = 0;
|
||||||
const STEP_BUSY_OR_GAS = 1;
|
const STEP_BUSY_OR_ADVANCED = 1;
|
||||||
const STEP_BUSY = 2;
|
const STEP_BUSY = 2;
|
||||||
|
|
||||||
const TITLES = {
|
const TITLES = {
|
||||||
transfer: 'function details',
|
transfer:
|
||||||
sending: 'sending',
|
<FormattedMessage
|
||||||
complete: 'complete',
|
id='executeContract.steps.transfer'
|
||||||
gas: 'gas selection',
|
defaultMessage='function details' />,
|
||||||
rejected: 'rejected'
|
sending:
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.steps.sending'
|
||||||
|
defaultMessage='sending' />,
|
||||||
|
complete:
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.steps.complete'
|
||||||
|
defaultMessage='complete' />,
|
||||||
|
advanced:
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.steps.advanced'
|
||||||
|
defaultMessage='advanced options' />,
|
||||||
|
rejected:
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.steps.rejected'
|
||||||
|
defaultMessage='rejected' />
|
||||||
};
|
};
|
||||||
const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete];
|
const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete];
|
||||||
const STAGES_GAS = [TITLES.transfer, TITLES.gas, TITLES.sending, TITLES.complete];
|
const STAGES_ADVANCED = [TITLES.transfer, TITLES.advanced, TITLES.sending, TITLES.complete];
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class ExecuteContract extends Component {
|
class ExecuteContract extends Component {
|
||||||
@ -70,13 +84,15 @@ class ExecuteContract extends Component {
|
|||||||
gasStore = new GasPriceEditor.Store(this.context.api, { gasLimit: this.props.gasLimit });
|
gasStore = new GasPriceEditor.Store(this.context.api, { gasLimit: this.props.gasLimit });
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
advancedOptions: false,
|
||||||
amount: '0',
|
amount: '0',
|
||||||
amountError: null,
|
amountError: null,
|
||||||
busyState: null,
|
busyState: null,
|
||||||
fromAddressError: null,
|
fromAddressError: null,
|
||||||
func: null,
|
func: null,
|
||||||
funcError: null,
|
funcError: null,
|
||||||
gasEdit: false,
|
minBlock: '0',
|
||||||
|
minBlockError: null,
|
||||||
rejected: false,
|
rejected: false,
|
||||||
sending: false,
|
sending: false,
|
||||||
step: STEP_DETAILS,
|
step: STEP_DETAILS,
|
||||||
@ -101,8 +117,8 @@ class ExecuteContract extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { sending, step, gasEdit, rejected } = this.state;
|
const { advancedOptions, rejected, sending, step } = this.state;
|
||||||
const steps = gasEdit ? STAGES_GAS : STAGES_BASIC;
|
const steps = advancedOptions ? STAGES_ADVANCED : STAGES_BASIC;
|
||||||
|
|
||||||
if (rejected) {
|
if (rejected) {
|
||||||
steps[steps.length - 1] = TITLES.rejected;
|
steps[steps.length - 1] = TITLES.rejected;
|
||||||
@ -115,7 +131,7 @@ class ExecuteContract extends Component {
|
|||||||
current={ step }
|
current={ step }
|
||||||
steps={ steps }
|
steps={ steps }
|
||||||
visible
|
visible
|
||||||
waiting={ gasEdit ? [STEP_BUSY] : [STEP_BUSY_OR_GAS] }>
|
waiting={ advancedOptions ? [STEP_BUSY] : [STEP_BUSY_OR_ADVANCED] }>
|
||||||
{ this.renderStep() }
|
{ this.renderStep() }
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
@ -123,20 +139,28 @@ class ExecuteContract extends Component {
|
|||||||
|
|
||||||
renderDialogActions () {
|
renderDialogActions () {
|
||||||
const { onClose, fromAddress } = this.props;
|
const { onClose, fromAddress } = this.props;
|
||||||
const { gasEdit, sending, step, fromAddressError, valuesError } = this.state;
|
const { advancedOptions, sending, step, fromAddressError, minBlockError, valuesError } = this.state;
|
||||||
const hasError = fromAddressError || valuesError.find((error) => error);
|
const hasError = fromAddressError || minBlockError || valuesError.find((error) => error);
|
||||||
|
|
||||||
const cancelBtn = (
|
const cancelBtn = (
|
||||||
<Button
|
<Button
|
||||||
key='cancel'
|
key='cancel'
|
||||||
label='Cancel'
|
label={
|
||||||
icon={ <ContentClear /> }
|
<FormattedMessage
|
||||||
|
id='executeContract.button.cancel'
|
||||||
|
defaultMessage='cancel' />
|
||||||
|
}
|
||||||
|
icon={ <CancelIcon /> }
|
||||||
onClick={ onClose } />
|
onClick={ onClose } />
|
||||||
);
|
);
|
||||||
const postBtn = (
|
const postBtn = (
|
||||||
<Button
|
<Button
|
||||||
key='postTransaction'
|
key='postTransaction'
|
||||||
label='post transaction'
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.button.post'
|
||||||
|
defaultMessage='post transaction' />
|
||||||
|
}
|
||||||
disabled={ !!(sending || hasError) }
|
disabled={ !!(sending || hasError) }
|
||||||
icon={ <IdentityIcon address={ fromAddress } button /> }
|
icon={ <IdentityIcon address={ fromAddress } button /> }
|
||||||
onClick={ this.postTransaction } />
|
onClick={ this.postTransaction } />
|
||||||
@ -144,28 +168,36 @@ class ExecuteContract extends Component {
|
|||||||
const nextBtn = (
|
const nextBtn = (
|
||||||
<Button
|
<Button
|
||||||
key='nextStep'
|
key='nextStep'
|
||||||
label='next'
|
label={
|
||||||
icon={ <NavigationArrowForward /> }
|
<FormattedMessage
|
||||||
|
id='executeContract.button.next'
|
||||||
|
defaultMessage='next' />
|
||||||
|
}
|
||||||
|
icon={ <NextIcon /> }
|
||||||
onClick={ this.onNextClick } />
|
onClick={ this.onNextClick } />
|
||||||
);
|
);
|
||||||
const prevBtn = (
|
const prevBtn = (
|
||||||
<Button
|
<Button
|
||||||
key='prevStep'
|
key='prevStep'
|
||||||
label='prev'
|
label={
|
||||||
icon={ <NavigationArrowBack /> }
|
<FormattedMessage
|
||||||
|
id='executeContract.button.prev'
|
||||||
|
defaultMessage='prev' />
|
||||||
|
}
|
||||||
|
icon={ <PrevIcon /> }
|
||||||
onClick={ this.onPrevClick } />
|
onClick={ this.onPrevClick } />
|
||||||
);
|
);
|
||||||
|
|
||||||
if (step === STEP_DETAILS) {
|
if (step === STEP_DETAILS) {
|
||||||
return [
|
return [
|
||||||
cancelBtn,
|
cancelBtn,
|
||||||
gasEdit ? nextBtn : postBtn
|
advancedOptions ? nextBtn : postBtn
|
||||||
];
|
];
|
||||||
} else if (step === (gasEdit ? STEP_BUSY : STEP_BUSY_OR_GAS)) {
|
} else if (step === (advancedOptions ? STEP_BUSY : STEP_BUSY_OR_ADVANCED)) {
|
||||||
return [
|
return [
|
||||||
cancelBtn
|
cancelBtn
|
||||||
];
|
];
|
||||||
} else if (gasEdit && (step === STEP_BUSY_OR_GAS)) {
|
} else if (advancedOptions && (step === STEP_BUSY_OR_ADVANCED)) {
|
||||||
return [
|
return [
|
||||||
cancelBtn,
|
cancelBtn,
|
||||||
prevBtn,
|
prevBtn,
|
||||||
@ -176,23 +208,34 @@ class ExecuteContract extends Component {
|
|||||||
return [
|
return [
|
||||||
<Button
|
<Button
|
||||||
key='close'
|
key='close'
|
||||||
label='Done'
|
label={
|
||||||
icon={ <ActionDoneAll /> }
|
<FormattedMessage
|
||||||
|
id='executeContract.button.done'
|
||||||
|
defaultMessage='done' />
|
||||||
|
}
|
||||||
|
icon={ <DoneIcon /> }
|
||||||
onClick={ onClose } />
|
onClick={ onClose } />
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
renderStep () {
|
renderStep () {
|
||||||
const { onFromAddressChange } = this.props;
|
const { onFromAddressChange } = this.props;
|
||||||
const { gasEdit, step, busyState, txhash, rejected } = this.state;
|
const { advancedOptions, step, busyState, minBlock, minBlockError, txhash, rejected } = this.state;
|
||||||
const { errorEstimated } = this.gasStore;
|
const { errorEstimated } = this.gasStore;
|
||||||
|
|
||||||
if (rejected) {
|
if (rejected) {
|
||||||
return (
|
return (
|
||||||
<BusyStep
|
<BusyStep
|
||||||
title='The execution has been rejected'
|
title={
|
||||||
state='You can safely close this window, the function execution will not occur.'
|
<FormattedMessage
|
||||||
/>
|
id='executeContract.rejected.title'
|
||||||
|
defaultMessage='The execution has been rejected' />
|
||||||
|
}
|
||||||
|
state={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.rejected.state'
|
||||||
|
defaultMessage='You can safely close this window, the function execution will not occur.' />
|
||||||
|
} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,19 +248,26 @@ class ExecuteContract extends Component {
|
|||||||
onAmountChange={ this.onAmountChange }
|
onAmountChange={ this.onAmountChange }
|
||||||
onFromAddressChange={ onFromAddressChange }
|
onFromAddressChange={ onFromAddressChange }
|
||||||
onFuncChange={ this.onFuncChange }
|
onFuncChange={ this.onFuncChange }
|
||||||
onGasEditClick={ this.onGasEditClick }
|
onAdvancedClick={ this.onAdvancedClick }
|
||||||
onValueChange={ this.onValueChange } />
|
onValueChange={ this.onValueChange } />
|
||||||
);
|
);
|
||||||
} else if (step === (gasEdit ? STEP_BUSY : STEP_BUSY_OR_GAS)) {
|
} else if (step === (advancedOptions ? STEP_BUSY : STEP_BUSY_OR_ADVANCED)) {
|
||||||
return (
|
return (
|
||||||
<BusyStep
|
<BusyStep
|
||||||
title='The function execution is in progress'
|
title={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.busy.title'
|
||||||
|
defaultMessage='The function execution is in progress' />
|
||||||
|
}
|
||||||
state={ busyState } />
|
state={ busyState } />
|
||||||
);
|
);
|
||||||
} else if (gasEdit && (step === STEP_BUSY_OR_GAS)) {
|
} else if (advancedOptions && (step === STEP_BUSY_OR_ADVANCED)) {
|
||||||
return (
|
return (
|
||||||
<GasPriceEditor
|
<AdvancedStep
|
||||||
store={ this.gasStore } />
|
gasStore={ this.gasStore }
|
||||||
|
minBlock={ minBlock }
|
||||||
|
minBlockError={ minBlockError }
|
||||||
|
onMinBlockChange={ this.onMinBlockChange } />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,6 +295,15 @@ class ExecuteContract extends Component {
|
|||||||
}, this.estimateGas);
|
}, this.estimateGas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMinBlockChange = (minBlock) => {
|
||||||
|
const minBlockError = validateUint(minBlock).valueError;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
minBlock,
|
||||||
|
minBlockError
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onValueChange = (event, index, _value) => {
|
onValueChange = (event, index, _value) => {
|
||||||
const { func, values, valuesError } = this.state;
|
const { func, values, valuesError } = this.state;
|
||||||
const input = func.inputs.find((input, _index) => index === _index);
|
const input = func.inputs.find((input, _index) => index === _index);
|
||||||
@ -305,22 +364,28 @@ class ExecuteContract extends Component {
|
|||||||
postTransaction = () => {
|
postTransaction = () => {
|
||||||
const { api, store } = this.context;
|
const { api, store } = this.context;
|
||||||
const { fromAddress } = this.props;
|
const { fromAddress } = this.props;
|
||||||
const { amount, func, gasEdit, values } = this.state;
|
const { advancedOptions, amount, func, minBlock, values } = this.state;
|
||||||
const steps = gasEdit ? STAGES_GAS : STAGES_BASIC;
|
const steps = advancedOptions ? STAGES_ADVANCED : STAGES_BASIC;
|
||||||
const finalstep = steps.length - 1;
|
const finalstep = steps.length - 1;
|
||||||
const options = {
|
const options = {
|
||||||
gas: this.gasStore.gas,
|
gas: this.gasStore.gas,
|
||||||
gasPrice: this.gasStore.price,
|
gasPrice: this.gasStore.price,
|
||||||
from: fromAddress,
|
from: fromAddress,
|
||||||
|
minBlock: new BigNumber(minBlock || 0).gt(0) ? minBlock : null,
|
||||||
value: api.util.toWei(amount || 0)
|
value: api.util.toWei(amount || 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setState({ sending: true, step: gasEdit ? STEP_BUSY : STEP_BUSY_OR_GAS });
|
this.setState({ sending: true, step: advancedOptions ? STEP_BUSY : STEP_BUSY_OR_ADVANCED });
|
||||||
|
|
||||||
func
|
func
|
||||||
.postTransaction(options, values)
|
.postTransaction(options, values)
|
||||||
.then((requestId) => {
|
.then((requestId) => {
|
||||||
this.setState({ busyState: 'Waiting for authorization in the Parity Signer' });
|
this.setState({
|
||||||
|
busyState:
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.busy.waitAuth'
|
||||||
|
defaultMessage='Waiting for authorization in the Parity Signer' />
|
||||||
|
});
|
||||||
|
|
||||||
return api
|
return api
|
||||||
.pollMethod('parity_checkRequest', requestId)
|
.pollMethod('parity_checkRequest', requestId)
|
||||||
@ -334,7 +399,15 @@ class ExecuteContract extends Component {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((txhash) => {
|
.then((txhash) => {
|
||||||
this.setState({ sending: false, step: finalstep, txhash, busyState: 'Your transaction has been posted to the network' });
|
this.setState({
|
||||||
|
sending: false,
|
||||||
|
step: finalstep,
|
||||||
|
txhash,
|
||||||
|
busyState:
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.busy.posted'
|
||||||
|
defaultMessage='Your transaction has been posted to the network' />
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('postTransaction', error);
|
console.error('postTransaction', error);
|
||||||
@ -342,9 +415,9 @@ class ExecuteContract extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onGasEditClick = () => {
|
onAdvancedClick = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
gasEdit: !this.state.gasEdit
|
advancedOptions: !this.state.advancedOptions
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,29 +15,50 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { GasPriceEditor, Form, Input } from '~/ui';
|
import { GasPriceEditor, Form, Input } from '~/ui';
|
||||||
|
|
||||||
|
import styles from '../transfer.css';
|
||||||
|
|
||||||
export default class Extras extends Component {
|
export default class Extras extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
isEth: PropTypes.bool,
|
|
||||||
data: PropTypes.string,
|
data: PropTypes.string,
|
||||||
dataError: PropTypes.string,
|
dataError: PropTypes.string,
|
||||||
total: PropTypes.string,
|
gasStore: PropTypes.object.isRequired,
|
||||||
totalError: PropTypes.string,
|
isEth: PropTypes.bool,
|
||||||
|
minBlock: PropTypes.string,
|
||||||
|
minBlockError: PropTypes.string,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
gasStore: PropTypes.object.isRequired
|
total: PropTypes.string,
|
||||||
|
totalError: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { gasStore, onChange } = this.props;
|
const { gasStore, minBlock, minBlockError, onChange } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
{ this.renderData() }
|
{ this.renderData() }
|
||||||
<GasPriceEditor
|
<Input
|
||||||
store={ gasStore }
|
error={ minBlockError }
|
||||||
onChange={ onChange } />
|
hint={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.advanced.minBlock.hint'
|
||||||
|
defaultMessage='Only post the transaction after this block' />
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='executeContract.advanced.minBlock.label'
|
||||||
|
defaultMessage='BlockNumber to send from' />
|
||||||
|
}
|
||||||
|
value={ minBlock }
|
||||||
|
onChange={ this.onEditMinBlock } />
|
||||||
|
<div className={ styles.gaseditor }>
|
||||||
|
<GasPriceEditor
|
||||||
|
store={ gasStore }
|
||||||
|
onChange={ onChange } />
|
||||||
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -50,18 +71,28 @@ export default class Extras extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Input
|
||||||
<Input
|
error={ dataError }
|
||||||
hint='the data to pass through with the transaction'
|
hint={
|
||||||
label='transaction data'
|
<FormattedMessage
|
||||||
value={ data }
|
id='transfer.advanced.data.hint'
|
||||||
error={ dataError }
|
defaultMessage='the data to pass through with the transaction' />
|
||||||
onChange={ this.onEditData } />
|
}
|
||||||
</div>
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='transfer.advanced.data.label'
|
||||||
|
defaultMessage='transaction data' />
|
||||||
|
}
|
||||||
|
onChange={ this.onEditData }
|
||||||
|
value={ data } />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onEditData = (event) => {
|
onEditData = (event) => {
|
||||||
this.props.onChange('data', event.target.value);
|
this.props.onChange('data', event.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEditMinBlock = (event) => {
|
||||||
|
this.props.onChange('minBlock', event.target.value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,9 @@ export default class TransferStore {
|
|||||||
@observable data = '';
|
@observable data = '';
|
||||||
@observable dataError = null;
|
@observable dataError = null;
|
||||||
|
|
||||||
|
@observable minBlock = '0';
|
||||||
|
@observable minBlockError = null;
|
||||||
|
|
||||||
@observable recipient = '';
|
@observable recipient = '';
|
||||||
@observable recipientError = ERRORS.requireRecipient;
|
@observable recipientError = ERRORS.requireRecipient;
|
||||||
|
|
||||||
@ -84,7 +87,7 @@ export default class TransferStore {
|
|||||||
|
|
||||||
@computed get isValid () {
|
@computed get isValid () {
|
||||||
const detailsValid = !this.recipientError && !this.valueError && !this.totalError && !this.senderError;
|
const detailsValid = !this.recipientError && !this.valueError && !this.totalError && !this.senderError;
|
||||||
const extrasValid = !this.gasStore.errorGas && !this.gasStore.errorPrice && !this.totalError;
|
const extrasValid = !this.gasStore.errorGas && !this.gasStore.errorPrice && !this.minBlockError && !this.totalError;
|
||||||
const verifyValid = !this.passwordError;
|
const verifyValid = !this.passwordError;
|
||||||
|
|
||||||
switch (this.stage) {
|
switch (this.stage) {
|
||||||
@ -92,7 +95,9 @@ export default class TransferStore {
|
|||||||
return detailsValid;
|
return detailsValid;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return this.extras ? extrasValid : verifyValid;
|
return this.extras
|
||||||
|
? extrasValid
|
||||||
|
: verifyValid;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
return verifyValid;
|
return verifyValid;
|
||||||
@ -155,6 +160,9 @@ export default class TransferStore {
|
|||||||
case 'gasPrice':
|
case 'gasPrice':
|
||||||
return this._onUpdateGasPrice(value);
|
return this._onUpdateGasPrice(value);
|
||||||
|
|
||||||
|
case 'minBlock':
|
||||||
|
return this._onUpdateMinBlock(value);
|
||||||
|
|
||||||
case 'recipient':
|
case 'recipient':
|
||||||
return this._onUpdateRecipient(value);
|
return this._onUpdateRecipient(value);
|
||||||
|
|
||||||
@ -254,6 +262,14 @@ export default class TransferStore {
|
|||||||
this.recalculate();
|
this.recalculate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action _onUpdateMinBlock = (minBlock) => {
|
||||||
|
console.log('minBlock', minBlock);
|
||||||
|
transaction(() => {
|
||||||
|
this.minBlock = minBlock;
|
||||||
|
this.minBlockError = this._validatePositiveNumber(minBlock);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@action _onUpdateGasPrice = (gasPrice) => {
|
@action _onUpdateGasPrice = (gasPrice) => {
|
||||||
this.recalculate();
|
this.recalculate();
|
||||||
}
|
}
|
||||||
@ -412,6 +428,9 @@ export default class TransferStore {
|
|||||||
|
|
||||||
send () {
|
send () {
|
||||||
const { options, values } = this._getTransferParams();
|
const { options, values } = this._getTransferParams();
|
||||||
|
|
||||||
|
options.minBlock = new BigNumber(this.minBlock || 0).gt(0) ? this.minBlock : null;
|
||||||
|
|
||||||
return this._getTransferMethod().postTransaction(options, values);
|
return this._getTransferMethod().postTransaction(options, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,65 +15,68 @@
|
|||||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.columns {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
&>div {
|
||||||
|
flex: 0 1 50%;
|
||||||
|
position: relative;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.gaseditor {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
line-height: 1.618em;
|
line-height: 1.618em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.columns {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
|
||||||
position: relative;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
flex-wrap: wrap;
|
||||||
|
|
||||||
.columns>div {
|
|
||||||
flex: 0 1 50%;
|
|
||||||
width: 50%;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floatbutton {
|
.floatbutton {
|
||||||
text-align: right;
|
|
||||||
float: right;
|
float: right;
|
||||||
margin-left: -100%;
|
margin-left: -100%;
|
||||||
margin-top: 28px;
|
margin-top: 28px;
|
||||||
}
|
text-align: right;
|
||||||
|
|
||||||
.floatbutton>div {
|
&>div {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tokenSelect {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.token {
|
.token {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
margin: 0 16px 0 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tokenSelect .token {
|
.tokenSelect {
|
||||||
margin-top: 10px;
|
.token {
|
||||||
}
|
margin-top: 10px;
|
||||||
|
}
|
||||||
.token img {
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
margin: 0 16px 0 0;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token div {
|
|
||||||
height: 32px;
|
|
||||||
line-height: 32px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tokenbalance {
|
.tokenbalance {
|
||||||
|
@ -20,13 +20,9 @@ import { bindActionCreators } from 'redux';
|
|||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { pick } from 'lodash';
|
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 { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash, Input } from '~/ui';
|
||||||
|
import { newError } from '~/ui/Errors/actions';
|
||||||
|
import { CancelIcon, DoneIcon, NextIcon, PrevIcon } from '~/ui/Icons';
|
||||||
import { nullableProptype } from '~/util/proptypes';
|
import { nullableProptype } from '~/util/proptypes';
|
||||||
|
|
||||||
import Details from './Details';
|
import Details from './Details';
|
||||||
@ -188,17 +184,19 @@ class Transfer extends Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { isEth, data, dataError, total, totalError } = this.store;
|
const { isEth, data, dataError, minBlock, minBlockError, total, totalError } = this.store;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Extras
|
<Extras
|
||||||
isEth={ isEth }
|
|
||||||
data={ data }
|
data={ data }
|
||||||
dataError={ dataError }
|
dataError={ dataError }
|
||||||
total={ total }
|
|
||||||
totalError={ totalError }
|
|
||||||
gasStore={ this.store.gasStore }
|
gasStore={ this.store.gasStore }
|
||||||
onChange={ this.store.onUpdateDetails } />
|
isEth={ isEth }
|
||||||
|
minBlock={ minBlock }
|
||||||
|
minBlockError={ minBlockError }
|
||||||
|
onChange={ this.store.onUpdateDetails }
|
||||||
|
total={ total }
|
||||||
|
totalError={ totalError } />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,20 +206,20 @@ class Transfer extends Component {
|
|||||||
|
|
||||||
const cancelBtn = (
|
const cancelBtn = (
|
||||||
<Button
|
<Button
|
||||||
icon={ <ContentClear /> }
|
icon={ <CancelIcon /> }
|
||||||
label='Cancel'
|
label='Cancel'
|
||||||
onClick={ this.handleClose } />
|
onClick={ this.handleClose } />
|
||||||
);
|
);
|
||||||
const nextBtn = (
|
const nextBtn = (
|
||||||
<Button
|
<Button
|
||||||
disabled={ !this.store.isValid }
|
disabled={ !this.store.isValid }
|
||||||
icon={ <NavigationArrowForward /> }
|
icon={ <NextIcon /> }
|
||||||
label='Next'
|
label='Next'
|
||||||
onClick={ this.store.onNext } />
|
onClick={ this.store.onNext } />
|
||||||
);
|
);
|
||||||
const prevBtn = (
|
const prevBtn = (
|
||||||
<Button
|
<Button
|
||||||
icon={ <NavigationArrowBack /> }
|
icon={ <PrevIcon /> }
|
||||||
label='Back'
|
label='Back'
|
||||||
onClick={ this.store.onPrev } />
|
onClick={ this.store.onPrev } />
|
||||||
);
|
);
|
||||||
@ -234,7 +232,7 @@ class Transfer extends Component {
|
|||||||
);
|
);
|
||||||
const doneBtn = (
|
const doneBtn = (
|
||||||
<Button
|
<Button
|
||||||
icon={ <ActionDoneAll /> }
|
icon={ <DoneIcon /> }
|
||||||
label='Close'
|
label='Close'
|
||||||
onClick={ this.handleClose } />
|
onClick={ this.handleClose } />
|
||||||
);
|
);
|
||||||
|
@ -14,16 +14,18 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
|
||||||
import { MenuItem } from 'material-ui';
|
|
||||||
import { isEqual, pick } from 'lodash';
|
import { isEqual, pick } from 'lodash';
|
||||||
|
import { MenuItem } from 'material-ui';
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { fromWei } from '~/api/util/wei';
|
||||||
|
import { nodeOrStringProptype } from '~/util/proptypes';
|
||||||
|
|
||||||
import AutoComplete from '../AutoComplete';
|
import AutoComplete from '../AutoComplete';
|
||||||
import IdentityIcon from '../../IdentityIcon';
|
import IdentityIcon from '../../IdentityIcon';
|
||||||
import IdentityName from '../../IdentityName';
|
import IdentityName from '../../IdentityName';
|
||||||
|
|
||||||
import { fromWei } from '~/api/util/wei';
|
|
||||||
|
|
||||||
import styles from './addressSelect.css';
|
import styles from './addressSelect.css';
|
||||||
|
|
||||||
export default class AddressSelect extends Component {
|
export default class AddressSelect extends Component {
|
||||||
@ -40,9 +42,9 @@ export default class AddressSelect extends Component {
|
|||||||
contacts: PropTypes.object,
|
contacts: PropTypes.object,
|
||||||
contracts: PropTypes.object,
|
contracts: PropTypes.object,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
error: PropTypes.string,
|
error: nodeOrStringProptype(),
|
||||||
hint: PropTypes.string,
|
hint: nodeOrStringProptype(),
|
||||||
label: PropTypes.string,
|
label: nodeOrStringProptype(),
|
||||||
tokens: PropTypes.object,
|
tokens: PropTypes.object,
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
wallets: PropTypes.object
|
wallets: PropTypes.object
|
||||||
@ -116,18 +118,27 @@ export default class AddressSelect extends Component {
|
|||||||
<AutoComplete
|
<AutoComplete
|
||||||
className={ !icon ? '' : styles.paddedInput }
|
className={ !icon ? '' : styles.paddedInput }
|
||||||
disabled={ disabled }
|
disabled={ disabled }
|
||||||
label={ label }
|
|
||||||
hint={ hint ? `search for ${hint}` : 'search for an address' }
|
|
||||||
error={ error }
|
|
||||||
onChange={ this.onChange }
|
|
||||||
onBlur={ this.onBlur }
|
|
||||||
onUpdateInput={ allowInput && this.onUpdateInput }
|
|
||||||
value={ searchText }
|
|
||||||
filter={ this.handleFilter }
|
|
||||||
entries={ autocompleteEntries }
|
entries={ autocompleteEntries }
|
||||||
entry={ this.getEntry() || {} }
|
entry={ this.getEntry() || {} }
|
||||||
|
error={ error }
|
||||||
|
filter={ this.handleFilter }
|
||||||
|
hint={
|
||||||
|
<FormattedMessage
|
||||||
|
id='ui.addressSelect.search.hint'
|
||||||
|
defaultMessage='search for {hint}'
|
||||||
|
values={ {
|
||||||
|
hint: hint ||
|
||||||
|
<FormattedMessage
|
||||||
|
id='ui.addressSelect.search.address'
|
||||||
|
defaultMessage='address' />
|
||||||
|
} } />
|
||||||
|
}
|
||||||
|
label={ label }
|
||||||
|
onBlur={ this.onBlur }
|
||||||
|
onChange={ this.onChange }
|
||||||
|
onUpdateInput={ allowInput && this.onUpdateInput }
|
||||||
renderItem={ this.renderItem }
|
renderItem={ this.renderItem }
|
||||||
/>
|
value={ searchText } />
|
||||||
{ icon }
|
{ icon }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -148,9 +159,10 @@ export default class AddressSelect extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<IdentityIcon
|
<IdentityIcon
|
||||||
|
address={ value }
|
||||||
|
center
|
||||||
className={ classes.join(' ') }
|
className={ classes.join(' ') }
|
||||||
inline center
|
inline />
|
||||||
address={ value } />
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,9 +174,10 @@ export default class AddressSelect extends Component {
|
|||||||
|
|
||||||
if (!this.items[address] || this.items[address].balance !== balance) {
|
if (!this.items[address] || this.items[address].balance !== balance) {
|
||||||
this.items[address] = {
|
this.items[address] = {
|
||||||
|
address,
|
||||||
|
balance,
|
||||||
text: name && name.toUpperCase() || address,
|
text: name && name.toUpperCase() || address,
|
||||||
value: this.renderMenuItem(address),
|
value: this.renderMenuItem(address)
|
||||||
address, balance
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +202,7 @@ export default class AddressSelect extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderBalance (address) {
|
renderBalance (address) {
|
||||||
const balance = this.getBalance(address);
|
const balance = this.getBalance(address) || 0;
|
||||||
const value = fromWei(balance);
|
const value = fromWei(balance);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -207,12 +220,13 @@ export default class AddressSelect extends Component {
|
|||||||
const item = (
|
const item = (
|
||||||
<div className={ styles.account }>
|
<div className={ styles.account }>
|
||||||
<IdentityIcon
|
<IdentityIcon
|
||||||
|
address={ address }
|
||||||
|
center
|
||||||
className={ styles.image }
|
className={ styles.image }
|
||||||
inline center
|
inline />
|
||||||
address={ address } />
|
|
||||||
<IdentityName
|
<IdentityName
|
||||||
className={ styles.name }
|
address={ address }
|
||||||
address={ address } />
|
className={ styles.name } />
|
||||||
{ balance }
|
{ balance }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -221,8 +235,8 @@ export default class AddressSelect extends Component {
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
className={ styles.menuItem }
|
className={ styles.menuItem }
|
||||||
key={ address }
|
key={ address }
|
||||||
value={ address }
|
label={ item }
|
||||||
label={ item }>
|
value={ address }>
|
||||||
{ item }
|
{ item }
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
);
|
);
|
||||||
|
@ -14,12 +14,13 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
|
||||||
import keycode from 'keycode';
|
import keycode from 'keycode';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
import { MenuItem, AutoComplete as MUIAutoComplete, Divider as MUIDivider } from 'material-ui';
|
import { MenuItem, AutoComplete as MUIAutoComplete, Divider as MUIDivider } from 'material-ui';
|
||||||
import { PopoverAnimationVertical } from 'material-ui/Popover';
|
import { PopoverAnimationVertical } from 'material-ui/Popover';
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
import { isEqual } from 'lodash';
|
import { nodeOrStringProptype } from '~/util/proptypes';
|
||||||
|
|
||||||
import styles from './autocomplete.css';
|
import styles from './autocomplete.css';
|
||||||
|
|
||||||
@ -41,21 +42,21 @@ class Divider extends Component {
|
|||||||
|
|
||||||
export default class AutoComplete extends Component {
|
export default class AutoComplete extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
onUpdateInput: PropTypes.func,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
label: PropTypes.string,
|
|
||||||
hint: PropTypes.string,
|
|
||||||
error: PropTypes.string,
|
|
||||||
value: PropTypes.string,
|
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
filter: PropTypes.func,
|
disabled: PropTypes.bool,
|
||||||
renderItem: PropTypes.func,
|
|
||||||
entry: PropTypes.object,
|
entry: PropTypes.object,
|
||||||
entries: PropTypes.oneOfType([
|
entries: PropTypes.oneOfType([
|
||||||
PropTypes.array,
|
PropTypes.array,
|
||||||
PropTypes.object
|
PropTypes.object
|
||||||
])
|
]),
|
||||||
|
error: nodeOrStringProptype(),
|
||||||
|
filter: PropTypes.func,
|
||||||
|
hint: nodeOrStringProptype(),
|
||||||
|
label: nodeOrStringProptype(),
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
onUpdateInput: PropTypes.func,
|
||||||
|
renderItem: PropTypes.func,
|
||||||
|
value: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -122,10 +122,24 @@ class MethodDecoding extends Component {
|
|||||||
</span>
|
</span>
|
||||||
<span> for a total transaction value of </span>
|
<span> for a total transaction value of </span>
|
||||||
<span className={ styles.highlight }>{ this.renderEtherValue(gasValue) }</span>
|
<span className={ styles.highlight }>{ this.renderEtherValue(gasValue) }</span>
|
||||||
|
{ this.renderMinBlock() }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderMinBlock () {
|
||||||
|
const { historic, transaction } = this.props;
|
||||||
|
const { minBlock } = transaction;
|
||||||
|
|
||||||
|
if (!minBlock || minBlock.eq(0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>, { historic ? 'Submitted' : 'Submission' } at block <span className={ styles.highlight }>#{ minBlock.toFormat(0) }</span></span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderAction () {
|
renderAction () {
|
||||||
const { token } = this.props;
|
const { token } = this.props;
|
||||||
const { methodName, methodInputs, methodSignature, isDeploy, isReceived, isContract } = this.state;
|
const { methodName, methodInputs, methodSignature, isDeploy, isReceived, isContract } = this.state;
|
||||||
|
Loading…
Reference in New Issue
Block a user