Merge branch 'master' into jg-block-gasLimit
This commit is contained in:
		
						commit
						2de38c9238
					
				| @ -86,7 +86,7 @@ impl SecretStore for EthStore { | ||||
| 	fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error> { | ||||
| 		let keypair = try!(KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed)); | ||||
| 		let id: [u8; 16] = Random::random(); | ||||
| 		let account = SafeAccount::create(&keypair, id, password, self.iterations, UUID::from(id).into(), "{}".to_owned()); | ||||
| 		let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned()); | ||||
| 		let address = account.address.clone(); | ||||
| 		try!(self.save(account)); | ||||
| 		Ok(address) | ||||
|  | ||||
| @ -43,6 +43,7 @@ | ||||
|     "test": "mocha 'src/**/*.spec.js'", | ||||
|     "test:coverage": "istanbul cover _mocha -- 'src/**/*.spec.js'", | ||||
|     "test:e2e": "mocha 'src/**/*.e2e.js'", | ||||
|     "test:npm": "(cd .npmjs && npm i) && node test/npmLibrary && (rm -rf .npmjs/node_modules)", | ||||
|     "prepush": "npm run lint:cached" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|  | ||||
| @ -27,6 +27,7 @@ | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "bignumber.js": "^2.3.0", | ||||
|     "js-sha3": "^0.5.2" | ||||
|     "js-sha3": "^0.5.2", | ||||
|     "node-fetch": "^1.6.3" | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -84,7 +84,7 @@ export default class Ws extends JsonRpcBase { | ||||
|     this._connecting = false; | ||||
| 
 | ||||
|     if (this._autoConnect) { | ||||
|       this._connect(); | ||||
|       setTimeout(() => this._connect(), 500); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -14,10 +14,22 @@ | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import 'babel-polyfill/dist/polyfill.js'; | ||||
| import es6Promise from 'es6-promise'; | ||||
| es6Promise.polyfill(); | ||||
| 
 | ||||
| const isNode = typeof global !== 'undefined' && typeof global !== 'undefined'; | ||||
| const isBrowser = typeof self !== 'undefined' && typeof self.window !== 'undefined'; | ||||
| 
 | ||||
| if (isBrowser) { | ||||
|   require('whatwg-fetch'); | ||||
| } | ||||
| 
 | ||||
| if (isNode) { | ||||
|   global.fetch = require('node-fetch'); | ||||
| } | ||||
| 
 | ||||
| import Abi from './abi'; | ||||
| import Api from './api'; | ||||
| 
 | ||||
| export { | ||||
|   Abi, | ||||
|   Api | ||||
| }; | ||||
| module.exports = { Api, Abi }; | ||||
|  | ||||
| @ -14,19 +14,3 @@ | ||||
| /* You should have received a copy of the GNU General Public License | ||||
| /* along with Parity.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| .spaced { | ||||
|   margin: 0.25em 0; | ||||
| } | ||||
| 
 | ||||
| .typeContainer { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
| 
 | ||||
|   .desc { | ||||
|     font-size: 0.8em; | ||||
|     margin-bottom: 0.5em; | ||||
|     color: #ccc; | ||||
|     z-index: 2; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -20,13 +20,10 @@ import ContentClear from 'material-ui/svg-icons/content/clear'; | ||||
| import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward'; | ||||
| import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back'; | ||||
| 
 | ||||
| import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton'; | ||||
| 
 | ||||
| import { Button, Modal, Form, Input, InputAddress } from '../../ui'; | ||||
| import { Button, Modal, Form, Input, InputAddress, RadioButtons } from '../../ui'; | ||||
| import { ERRORS, validateAbi, validateAddress, validateName } from '../../util/validation'; | ||||
| 
 | ||||
| import { eip20, wallet } from '../../contracts/abi'; | ||||
| import styles from './addContract.css'; | ||||
| 
 | ||||
| const ABI_TYPES = [ | ||||
|   { | ||||
| @ -105,13 +102,12 @@ export default class AddContract extends Component { | ||||
|     const { abiTypeIndex } = this.state; | ||||
| 
 | ||||
|     return ( | ||||
|       <RadioButtonGroup | ||||
|         valueSelected={ abiTypeIndex } | ||||
|       <RadioButtons | ||||
|         name='contractType' | ||||
|         value={ abiTypeIndex } | ||||
|         values={ this.getAbiTypes() } | ||||
|         onChange={ this.onChangeABIType } | ||||
|       > | ||||
|         { this.renderAbiTypes() } | ||||
|       </RadioButtonGroup> | ||||
|       /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| @ -194,20 +190,13 @@ export default class AddContract extends Component { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderAbiTypes () { | ||||
|     return ABI_TYPES.map((type, index) => ( | ||||
|       <RadioButton | ||||
|         className={ styles.spaced } | ||||
|         value={ index } | ||||
|         label={ ( | ||||
|           <div className={ styles.typeContainer }> | ||||
|             <span>{ type.label }</span> | ||||
|             <span className={ styles.desc }>{ type.description }</span> | ||||
|           </div> | ||||
|         ) } | ||||
|         key={ index } | ||||
|       /> | ||||
|     )); | ||||
|   getAbiTypes () { | ||||
|     return ABI_TYPES.map((type, index) => ({ | ||||
|       label: type.label, | ||||
|       description: type.description, | ||||
|       key: index, | ||||
|       ...type | ||||
|     })); | ||||
|   } | ||||
| 
 | ||||
|   onNext = () => { | ||||
| @ -218,8 +207,8 @@ export default class AddContract extends Component { | ||||
|     this.setState({ step: this.state.step - 1 }); | ||||
|   } | ||||
| 
 | ||||
|   onChangeABIType = (event, index) => { | ||||
|     const abiType = ABI_TYPES[index]; | ||||
|   onChangeABIType = (value, index) => { | ||||
|     const abiType = value || ABI_TYPES[index]; | ||||
|     this.setState({ abiTypeIndex: index, abiType }); | ||||
|     this.onEditAbi(abiType.value); | ||||
|   } | ||||
|  | ||||
| @ -15,13 +15,12 @@ | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| import { MenuItem } from 'material-ui'; | ||||
| 
 | ||||
| import { AddressSelect, Form, Input, TypedInput } from '../../../ui'; | ||||
| import { AddressSelect, Form, Input, Select } from '../../../ui'; | ||||
| import { validateAbi } from '../../../util/validation'; | ||||
| import { parseAbiType } from '../../../util/abi'; | ||||
| 
 | ||||
| import styles from '../deployContract.css'; | ||||
| 
 | ||||
| export default class DetailsStep extends Component { | ||||
|   static contextTypes = { | ||||
|     api: PropTypes.object.isRequired | ||||
| @ -29,24 +28,26 @@ export default class DetailsStep extends Component { | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     accounts: PropTypes.object.isRequired, | ||||
|     abi: PropTypes.string, | ||||
|     abiError: PropTypes.string, | ||||
|     code: PropTypes.string, | ||||
|     codeError: PropTypes.string, | ||||
|     description: PropTypes.string, | ||||
|     descriptionError: PropTypes.string, | ||||
| 
 | ||||
|     onFromAddressChange: PropTypes.func.isRequired, | ||||
|     onNameChange: PropTypes.func.isRequired, | ||||
|     onDescriptionChange: PropTypes.func.isRequired, | ||||
|     onAbiChange: PropTypes.func.isRequired, | ||||
|     onCodeChange: PropTypes.func.isRequired, | ||||
|     onParamsChange: PropTypes.func.isRequired, | ||||
|     onInputsChange: PropTypes.func.isRequired, | ||||
| 
 | ||||
|     fromAddress: PropTypes.string, | ||||
|     fromAddressError: PropTypes.string, | ||||
|     name: PropTypes.string, | ||||
|     nameError: PropTypes.string, | ||||
|     params: PropTypes.array, | ||||
|     paramsError: PropTypes.array, | ||||
|     onAbiChange: PropTypes.func.isRequired, | ||||
|     onCodeChange: PropTypes.func.isRequired, | ||||
|     onFromAddressChange: PropTypes.func.isRequired, | ||||
|     onDescriptionChange: PropTypes.func.isRequired, | ||||
|     onNameChange: PropTypes.func.isRequired, | ||||
|     onParamsChange: PropTypes.func.isRequired, | ||||
|     description: PropTypes.string, | ||||
|     descriptionError: PropTypes.string, | ||||
|     abi: PropTypes.string, | ||||
|     abiError: PropTypes.string, | ||||
|     code: PropTypes.string, | ||||
|     codeError: PropTypes.string, | ||||
| 
 | ||||
|     readOnly: PropTypes.bool | ||||
|   }; | ||||
| 
 | ||||
| @ -55,7 +56,9 @@ export default class DetailsStep extends Component { | ||||
|   }; | ||||
| 
 | ||||
|   state = { | ||||
|     inputs: [] | ||||
|     solcOutput: '', | ||||
|     contracts: {}, | ||||
|     selectedContractIndex: 0 | ||||
|   } | ||||
| 
 | ||||
|   componentDidMount () { | ||||
| @ -63,6 +66,7 @@ export default class DetailsStep extends Component { | ||||
| 
 | ||||
|     if (abi) { | ||||
|       this.onAbiChange(abi); | ||||
|       this.setState({ solcOutput: abi }); | ||||
|     } | ||||
| 
 | ||||
|     if (code) { | ||||
| @ -71,8 +75,19 @@ export default class DetailsStep extends Component { | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { accounts } = this.props; | ||||
|     const { abi, abiError, code, codeError, fromAddress, fromAddressError, name, nameError, readOnly } = this.props; | ||||
|     const { | ||||
|       accounts, | ||||
|       readOnly, | ||||
| 
 | ||||
|       fromAddress, fromAddressError, | ||||
|       name, nameError, | ||||
|       description, descriptionError, | ||||
|       abiError, | ||||
|       code, codeError | ||||
|     } = this.props; | ||||
| 
 | ||||
|     const { solcOutput, contracts } = this.state; | ||||
|     const solc = contracts && Object.keys(contracts).length > 0; | ||||
| 
 | ||||
|     return ( | ||||
|       <Form> | ||||
| @ -83,18 +98,30 @@ export default class DetailsStep extends Component { | ||||
|           error={ fromAddressError } | ||||
|           accounts={ accounts } | ||||
|           onChange={ this.onFromAddressChange } /> | ||||
| 
 | ||||
|         <Input | ||||
|           label='contract name' | ||||
|           hint='a name for the deployed contract' | ||||
|           error={ nameError } | ||||
|           value={ name } | ||||
|           onSubmit={ this.onNameChange } /> | ||||
|           onChange={ this.onNameChange } /> | ||||
| 
 | ||||
|         <Input | ||||
|           label='abi' | ||||
|           hint='the abi of the contract to deploy' | ||||
|           label='contract description (optional)' | ||||
|           hint='a description for the contract' | ||||
|           error={ descriptionError } | ||||
|           value={ description } | ||||
|           onChange={ this.onDescriptionChange } /> | ||||
| 
 | ||||
|         { this.renderContractSelect() } | ||||
| 
 | ||||
|         <Input | ||||
|           label='abi / solc combined-output' | ||||
|           hint='the abi of the contract to deploy or solc combined-output' | ||||
|           error={ abiError } | ||||
|           value={ abi } | ||||
|           onSubmit={ this.onAbiChange } | ||||
|           value={ solcOutput } | ||||
|           onChange={ this.onSolcChange } | ||||
|           onSubmit={ this.onSolcSubmit } | ||||
|           readOnly={ readOnly } /> | ||||
|         <Input | ||||
|           label='code' | ||||
| @ -102,66 +129,104 @@ export default class DetailsStep extends Component { | ||||
|           error={ codeError } | ||||
|           value={ code } | ||||
|           onSubmit={ this.onCodeChange } | ||||
|           readOnly={ readOnly } /> | ||||
|           readOnly={ readOnly || solc } /> | ||||
| 
 | ||||
|         { this.renderConstructorInputs() } | ||||
|       </Form> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderConstructorInputs () { | ||||
|     const { accounts, params, paramsError } = this.props; | ||||
|     const { inputs } = this.state; | ||||
|   renderContractSelect () { | ||||
|     const { contracts } = this.state; | ||||
| 
 | ||||
|     if (!inputs || !inputs.length) { | ||||
|     if (!contracts || Object.keys(contracts).length === 0) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     return inputs.map((input, index) => { | ||||
|       const onChange = (value) => this.onParamChange(index, value); | ||||
|     const { selectedContractIndex } = this.state; | ||||
|     const contractsItems = Object.keys(contracts).map((name, index) => ( | ||||
|       <MenuItem | ||||
|         key={ index } | ||||
|         label={ name } | ||||
|         value={ index } | ||||
|       > | ||||
|         { name } | ||||
|       </MenuItem> | ||||
|     )); | ||||
| 
 | ||||
|       const label = `${input.name ? `${input.name}: ` : ''}${input.type}`; | ||||
|       const value = params[index]; | ||||
|       const error = paramsError[index]; | ||||
|       const param = parseAbiType(input.type); | ||||
|     return ( | ||||
|       <Select | ||||
|         label='select a contract' | ||||
|         onChange={ this.onContractChange } | ||||
|         value={ selectedContractIndex } | ||||
|       > | ||||
|         { contractsItems } | ||||
|       </Select> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|       return ( | ||||
|         <div key={ index } className={ styles.funcparams }> | ||||
|           <TypedInput | ||||
|             label={ label } | ||||
|             value={ value } | ||||
|             error={ error } | ||||
|             accounts={ accounts } | ||||
|             onChange={ onChange } | ||||
|             param={ param } | ||||
|           /> | ||||
|         </div> | ||||
|       ); | ||||
|   onContractChange = (event, index) => { | ||||
|     const { contracts } = this.state; | ||||
|     const contractName = Object.keys(contracts)[index]; | ||||
|     const contract = contracts[contractName]; | ||||
| 
 | ||||
|     const { abi, bin } = contract; | ||||
|     const code = /^0x/.test(bin) ? bin : `0x${bin}`; | ||||
| 
 | ||||
|     this.setState({ selectedContractIndex: index }, () => { | ||||
|       this.onAbiChange(abi); | ||||
|       this.onCodeChange(code); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   onSolcChange = (event, value) => { | ||||
|     // Change triggered only if valid
 | ||||
|     if (this.props.abiError) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     this.onSolcSubmit(value); | ||||
|   } | ||||
| 
 | ||||
|   onSolcSubmit = (value) => { | ||||
|     try { | ||||
|       const solcParsed = JSON.parse(value); | ||||
| 
 | ||||
|       if (!solcParsed || !solcParsed.contracts) { | ||||
|         throw new Error('Wrong solc output'); | ||||
|       } | ||||
| 
 | ||||
|       this.setState({ contracts: solcParsed.contracts }, () => { | ||||
|         this.onContractChange(null, 0); | ||||
|       }); | ||||
|     } catch (e) { | ||||
|       this.setState({ contracts: null }); | ||||
|       this.onAbiChange(value); | ||||
|     } | ||||
| 
 | ||||
|     this.setState({ solcOutput: value }); | ||||
|   } | ||||
| 
 | ||||
|   onFromAddressChange = (event, fromAddress) => { | ||||
|     const { onFromAddressChange } = this.props; | ||||
| 
 | ||||
|     onFromAddressChange(fromAddress); | ||||
|   } | ||||
| 
 | ||||
|   onNameChange = (name) => { | ||||
|   onNameChange = (event, name) => { | ||||
|     const { onNameChange } = this.props; | ||||
| 
 | ||||
|     onNameChange(name); | ||||
|   } | ||||
| 
 | ||||
|   onParamChange = (index, value) => { | ||||
|     const { params, onParamsChange } = this.props; | ||||
|   onDescriptionChange = (event, description) => { | ||||
|     const { onDescriptionChange } = this.props; | ||||
| 
 | ||||
|     params[index] = value; | ||||
|     onParamsChange(params); | ||||
|     onDescriptionChange(description); | ||||
|   } | ||||
| 
 | ||||
|   onAbiChange = (abi) => { | ||||
|     const { api } = this.context; | ||||
|     const { onAbiChange, onParamsChange } = this.props; | ||||
|     const { onAbiChange, onParamsChange, onInputsChange } = this.props; | ||||
|     const { abiError, abiParsed } = validateAbi(abi, api); | ||||
| 
 | ||||
|     if (!abiError) { | ||||
| @ -176,10 +241,10 @@ export default class DetailsStep extends Component { | ||||
|       }); | ||||
| 
 | ||||
|       onParamsChange(params); | ||||
|       this.setState({ inputs }); | ||||
|       onInputsChange(inputs); | ||||
|     } else { | ||||
|       onParamsChange([]); | ||||
|       this.setState({ inputs: [] }); | ||||
|       onInputsChange([]); | ||||
|     } | ||||
| 
 | ||||
|     onAbiChange(abi); | ||||
|  | ||||
							
								
								
									
										17
									
								
								js/src/modals/DeployContract/ParametersStep/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								js/src/modals/DeployContract/ParametersStep/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| export default from './parametersStep'; | ||||
							
								
								
									
										105
									
								
								js/src/modals/DeployContract/ParametersStep/parametersStep.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								js/src/modals/DeployContract/ParametersStep/parametersStep.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,105 @@ | ||||
| // 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 <http://www.gnu.org/licenses/>.
 | ||||
| // 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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| 
 | ||||
| import { Form, TypedInput } from '../../../ui'; | ||||
| import { parseAbiType } from '../../../util/abi'; | ||||
| 
 | ||||
| import styles from '../deployContract.css'; | ||||
| 
 | ||||
| export default class ParametersStep extends Component { | ||||
|   static contextTypes = { | ||||
|     api: PropTypes.object.isRequired | ||||
|   }; | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     accounts: PropTypes.object.isRequired, | ||||
|     onParamsChange: PropTypes.func.isRequired, | ||||
| 
 | ||||
|     inputs: PropTypes.array, | ||||
|     params: PropTypes.array, | ||||
|     paramsError: PropTypes.array | ||||
|   }; | ||||
| 
 | ||||
|   render () { | ||||
|     return ( | ||||
|       <Form> | ||||
|         { this.renderConstructorInputs() } | ||||
|       </Form> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderConstructorInputs () { | ||||
|     const { accounts, params, paramsError } = this.props; | ||||
|     const { inputs } = this.props; | ||||
| 
 | ||||
|     if (!inputs || !inputs.length) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     const inputsComponents = inputs.map((input, index) => { | ||||
|       const onChange = (value) => this.onParamChange(index, value); | ||||
| 
 | ||||
|       const label = `${input.name ? `${input.name}: ` : ''}${input.type}`; | ||||
|       const value = params[index]; | ||||
|       const error = paramsError[index]; | ||||
|       const param = parseAbiType(input.type); | ||||
| 
 | ||||
|       return ( | ||||
|         <div key={ index } className={ styles.funcparams }> | ||||
|           <TypedInput | ||||
|             label={ label } | ||||
|             value={ value } | ||||
|             error={ error } | ||||
|             accounts={ accounts } | ||||
|             onChange={ onChange } | ||||
|             param={ param } | ||||
|           /> | ||||
|         </div> | ||||
|       ); | ||||
|     }); | ||||
| 
 | ||||
|     return ( | ||||
|       <div> | ||||
|         <p>Choose the contract parameters</p> | ||||
|         { inputsComponents } | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   onParamChange = (index, value) => { | ||||
|     const { params, onParamsChange } = this.props; | ||||
| 
 | ||||
|     params[index] = value; | ||||
|     onParamsChange(params); | ||||
|   } | ||||
| } | ||||
| @ -31,3 +31,7 @@ | ||||
| .funcparams { | ||||
|   padding-left: 3em; | ||||
| } | ||||
| 
 | ||||
| p { | ||||
|   color: rgba(255, 255, 255, 0.498039); | ||||
| } | ||||
|  | ||||
| @ -22,13 +22,19 @@ import { BusyStep, CompletedStep, CopyToClipboard, Button, IdentityIcon, Modal, | ||||
| import { ERRORS, validateAbi, validateCode, validateName } from '../../util/validation'; | ||||
| 
 | ||||
| import DetailsStep from './DetailsStep'; | ||||
| import ParametersStep from './ParametersStep'; | ||||
| import ErrorStep from './ErrorStep'; | ||||
| 
 | ||||
| import styles from './deployContract.css'; | ||||
| 
 | ||||
| import { ERROR_CODES } from '../../api/transport/error'; | ||||
| 
 | ||||
| const steps = ['contract details', 'deployment', 'completed']; | ||||
| const STEPS = { | ||||
|   CONTRACT_DETAILS: { title: 'contract details' }, | ||||
|   CONTRACT_PARAMETERS: { title: 'contract parameters' }, | ||||
|   DEPLOYMENT: { title: 'deployment', waiting: true }, | ||||
|   COMPLETED: { title: 'completed' } | ||||
| }; | ||||
| 
 | ||||
| export default class DeployContract extends Component { | ||||
|   static contextTypes = { | ||||
| @ -55,7 +61,6 @@ export default class DeployContract extends Component { | ||||
|     abiError: ERRORS.invalidAbi, | ||||
|     code: '', | ||||
|     codeError: ERRORS.invalidCode, | ||||
|     deployState: '', | ||||
|     description: '', | ||||
|     descriptionError: null, | ||||
|     fromAddress: Object.keys(this.props.accounts)[0], | ||||
| @ -64,9 +69,12 @@ export default class DeployContract extends Component { | ||||
|     nameError: ERRORS.invalidName, | ||||
|     params: [], | ||||
|     paramsError: [], | ||||
|     step: 0, | ||||
|     inputs: [], | ||||
| 
 | ||||
|     deployState: '', | ||||
|     deployError: null, | ||||
|     rejected: false | ||||
|     rejected: false, | ||||
|     step: 'CONTRACT_DETAILS' | ||||
|   } | ||||
| 
 | ||||
|   componentWillMount () { | ||||
| @ -95,20 +103,30 @@ export default class DeployContract extends Component { | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { step, deployError, rejected } = this.state; | ||||
|     const { step, deployError, rejected, inputs } = this.state; | ||||
| 
 | ||||
|     const realStep = Object.keys(STEPS).findIndex((k) => k === step); | ||||
|     const realSteps = deployError || rejected | ||||
|       ? null | ||||
|       : Object.keys(STEPS) | ||||
|         .filter((k) => k !== 'CONTRACT_PARAMETERS' || inputs.length > 0) | ||||
|         .map((k) => STEPS[k]); | ||||
| 
 | ||||
|     const realSteps = deployError || rejected ? null : steps; | ||||
|     const title = realSteps | ||||
|       ? null | ||||
|       : (deployError ? 'deployment failed' : 'rejected'); | ||||
| 
 | ||||
|     const waiting = realSteps | ||||
|       ? realSteps.map((s, i) => s.waiting ? i : false).filter((v) => v !== false) | ||||
|       : null; | ||||
| 
 | ||||
|     return ( | ||||
|       <Modal | ||||
|         actions={ this.renderDialogActions() } | ||||
|         current={ step } | ||||
|         steps={ realSteps } | ||||
|         current={ realStep } | ||||
|         steps={ realSteps ? realSteps.map((s) => s.title) : null } | ||||
|         title={ title } | ||||
|         waiting={ realSteps ? [1] : null } | ||||
|         waiting={ waiting } | ||||
|         visible | ||||
|         scroll> | ||||
|         { this.renderStep() } | ||||
| @ -146,20 +164,29 @@ export default class DeployContract extends Component { | ||||
|     } | ||||
| 
 | ||||
|     switch (step) { | ||||
|       case 0: | ||||
|       case 'CONTRACT_DETAILS': | ||||
|         return [ | ||||
|           cancelBtn, | ||||
|           <Button | ||||
|             disabled={ !isValid } | ||||
|             icon={ <IdentityIcon button address={ fromAddress } /> } | ||||
|             label='Next' | ||||
|             onClick={ this.onParametersStep } /> | ||||
|         ]; | ||||
| 
 | ||||
|       case 'CONTRACT_PARAMETERS': | ||||
|         return [ | ||||
|           cancelBtn, | ||||
|           <Button | ||||
|             icon={ <IdentityIcon button address={ fromAddress } /> } | ||||
|             label='Create' | ||||
|             onClick={ this.onDeployStart } /> | ||||
|         ]; | ||||
| 
 | ||||
|       case 1: | ||||
|       case 'DEPLOYMENT': | ||||
|         return [ closeBtn ]; | ||||
| 
 | ||||
|       case 2: | ||||
|       case 'COMPLETED': | ||||
|         return [ closeBtnOk ]; | ||||
|     } | ||||
|   } | ||||
| @ -184,21 +211,33 @@ export default class DeployContract extends Component { | ||||
|     } | ||||
| 
 | ||||
|     switch (step) { | ||||
|       case 0: | ||||
|       case 'CONTRACT_DETAILS': | ||||
|         return ( | ||||
|           <DetailsStep | ||||
|             { ...this.state } | ||||
|             readOnly={ readOnly } | ||||
|             accounts={ accounts } | ||||
|             onAbiChange={ this.onAbiChange } | ||||
|             onCodeChange={ this.onCodeChange } | ||||
|             readOnly={ readOnly } | ||||
|             onFromAddressChange={ this.onFromAddressChange } | ||||
|             onDescriptionChange={ this.onDescriptionChange } | ||||
|             onNameChange={ this.onNameChange } | ||||
|             onParamsChange={ this.onParamsChange } /> | ||||
|             onAbiChange={ this.onAbiChange } | ||||
|             onCodeChange={ this.onCodeChange } | ||||
|             onParamsChange={ this.onParamsChange } | ||||
|             onInputsChange={ this.onInputsChange } | ||||
|           /> | ||||
|         ); | ||||
| 
 | ||||
|       case 1: | ||||
|       case 'CONTRACT_PARAMETERS': | ||||
|         return ( | ||||
|           <ParametersStep | ||||
|             { ...this.state } | ||||
|             readOnly={ readOnly } | ||||
|             accounts={ accounts } | ||||
|             onParamsChange={ this.onParamsChange } | ||||
|           /> | ||||
|         ); | ||||
| 
 | ||||
|       case 'DEPLOYMENT': | ||||
|         const body = txhash | ||||
|           ? <TxHash hash={ txhash } /> | ||||
|           : null; | ||||
| @ -210,7 +249,7 @@ export default class DeployContract extends Component { | ||||
|           </BusyStep> | ||||
|         ); | ||||
| 
 | ||||
|       case 2: | ||||
|       case 'COMPLETED': | ||||
|         return ( | ||||
|           <CompletedStep> | ||||
|             <div>Your contract has been deployed at</div> | ||||
| @ -225,12 +264,23 @@ export default class DeployContract extends Component { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   onParametersStep = () => { | ||||
|     const { inputs } = this.state; | ||||
| 
 | ||||
|     if (inputs.length) { | ||||
|       return this.setState({ step: 'CONTRACT_PARAMETERS' }); | ||||
|     } | ||||
| 
 | ||||
|     return this.onDeployStart(); | ||||
|   } | ||||
| 
 | ||||
|   onDescriptionChange = (description) => { | ||||
|     this.setState({ description, descriptionError: null }); | ||||
|   } | ||||
| 
 | ||||
|   onFromAddressChange = (fromAddress) => { | ||||
|     const { api } = this.context; | ||||
| 
 | ||||
|     const fromAddressError = api.util.isAddressValid(fromAddress) | ||||
|       ? null | ||||
|       : 'a valid account as the contract owner needs to be selected'; | ||||
| @ -246,6 +296,10 @@ export default class DeployContract extends Component { | ||||
|     this.setState({ params }); | ||||
|   } | ||||
| 
 | ||||
|   onInputsChange = (inputs) => { | ||||
|     this.setState({ inputs }); | ||||
|   } | ||||
| 
 | ||||
|   onAbiChange = (abi) => { | ||||
|     const { api } = this.context; | ||||
| 
 | ||||
| @ -267,7 +321,7 @@ export default class DeployContract extends Component { | ||||
|       from: fromAddress | ||||
|     }; | ||||
| 
 | ||||
|     this.setState({ step: 1 }); | ||||
|     this.setState({ step: 'DEPLOYMENT' }); | ||||
| 
 | ||||
|     api | ||||
|       .newContract(abiParsed) | ||||
| @ -286,7 +340,7 @@ export default class DeployContract extends Component { | ||||
|         ]) | ||||
|         .then(() => { | ||||
|           console.log(`contract deployed at ${address}`); | ||||
|           this.setState({ step: 2, address }); | ||||
|           this.setState({ step: 'DEPLOYMENT', address }); | ||||
|         }); | ||||
|       }) | ||||
|       .catch((error) => { | ||||
|  | ||||
| @ -16,7 +16,7 @@ | ||||
| 
 | ||||
| import { newError } from '../ui/Errors/actions'; | ||||
| import { setAddressImage } from './providers/imagesActions'; | ||||
| import { clearStatusLogs, toggleStatusLogs } from './providers/statusActions'; | ||||
| import { clearStatusLogs, toggleStatusLogs, toggleStatusRefresh } from './providers/statusActions'; | ||||
| import { toggleView } from '../views/Settings'; | ||||
| 
 | ||||
| export { | ||||
| @ -24,5 +24,6 @@ export { | ||||
|   clearStatusLogs, | ||||
|   setAddressImage, | ||||
|   toggleStatusLogs, | ||||
|   toggleStatusRefresh, | ||||
|   toggleView | ||||
| }; | ||||
|  | ||||
| @ -15,32 +15,29 @@ | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import { statusBlockNumber, statusCollection, statusLogs } from './statusActions'; | ||||
| import { isEqual } from 'lodash'; | ||||
| 
 | ||||
| export default class Status { | ||||
|   constructor (store, api) { | ||||
|     this._api = api; | ||||
|     this._store = store; | ||||
| 
 | ||||
|     this._pingable = false; | ||||
|     this._apiStatus = {}; | ||||
|     this._status = {}; | ||||
|     this._longStatus = {}; | ||||
|     this._minerSettings = {}; | ||||
| 
 | ||||
|     this._pollPingTimeoutId = null; | ||||
|     this._longStatusTimeoutId = null; | ||||
|   } | ||||
| 
 | ||||
|   start () { | ||||
|     this._subscribeBlockNumber(); | ||||
|     this._pollPing(); | ||||
|     this._pollStatus(); | ||||
|     this._pollLongStatus(); | ||||
|     this._pollLogs(); | ||||
|     this._fetchEnode(); | ||||
|   } | ||||
| 
 | ||||
|   _fetchEnode () { | ||||
|     this._api.parity | ||||
|       .enode() | ||||
|       .then((enode) => { | ||||
|         this._store.dispatch(statusCollection({ enode })); | ||||
|       }) | ||||
|       .catch(() => { | ||||
|         window.setTimeout(() => { | ||||
|           this._fetchEnode(); | ||||
|         }, 1000); | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   _subscribeBlockNumber () { | ||||
| @ -66,10 +63,43 @@ export default class Status { | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Pinging should be smart. It should only | ||||
|    * be used when the UI is connecting or the | ||||
|    * Node is deconnected. | ||||
|    * | ||||
|    * @see src/views/Connection/connection.js | ||||
|    */ | ||||
|   _shouldPing = () => { | ||||
|     const { isConnected, isConnecting } = this._apiStatus; | ||||
|     return isConnecting || !isConnected; | ||||
|   } | ||||
| 
 | ||||
|   _stopPollPing = () => { | ||||
|     if (!this._pollPingTimeoutId) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     clearTimeout(this._pollPingTimeoutId); | ||||
|     this._pollPingTimeoutId = null; | ||||
|   } | ||||
| 
 | ||||
|   _pollPing = () => { | ||||
|     const dispatch = (status, timeout = 500) => { | ||||
|       this._store.dispatch(statusCollection({ isPingable: status })); | ||||
|       setTimeout(this._pollPing, timeout); | ||||
|     // Already pinging, don't try again
 | ||||
|     if (this._pollPingTimeoutId) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const dispatch = (pingable, timeout = 1000) => { | ||||
|       if (pingable !== this._pingable) { | ||||
|         this._pingable = pingable; | ||||
|         this._store.dispatch(statusCollection({ isPingable: pingable })); | ||||
|       } | ||||
| 
 | ||||
|       this._pollPingTimeoutId = setTimeout(() => { | ||||
|         this._stopPollPing(); | ||||
|         this._pollPing(); | ||||
|       }, timeout); | ||||
|     }; | ||||
| 
 | ||||
|     fetch('/', { method: 'HEAD' }) | ||||
| @ -88,61 +118,162 @@ export default class Status { | ||||
|   } | ||||
| 
 | ||||
|   _pollStatus = () => { | ||||
|     const { secureToken, isConnected, isConnecting, needsToken } = this._api; | ||||
| 
 | ||||
|     const nextTimeout = (timeout = 1000) => { | ||||
|       setTimeout(this._pollStatus, timeout); | ||||
|     }; | ||||
| 
 | ||||
|     this._store.dispatch(statusCollection({ isConnected, isConnecting, needsToken, secureToken })); | ||||
|     const { isConnected, isConnecting, needsToken, secureToken } = this._api; | ||||
| 
 | ||||
|     const apiStatus = { | ||||
|       isConnected, | ||||
|       isConnecting, | ||||
|       needsToken, | ||||
|       secureToken | ||||
|     }; | ||||
| 
 | ||||
|     const gotReconnected = !this._apiStatus.isConnected && apiStatus.isConnected; | ||||
| 
 | ||||
|     if (gotReconnected) { | ||||
|       this._pollLongStatus(); | ||||
|     } | ||||
| 
 | ||||
|     if (!isEqual(apiStatus, this._apiStatus)) { | ||||
|       this._store.dispatch(statusCollection(apiStatus)); | ||||
|       this._apiStatus = apiStatus; | ||||
|     } | ||||
| 
 | ||||
|     // Ping if necessary, otherwise stop pinging
 | ||||
|     if (this._shouldPing()) { | ||||
|       this._pollPing(); | ||||
|     } else { | ||||
|       this._stopPollPing(); | ||||
|     } | ||||
| 
 | ||||
|     if (!isConnected) { | ||||
|       nextTimeout(250); | ||||
|       return; | ||||
|       return nextTimeout(250); | ||||
|     } | ||||
| 
 | ||||
|     const { refreshStatus } = this._store.getState().nodeStatus; | ||||
| 
 | ||||
|     const statusPromises = [ this._api.eth.syncing() ]; | ||||
| 
 | ||||
|     if (refreshStatus) { | ||||
|       statusPromises.push(this._api.eth.hashrate()); | ||||
|       statusPromises.push(this._api.parity.netPeers()); | ||||
|     } | ||||
| 
 | ||||
|     Promise | ||||
|       .all(statusPromises) | ||||
|       .then((statusResults) => { | ||||
|         const status = statusResults.length === 1 | ||||
|           ? { | ||||
|             syncing: statusResults[0] | ||||
|           } | ||||
|           : { | ||||
|             syncing: statusResults[0], | ||||
|             hashrate: statusResults[1], | ||||
|             netPeers: statusResults[2] | ||||
|           }; | ||||
| 
 | ||||
|         if (!isEqual(status, this._status)) { | ||||
|           this._store.dispatch(statusCollection(status)); | ||||
|           this._status = status; | ||||
|         } | ||||
| 
 | ||||
|         nextTimeout(); | ||||
|       }) | ||||
|       .catch((error) => { | ||||
|         console.error('_pollStatus', error); | ||||
|         nextTimeout(250); | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Miner settings should never changes unless | ||||
|    * Parity is restarted, or if the values are changed | ||||
|    * from the UI | ||||
|    */ | ||||
|   _pollMinerSettings = () => { | ||||
|     Promise | ||||
|       .all([ | ||||
|         this._api.eth.coinbase(), | ||||
|         this._api.parity.extraData(), | ||||
|         this._api.parity.minGasPrice(), | ||||
|         this._api.parity.gasFloorTarget() | ||||
|       ]) | ||||
|       .then(([ | ||||
|         coinbase, extraData, minGasPrice, gasFloorTarget | ||||
|       ]) => { | ||||
|         const minerSettings = { | ||||
|           coinbase, | ||||
|           extraData, | ||||
|           minGasPrice, | ||||
|           gasFloorTarget | ||||
|         }; | ||||
| 
 | ||||
|         if (!isEqual(minerSettings, this._minerSettings)) { | ||||
|           this._store.dispatch(statusCollection(minerSettings)); | ||||
|           this._minerSettings = minerSettings; | ||||
|         } | ||||
|       }) | ||||
|       .catch((error) => { | ||||
|         console.error('_pollMinerSettings', error); | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * The data fetched here should not change | ||||
|    * unless Parity is restarted. They are thus | ||||
|    * fetched every 30s just in case, and whenever | ||||
|    * the client got reconnected. | ||||
|    */ | ||||
|   _pollLongStatus = () => { | ||||
|     const nextTimeout = (timeout = 30000) => { | ||||
|       if (this._longStatusTimeoutId) { | ||||
|         clearTimeout(this._longStatusTimeoutId); | ||||
|       } | ||||
| 
 | ||||
|       this._longStatusTimeoutId = setTimeout(this._pollLongStatus, timeout); | ||||
|     }; | ||||
| 
 | ||||
|     // Poll Miner settings just in case
 | ||||
|     this._pollMinerSettings(); | ||||
| 
 | ||||
|     Promise | ||||
|       .all([ | ||||
|         this._api.web3.clientVersion(), | ||||
|         this._api.eth.coinbase(), | ||||
|         this._api.parity.defaultExtraData(), | ||||
|         this._api.parity.extraData(), | ||||
|         this._api.parity.gasFloorTarget(), | ||||
|         this._api.eth.hashrate(), | ||||
|         this._api.parity.minGasPrice(), | ||||
|         this._api.parity.netChain(), | ||||
|         this._api.parity.netPeers(), | ||||
|         this._api.parity.netPort(), | ||||
|         this._api.parity.nodeName(), | ||||
|         this._api.parity.rpcSettings(), | ||||
|         this._api.eth.syncing() | ||||
|         this._api.parity.enode() | ||||
|       ]) | ||||
|       .then(([clientVersion, coinbase, defaultExtraData, extraData, gasFloorTarget, hashrate, minGasPrice, netChain, netPeers, netPort, nodeName, rpcSettings, syncing, traceMode]) => { | ||||
|       .then(([ | ||||
|         clientVersion, defaultExtraData, netChain, netPort, rpcSettings, enode | ||||
|       ]) => { | ||||
|         const isTest = netChain === 'morden' || netChain === 'testnet'; | ||||
| 
 | ||||
|         this._store.dispatch(statusCollection({ | ||||
|         const longStatus = { | ||||
|           clientVersion, | ||||
|           coinbase, | ||||
|           defaultExtraData, | ||||
|           extraData, | ||||
|           gasFloorTarget, | ||||
|           hashrate, | ||||
|           minGasPrice, | ||||
|           netChain, | ||||
|           netPeers, | ||||
|           netPort, | ||||
|           nodeName, | ||||
|           rpcSettings, | ||||
|           syncing, | ||||
|           isTest, | ||||
|           traceMode | ||||
|         })); | ||||
|           enode, | ||||
|           isTest | ||||
|         }; | ||||
| 
 | ||||
|         if (!isEqual(longStatus, this._longStatus)) { | ||||
|           this._store.dispatch(statusCollection(longStatus)); | ||||
|           this._longStatus = longStatus; | ||||
|         } | ||||
| 
 | ||||
|         nextTimeout(); | ||||
|       }) | ||||
|       .catch((error) => { | ||||
|         console.error('_pollStatus', error); | ||||
|         console.error('_pollLongStatus', error); | ||||
|         nextTimeout(250); | ||||
|       }); | ||||
| 
 | ||||
|     nextTimeout(); | ||||
|   } | ||||
| 
 | ||||
|   _pollLogs = () => { | ||||
|  | ||||
| @ -47,3 +47,10 @@ export function clearStatusLogs () { | ||||
|     type: 'clearStatusLogs' | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function toggleStatusRefresh (refreshStatus) { | ||||
|   return { | ||||
|     type: 'toggleStatusRefresh', | ||||
|     refreshStatus | ||||
|   }; | ||||
| } | ||||
|  | ||||
| @ -38,12 +38,13 @@ const initialState = { | ||||
|     max: new BigNumber(0) | ||||
|   }, | ||||
|   netPort: new BigNumber(0), | ||||
|   nodeName: '', | ||||
|   rpcSettings: {}, | ||||
|   syncing: false, | ||||
|   isApiConnected: true, | ||||
|   isPingConnected: true, | ||||
|   isConnected: false, | ||||
|   isConnecting: false, | ||||
|   isPingable: false, | ||||
|   isTest: false, | ||||
|   refreshStatus: false, | ||||
|   traceMode: undefined | ||||
| }; | ||||
| 
 | ||||
| @ -74,5 +75,10 @@ export default handleActions({ | ||||
| 
 | ||||
|   clearStatusLogs (state, action) { | ||||
|     return Object.assign({}, state, { devLogs: [] }); | ||||
|   }, | ||||
| 
 | ||||
|   toggleStatusRefresh (state, action) { | ||||
|     const { refreshStatus } = action; | ||||
|     return Object.assign({}, state, { refreshStatus }); | ||||
|   } | ||||
| }, initialState); | ||||
|  | ||||
							
								
								
									
										17
									
								
								js/src/ui/Form/RadioButtons/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								js/src/ui/Form/RadioButtons/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| export default from './radioButtons'; | ||||
							
								
								
									
										32
									
								
								js/src/ui/Form/RadioButtons/radioButtons.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								js/src/ui/Form/RadioButtons/radioButtons.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| /* 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 <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
| 
 | ||||
| .spaced { | ||||
|   margin: 0.25em 0; | ||||
| } | ||||
| 
 | ||||
| .typeContainer { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
| 
 | ||||
|   .desc { | ||||
|     font-size: 0.8em; | ||||
|     margin-bottom: 0.5em; | ||||
|     color: #ccc; | ||||
|     z-index: 2; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										100
									
								
								js/src/ui/Form/RadioButtons/radioButtons.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								js/src/ui/Form/RadioButtons/radioButtons.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | ||||
| // 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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| 
 | ||||
| import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton'; | ||||
| 
 | ||||
| import styles from './radioButtons.css'; | ||||
| 
 | ||||
| export default class RadioButtons extends Component { | ||||
|   static propTypes = { | ||||
|     onChange: PropTypes.func.isRequired, | ||||
|     values: PropTypes.array.isRequired, | ||||
| 
 | ||||
|     value: PropTypes.any, | ||||
|     name: PropTypes.string | ||||
|   }; | ||||
| 
 | ||||
|   static defaultProps = { | ||||
|     value: 0, | ||||
|     name: '' | ||||
|   }; | ||||
| 
 | ||||
|   render () { | ||||
|     const { value, values } = this.props; | ||||
| 
 | ||||
|     const index = parseInt(value); | ||||
|     const selectedValue = typeof value !== 'object' ? values[index] : value; | ||||
|     const key = this.getKey(selectedValue, index); | ||||
| 
 | ||||
|     return ( | ||||
|       <RadioButtonGroup | ||||
|         valueSelected={ key } | ||||
|         name={ name } | ||||
|         onChange={ this.onChange } | ||||
|       > | ||||
|         { this.renderContent() } | ||||
|       </RadioButtonGroup> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderContent () { | ||||
|     const { values } = this.props; | ||||
| 
 | ||||
|     return values.map((value, index) => { | ||||
|       const label = typeof value === 'string' ? value : value.label || ''; | ||||
|       const description = (typeof value !== 'string' && value.description) || null; | ||||
|       const key = this.getKey(value, index); | ||||
| 
 | ||||
|       return ( | ||||
|         <RadioButton | ||||
|           className={ styles.spaced } | ||||
|           key={ index } | ||||
| 
 | ||||
|           value={ key } | ||||
|           label={ ( | ||||
|             <div className={ styles.typeContainer }> | ||||
|               <span>{ label }</span> | ||||
|               { | ||||
|                 description | ||||
|                 ? ( | ||||
|                   <span className={ styles.desc }>{ description }</span> | ||||
|                 ) | ||||
|                 : null | ||||
|               } | ||||
|             </div> | ||||
|           ) } | ||||
|         /> | ||||
|       ); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   getKey (value, index) { | ||||
|     if (typeof value !== 'string') { | ||||
|       return typeof value.key === 'undefined' ? index : value.key; | ||||
|     } | ||||
| 
 | ||||
|     return index; | ||||
|   } | ||||
| 
 | ||||
|   onChange = (event, index) => { | ||||
|     const { onChange, values } = this.props; | ||||
| 
 | ||||
|     const value = values[index] || values.find((v) => v.key === index); | ||||
|     onChange(value, index); | ||||
|   } | ||||
| } | ||||
| @ -23,6 +23,7 @@ import InputAddressSelect from './InputAddressSelect'; | ||||
| import InputChip from './InputChip'; | ||||
| import InputInline from './InputInline'; | ||||
| import Select from './Select'; | ||||
| import RadioButtons from './RadioButtons'; | ||||
| 
 | ||||
| export default from './form'; | ||||
| export { | ||||
| @ -34,5 +35,6 @@ export { | ||||
|   InputAddressSelect, | ||||
|   InputChip, | ||||
|   InputInline, | ||||
|   Select | ||||
|   Select, | ||||
|   RadioButtons | ||||
| }; | ||||
|  | ||||
| @ -43,7 +43,7 @@ class Modal extends Component { | ||||
|     waiting: PropTypes.array, | ||||
|     scroll: PropTypes.bool, | ||||
|     steps: PropTypes.array, | ||||
|     title: React.PropTypes.oneOfType([ | ||||
|     title: PropTypes.oneOfType([ | ||||
|       PropTypes.node, PropTypes.string | ||||
|     ]), | ||||
|     visible: PropTypes.bool.isRequired, | ||||
|  | ||||
| @ -29,7 +29,7 @@ import ContextProvider from './ContextProvider'; | ||||
| import CopyToClipboard from './CopyToClipboard'; | ||||
| import Editor from './Editor'; | ||||
| import Errors from './Errors'; | ||||
| import Form, { AddressSelect, FormWrap, TypedInput, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select } from './Form'; | ||||
| import Form, { AddressSelect, FormWrap, TypedInput, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select, RadioButtons } from './Form'; | ||||
| import IdentityIcon from './IdentityIcon'; | ||||
| import IdentityName from './IdentityName'; | ||||
| import MethodDecoding from './MethodDecoding'; | ||||
| @ -78,6 +78,7 @@ export { | ||||
|   muiTheme, | ||||
|   Page, | ||||
|   ParityBackground, | ||||
|   RadioButtons, | ||||
|   SignerIcon, | ||||
|   Tags, | ||||
|   Tooltip, | ||||
|  | ||||
| @ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react'; | ||||
| import { bindActionCreators } from 'redux'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { clearStatusLogs, toggleStatusLogs } from '../../../../redux/actions'; | ||||
| import { clearStatusLogs, toggleStatusLogs, toggleStatusRefresh } from '../../../../redux/actions'; | ||||
| 
 | ||||
| import Debug from '../../components/Debug'; | ||||
| import Status from '../../components/Status'; | ||||
| @ -31,6 +31,14 @@ class StatusPage extends Component { | ||||
|     actions: PropTypes.object.isRequired | ||||
|   } | ||||
| 
 | ||||
|   componentWillMount () { | ||||
|     this.props.actions.toggleStatusRefresh(true); | ||||
|   } | ||||
| 
 | ||||
|   componentWillUnmount () { | ||||
|     this.props.actions.toggleStatusRefresh(false); | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     return ( | ||||
|       <div className={ styles.body }> | ||||
| @ -49,7 +57,8 @@ function mapDispatchToProps (dispatch) { | ||||
|   return { | ||||
|     actions: bindActionCreators({ | ||||
|       clearStatusLogs, | ||||
|       toggleStatusLogs | ||||
|       toggleStatusLogs, | ||||
|       toggleStatusRefresh | ||||
|     }, dispatch) | ||||
|   }; | ||||
| } | ||||
|  | ||||
							
								
								
									
										45
									
								
								js/test/npmLibrary.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								js/test/npmLibrary.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| // 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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| try { | ||||
|   var Api = require('../.npmjs/library.js').Api; | ||||
|   var Abi = require('../.npmjs/library.js').Abi; | ||||
| 
 | ||||
|   if (typeof Api !== 'function') { | ||||
|     throw new Error('No Api'); | ||||
|   } | ||||
| 
 | ||||
|   if (typeof Abi !== 'function') { | ||||
|     throw new Error('No Abi'); | ||||
|   } | ||||
| 
 | ||||
|   var transport = new Api.Transport.Http('http://localhost:8545'); | ||||
|   var api = new Api(transport); | ||||
| 
 | ||||
|   api.eth | ||||
|     .blockNumber() | ||||
|     .then((block) => { | ||||
|       console.log('library working fine', '(block #' + block.toFormat() + ')'); | ||||
|       process.exit(0); | ||||
|     }) | ||||
|     .catch(() => { | ||||
|       console.log('library working fine (disconnected)'); | ||||
|       process.exit(0); | ||||
|     }); | ||||
| } catch (e) { | ||||
|   console.error('An error occured:', e.toString().split('\n')[0]); | ||||
|   process.exit(1); | ||||
| } | ||||
| @ -26,6 +26,7 @@ const DEST = process.env.BUILD_DEST || '.build'; | ||||
| 
 | ||||
| module.exports = { | ||||
|   context: path.join(__dirname, './src'), | ||||
|   target: 'node', | ||||
|   entry: { | ||||
|     // library
 | ||||
|     'inject': ['./web3.js'], | ||||
| @ -34,9 +35,18 @@ module.exports = { | ||||
|   }, | ||||
|   output: { | ||||
|     path: path.join(__dirname, DEST), | ||||
|     filename: '[name].js' | ||||
|     filename: '[name].js', | ||||
|     library: '[name].js', | ||||
|     libraryTarget: 'umd' | ||||
|   }, | ||||
|   externals: { | ||||
|     'node-fetch': 'node-fetch', | ||||
|     'vertx': 'vertx' | ||||
|   }, | ||||
|   module: { | ||||
|     noParse: [ | ||||
|       /babel-polyfill/ | ||||
|     ], | ||||
|     loaders: [ | ||||
|       { | ||||
|         test: /\.js$/, | ||||
|  | ||||
| @ -24,13 +24,23 @@ const isProd = ENV === 'production'; | ||||
| 
 | ||||
| module.exports = { | ||||
|   context: path.join(__dirname, './src'), | ||||
|   target: 'node', | ||||
|   entry: 'library.js', | ||||
|   output: { | ||||
|     path: path.join(__dirname, '.npmjs'), | ||||
|     filename: 'library.js', | ||||
|     libraryTarget: 'commonjs' | ||||
|     library: 'Parity', | ||||
|     libraryTarget: 'umd', | ||||
|     umdNamedDefine: true | ||||
|   }, | ||||
|   externals: { | ||||
|     'node-fetch': 'node-fetch', | ||||
|     'vertx': 'vertx' | ||||
|   }, | ||||
|   module: { | ||||
|     noParse: [ | ||||
|       /babel-polyfill/ | ||||
|     ], | ||||
|     loaders: [ | ||||
|       { | ||||
|         test: /(\.jsx|\.js)$/, | ||||
|  | ||||
| @ -112,7 +112,7 @@ fn should_be_able_to_set_meta() { | ||||
| 
 | ||||
| 	let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#; | ||||
| 	let res = tester.io.handle_request_sync(request); | ||||
| 	let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{}\":{{\"meta\":\"{{foo: 69}}\",\"name\":\"{}\",\"uuid\":\"{}\"}}}},\"id\":1}}", address.hex(), uuid, uuid); | ||||
| 	let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{}\":{{\"meta\":\"{{foo: 69}}\",\"name\":\"\",\"uuid\":\"{}\"}}}},\"id\":1}}", address.hex(), uuid); | ||||
| 	assert_eq!(res, Some(response)); | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user