From cd143e475a615ba5591a2d35df986625976928a0 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Thu, 10 Nov 2016 20:35:47 +0100 Subject: [PATCH 01/10] WIP refacto typed input --- .../DeployContract/DetailsStep/detailsStep.js | 278 ++++++++++++++---- 1 file changed, 228 insertions(+), 50 deletions(-) diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index e0f02bc70..568d1bff1 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -16,12 +16,124 @@ import React, { Component, PropTypes } from 'react'; import { MenuItem } from 'material-ui'; +import { range } from 'lodash'; import { AddressSelect, Form, Input, InputAddressSelect, Select } from '../../../ui'; import { validateAbi } from '../../../util/validation'; import styles from '../deployContract.css'; +class TypedInput extends Component { + + static propTypes = { + onChange: PropTypes.func.isRequired, + accounts: PropTypes.object.isRequired, + type: PropTypes.string.isRequired, + + error: PropTypes.any, + value: PropTypes.any, + label: PropTypes.string + }; + + render () { + const { type } = this.props; + + const arrayRegex = /^(.+)\[(\d*)\]$/; + + if (arrayRegex.test(type)) { + const matches = arrayRegex.exec(type); + return this.renderArray(matches[1], matches[2] || Infinity); + } + + switch (type) { + case 'address': + return this.renderAddress(); + + case 'bool': + return this.renderBoolean(); + + default: + return this.renderDefault(); + } + } + + renderArray (type, max) { + const { label, value, error } = this.props; + + return ( + < + ); + } + + renderDefault () { + const { label, value, error } = this.props; + + return ( + + ); + } + + renderAddress () { + const { accounts, label, value, error } = this.props; + + return ( + + ); + } + + renderBoolean () { + const { label, value, error } = this.props; + + const boolitems = ['false', 'true'].map((bool) => { + return ( + + { bool } + + ); + }); + + return ( + + ); + } + + onChangeBool = (event, _index, value) => { + this.props.onChange(value === 'true'); + } + + onChange = (event, value) => { + this.props.onChange(value); + } + + onSubmit = (value) => { + this.props.onChange(value); + } + +} + export default class DetailsStep extends Component { static contextTypes = { api: PropTypes.object.isRequired @@ -98,59 +210,22 @@ export default class DetailsStep extends Component { } return inputs.map((input, index) => { - const onChange = (event, value) => this.onParamChange(index, value); - const onChangeBool = (event, _index, value) => this.onParamChange(index, value === 'true'); - const onSubmit = (value) => this.onParamChange(index, value); - const label = `${input.name}: ${input.type}`; - let inputBox = null; + const onChange = (value) => this.onParamChange(index, value); - switch (input.type) { - case 'address': - inputBox = ( - - ); - break; - - case 'bool': - const boolitems = ['false', 'true'].map((bool) => { - return ( - { bool } - ); - }); - inputBox = ( - - ); - break; - - default: - inputBox = ( - - ); - break; - } + const label = `${input.name ? `${input.name}: ` : ''}${input.type}`; + const value = params[index]; + const error = paramsError[index]; return (
- { inputBox } +
); }); @@ -181,7 +256,9 @@ export default class DetailsStep extends Component { const { abiError, abiParsed } = validateAbi(abi, api); if (!abiError) { - const { inputs } = abiParsed.find((method) => method.type === 'constructor') || { inputs: [] }; + const { inputs } = abiParsed + .find((method) => method.type === 'constructor') || { inputs: [] }; + const params = []; inputs.forEach((input) => { @@ -228,3 +305,104 @@ export default class DetailsStep extends Component { onCodeChange(code); } } + +const ARRAY_TYPE = 'ARRAY_TYPE'; +const ADDRESS_TYPE = 'ADDRESS_TYPE'; +const STRING_TYPE = 'STRING_TYPE'; +const BOOL_TYPE = 'BOOL_TYPE'; +const BYTES_TYPE = 'BYTES_TYPE'; +const UINT_TYPE = 'UINT_TYPE'; +const INT_TYPE = 'INT_TYPE'; + +function parseAbiType (type) { + if (type === 'string') { + return { + type: STRING_TYPE, + default: '' + }; + } + + if (type === 'bool') { + return { + type: BOOL_TYPE, + default: false + }; + } + + if (type === 'address') { + return { + type: ADDRESS_TYPE, + default: '0x' + }; + } + + const arrayRegex = /^(.+)\[(\d*)\]$/; + + if (arrayRegex.test(type)) { + const matches = arrayRegex.exec(type); + + const subtype = parseAbiType(matches[1]); + const M = parseInt(matches[2]) || Infinity; + const defaultValue = M === Infinity + ? [] + : range(M).map(() => subtype.default); + + return { + type: ARRAY_TYPE, + subtype: subtype, + length: M, + default: defaultValue + }; + } + + const lengthRegex = /^([a-z]+)(\d{1,3})$/; + + if (lengthRegex.test(type)) { + const subtype = parseAbiType(matches[1]); + const length = parseInt(matches[2]) || Infinity; + + return { + ...subtype, + length + }; + } + + if (type === 'bytes') { + return { + type: BYTES_TYPE, + default: '0x' + }; + } + + if (type === 'uint') { + return { + type: UINT_TYPE, + default: 0, + length: 256 + }; + } + + if (type === 'int') { + return { + type: INT_TYPE, + default: 0, + length: 256 + }; + } + + if (type === 'ufixed') { + return { + type: UFIXED_TYPE, + default: 0, + length: 256 + }; + } + + if (type === 'fixed') { + return { + type: FIXED_TYPE, + default: 0, + length: 256 + }; + } +} From b59df1d7b83523094c498dd09141291c942f8c4f Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 11 Nov 2016 12:37:01 +0100 Subject: [PATCH 02/10] WIP // Real ABI params in Deploy Constructor #3314 --- .../DeployContract/DetailsStep/detailsStep.js | 217 +++++++++++------- .../modals/DeployContract/deployContract.css | 14 ++ js/src/ui/Form/Input/input.js | 8 +- 3 files changed, 154 insertions(+), 85 deletions(-) diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 568d1bff1..51d2318c8 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -28,7 +28,7 @@ class TypedInput extends Component { static propTypes = { onChange: PropTypes.func.isRequired, accounts: PropTypes.object.isRequired, - type: PropTypes.string.isRequired, + param: PropTypes.object.isRequired, error: PropTypes.any, value: PropTypes.any, @@ -36,32 +36,84 @@ class TypedInput extends Component { }; render () { - const { type } = this.props; + const { param } = this.props; + const { type } = param; - const arrayRegex = /^(.+)\[(\d*)\]$/; + if (type === ARRAY_TYPE) { + const { accounts, label, value = param.default } = this.props; + const { subtype, length } = param; - if (arrayRegex.test(type)) { - const matches = arrayRegex.exec(type); - return this.renderArray(matches[1], matches[2] || Infinity); + if (length) { + const inputs = range(length).map((_, index) => { + const onChange = (inputValue) => { + const newValues = [].concat(this.props.value); + newValues[index] = inputValue; + this.props.onChange(newValues); + }; + + return ( + + ); + }); + + return ( +
+ + { inputs } +
+ ); + } } - switch (type) { - case 'address': - return this.renderAddress(); - - case 'bool': - return this.renderBoolean(); - - default: - return this.renderDefault(); - } + return this.renderType(type); } - renderArray (type, max) { - const { label, value, error } = this.props; + renderType (type) { + if (type === ADDRESS_TYPE) { + return this.renderAddress(); + } + + if (type === BOOL_TYPE) { + return this.renderBoolean(); + } + + if (type === STRING_TYPE) { + return this.renderDefault(); + } + + if (type === BYTES_TYPE) { + return this.renderDefault(); + } + + if (type === INT_TYPE) { + return this.renderNumber(); + } + + if (type === FIXED_TYPE) { + return this.renderNumber(); + } + + return this.renderDefault(); + } + + renderNumber () { + const { label, value, error, param } = this.props; return ( - < + ); } @@ -215,6 +267,7 @@ export default class DetailsStep extends Component { const label = `${input.name ? `${input.name}: ` : ''}${input.type}`; const value = params[index]; const error = paramsError[index]; + const param = parseAbiType(input.type) return (
@@ -224,7 +277,7 @@ export default class DetailsStep extends Component { error={ error } accounts={ accounts } onChange={ onChange } - type={ input.type } + param={ param } />
); @@ -262,31 +315,8 @@ export default class DetailsStep extends Component { const params = []; inputs.forEach((input) => { - switch (input.type) { - case 'address': - params.push('0x'); - break; - - case 'bool': - params.push(false); - break; - - case 'bytes': - params.push('0x'); - break; - - case 'uint': - params.push('0'); - break; - - case 'string': - params.push(''); - break; - - default: - params.push('0'); - break; - } + const param = parseAbiType(input.type); + params.push(param.default); }); onParamsChange(params); @@ -311,10 +341,58 @@ const ADDRESS_TYPE = 'ADDRESS_TYPE'; const STRING_TYPE = 'STRING_TYPE'; const BOOL_TYPE = 'BOOL_TYPE'; const BYTES_TYPE = 'BYTES_TYPE'; -const UINT_TYPE = 'UINT_TYPE'; const INT_TYPE = 'INT_TYPE'; +const FIXED_TYPE = 'FIXED_TYPE'; function parseAbiType (type) { + const arrayRegex = /^(.+)\[(\d*)\]$/; + + if (arrayRegex.test(type)) { + const matches = arrayRegex.exec(type); + + const subtype = parseAbiType(matches[1]); + const M = parseInt(matches[2]) || null; + const defaultValue = !M + ? [] + : range(M).map(() => subtype.default); + + return { + type: ARRAY_TYPE, + subtype: subtype, + length: M, + default: defaultValue + }; + } + + const lengthRegex = /^(u?int|bytes)(\d{1,3})$/; + + if (lengthRegex.test(type)) { + const matches = lengthRegex.exec(type); + + const subtype = parseAbiType(matches[1]); + const length = parseInt(matches[2]); + + return { + ...subtype, + length + }; + } + + const fixedLengthRegex = /^(u?fixed)(\d{1,3})x(\d{1,3})$/; + + if (fixedLengthRegex.test(type)) { + const matches = fixedLengthRegex.exec(type); + + const subtype = parseAbiType(matches[1]); + const M = parseInt(matches[2]); + const N = parseInt(matches[3]); + + return { + ...subtype, + M, N + }; + } + if (type === 'string') { return { type: STRING_TYPE, @@ -336,37 +414,6 @@ function parseAbiType (type) { }; } - const arrayRegex = /^(.+)\[(\d*)\]$/; - - if (arrayRegex.test(type)) { - const matches = arrayRegex.exec(type); - - const subtype = parseAbiType(matches[1]); - const M = parseInt(matches[2]) || Infinity; - const defaultValue = M === Infinity - ? [] - : range(M).map(() => subtype.default); - - return { - type: ARRAY_TYPE, - subtype: subtype, - length: M, - default: defaultValue - }; - } - - const lengthRegex = /^([a-z]+)(\d{1,3})$/; - - if (lengthRegex.test(type)) { - const subtype = parseAbiType(matches[1]); - const length = parseInt(matches[2]) || Infinity; - - return { - ...subtype, - length - }; - } - if (type === 'bytes') { return { type: BYTES_TYPE, @@ -376,9 +423,10 @@ function parseAbiType (type) { if (type === 'uint') { return { - type: UINT_TYPE, + type: INT_TYPE, default: 0, - length: 256 + length: 256, + signed: false }; } @@ -386,15 +434,17 @@ function parseAbiType (type) { return { type: INT_TYPE, default: 0, - length: 256 + length: 256, + signed: true }; } if (type === 'ufixed') { return { - type: UFIXED_TYPE, + type: FIXED_TYPE, default: 0, - length: 256 + length: 256, + signed: false }; } @@ -402,7 +452,8 @@ function parseAbiType (type) { return { type: FIXED_TYPE, default: 0, - length: 256 + length: 256, + signed: true }; } } diff --git a/js/src/modals/DeployContract/deployContract.css b/js/src/modals/DeployContract/deployContract.css index 45fd7a852..b8ce35b8a 100644 --- a/js/src/modals/DeployContract/deployContract.css +++ b/js/src/modals/DeployContract/deployContract.css @@ -31,3 +31,17 @@ .funcparams { padding-left: 3em; } + +.inputs { + padding-top: 18px; + + label { + line-height: 22px; + pointer-events: none; + color: rgba(255, 255, 255, 0.498039); + -webkit-user-select: none; + font-size: 12px; + top: 8px; + position: relative; + } +} diff --git a/js/src/ui/Form/Input/input.js b/js/src/ui/Form/Input/input.js index 24a0a8089..bc7c60f1e 100644 --- a/js/src/ui/Form/Input/input.js +++ b/js/src/ui/Form/Input/input.js @@ -65,7 +65,9 @@ export default class Input extends Component { hideUnderline: PropTypes.bool, value: PropTypes.oneOfType([ PropTypes.number, PropTypes.string - ]) + ]), + min: PropTypes.any, + max: PropTypes.any }; static defaultProps = { @@ -98,7 +100,7 @@ export default class Input extends Component { render () { const { value } = this.state; - const { children, className, hideUnderline, disabled, error, label, hint, multiLine, rows, type } = this.props; + const { children, className, hideUnderline, disabled, error, label, hint, multiLine, rows, type, min, max } = this.props; const readOnly = this.props.readOnly || disabled; @@ -142,6 +144,8 @@ export default class Input extends Component { onChange={ this.onChange } onKeyDown={ this.onKeyDown } inputStyle={ inputStyle } + min={ min } + max={ max } > { children } From deaf863c9ef710de639c7810c8b1acb8cedffb1f Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 11 Nov 2016 12:41:44 +0100 Subject: [PATCH 03/10] Fixing linting --- js/src/modals/DeployContract/DetailsStep/detailsStep.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 51d2318c8..c04a505bf 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -267,7 +267,7 @@ export default class DetailsStep extends Component { const label = `${input.name ? `${input.name}: ` : ''}${input.type}`; const value = params[index]; const error = paramsError[index]; - const param = parseAbiType(input.type) + const param = parseAbiType(input.type); return (
From 046d2f23337606272b8336b570e1d06437256d67 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Fri, 11 Nov 2016 13:09:16 +0100 Subject: [PATCH 04/10] Scrollable --- js/src/modals/DeployContract/deployContract.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index 588d16f6a..3bcea7c29 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -67,7 +67,8 @@ export default class DeployContract extends Component { steps={ deployError ? null : steps } title={ deployError ? 'deployment failed' : null } waiting={ [1] } - visible> + visible + scroll> { this.renderStep() } ); From 9e2214c513f24e4064321375a1d01c9b1dc766c1 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Mon, 14 Nov 2016 17:00:36 +0100 Subject: [PATCH 05/10] Working add/remove fields for var. length #3314 --- .../DeployContract/DetailsStep/detailsStep.js | 90 ++++++++++++++----- .../modals/DeployContract/deployContract.css | 4 +- .../ui/Form/AddressSelect/addressSelect.css | 4 + js/src/ui/Form/AddressSelect/addressSelect.js | 10 ++- 4 files changed, 82 insertions(+), 26 deletions(-) diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index c04a505bf..67c5586f0 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -18,6 +18,10 @@ import React, { Component, PropTypes } from 'react'; import { MenuItem } from 'material-ui'; import { range } from 'lodash'; +import IconButton from 'material-ui/IconButton'; +import AddIcon from 'material-ui/svg-icons/content/add'; +import RemoveIcon from 'material-ui/svg-icons/content/remove'; + import { AddressSelect, Form, Input, InputAddressSelect, Select } from '../../../ui'; import { validateAbi } from '../../../util/validation'; @@ -43,37 +47,65 @@ class TypedInput extends Component { const { accounts, label, value = param.default } = this.props; const { subtype, length } = param; - if (length) { - const inputs = range(length).map((_, index) => { - const onChange = (inputValue) => { - const newValues = [].concat(this.props.value); - newValues[index] = inputValue; - this.props.onChange(newValues); - }; + const fixedLength = !!length; - return ( - - ); - }); + const inputs = range(length || value.length).map((_, index) => { + const onChange = (inputValue) => { + const newValues = [].concat(this.props.value); + newValues[index] = inputValue; + this.props.onChange(newValues); + }; return ( -
- - { inputs } -
+ ); - } + }); + + return ( +
+ + { fixedLength ? null : this.renderLength() } + { inputs } +
+ ); } return this.renderType(type); } + renderLength () { + const style = { + width: 16, + height: 16 + }; + + return ( +
+ + + + + + + +
+ ); + } + renderType (type) { if (type === ADDRESS_TYPE) { return this.renderAddress(); @@ -184,6 +216,20 @@ class TypedInput extends Component { this.props.onChange(value); } + onAddField = () => { + const { value, onChange, param } = this.props; + const newValues = [].concat(value, param.subtype.default); + + onChange(newValues); + } + + onRemoveField = () => { + const { value, onChange } = this.props; + const newValues = value.slice(0, -1); + + onChange(newValues); + } + } export default class DetailsStep extends Component { diff --git a/js/src/modals/DeployContract/deployContract.css b/js/src/modals/DeployContract/deployContract.css index b8ce35b8a..ef92b24af 100644 --- a/js/src/modals/DeployContract/deployContract.css +++ b/js/src/modals/DeployContract/deployContract.css @@ -33,7 +33,7 @@ } .inputs { - padding-top: 18px; + padding-top: 2px; label { line-height: 22px; @@ -41,7 +41,7 @@ color: rgba(255, 255, 255, 0.498039); -webkit-user-select: none; font-size: 12px; - top: 8px; + top: 11px; position: relative; } } diff --git a/js/src/ui/Form/AddressSelect/addressSelect.css b/js/src/ui/Form/AddressSelect/addressSelect.css index 98dab4355..30671db73 100644 --- a/js/src/ui/Form/AddressSelect/addressSelect.css +++ b/js/src/ui/Form/AddressSelect/addressSelect.css @@ -39,6 +39,10 @@ position: absolute; left: 0; top: 35px; + + &.noLabel { + top: 11px; + } } .paddedInput input { diff --git a/js/src/ui/Form/AddressSelect/addressSelect.js b/js/src/ui/Form/AddressSelect/addressSelect.js index ac7f2da51..97195efe7 100644 --- a/js/src/ui/Form/AddressSelect/addressSelect.js +++ b/js/src/ui/Form/AddressSelect/addressSelect.js @@ -106,15 +106,21 @@ export default class AddressSelect extends Component { } renderIdentityIcon (inputValue) { - const { error, value } = this.props; + const { error, value, label } = this.props; if (error || !inputValue || value.length !== 42) { return null; } + const classes = [ styles.icon ]; + + if (!label) { + classes.push(styles.noLabel); + } + return ( ); From 29442673f8bae829df4cf93fc84878811af227a4 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Mon, 14 Nov 2016 17:39:45 +0100 Subject: [PATCH 06/10] Clean-Up #3314 --- .../DeployContract/DetailsStep/detailsStep.js | 337 +----------------- .../modals/DeployContract/deployContract.css | 14 - .../modals/DeployContract/deployContract.js | 2 - js/src/ui/Form/TypedInput/index.js | 17 + js/src/ui/Form/TypedInput/typedInput.css | 31 ++ js/src/ui/Form/TypedInput/typedInput.js | 239 +++++++++++++ js/src/ui/Form/index.js | 2 + js/src/ui/index.js | 3 +- js/src/util/abi.js | 146 ++++++++ 9 files changed, 440 insertions(+), 351 deletions(-) create mode 100644 js/src/ui/Form/TypedInput/index.js create mode 100644 js/src/ui/Form/TypedInput/typedInput.css create mode 100644 js/src/ui/Form/TypedInput/typedInput.js create mode 100644 js/src/util/abi.js diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 1773fd0da..6e23f79c9 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -15,223 +15,13 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; -import { MenuItem } from 'material-ui'; -import { range } from 'lodash'; -import IconButton from 'material-ui/IconButton'; -import AddIcon from 'material-ui/svg-icons/content/add'; -import RemoveIcon from 'material-ui/svg-icons/content/remove'; - -import { AddressSelect, Form, Input, InputAddressSelect, Select } from '../../../ui'; +import { AddressSelect, Form, Input, TypedInput } from '../../../ui'; import { validateAbi } from '../../../util/validation'; +import { parseAbiType } from '../../../util/abi'; import styles from '../deployContract.css'; -class TypedInput extends Component { - - static propTypes = { - onChange: PropTypes.func.isRequired, - accounts: PropTypes.object.isRequired, - param: PropTypes.object.isRequired, - - error: PropTypes.any, - value: PropTypes.any, - label: PropTypes.string - }; - - render () { - const { param } = this.props; - const { type } = param; - - if (type === ARRAY_TYPE) { - const { accounts, label, value = param.default } = this.props; - const { subtype, length } = param; - - const fixedLength = !!length; - - const inputs = range(length || value.length).map((_, index) => { - const onChange = (inputValue) => { - const newValues = [].concat(this.props.value); - newValues[index] = inputValue; - this.props.onChange(newValues); - }; - - return ( - - ); - }); - - return ( -
- - { fixedLength ? null : this.renderLength() } - { inputs } -
- ); - } - - return this.renderType(type); - } - - renderLength () { - const style = { - width: 16, - height: 16 - }; - - return ( -
- - - - - - - -
- ); - } - - renderType (type) { - if (type === ADDRESS_TYPE) { - return this.renderAddress(); - } - - if (type === BOOL_TYPE) { - return this.renderBoolean(); - } - - if (type === STRING_TYPE) { - return this.renderDefault(); - } - - if (type === BYTES_TYPE) { - return this.renderDefault(); - } - - if (type === INT_TYPE) { - return this.renderNumber(); - } - - if (type === FIXED_TYPE) { - return this.renderNumber(); - } - - return this.renderDefault(); - } - - renderNumber () { - const { label, value, error, param } = this.props; - - return ( - - ); - } - - renderDefault () { - const { label, value, error } = this.props; - - return ( - - ); - } - - renderAddress () { - const { accounts, label, value, error } = this.props; - - return ( - - ); - } - - renderBoolean () { - const { label, value, error } = this.props; - - const boolitems = ['false', 'true'].map((bool) => { - return ( - - { bool } - - ); - }); - - return ( - - ); - } - - onChangeBool = (event, _index, value) => { - this.props.onChange(value === 'true'); - } - - onChange = (event, value) => { - this.props.onChange(value); - } - - onSubmit = (value) => { - this.props.onChange(value); - } - - onAddField = () => { - const { value, onChange, param } = this.props; - const newValues = [].concat(value, param.subtype.default); - - onChange(newValues); - } - - onRemoveField = () => { - const { value, onChange } = this.props; - const newValues = value.slice(0, -1); - - onChange(newValues); - } - -} - export default class DetailsStep extends Component { static contextTypes = { api: PropTypes.object.isRequired @@ -313,6 +103,7 @@ export default class DetailsStep extends Component { value={ code } onSubmit={ this.onCodeChange } readOnly={ readOnly } /> + { this.renderConstructorInputs() } ); @@ -400,125 +191,3 @@ export default class DetailsStep extends Component { onCodeChange(code); } } - -const ARRAY_TYPE = 'ARRAY_TYPE'; -const ADDRESS_TYPE = 'ADDRESS_TYPE'; -const STRING_TYPE = 'STRING_TYPE'; -const BOOL_TYPE = 'BOOL_TYPE'; -const BYTES_TYPE = 'BYTES_TYPE'; -const INT_TYPE = 'INT_TYPE'; -const FIXED_TYPE = 'FIXED_TYPE'; - -function parseAbiType (type) { - const arrayRegex = /^(.+)\[(\d*)\]$/; - - if (arrayRegex.test(type)) { - const matches = arrayRegex.exec(type); - - const subtype = parseAbiType(matches[1]); - const M = parseInt(matches[2]) || null; - const defaultValue = !M - ? [] - : range(M).map(() => subtype.default); - - return { - type: ARRAY_TYPE, - subtype: subtype, - length: M, - default: defaultValue - }; - } - - const lengthRegex = /^(u?int|bytes)(\d{1,3})$/; - - if (lengthRegex.test(type)) { - const matches = lengthRegex.exec(type); - - const subtype = parseAbiType(matches[1]); - const length = parseInt(matches[2]); - - return { - ...subtype, - length - }; - } - - const fixedLengthRegex = /^(u?fixed)(\d{1,3})x(\d{1,3})$/; - - if (fixedLengthRegex.test(type)) { - const matches = fixedLengthRegex.exec(type); - - const subtype = parseAbiType(matches[1]); - const M = parseInt(matches[2]); - const N = parseInt(matches[3]); - - return { - ...subtype, - M, N - }; - } - - if (type === 'string') { - return { - type: STRING_TYPE, - default: '' - }; - } - - if (type === 'bool') { - return { - type: BOOL_TYPE, - default: false - }; - } - - if (type === 'address') { - return { - type: ADDRESS_TYPE, - default: '0x' - }; - } - - if (type === 'bytes') { - return { - type: BYTES_TYPE, - default: '0x' - }; - } - - if (type === 'uint') { - return { - type: INT_TYPE, - default: 0, - length: 256, - signed: false - }; - } - - if (type === 'int') { - return { - type: INT_TYPE, - default: 0, - length: 256, - signed: true - }; - } - - if (type === 'ufixed') { - return { - type: FIXED_TYPE, - default: 0, - length: 256, - signed: false - }; - } - - if (type === 'fixed') { - return { - type: FIXED_TYPE, - default: 0, - length: 256, - signed: true - }; - } -} diff --git a/js/src/modals/DeployContract/deployContract.css b/js/src/modals/DeployContract/deployContract.css index ef92b24af..45fd7a852 100644 --- a/js/src/modals/DeployContract/deployContract.css +++ b/js/src/modals/DeployContract/deployContract.css @@ -31,17 +31,3 @@ .funcparams { padding-left: 3em; } - -.inputs { - padding-top: 2px; - - label { - line-height: 22px; - pointer-events: none; - color: rgba(255, 255, 255, 0.498039); - -webkit-user-select: none; - font-size: 12px; - top: 11px; - position: relative; - } -} diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index d38851ccb..257ede1f1 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -278,8 +278,6 @@ export default class DeployContract extends Component { return; } - console.log('onDeploymentState', data); - switch (data.state) { case 'estimateGas': case 'postTransaction': diff --git a/js/src/ui/Form/TypedInput/index.js b/js/src/ui/Form/TypedInput/index.js new file mode 100644 index 000000000..9c12bf7eb --- /dev/null +++ b/js/src/ui/Form/TypedInput/index.js @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (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 './typedInput'; diff --git a/js/src/ui/Form/TypedInput/typedInput.css b/js/src/ui/Form/TypedInput/typedInput.css new file mode 100644 index 000000000..c13206c96 --- /dev/null +++ b/js/src/ui/Form/TypedInput/typedInput.css @@ -0,0 +1,31 @@ +/* Copyright 2015, 2016 Ethcore (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 . +*/ + +.inputs { + padding-top: 2px; + overflow-x: hidden; + + label { + line-height: 22px; + pointer-events: none; + color: rgba(255, 255, 255, 0.498039); + -webkit-user-select: none; + font-size: 12px; + top: 11px; + position: relative; + } +} diff --git a/js/src/ui/Form/TypedInput/typedInput.js b/js/src/ui/Form/TypedInput/typedInput.js new file mode 100644 index 000000000..6bcb5bbf1 --- /dev/null +++ b/js/src/ui/Form/TypedInput/typedInput.js @@ -0,0 +1,239 @@ +// Copyright 2015, 2016 Ethcore (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 { MenuItem } from 'material-ui'; +import { range } from 'lodash'; + +import IconButton from 'material-ui/IconButton'; +import AddIcon from 'material-ui/svg-icons/content/add'; +import RemoveIcon from 'material-ui/svg-icons/content/remove'; + +import { Input, InputAddressSelect, Select } from '../../../ui'; +import { ABI_TYPES } from '../../../util/abi'; + +import styles from './typedInput.css'; + +export default class TypedInput extends Component { + + static propTypes = { + onChange: PropTypes.func.isRequired, + accounts: PropTypes.object.isRequired, + param: PropTypes.object.isRequired, + + error: PropTypes.any, + value: PropTypes.any, + label: PropTypes.string + }; + + render () { + const { param } = this.props; + const { type } = param; + + if (type === ABI_TYPES.ARRAY) { + const { accounts, label, value = param.default } = this.props; + const { subtype, length } = param; + + const fixedLength = !!length; + + const inputs = range(length || value.length).map((_, index) => { + const onChange = (inputValue) => { + const newValues = [].concat(this.props.value); + newValues[index] = inputValue; + this.props.onChange(newValues); + }; + + return ( + + ); + }); + + return ( +
+ + { fixedLength ? null : this.renderLength() } + { inputs } +
+ ); + } + + return this.renderType(type); + } + + renderLength () { + const iconStyle = { + width: 16, + height: 16 + }; + + const style = { + width: 32, + height: 32, + padding: 0 + }; + + return ( +
+ + + + + + + +
+ ); + } + + renderType (type) { + if (type === ABI_TYPES.ADDRESS) { + return this.renderAddress(); + } + + if (type === ABI_TYPES.BOOL) { + return this.renderBoolean(); + } + + if (type === ABI_TYPES.STRING) { + return this.renderDefault(); + } + + if (type === ABI_TYPES.BYTES) { + return this.renderDefault(); + } + + if (type === ABI_TYPES.INT) { + return this.renderNumber(); + } + + if (type === ABI_TYPES.FIXED) { + return this.renderNumber(); + } + + return this.renderDefault(); + } + + renderNumber () { + const { label, value, error, param } = this.props; + + return ( + + ); + } + + renderDefault () { + const { label, value, error } = this.props; + + return ( + + ); + } + + renderAddress () { + const { accounts, label, value, error } = this.props; + + return ( + + ); + } + + renderBoolean () { + const { label, value, error } = this.props; + + const boolitems = ['false', 'true'].map((bool) => { + return ( + + { bool } + + ); + }); + + return ( + + ); + } + + onChangeBool = (event, _index, value) => { + this.props.onChange(value === 'true'); + } + + onChange = (event, value) => { + this.props.onChange(value); + } + + onSubmit = (value) => { + this.props.onChange(value); + } + + onAddField = () => { + const { value, onChange, param } = this.props; + const newValues = [].concat(value, param.subtype.default); + + onChange(newValues); + } + + onRemoveField = () => { + const { value, onChange } = this.props; + const newValues = value.slice(0, -1); + + onChange(newValues); + } + +} diff --git a/js/src/ui/Form/index.js b/js/src/ui/Form/index.js index 061cdddd0..46bb106f4 100644 --- a/js/src/ui/Form/index.js +++ b/js/src/ui/Form/index.js @@ -16,6 +16,7 @@ import AddressSelect from './AddressSelect'; import FormWrap from './FormWrap'; +import TypedInput from './TypedInput'; import Input from './Input'; import InputAddress from './InputAddress'; import InputAddressSelect from './InputAddressSelect'; @@ -27,6 +28,7 @@ export default from './form'; export { AddressSelect, FormWrap, + TypedInput, Input, InputAddress, InputAddressSelect, diff --git a/js/src/ui/index.js b/js/src/ui/index.js index ebfce3ad1..69a7d26c3 100644 --- a/js/src/ui/index.js +++ b/js/src/ui/index.js @@ -29,7 +29,7 @@ import ContextProvider from './ContextProvider'; import CopyToClipboard from './CopyToClipboard'; import Editor from './Editor'; import Errors from './Errors'; -import Form, { AddressSelect, FormWrap, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select } from './Form'; +import Form, { AddressSelect, FormWrap, TypedInput, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select } from './Form'; import IdentityIcon from './IdentityIcon'; import IdentityName from './IdentityName'; import MethodDecoding from './MethodDecoding'; @@ -62,6 +62,7 @@ export { Errors, Form, FormWrap, + TypedInput, Input, InputAddress, InputAddressSelect, diff --git a/js/src/util/abi.js b/js/src/util/abi.js new file mode 100644 index 000000000..9c6c597f5 --- /dev/null +++ b/js/src/util/abi.js @@ -0,0 +1,146 @@ +// Copyright 2015, 2016 Ethcore (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 { range } from 'lodash'; + +const ARRAY_TYPE = 'ARRAY_TYPE'; +const ADDRESS_TYPE = 'ADDRESS_TYPE'; +const STRING_TYPE = 'STRING_TYPE'; +const BOOL_TYPE = 'BOOL_TYPE'; +const BYTES_TYPE = 'BYTES_TYPE'; +const INT_TYPE = 'INT_TYPE'; +const FIXED_TYPE = 'FIXED_TYPE'; + +export const ABI_TYPES = { + ARRAY: ARRAY_TYPE, ADDRESS: ADDRESS_TYPE, + STRING: STRING_TYPE, BOOL: BOOL_TYPE, + BYTES: BYTES_TYPE, INT: INT_TYPE, + FIXED: FIXED_TYPE +}; + +export function parseAbiType (type) { + const arrayRegex = /^(.+)\[(\d*)\]$/; + + if (arrayRegex.test(type)) { + const matches = arrayRegex.exec(type); + + const subtype = parseAbiType(matches[1]); + const M = parseInt(matches[2]) || null; + const defaultValue = !M + ? [] + : range(M).map(() => subtype.default); + + return { + type: ARRAY_TYPE, + subtype: subtype, + length: M, + default: defaultValue + }; + } + + const lengthRegex = /^(u?int|bytes)(\d{1,3})$/; + + if (lengthRegex.test(type)) { + const matches = lengthRegex.exec(type); + + const subtype = parseAbiType(matches[1]); + const length = parseInt(matches[2]); + + return { + ...subtype, + length + }; + } + + const fixedLengthRegex = /^(u?fixed)(\d{1,3})x(\d{1,3})$/; + + if (fixedLengthRegex.test(type)) { + const matches = fixedLengthRegex.exec(type); + + const subtype = parseAbiType(matches[1]); + const M = parseInt(matches[2]); + const N = parseInt(matches[3]); + + return { + ...subtype, + M, N + }; + } + + if (type === 'string') { + return { + type: STRING_TYPE, + default: '' + }; + } + + if (type === 'bool') { + return { + type: BOOL_TYPE, + default: false + }; + } + + if (type === 'address') { + return { + type: ADDRESS_TYPE, + default: '0x' + }; + } + + if (type === 'bytes') { + return { + type: BYTES_TYPE, + default: '0x' + }; + } + + if (type === 'uint') { + return { + type: INT_TYPE, + default: 0, + length: 256, + signed: false + }; + } + + if (type === 'int') { + return { + type: INT_TYPE, + default: 0, + length: 256, + signed: true + }; + } + + if (type === 'ufixed') { + return { + type: FIXED_TYPE, + default: 0, + length: 256, + signed: false + }; + } + + if (type === 'fixed') { + return { + type: FIXED_TYPE, + default: 0, + length: 256, + signed: true + }; + } +} From 53227276175a813dcad7f4d96fd82b3b5dd09c5b Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Mon, 14 Nov 2016 20:29:37 +0100 Subject: [PATCH 07/10] Fixing linting issues #3430 --- js/src/util/abi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/util/abi.js b/js/src/util/abi.js index 9c6c597f5..2d849f09d 100644 --- a/js/src/util/abi.js +++ b/js/src/util/abi.js @@ -32,7 +32,7 @@ export const ABI_TYPES = { }; export function parseAbiType (type) { - const arrayRegex = /^(.+)\[(\d*)\]$/; + const arrayRegex = /^(.+)\[(\d*)]$/; if (arrayRegex.test(type)) { const matches = arrayRegex.exec(type); From 2b30d4b75f23363cb1cea0d9494a6ad9439ca47b Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Tue, 15 Nov 2016 12:35:50 +0100 Subject: [PATCH 08/10] Empty default Address Field #3395 --- js/src/util/abi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/util/abi.js b/js/src/util/abi.js index 2d849f09d..e7947233b 100644 --- a/js/src/util/abi.js +++ b/js/src/util/abi.js @@ -97,7 +97,7 @@ export function parseAbiType (type) { if (type === 'address') { return { type: ADDRESS_TYPE, - default: '0x' + default: '' }; } From 723005635a68e98fb9e960909e27467200c0c3ae Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Tue, 15 Nov 2016 12:50:20 +0100 Subject: [PATCH 09/10] Close and not Cancel on deploy contract #3393 --- js/src/abi/util/pad.js | 3 ++- js/src/modals/DeployContract/deployContract.js | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/js/src/abi/util/pad.js b/js/src/abi/util/pad.js index a7d940431..5a6751a4f 100644 --- a/js/src/abi/util/pad.js +++ b/js/src/abi/util/pad.js @@ -46,7 +46,8 @@ function stringToBytes (input) { if (isArray(input)) { return input; } else if (input.substr(0, 2) === '0x') { - return input.substr(2).toLowerCase().match(/.{1,2}/g).map((value) => parseInt(value, 16)); + const matches = input.substr(2).toLowerCase().match(/.{1,2}/g) || []; + return matches.map((value) => parseInt(value, 16)); } else { return input.split('').map((char) => char.charCodeAt(0)); } diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index 257ede1f1..952217edc 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -119,6 +119,13 @@ export default class DeployContract extends Component { onClick={ this.onClose } /> ); + const closeBtn = ( +