Add block & timestamp conditions to Signer (#4411)
* WIP * WIP (with lint) * Update ui/RadioButtons * transaction.condition * Date & Time selection in-place * Swap to condition-only * Fix tests, align naming * Pick error properly from validation * Update tests * condition: time sent withough ms * Format numbers as base-10 * override popup styles (zIndex) * Pass condition to signer * Update expectation (failing test typo) * Adjust min/max height for expanded bar * Fix address display * Fix name display * Number inputs for gas/gasPrice/blockNumber * Default blockNumber to 1 (align with min setting) * Update tests with min value * Add Block Number * Fix failing tests (after blockNumber intro)
This commit is contained in:
@@ -15,45 +15,22 @@
|
||||
// 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 { 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
|
||||
gasStore: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
render () {
|
||||
const { gasStore, minBlock, minBlockError, onMinBlockChange } = this.props;
|
||||
const { gasStore } = 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 className={ styles.gaseditor }>
|
||||
<GasPriceEditor store={ gasStore } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
// 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 { pick } from 'lodash';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
@@ -100,8 +99,6 @@ class ExecuteContract extends Component {
|
||||
fromAddressError: null,
|
||||
func: null,
|
||||
funcError: null,
|
||||
minBlock: '0',
|
||||
minBlockError: null,
|
||||
rejected: false,
|
||||
sending: false,
|
||||
step: STEP_DETAILS,
|
||||
@@ -167,8 +164,8 @@ class ExecuteContract extends Component {
|
||||
|
||||
renderDialogActions () {
|
||||
const { onClose, fromAddress } = this.props;
|
||||
const { advancedOptions, sending, step, fromAddressError, minBlockError, valuesError } = this.state;
|
||||
const hasError = fromAddressError || minBlockError || valuesError.find((error) => error);
|
||||
const { advancedOptions, sending, step, fromAddressError, valuesError } = this.state;
|
||||
const hasError = fromAddressError || valuesError.find((error) => error);
|
||||
|
||||
const cancelBtn = (
|
||||
<Button
|
||||
@@ -258,7 +255,7 @@ class ExecuteContract extends Component {
|
||||
|
||||
renderStep () {
|
||||
const { onFromAddressChange } = this.props;
|
||||
const { advancedOptions, step, busyState, minBlock, minBlockError, txhash, rejected } = this.state;
|
||||
const { advancedOptions, step, busyState, txhash, rejected } = this.state;
|
||||
|
||||
if (rejected) {
|
||||
return (
|
||||
@@ -305,12 +302,7 @@ class ExecuteContract extends Component {
|
||||
);
|
||||
} else if (advancedOptions && (step === STEP_BUSY_OR_ADVANCED)) {
|
||||
return (
|
||||
<AdvancedStep
|
||||
gasStore={ this.gasStore }
|
||||
minBlock={ minBlock }
|
||||
minBlockError={ minBlockError }
|
||||
onMinBlockChange={ this.onMinBlockChange }
|
||||
/>
|
||||
<AdvancedStep gasStore={ this.gasStore } />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -339,15 +331,6 @@ class ExecuteContract extends Component {
|
||||
}, this.estimateGas);
|
||||
}
|
||||
|
||||
onMinBlockChange = (minBlock) => {
|
||||
const minBlockError = validateUint(minBlock).valueError;
|
||||
|
||||
this.setState({
|
||||
minBlock,
|
||||
minBlockError
|
||||
});
|
||||
}
|
||||
|
||||
onValueChange = (event, index, _value) => {
|
||||
const { func, values, valuesError } = this.state;
|
||||
const input = func.inputs.find((input, _index) => index === _index);
|
||||
@@ -409,17 +392,14 @@ class ExecuteContract extends Component {
|
||||
postTransaction = () => {
|
||||
const { api, store } = this.context;
|
||||
const { fromAddress } = this.props;
|
||||
const { advancedOptions, amount, func, minBlock, values } = this.state;
|
||||
const { advancedOptions, amount, func, values } = this.state;
|
||||
const steps = advancedOptions ? STAGES_ADVANCED : STAGES_BASIC;
|
||||
const finalstep = steps.length - 1;
|
||||
|
||||
const options = {
|
||||
gas: this.gasStore.gas,
|
||||
gasPrice: this.gasStore.price,
|
||||
const options = this.gasStore.overrideTransaction({
|
||||
from: fromAddress,
|
||||
minBlock: new BigNumber(minBlock || 0).gt(0) ? minBlock : null,
|
||||
value: api.util.toWei(amount || 0)
|
||||
};
|
||||
});
|
||||
|
||||
this.setState({ sending: true, step: advancedOptions ? STEP_BUSY : STEP_BUSY_OR_ADVANCED });
|
||||
|
||||
|
||||
@@ -27,36 +27,17 @@ export default class Extras extends Component {
|
||||
dataError: PropTypes.string,
|
||||
gasStore: PropTypes.object.isRequired,
|
||||
isEth: PropTypes.bool,
|
||||
minBlock: PropTypes.string,
|
||||
minBlockError: PropTypes.string,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
total: PropTypes.string,
|
||||
totalError: PropTypes.string
|
||||
}
|
||||
|
||||
render () {
|
||||
const { gasStore, minBlock, minBlockError, onChange } = this.props;
|
||||
const { gasStore, onChange } = this.props;
|
||||
|
||||
return (
|
||||
<Form>
|
||||
{ this.renderData() }
|
||||
<Input
|
||||
error={ minBlockError }
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='transferModal.minBlock.hint'
|
||||
defaultMessage='Only post the transaction after this block'
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='transferModal.minBlock.label'
|
||||
defaultMessage='BlockNumber to send from'
|
||||
/>
|
||||
}
|
||||
value={ minBlock }
|
||||
onChange={ this.onEditMinBlock }
|
||||
/>
|
||||
<div className={ styles.gaseditor }>
|
||||
<GasPriceEditor
|
||||
store={ gasStore }
|
||||
@@ -98,8 +79,4 @@ export default class Extras extends Component {
|
||||
onEditData = (event) => {
|
||||
this.props.onChange('data', event.target.value);
|
||||
}
|
||||
|
||||
onEditMinBlock = (event) => {
|
||||
this.props.onChange('minBlock', event.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,9 +52,6 @@ export default class TransferStore {
|
||||
@observable data = '';
|
||||
@observable dataError = null;
|
||||
|
||||
@observable minBlock = '0';
|
||||
@observable minBlockError = null;
|
||||
|
||||
@observable recipient = '';
|
||||
@observable recipientError = ERRORS.requireRecipient;
|
||||
|
||||
@@ -78,39 +75,6 @@ export default class TransferStore {
|
||||
|
||||
gasStore = null;
|
||||
|
||||
@computed get steps () {
|
||||
const steps = [].concat(this.extras ? STAGES_EXTRA : STAGES_BASIC);
|
||||
|
||||
if (this.rejected) {
|
||||
steps[steps.length - 1] = TITLES.rejected;
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
@computed get isValid () {
|
||||
const detailsValid = !this.recipientError && !this.valueError && !this.totalError && !this.senderError;
|
||||
const extrasValid = !this.gasStore.errorGas && !this.gasStore.errorPrice && !this.minBlockError && !this.totalError;
|
||||
const verifyValid = !this.passwordError;
|
||||
|
||||
switch (this.stage) {
|
||||
case 0:
|
||||
return detailsValid;
|
||||
|
||||
case 1:
|
||||
return this.extras
|
||||
? extrasValid
|
||||
: verifyValid;
|
||||
|
||||
case 2:
|
||||
return verifyValid;
|
||||
}
|
||||
}
|
||||
|
||||
get token () {
|
||||
return this.balance.tokens.find((balance) => balance.token.tag === this.tag).token;
|
||||
}
|
||||
|
||||
constructor (api, props) {
|
||||
this.api = api;
|
||||
|
||||
@@ -135,6 +99,39 @@ export default class TransferStore {
|
||||
}
|
||||
}
|
||||
|
||||
@computed get steps () {
|
||||
const steps = [].concat(this.extras ? STAGES_EXTRA : STAGES_BASIC);
|
||||
|
||||
if (this.rejected) {
|
||||
steps[steps.length - 1] = TITLES.rejected;
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
@computed get isValid () {
|
||||
const detailsValid = !this.recipientError && !this.valueError && !this.totalError && !this.senderError;
|
||||
const extrasValid = !this.gasStore.errorGas && !this.gasStore.errorPrice && !this.gasStore.conditionBlockError && !this.totalError;
|
||||
const verifyValid = !this.passwordError;
|
||||
|
||||
switch (this.stage) {
|
||||
case 0:
|
||||
return detailsValid;
|
||||
|
||||
case 1:
|
||||
return this.extras
|
||||
? extrasValid
|
||||
: verifyValid;
|
||||
|
||||
case 2:
|
||||
return verifyValid;
|
||||
}
|
||||
}
|
||||
|
||||
get token () {
|
||||
return this.balance.tokens.find((balance) => balance.token.tag === this.tag).token;
|
||||
}
|
||||
|
||||
@action onNext = () => {
|
||||
this.stage += 1;
|
||||
}
|
||||
@@ -164,9 +161,6 @@ export default class TransferStore {
|
||||
case 'gasPrice':
|
||||
return this._onUpdateGasPrice(value);
|
||||
|
||||
case 'minBlock':
|
||||
return this._onUpdateMinBlock(value);
|
||||
|
||||
case 'recipient':
|
||||
return this._onUpdateRecipient(value);
|
||||
|
||||
@@ -284,14 +278,6 @@ export default class TransferStore {
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
@action _onUpdateMinBlock = (minBlock) => {
|
||||
console.log('minBlock', minBlock);
|
||||
transaction(() => {
|
||||
this.minBlock = minBlock;
|
||||
this.minBlockError = this._validatePositiveNumber(minBlock);
|
||||
});
|
||||
}
|
||||
|
||||
@action _onUpdateGasPrice = (gasPrice) => {
|
||||
this.recalculate();
|
||||
}
|
||||
@@ -590,7 +576,6 @@ export default class TransferStore {
|
||||
send () {
|
||||
const { options, values } = this._getTransferParams();
|
||||
|
||||
options.minBlock = new BigNumber(this.minBlock || 0).gt(0) ? this.minBlock : null;
|
||||
log.debug('@send', 'transfer value', options.value && options.value.toFormat());
|
||||
|
||||
return this._getTransferMethod().postTransaction(options, values);
|
||||
@@ -639,15 +624,12 @@ export default class TransferStore {
|
||||
const to = (isEth && !isWallet) ? this.recipient
|
||||
: (this.isWallet ? this.wallet.address : this.token.address);
|
||||
|
||||
const options = {
|
||||
const options = this.gasStore.overrideTransaction({
|
||||
from: this.sender || this.account.address,
|
||||
to
|
||||
};
|
||||
});
|
||||
|
||||
if (!gas) {
|
||||
options.gas = this.gasStore.gas;
|
||||
options.gasPrice = this.gasStore.price;
|
||||
} else {
|
||||
if (gas) {
|
||||
options.gas = MAX_GAS_ESTIMATION;
|
||||
}
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ class Transfer extends Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { isEth, data, dataError, minBlock, minBlockError, total, totalError } = this.store;
|
||||
const { isEth, data, dataError, total, totalError } = this.store;
|
||||
|
||||
return (
|
||||
<Extras
|
||||
@@ -215,8 +215,6 @@ class Transfer extends Component {
|
||||
dataError={ dataError }
|
||||
gasStore={ this.store.gasStore }
|
||||
isEth={ isEth }
|
||||
minBlock={ minBlock }
|
||||
minBlockError={ minBlockError }
|
||||
onChange={ this.store.onUpdateDetails }
|
||||
total={ total }
|
||||
totalError={ totalError }
|
||||
|
||||
Reference in New Issue
Block a user