diff options
Diffstat (limited to 'ui/app')
-rw-r--r-- | ui/app/actions.js | 28 | ||||
-rw-r--r-- | ui/app/app.js | 35 | ||||
-rw-r--r-- | ui/app/components/binary-renderer.js | 43 | ||||
-rw-r--r-- | ui/app/components/ens-input.js | 37 | ||||
-rw-r--r-- | ui/app/components/hex-as-decimal-input.js | 14 | ||||
-rw-r--r-- | ui/app/components/pending-personal-msg-details.js | 14 | ||||
-rw-r--r-- | ui/app/components/pending-tx-details.js | 29 | ||||
-rw-r--r-- | ui/app/components/pending-tx.js | 34 | ||||
-rw-r--r-- | ui/app/config.js | 1 | ||||
-rw-r--r-- | ui/app/css/lib.css | 6 | ||||
-rw-r--r-- | ui/app/reducers/metamask.js | 6 |
11 files changed, 188 insertions, 59 deletions
diff --git a/ui/app/actions.js b/ui/app/actions.js index 7f972fb37..d4fd7553b 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -112,11 +112,13 @@ var actions = { // config screen SHOW_CONFIG_PAGE: 'SHOW_CONFIG_PAGE', SET_RPC_TARGET: 'SET_RPC_TARGET', + SET_DEFAULT_RPC_TARGET: 'SET_DEFAULT_RPC_TARGET', SET_PROVIDER_TYPE: 'SET_PROVIDER_TYPE', USE_ETHERSCAN_PROVIDER: 'USE_ETHERSCAN_PROVIDER', useEtherscanProvider: useEtherscanProvider, showConfigPage: showConfigPage, setRpcTarget: setRpcTarget, + setDefaultRpcTarget: setDefaultRpcTarget, setProviderType: setProviderType, // loading overlay SHOW_LOADING: 'SHOW_LOADING_INDICATION', @@ -669,12 +671,28 @@ function markAccountsFound() { // config // +// default rpc target refers to localhost:8545 in this instance. +function setDefaultRpcTarget (rpcList) { + log.debug(`background.setDefaultRpcTarget`) + return (dispatch) => { + background.setDefaultRpc((err, result) => { + if (err) { + log.error(err) + return dispatch(self.displayWarning('Had a problem changing networks.')) + } + }) + } +} + function setRpcTarget (newRpc) { log.debug(`background.setRpcTarget`) - background.setRpcTarget(newRpc) - return { - type: actions.SET_RPC_TARGET, - value: newRpc, + return (dispatch) => { + background.setCustomRpc(newRpc, (err, result) => { + if (err) { + log.error(err) + return dispatch(self.displayWarning('Had a problem changing networks!')) + } + }) } } @@ -750,7 +768,7 @@ function exportAccount (address) { dispatch(self.hideLoadingIndication()) if (err) { - console.error(err) + log.error(err) return dispatch(self.displayWarning('Had a problem exporting the account.')) } diff --git a/ui/app/app.js b/ui/app/app.js index 63fab5db8..2bc92b54c 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -58,6 +58,7 @@ function mapStateToProps (state) { forgottenPassword: state.appState.forgottenPassword, lastUnreadNotice: state.metamask.lastUnreadNotice, lostAccounts: state.metamask.lostAccounts, + frequentRpcList: state.metamask.frequentRpcList || [], } } @@ -211,6 +212,7 @@ App.prototype.renderAppBar = function () { App.prototype.renderNetworkDropdown = function () { const props = this.props + const rpcList = props.frequentRpcList const state = this.state || {} const isOpen = state.isNetworkMenuOpen @@ -256,12 +258,13 @@ App.prototype.renderNetworkDropdown = function () { h(DropMenuItem, { label: 'Localhost 8545', closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - action: () => props.dispatch(actions.setRpcTarget('http://localhost:8545')), + action: () => props.dispatch(actions.setDefaultRpcTarget(rpcList)), icon: h('i.fa.fa-question-circle.fa-lg'), activeNetworkRender: props.provider.rpcTarget, }), this.renderCustomOption(props.provider), + this.renderCommonRpc(rpcList, props.provider), props.isUnlocked && h(DropMenuItem, { label: 'Custom RPC', @@ -496,6 +499,12 @@ App.prototype.renderCustomOption = function (provider) { const { rpcTarget, type } = provider if (type !== 'rpc') return null + // Concatenate long URLs + let label = rpcTarget + if (rpcTarget.length > 31) { + label = label.substr(0, 34) + '...' + } + switch (rpcTarget) { case 'http://localhost:8545': @@ -503,10 +512,32 @@ App.prototype.renderCustomOption = function (provider) { default: return h(DropMenuItem, { - label: `${rpcTarget}`, + label, + key: rpcTarget, closeMenu: () => this.setState({ isNetworkMenuOpen: false }), icon: h('i.fa.fa-question-circle.fa-lg'), activeNetworkRender: 'custom', }) } } + +App.prototype.renderCommonRpc = function (rpcList, provider) { + const { rpcTarget } = provider + const props = this.props + + return rpcList.map((rpc) => { + if ((rpc === 'http://localhost:8545') || (rpc === rpcTarget)) { + return null + } else { + return h(DropMenuItem, { + label: rpc, + key: rpc, + closeMenu: () => this.setState({ isNetworkMenuOpen: false }), + action: () => props.dispatch(actions.setRpcTarget(rpc)), + icon: h('i.fa.fa-question-circle.fa-lg'), + activeNetworkRender: rpc, + }) + } + }) + +} diff --git a/ui/app/components/binary-renderer.js b/ui/app/components/binary-renderer.js new file mode 100644 index 000000000..a9d49b128 --- /dev/null +++ b/ui/app/components/binary-renderer.js @@ -0,0 +1,43 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const ethUtil = require('ethereumjs-util') + +module.exports = BinaryRenderer + +inherits(BinaryRenderer, Component) +function BinaryRenderer () { + Component.call(this) +} + +BinaryRenderer.prototype.render = function () { + const props = this.props + const { value } = props + const text = this.hexToText(value) + + return ( + h('textarea.font-small', { + readOnly: true, + style: { + width: '315px', + maxHeight: '210px', + resize: 'none', + border: 'none', + background: 'white', + padding: '3px', + }, + defaultValue: text, + }) + ) +} + +BinaryRenderer.prototype.hexToText = function (hex) { + try { + const stripped = ethUtil.stripHexPrefix(hex) + const buff = Buffer.from(stripped, 'hex') + return buff.toString('utf8') + } catch (e) { + return hex + } +} + diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js index f5edab9fd..f018cc632 100644 --- a/ui/app/components/ens-input.js +++ b/ui/app/components/ens-input.js @@ -3,7 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const extend = require('xtend') const debounce = require('debounce') -const ENS = require('ethereum-ens') +const ENS = require('ethjs-ens') const ensRE = /.+\.eth$/ const networkResolvers = { @@ -30,6 +30,8 @@ EnsInput.prototype.render = function () { console.dir(recipient) return this.setState({ loadingEns: false, + ensResolution: null, + ensFailure: null, }) } @@ -53,12 +55,16 @@ EnsInput.prototype.componentDidMount = function () { let resolverAddress = networkResolvers[network] if (resolverAddress) { - this.ens = new ENS(web3, resolverAddress) + const provider = web3.currentProvider + this.ens = new ENS({ provider, network }) this.checkName = debounce(this.lookupEnsName.bind(this), 200) } } EnsInput.prototype.lookupEnsName = function () { + const recipient = document.querySelector('input[name="address"]').value + const { ensResolution } = this.state + if (!this.ens) { return this.setState({ loadingEns: false, @@ -67,17 +73,23 @@ EnsInput.prototype.lookupEnsName = function () { }) } - const recipient = document.querySelector('input[name="address"]').value log.info(`ENS attempting to resolve name: ${recipient}`) - this.ens.resolver(recipient).addr() + this.ens.lookup(recipient.trim()) .then((address) => { - this.setState({ - loadingEns: false, - ensResolution: address, - hoverText: address, - }) + console.log('ens called back with ' + address) + + if (address !== ensResolution) { + this.setState({ + loadingEns: false, + ensResolution: address, + hoverText: address, + }) + } }) .catch((reason) => { + console.log('ens threw error: ' + reason.message) + console.trace(reason) + debugger return this.setState({ loadingEns: false, ensFailure: true, @@ -86,10 +98,12 @@ EnsInput.prototype.lookupEnsName = function () { }) } -EnsInput.prototype.componentDidUpdate = function () { +EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) { const state = this.state || {} const { ensResolution } = state - if (ensResolution && this.props.onChange) { + if (ensResolution && this.props.onChange && + ensResolution !== prevState.ensResolution) { + console.log('Firing on change to parent') this.props.onChange(ensResolution) } } @@ -115,6 +129,7 @@ EnsInput.prototype.ensIconContents = function (recipient) { style: { width: '30px', height: '30px', + transform: 'translateY(-6px)', }, }) } diff --git a/ui/app/components/hex-as-decimal-input.js b/ui/app/components/hex-as-decimal-input.js index 523c1264b..c89ed0416 100644 --- a/ui/app/components/hex-as-decimal-input.js +++ b/ui/app/components/hex-as-decimal-input.js @@ -39,15 +39,17 @@ HexAsDecimalInput.prototype.render = function () { }, }, [ h('input.ether-balance.ether-balance-amount', { + type: 'number', style: extend({ display: 'block', textAlign: 'right', backgroundColor: 'transparent', border: '1px solid #bdbdbd', + }, style), value: decimalValue, onChange: (event) => { - const hexString = hexify(event.target.value) + const hexString = (event.target.value === '') ? '' : hexify(event.target.value) onChange(hexString) }, }), @@ -70,7 +72,11 @@ function hexify (decimalString) { } function decimalize (input, toEth) { - const strippedInput = ethUtil.stripHexPrefix(input) - const inputBN = new BN(strippedInput, 'hex') - return inputBN.toString(10) + if (input === '') { + return '' + } else { + const strippedInput = ethUtil.stripHexPrefix(input) + const inputBN = new BN(strippedInput, 'hex') + return inputBN.toString(10) + } } diff --git a/ui/app/components/pending-personal-msg-details.js b/ui/app/components/pending-personal-msg-details.js index ffd11ca0b..fa2c6416c 100644 --- a/ui/app/components/pending-personal-msg-details.js +++ b/ui/app/components/pending-personal-msg-details.js @@ -3,6 +3,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const AccountPanel = require('./account-panel') +const BinaryRenderer = require('./binary-renderer') module.exports = PendingMsgDetails @@ -41,18 +42,7 @@ PendingMsgDetails.prototype.render = function () { // message data h('div', [ h('label.font-small', { style: { display: 'block' } }, 'MESSAGE'), - h('textarea.font-small', { - readOnly: true, - style: { - width: '315px', - maxHeight: '210px', - resize: 'none', - border: 'none', - background: 'white', - padding: '3px', - }, - defaultValue: data, - }), + h(BinaryRenderer, { value: data }), ]), ]) diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index b1ab9576b..e92ce575f 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -32,10 +32,8 @@ PTXP.render = function () { var account = props.accounts[address] var balance = account ? account.balance : '0x0' - const gas = state.gas || txParams.gas - const gasPrice = state.gasPrice || txData.gasPrice - const gasDefault = txParams.gas - const gasPriceDefault = txData.gasPrice + const gas = (state.gas === undefined) ? txParams.gas : state.gas + const gasPrice = (state.gasPrice === undefined) ? txData.gasPrice : state.gasPrice var txFee = state.txFee || txData.txFee || '' var maxCost = state.maxCost || txData.maxCost || '' @@ -131,11 +129,7 @@ PTXP.render = function () { }, onChange: (newHex) => { log.info(`Gas limit changed to ${newHex}`) - if (newHex === '0x0') { - this.setState({gas: gasDefault}) - } else { - this.setState({ gas: newHex }) - } + this.setState({ gas: newHex }) }, }), ]), @@ -155,11 +149,7 @@ PTXP.render = function () { }, onChange: (newHex) => { log.info(`Gas price changed to: ${newHex}`) - if (newHex === '0x0') { - this.setState({gasPrice: gasPriceDefault}) - } else { - this.setState({ gasPrice: newHex }) - } + this.setState({ gasPrice: newHex }) }, }), ]), @@ -316,7 +306,6 @@ PTXP.gatherParams = function () { const state = this.state || {} const txData = state.txData || props.txData const txParams = txData.txParams - const gas = state.gas || txParams.gas const gasPrice = state.gasPrice || txParams.gasPrice const resultTx = extend(txParams, { @@ -330,6 +319,16 @@ PTXP.gatherParams = function () { return resultTxMeta } +PTXP.verifyGasParams = function () { + // We call this in case the gas has not been modified at all + if (!this.state) { return true } + return this._notZeroOrEmptyString(this.state.gas) && this._notZeroOrEmptyString(this.state.gasPrice) +} + +PTXP._notZeroOrEmptyString = function (obj) { + return obj !== '' && obj !== '0x0' +} + function forwardCarrat () { return ( diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js index d39cbc0f8..2ab6f25a9 100644 --- a/ui/app/components/pending-tx.js +++ b/ui/app/components/pending-tx.js @@ -1,10 +1,18 @@ const Component = require('react').Component +const connect = require('react-redux').connect const h = require('react-hyperscript') const inherits = require('util').inherits const PendingTxDetails = require('./pending-tx-details') const extend = require('xtend') +const actions = require('../actions') -module.exports = PendingTx +module.exports = connect(mapStateToProps)(PendingTx) + +function mapStateToProps (state) { + return { + + } +} inherits(PendingTx, Component) function PendingTx () { @@ -60,25 +68,31 @@ PendingTx.prototype.render = function () { }, [ props.insufficientBalance ? - h('button.btn-green', { + h('button', { onClick: props.buyEth, }, 'Buy Ether') : null, - h('button.confirm', { + h('button', { + onClick: () => { + this.refs.details.resetGasFields() + }, + }, 'Reset'), + + h('button.confirm.btn-green', { disabled: props.insufficientBalance, - onClick: props.sendTransaction, + onClick: (txData, event) => { + if (this.refs.details.verifyGasParams()) { + props.sendTransaction(txData, event) + } else { + this.props.dispatch(actions.displayWarning('Invalid Gas Parameters')) + } + }, }, 'Accept'), h('button.cancel.btn-red', { onClick: props.cancelTransaction, }, 'Reject'), - - h('button', { - onClick: () => { - this.refs.details.resetGasFields() - }, - }, 'Reset'), ]), ]) ) diff --git a/ui/app/config.js b/ui/app/config.js index 65b1ed712..00a4cba88 100644 --- a/ui/app/config.js +++ b/ui/app/config.js @@ -5,6 +5,7 @@ const connect = require('react-redux').connect const actions = require('./actions') const currencies = require('./conversion.json').rows const validUrl = require('valid-url') + module.exports = connect(mapStateToProps)(ConfigScreen) function mapStateToProps (state) { diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css index a8df1d115..99c6f1b9d 100644 --- a/ui/app/css/lib.css +++ b/ui/app/css/lib.css @@ -256,3 +256,9 @@ hr.horizontal-line { text-overflow: ellipsis; white-space: nowrap; } + +.critical-error { + text-align: center; + margin-top: 20px; + color: red; +} diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 3875cf6d1..a3c07d977 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -18,6 +18,7 @@ function reduceMetamask (state, action) { conversionDate: 'N/A', noActiveNotices: true, lastUnreadNotice: undefined, + frequentRpcList: [], }, state.metamask) switch (action.type) { @@ -53,6 +54,11 @@ function reduceMetamask (state, action) { isUnlocked: false, }) + case actions.SET_RPC_LIST: + return extend(metamaskState, { + frequentRpcList: action.value, + }) + case actions.SET_RPC_TARGET: return extend(metamaskState, { provider: { |