diff --git a/ui/app/send.js b/ui/app/send.js index bfc569b7d..481682bc8 100644 --- a/ui/app/send.js +++ b/ui/app/send.js @@ -18,12 +18,10 @@ const { signTx, } = require('./actions') const { stripHexPrefix, addHexPrefix } = require('ethereumjs-util') -const { isHex, numericBalance } = require('./util') +const { isHex, numericBalance, isValidAddress } = require('./util') const { conversionUtil } = require('./conversion-util') const BigNumber = require('bignumber.js') -const ARAGON = '960b236A07cf122663c4303350609A66A7B288C0' - module.exports = connect(mapStateToProps)(SendTransactionScreen) function mapStateToProps (state) { @@ -81,9 +79,180 @@ function SendTransactionScreen () { this.back = this.back.bind(this) this.closeTooltip = this.closeTooltip.bind(this) this.onSubmit = this.onSubmit.bind(this) - this.recipientDidChange = this.recipientDidChange.bind(this) this.setActiveCurrency = this.setActiveCurrency.bind(this) this.toggleTooltip = this.toggleTooltip.bind(this) + + this.renderFromInput = this.renderFromInput.bind(this) + this.renderToInput = this.renderToInput.bind(this) + this.renderAmountInput = this.renderAmountInput.bind(this) + this.renderGasInput = this.renderGasInput.bind(this) + this.renderMemoInput = this.renderMemoInput.bind(this) +} + +SendTransactionScreen.prototype.renderFromInput = function (from, identities) { + return h('div.send-screen-input-wrapper', [ + + h('div', 'From:'), + + h('input.large-input.send-screen-input', { + list: 'accounts', + placeholder: 'Account', + value: from, + onChange: (event) => { + this.setState({ + newTx: { + ...this.state.newTx, + from: event.target.value, + }, + }) + }, + }), + + h('datalist#accounts', [ + Object.entries(identities).map(([key, { address, name }]) => { + return h('option', { + value: address, + label: name, + key: address, + }) + }), + ]), + + ]) +} + +SendTransactionScreen.prototype.renderToInput = function (to, identities, addressBook) { + return h('div.send-screen-input-wrapper', [ + + h('div', 'To:'), + + h('input.large-input.send-screen-input', { + name: 'address', + list: 'addresses', + placeholder: 'Address', + value: to, + onChange: (event) => { + this.setState({ + newTx: { + ...this.state.newTx, + to: event.target.value, + }, + }) + }, + }), + + h('datalist#addresses', [ + // Corresponds to the addresses owned. + ...Object.entries(identities).map(([key, { address, name }]) => { + return h('option', { + value: address, + label: name, + key: address, + }) + }), + // Corresponds to previously sent-to addresses. + ...addressBook.map(({ address, name }) => { + return h('option', { + value: address, + label: name, + key: address, + }) + }), + ]), + + ]) +} + +SendTransactionScreen.prototype.renderAmountInput = function (activeCurrency) { + return h('div.send-screen-input-wrapper', [ + + h('div.send-screen-amount-labels', [ + h('span', 'Amount'), + h(CurrencyToggle, { + activeCurrency, + onClick: (newCurrency) => this.setActiveCurrency(newCurrency), + }), // holding on icon from design + ]), + + h('input.large-input.send-screen-input', { + placeholder: `0 ${activeCurrency}`, + type: 'number', + onChange: (event) => { + this.setState({ + newTx: Object.assign( + this.state.newTx, + { + amount: event.target.value, + } + ), + }) + }, + }), + + ]) +} + +SendTransactionScreen.prototype.renderGasInput = function (gasPrice, gas, activeCurrency, conversionRate, blockGasLimit) { + return h('div.send-screen-input-wrapper', [ + this.state.tooltipIsOpen && h(GasTooltip, { + className: 'send-tooltip', + gasPrice, + gasLimit: gas, + onClose: this.closeTooltip, + onFeeChange: ({gasLimit, gasPrice}) => { + this.setState({ + newTx: { + ...this.state.newTx, + gas: gasLimit, + gasPrice, + }, + }) + }, + }), + + h('div.send-screen-gas-labels', [ + h('span', [ + h('i.fa.fa-bolt'), + 'Gas fee:', + ]), + h('span', 'What\'s this?'), + ]), + + // TODO: handle loading time when switching to USD + h('div.large-input.send-screen-gas-input', {}, [ + h(GasFeeDisplay, { + activeCurrency, + conversionRate, + gas, + gasPrice, + blockGasLimit, + }), + h('div.send-screen-gas-input-customize', { + onClick: this.toggleTooltip, + }, [ + 'Customize', + ]), + ]), + + ]) +} + +SendTransactionScreen.prototype.renderMemoInput = function () { + return h('div.send-screen-input-wrapper', [ + h('div', 'Transaction memo (optional)'), + h('input.large-input.send-screen-input', { + onChange: () => { + this.setState({ + newTx: Object.assign( + this.state.newTx, + { + memo: event.target.value, + } + ), + }) + }, + }), + ]) } SendTransactionScreen.prototype.render = function () { @@ -93,16 +262,13 @@ SendTransactionScreen.prototype.render = function () { const { // selectedIdentity, // network, - // identities, - // addressBook, + identities, + addressBook, conversionRate, } = props const { blockGasLimit, newTx, activeCurrency } = this.state const { gas, gasPrice } = newTx - // console.log(`activeCurrency`, activeCurrency) - // console.log({ selectedIdentity, identities }) - // console.log('SendTransactionScreen state:', this.state) return ( @@ -116,199 +282,16 @@ SendTransactionScreen.prototype.render = function () { h('div.send-screen__subtitle', 'Send Ethereum to anyone with an Ethereum account'), - h('div.send-screen-input-wrapper', [ - - h('div', 'From:'), - - h('input.large-input.send-screen-input', { - list: 'accounts', - placeholder: 'Account', - value: this.state.newTx.from, - onChange: (event) => { - console.log('event', event.target.value) - this.setState({ - newTx: { - ...this.state.newTx, - from: event.target.value, - }, - }) - }, - }), - - h('datalist#accounts', [ - Object.keys(props.identities).map((key) => { - const identity = props.identities[key] - return h('option', { - value: identity.address, - label: identity.name, - key: identity.address, - }) - }), - ]), - - ]), - - h('div.send-screen-input-wrapper', [ - - h('div', 'To:'), - - h('input.large-input.send-screen-input', { - name: 'address', - list: 'addresses', - placeholder: 'Address', - value: this.state.newTx.to, - onChange: (event) => { - console.log('event', event.target.value) - this.setState({ - newTx: { - ...this.state.newTx, - to: event.target.value, - }, - }) - }, - }), - - h('datalist#addresses', [ - // Corresponds to the addresses owned. - Object.entries(props.identities).map(([key, { address, name }]) => { - return h('option', { - value: address, - label: name, - key: address, - }) - }), - // Corresponds to previously sent-to addresses. - props.addressBook.map(({ address, name }) => { - return h('option', { - value: address, - label: name, - key: address, - }) - }), - ]), - - // h(EnsInput, { - // name: 'address', - // placeholder: 'Recipient Address', - // value: this.state.newTx.to, - // onChange: (event) => { - // this.setState({ - // newTx: Object.assign( - // this.state.newTx, - // { - // to: event.target.value, - // } - // ), - // }) - // }, - // network, - // identities, - // addressBook, - // }), - - ]), - - h('div.send-screen-input-wrapper', [ - - h('div.send-screen-amount-labels', [ - h('span', 'Amount'), - h(CurrencyToggle, { - activeCurrency, - onClick: (newCurrency) => this.setActiveCurrency(newCurrency), - }), // holding on icon from design - ]), - - h('input.large-input.send-screen-input', { - placeholder: `0 ${activeCurrency}`, - type: 'number', - onChange: (event) => { - this.setState({ - newTx: Object.assign( - this.state.newTx, - { - amount: event.target.value, - } - ), - }) - }, - }), - - ]), - - h('div.send-screen-input-wrapper', [ - this.state.tooltipIsOpen && h(GasTooltip, { - className: 'send-tooltip', - gasPrice, - gasLimit: gas, - onClose: this.closeTooltip, - onFeeChange: ({gasLimit, gasPrice}) => { - this.setState({ - newTx: { - ...this.state.newTx, - gas: gasLimit, - gasPrice, - }, - }) - }, - }), - - h('div.send-screen-gas-labels', [ - h('span', [ - h('i.fa.fa-bolt'), - 'Gas fee:', - ]), - h('span', 'What\'s this?'), - ]), - - // TODO: handle loading time when switching to USD - h('div.large-input.send-screen-gas-input', {}, [ - h(GasFeeDisplay, { - activeCurrency, - conversionRate, - gas, - gasPrice, - blockGasLimit, - }), - h('div.send-screen-gas-input-customize', { - onClick: this.toggleTooltip, - }, [ - 'Customize', - ]), - ]), - - ]), - - h('div.send-screen-input-wrapper', [ - h('div', 'Transaction memo (optional)'), - h('input.large-input.send-screen-input', { - onChange: () => { - this.setState({ - newTx: Object.assign( - this.state.newTx, - { - memo: event.target.value, - } - ), - }) - }, - }), - ]), - - h('div.send-screen-input-wrapper', {}, [ - h('div', {}, ['Data (optional)']), - h('input.large-input.send-screen-input', { - onChange: () => { - this.setState({ - newTx: Object.assign( - this.state.newTx, - { - txData: event.target.value, - } - ), - }) - }, - }), - ]), + this.renderFromInput(this.state.newTx.from, identities), + + this.renderToInput(this.state.newTx.to, identities, addressBook), + + this.renderAmountInput(activeCurrency), + + this.renderGasInput(gasPrice, gas, activeCurrency, conversionRate, blockGasLimit), + + this.renderMemoInput(), + ]), // Buttons underneath card @@ -337,41 +320,21 @@ SendTransactionScreen.prototype.setActiveCurrency = function (newCurrency) { this.setState({ activeCurrency: newCurrency }) } -SendTransactionScreen.prototype.navigateToAccounts = function (event) { - event.stopPropagation() - this.props.dispatch(showAccountsPage()) -} - SendTransactionScreen.prototype.back = function () { var address = this.props.address this.props.dispatch(backToAccountDetail(address)) } -SendTransactionScreen.prototype.recipientDidChange = function (recipient, nickname) { - this.setState({ - recipient: recipient, - nickname: nickname, - }) -} - SendTransactionScreen.prototype.onSubmit = function (event) { event.preventDefault() + const { warning } = this.props const state = this.state || {} - // const recipient = state.recipient || document.querySelector('input[name="address"]').value.replace(/^[.\s]+|[.\s]+$/g, '') const recipient = state.newTx.to - const nickname = state.nickname || ' ' - // const input = document.querySelector('input[name="amount"]').value - // const input = state.newTx.value - // const value = util.normalizeEthStringToWei(input) - - // https://consensys.slack.com/archives/G1L7H42BT/p1503439134000169?thread_ts=1503438076.000411&cid=G1L7H42BT - // From @kumavis: "not needed for MVP but we will end up adding it again so consider just adding it now" - const txData = false - // Must replace with memo data. - // const txData = document.querySelector('input[name="txData"]').value + // TODO: convert this to hex when created and include it in send + const txData = state.newTx.memo let message @@ -385,14 +348,9 @@ SendTransactionScreen.prototype.onSubmit = function (event) { // return this.props.dispatch(actions.displayWarning(message)) // } - if ((util.isInvalidChecksumAddress(recipient))) { - message = 'Recipient address checksum is invalid.' - return this.props.dispatch(actions.displayWarning(message)) - } - - if ((!util.isValidAddress(recipient) && !txData) || (!recipient && !txData)) { + if (!isValidAddress(recipient) && !recipient) { message = 'Recipient address is invalid.' - return this.props.dispatch(actions.displayWarning(message)) + return this.props.dispatch(displayWarning(message)) } if (txData && !isHex(stripHexPrefix(txData))) { @@ -424,7 +382,6 @@ SendTransactionScreen.prototype.onSubmit = function (event) { value: sendAmount, - // New: gas will now be specified on this step gas: this.state.newTx.gas, gasPrice: this.state.newTx.gasPrice, } @@ -432,5 +389,7 @@ SendTransactionScreen.prototype.onSubmit = function (event) { if (recipient) txParams.to = addHexPrefix(recipient) if (txData) txParams.data = txData - this.props.dispatch(signTx(txParams)) + if (!warning) { + this.props.dispatch(signTx(txParams)) + } }