From e8597e2e9123f77ff204afdb99bcf25f22b3c91f Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Tue, 14 Feb 2017 13:08:38 +0100 Subject: [PATCH] Fix contract queries bug (#4534) * Fix contract queries and multiple Address Selector issues * Linting * Use standard new Error --- js/src/api/contract/contract.js | 33 +++++++++-- .../ui/Form/AddressSelect/addressSelect.css | 4 ++ js/src/ui/Form/AddressSelect/addressSelect.js | 21 ++++--- .../Form/AddressSelect/addressSelectStore.js | 4 ++ js/src/ui/Form/TypedInput/typedInput.js | 2 +- js/src/views/Contract/Queries/inputQuery.js | 58 +++++++++++++------ js/src/views/Contract/Queries/queries.js | 25 ++++++-- 7 files changed, 109 insertions(+), 38 deletions(-) diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index 3869bddca..570c36287 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -241,13 +241,32 @@ export default class Contract { _bindFunction = (func) => { func.contract = this; - func.call = (options, values = []) => { - const callParams = this._encodeOptions(func, this._addOptionsTo(options), values); + func.call = (_options = {}, values = []) => { + const rawTokens = !!_options.rawTokens; + const options = { + ..._options + }; + + delete options.rawTokens; + + let callParams; + + try { + callParams = this._encodeOptions(func, this._addOptionsTo(options), values); + } catch (error) { + return Promise.reject(error); + } return this._api.eth .call(callParams) .then((encoded) => func.decodeOutput(encoded)) - .then((tokens) => tokens.map((token) => token.value)) + .then((tokens) => { + if (rawTokens) { + return tokens; + } + + return tokens.map((token) => token.value); + }) .then((returns) => returns.length === 1 ? returns[0] : returns) .catch((error) => { console.warn(`${func.name}.call`, values, error); @@ -257,7 +276,13 @@ export default class Contract { if (!func.constant) { func.postTransaction = (options, values = []) => { - const _options = this._encodeOptions(func, this._addOptionsTo(options), values); + let _options; + + try { + _options = this._encodeOptions(func, this._addOptionsTo(options), values); + } catch (error) { + return Promise.reject(error); + } return this._api.parity .postTransaction(_options) diff --git a/js/src/ui/Form/AddressSelect/addressSelect.css b/js/src/ui/Form/AddressSelect/addressSelect.css index 43d6a7075..839c025b5 100644 --- a/js/src/ui/Form/AddressSelect/addressSelect.css +++ b/js/src/ui/Form/AddressSelect/addressSelect.css @@ -107,6 +107,10 @@ } } +.container { + margin-bottom: 1em; +} + .categories { display: flex; flex: 1; diff --git a/js/src/ui/Form/AddressSelect/addressSelect.js b/js/src/ui/Form/AddressSelect/addressSelect.js index d3c7462c3..2f47484b2 100644 --- a/js/src/ui/Form/AddressSelect/addressSelect.js +++ b/js/src/ui/Form/AddressSelect/addressSelect.js @@ -206,12 +206,11 @@ class AddressSelect extends Component { style={ BOTTOM_BORDER_STYLE } /> - - { this.renderCurrentInput() } - { this.renderRegistryValues() } } > + { this.renderCurrentInput() } + { this.renderRegistryValues() } { this.renderAccounts() } ); @@ -245,8 +244,8 @@ class AddressSelect extends Component { } return ( -
- { this.renderAccountCard({ address }) } +
+ { this.renderAccountCard({ address, index: 'currentInput_0' }) }
); } @@ -266,7 +265,7 @@ class AddressSelect extends Component { }); return ( -
+
{ accounts }
); @@ -361,7 +360,7 @@ class AddressSelect extends Component { validateCustomInput = () => { const { allowInput } = this.props; - const { inputValue } = this.store; + const { inputValue } = this.state; const { values } = this.store; // If input is HEX and allowInput === true, send it @@ -587,7 +586,13 @@ class AddressSelect extends Component { this.handleDOMAction('inputAddress', 'focus'); } - this.setState({ expanded: false }); + this.store.resetRegistryValues(); + + this.setState({ + expanded: false, + focusedItem: null, + inputValue: '' + }); } handleInputBlur = () => { diff --git a/js/src/ui/Form/AddressSelect/addressSelectStore.js b/js/src/ui/Form/AddressSelect/addressSelectStore.js index 1e2c6e964..7fc1db480 100644 --- a/js/src/ui/Form/AddressSelect/addressSelectStore.js +++ b/js/src/ui/Form/AddressSelect/addressSelectStore.js @@ -204,6 +204,10 @@ export default class AddressSelectStore { this.handleChange(); } + @action resetRegistryValues = () => { + this.registryValues = []; + } + @action handleChange = (value = '') => { let index = 0; diff --git a/js/src/ui/Form/TypedInput/typedInput.js b/js/src/ui/Form/TypedInput/typedInput.js index 79365faeb..16b3b0ca1 100644 --- a/js/src/ui/Form/TypedInput/typedInput.js +++ b/js/src/ui/Form/TypedInput/typedInput.js @@ -125,7 +125,7 @@ export default class TypedInput extends Component { return (
- { fixedLength ? null : this.renderLength() } + { fixedLength || readOnly ? null : this.renderLength() } { inputs }
); diff --git a/js/src/views/Contract/Queries/inputQuery.js b/js/src/views/Contract/Queries/inputQuery.js index 43bb467bd..1418aa6b7 100644 --- a/js/src/views/Contract/Queries/inputQuery.js +++ b/js/src/views/Contract/Queries/inputQuery.js @@ -14,17 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import BigNumber from 'bignumber.js'; import React, { Component, PropTypes } from 'react'; import LinearProgress from 'material-ui/LinearProgress'; import { Card, CardActions, CardTitle, CardText } from 'material-ui/Card'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { newError } from '~/redux/actions'; import { Button, TypedInput } from '~/ui'; import { arrayOrObjectProptype } from '~/util/proptypes'; import styles from './queries.css'; -export default class InputQuery extends Component { +class InputQuery extends Component { static contextTypes = { api: PropTypes.object }; @@ -35,6 +37,7 @@ export default class InputQuery extends Component { inputs: arrayOrObjectProptype().isRequired, outputs: arrayOrObjectProptype().isRequired, name: PropTypes.string.isRequired, + newError: PropTypes.func.isRequired, signature: PropTypes.string.isRequired, className: PropTypes.string }; @@ -65,7 +68,7 @@ export default class InputQuery extends Component { const { isValid } = this.state; const inputsFields = inputs - .map(input => this.renderInput(input)); + .map((input, index) => this.renderInput(input, index)); return (
@@ -119,7 +122,7 @@ export default class InputQuery extends Component { ); return ( -
+
{ out.name }
@@ -129,7 +132,7 @@ export default class InputQuery extends Component { }); } - renderInput (input) { + renderInput (input, index) { const { values } = this.state; const { name, type } = input; const label = `${name ? `${name}: ` : ''}${type}`; @@ -140,41 +143,42 @@ export default class InputQuery extends Component { this.setState({ values: { ...values, - [ name ]: value + [ index ]: value } }); }; return ( -
+
); } - renderValue (value) { + renderValue (token) { + const { api } = this.context; + const { type, value } = token; + if (value === null || value === undefined) { return 'no data'; } - const { api } = this.context; - - if (api.util.isInstanceOf(value, BigNumber)) { - return value.toFormat(0); + if (type === 'array' || type === 'fixedArray') { + return value.map((tok) => this.renderValue(tok)); } - if (api.util.isArray(value)) { + if (Array.isArray(value)) { return api.util.bytesToHex(value); } - return value.toString(); + return value; } onClick = () => { @@ -186,11 +190,11 @@ export default class InputQuery extends Component { results: [] }); - const inputValues = inputs.map(input => values[input.name]); + const inputValues = inputs.map((input, index) => values[index] || ''); contract .instance[signature] - .call({}, inputValues) + .call({ rawTokens: true }, inputValues) .then(results => { if (outputs.length === 1) { results = [ results ]; @@ -201,8 +205,24 @@ export default class InputQuery extends Component { results }); }) - .catch(e => { - console.error(`sending ${name} with params`, inputValues, e); + .catch((error) => { + console.error(`sending ${name} with params`, inputValues, error.message); + + this.props.newError(error); + this.setState({ + isLoading: false + }); }); }; } + +function mapDispatchToProps (dispatch) { + return bindActionCreators({ + newError + }, dispatch); +} + +export default connect( + null, + mapDispatchToProps +)(InputQuery); diff --git a/js/src/views/Contract/Queries/queries.js b/js/src/views/Contract/Queries/queries.js index 71b4b2443..dd84109fb 100644 --- a/js/src/views/Contract/Queries/queries.js +++ b/js/src/views/Contract/Queries/queries.js @@ -61,12 +61,25 @@ export default class Queries extends Component { return (
-
- { noInputQueries } -
-
- { withInputQueries } -
+ { + noInputQueries.length > 0 + ? ( +
+ { noInputQueries } +
+ ) + : null + } + + { + withInputQueries.length > 0 + ? ( +
+ { withInputQueries } +
+ ) + : null + }
);