diff --git a/js/src/api/format/input.js b/js/src/api/format/input.js
index 4cbcbdce4..4307fc912 100644
--- a/js/src/api/format/input.js
+++ b/js/src/api/format/input.js
@@ -137,6 +137,10 @@ export function inOptions (options) {
options[key] = inNumber16((new BigNumber(options[key])).round());
break;
+ case 'minBlock':
+ options[key] = options[key] ? inNumber16(options[key]) : null;
+ break;
+
case 'value':
case 'nonce':
options[key] = inNumber16(options[key]);
diff --git a/js/src/api/format/input.spec.js b/js/src/api/format/input.spec.js
index 27d200b93..699ac93c2 100644
--- a/js/src/api/format/input.spec.js
+++ b/js/src/api/format/input.spec.js
@@ -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`, () => {
const block = {};
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', () => {
expect(inOptions({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
});
diff --git a/js/src/api/format/output.js b/js/src/api/format/output.js
index e1887582a..73e2f7220 100644
--- a/js/src/api/format/output.js
+++ b/js/src/api/format/output.js
@@ -205,6 +205,10 @@ export function outTransaction (tx) {
tx[key] = outNumber(tx[key]);
break;
+ case 'minBlock':
+ tx[key] = tx[key] ? outNumber(tx[key]) : null;
+ break;
+
case 'creates':
case 'from':
case 'to':
diff --git a/js/src/api/format/output.spec.js b/js/src/api/format/output.spec.js
index ce50c69c1..3fa2f218d 100644
--- a/js/src/api/format/output.spec.js
+++ b/js/src/api/format/output.spec.js
@@ -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`, () => {
const block = {};
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', () => {
expect(outTransaction({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
});
diff --git a/js/src/modals/ExecuteContract/AdvancedStep/advancedStep.js b/js/src/modals/ExecuteContract/AdvancedStep/advancedStep.js
new file mode 100644
index 000000000..4142aa961
--- /dev/null
+++ b/js/src/modals/ExecuteContract/AdvancedStep/advancedStep.js
@@ -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 .
+
+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 (
+
+
+ }
+ label={
+
+ }
+ value={ minBlock }
+ onSubmit={ onMinBlockChange } />
+
+
+
+
+ );
+ }
+}
diff --git a/js/src/modals/ExecuteContract/AdvancedStep/index.js b/js/src/modals/ExecuteContract/AdvancedStep/index.js
new file mode 100644
index 000000000..3a4cc1028
--- /dev/null
+++ b/js/src/modals/ExecuteContract/AdvancedStep/index.js
@@ -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 .
+
+export default from './advancedStep';
diff --git a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js
index e2d18bf65..818f216f6 100644
--- a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js
+++ b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js
@@ -14,8 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-import React, { Component, PropTypes } from 'react';
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';
@@ -29,29 +30,28 @@ const CHECK_STYLE = {
export default class DetailsStep extends Component {
static propTypes = {
+ advancedOptions: PropTypes.bool,
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,
amountError: PropTypes.string,
balances: PropTypes.object,
+ contract: PropTypes.object.isRequired,
fromAddress: PropTypes.string,
fromAddressError: PropTypes.string,
func: PropTypes.object,
funcError: PropTypes.string,
- gasEdit: PropTypes.bool,
+ onAdvancedClick: PropTypes.func,
+ onAmountChange: PropTypes.func.isRequired,
+ onFromAddressChange: PropTypes.func.isRequired,
onFuncChange: PropTypes.func,
- onGasEditClick: PropTypes.func,
+ onValueChange: PropTypes.func.isRequired,
+ values: PropTypes.array.isRequired,
+ valuesError: PropTypes.array.isRequired,
warning: PropTypes.string
}
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 (
);
}
@@ -50,18 +71,28 @@ export default class Extras extends Component {
}
return (
-
-
-
+
+ }
+ label={
+
+ }
+ onChange={ this.onEditData }
+ value={ data } />
);
}
onEditData = (event) => {
this.props.onChange('data', event.target.value);
}
+
+ onEditMinBlock = (event) => {
+ this.props.onChange('minBlock', event.target.value);
+ }
}
diff --git a/js/src/modals/Transfer/store.js b/js/src/modals/Transfer/store.js
index 679e03609..c65a25fab 100644
--- a/js/src/modals/Transfer/store.js
+++ b/js/src/modals/Transfer/store.js
@@ -49,6 +49,9 @@ export default class TransferStore {
@observable data = '';
@observable dataError = null;
+ @observable minBlock = '0';
+ @observable minBlockError = null;
+
@observable recipient = '';
@observable recipientError = ERRORS.requireRecipient;
@@ -84,7 +87,7 @@ export default class TransferStore {
@computed get isValid () {
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;
switch (this.stage) {
@@ -92,7 +95,9 @@ export default class TransferStore {
return detailsValid;
case 1:
- return this.extras ? extrasValid : verifyValid;
+ return this.extras
+ ? extrasValid
+ : verifyValid;
case 2:
return verifyValid;
@@ -155,6 +160,9 @@ export default class TransferStore {
case 'gasPrice':
return this._onUpdateGasPrice(value);
+ case 'minBlock':
+ return this._onUpdateMinBlock(value);
+
case 'recipient':
return this._onUpdateRecipient(value);
@@ -254,6 +262,14 @@ 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();
}
@@ -412,6 +428,9 @@ export default class TransferStore {
send () {
const { options, values } = this._getTransferParams();
+
+ options.minBlock = new BigNumber(this.minBlock || 0).gt(0) ? this.minBlock : null;
+
return this._getTransferMethod().postTransaction(options, values);
}
diff --git a/js/src/modals/Transfer/transfer.css b/js/src/modals/Transfer/transfer.css
index 6e3ae9aa2..4c6b2df82 100644
--- a/js/src/modals/Transfer/transfer.css
+++ b/js/src/modals/Transfer/transfer.css
@@ -15,65 +15,68 @@
/* along with Parity. If not, see .
*/
+.columns {
+ display: flex;
+ position: relative;
+ flex-wrap: wrap;
+
+ &>div {
+ flex: 0 1 50%;
+ position: relative;
+ width: 50%;
+ }
+}
+
+.gaseditor {
+ margin-top: 1em;
+}
+
.info {
line-height: 1.618em;
width: 100%;
}
-.columns {
- display: flex;
- flex-wrap: wrap;
- position: relative;
-}
-
.row {
display: flex;
- flex-wrap: wrap;
- position: relative;
flex-direction: column;
-}
-
-.columns>div {
- flex: 0 1 50%;
- width: 50%;
+ flex-wrap: wrap;
position: relative;
}
.floatbutton {
- text-align: right;
float: right;
margin-left: -100%;
margin-top: 28px;
-}
+ text-align: right;
-.floatbutton>div {
- margin-right: 0.5em;
-}
-
-.tokenSelect {
+ &>div {
+ margin-right: 0.5em;
+ }
}
.token {
height: 32px;
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 {
- 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;
+.tokenSelect {
+ .token {
+ margin-top: 10px;
+ }
}
.tokenbalance {
diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js
index 19c337e5a..bbeb3fadb 100644
--- a/js/src/modals/Transfer/transfer.js
+++ b/js/src/modals/Transfer/transfer.js
@@ -20,13 +20,9 @@ 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 { newError } from '~/ui/Errors/actions';
+import { CancelIcon, DoneIcon, NextIcon, PrevIcon } from '~/ui/Icons';
import { nullableProptype } from '~/util/proptypes';
import Details from './Details';
@@ -188,17 +184,19 @@ class Transfer extends Component {
return null;
}
- const { isEth, data, dataError, total, totalError } = this.store;
+ const { isEth, data, dataError, minBlock, minBlockError, total, totalError } = this.store;
return (
+ isEth={ isEth }
+ minBlock={ minBlock }
+ minBlockError={ minBlockError }
+ onChange={ this.store.onUpdateDetails }
+ total={ total }
+ totalError={ totalError } />
);
}
@@ -208,20 +206,20 @@ class Transfer extends Component {
const cancelBtn = (
}
+ icon={ }
label='Cancel'
onClick={ this.handleClose } />
);
const nextBtn = (
}
+ icon={ }
label='Next'
onClick={ this.store.onNext } />
);
const prevBtn = (
}
+ icon={ }
label='Back'
onClick={ this.store.onPrev } />
);
@@ -234,7 +232,7 @@ class Transfer extends Component {
);
const doneBtn = (
}
+ icon={ }
label='Close'
onClick={ this.handleClose } />
);
diff --git a/js/src/ui/Form/AddressSelect/addressSelect.js b/js/src/ui/Form/AddressSelect/addressSelect.js
index c443ecaa2..550783669 100644
--- a/js/src/ui/Form/AddressSelect/addressSelect.js
+++ b/js/src/ui/Form/AddressSelect/addressSelect.js
@@ -14,16 +14,18 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-import React, { Component, PropTypes } from 'react';
-import { MenuItem } from 'material-ui';
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 IdentityIcon from '../../IdentityIcon';
import IdentityName from '../../IdentityName';
-import { fromWei } from '~/api/util/wei';
-
import styles from './addressSelect.css';
export default class AddressSelect extends Component {
@@ -40,9 +42,9 @@ export default class AddressSelect extends Component {
contacts: PropTypes.object,
contracts: PropTypes.object,
disabled: PropTypes.bool,
- error: PropTypes.string,
- hint: PropTypes.string,
- label: PropTypes.string,
+ error: nodeOrStringProptype(),
+ hint: nodeOrStringProptype(),
+ label: nodeOrStringProptype(),
tokens: PropTypes.object,
value: PropTypes.string,
wallets: PropTypes.object
@@ -116,18 +118,27 @@ export default class AddressSelect extends Component {
+ } } />
+ }
+ label={ label }
+ onBlur={ this.onBlur }
+ onChange={ this.onChange }
+ onUpdateInput={ allowInput && this.onUpdateInput }
renderItem={ this.renderItem }
- />
+ value={ searchText } />
{ icon }
);
@@ -148,9 +159,10 @@ export default class AddressSelect extends Component {
return (
+ inline />
);
}
@@ -162,9 +174,10 @@ export default class AddressSelect extends Component {
if (!this.items[address] || this.items[address].balance !== balance) {
this.items[address] = {
+ address,
+ balance,
text: name && name.toUpperCase() || address,
- value: this.renderMenuItem(address),
- address, balance
+ value: this.renderMenuItem(address)
};
}
@@ -189,7 +202,7 @@ export default class AddressSelect extends Component {
}
renderBalance (address) {
- const balance = this.getBalance(address);
+ const balance = this.getBalance(address) || 0;
const value = fromWei(balance);
return (
@@ -207,12 +220,13 @@ export default class AddressSelect extends Component {
const item = (
+ inline />
+ address={ address }
+ className={ styles.name } />
{ balance }
);
@@ -221,8 +235,8 @@ export default class AddressSelect extends Component {
+ label={ item }
+ value={ address }>
{ item }
);
diff --git a/js/src/ui/Form/AutoComplete/autocomplete.js b/js/src/ui/Form/AutoComplete/autocomplete.js
index 3ebd59772..aaab0b55c 100644
--- a/js/src/ui/Form/AutoComplete/autocomplete.js
+++ b/js/src/ui/Form/AutoComplete/autocomplete.js
@@ -14,12 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-import React, { Component, PropTypes } from 'react';
import keycode from 'keycode';
+import { isEqual } from 'lodash';
import { MenuItem, AutoComplete as MUIAutoComplete, Divider as MUIDivider } from 'material-ui';
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';
@@ -41,21 +42,21 @@ class Divider extends Component {
export default class AutoComplete extends Component {
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,
- filter: PropTypes.func,
- renderItem: PropTypes.func,
+ disabled: PropTypes.bool,
entry: PropTypes.object,
entries: PropTypes.oneOfType([
PropTypes.array,
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 = {
diff --git a/js/src/ui/MethodDecoding/methodDecoding.js b/js/src/ui/MethodDecoding/methodDecoding.js
index fcf7f7513..ad676da0a 100644
--- a/js/src/ui/MethodDecoding/methodDecoding.js
+++ b/js/src/ui/MethodDecoding/methodDecoding.js
@@ -122,10 +122,24 @@ class MethodDecoding extends Component {
for a total transaction value of
{ this.renderEtherValue(gasValue) }
+ { this.renderMinBlock() }
);
}
+ renderMinBlock () {
+ const { historic, transaction } = this.props;
+ const { minBlock } = transaction;
+
+ if (!minBlock || minBlock.eq(0)) {
+ return null;
+ }
+
+ return (
+ , { historic ? 'Submitted' : 'Submission' } at block #{ minBlock.toFormat(0) }
+ );
+ }
+
renderAction () {
const { token } = this.props;
const { methodName, methodInputs, methodSignature, isDeploy, isReceived, isContract } = this.state;