diff options
author | Dan J Miller <danjm.com@gmail.com> | 2017-10-14 04:19:22 +0800 |
---|---|---|
committer | Daniel Tsui <szehungdanieltsui@gmail.com> | 2017-10-14 04:19:22 +0800 |
commit | 803eaaf968161f16aaf72d59b979dfbb7fb9b352 (patch) | |
tree | adf8cbf5240e592ae0ede85be1181132612b2d8a /ui/app/components/send | |
parent | 81f62a7443d47461b5f9b20f442392562458c79a (diff) | |
download | tangerine-wallet-browser-803eaaf968161f16aaf72d59b979dfbb7fb9b352.tar tangerine-wallet-browser-803eaaf968161f16aaf72d59b979dfbb7fb9b352.tar.gz tangerine-wallet-browser-803eaaf968161f16aaf72d59b979dfbb7fb9b352.tar.bz2 tangerine-wallet-browser-803eaaf968161f16aaf72d59b979dfbb7fb9b352.tar.lz tangerine-wallet-browser-803eaaf968161f16aaf72d59b979dfbb7fb9b352.tar.xz tangerine-wallet-browser-803eaaf968161f16aaf72d59b979dfbb7fb9b352.tar.zst tangerine-wallet-browser-803eaaf968161f16aaf72d59b979dfbb7fb9b352.zip |
[NewUI] SendV2-#8: Send container handles tokens; gas info dynamic from state (#2364)
* Adds memo field to send-v2.
* Vertical align transaction with flexbox.
* Customize Gas UI
* Remove internal state from InputNumber and fix use in gastooltip.
* Move customize-gas-modal to its own folder and minor cleanup
* Create send container, get account info from state, and make currency display more reusable
* Adjusts send-v2 and container for send-token. Dynamically getting suggested gas prices.
Diffstat (limited to 'ui/app/components/send')
-rw-r--r-- | ui/app/components/send/account-list-item.js | 33 | ||||
-rw-r--r-- | ui/app/components/send/currency-display.js | 61 | ||||
-rw-r--r-- | ui/app/components/send/from-dropdown.js | 2 | ||||
-rw-r--r-- | ui/app/components/send/gas-fee-display-v2.js | 47 | ||||
-rw-r--r-- | ui/app/components/send/gas-tooltip.js | 4 | ||||
-rw-r--r-- | ui/app/components/send/memo-textarea.js | 33 | ||||
-rw-r--r-- | ui/app/components/send/send-v2-container.js | 62 | ||||
-rw-r--r-- | ui/app/components/send/to-autocomplete.js | 4 |
8 files changed, 214 insertions, 32 deletions
diff --git a/ui/app/components/send/account-list-item.js b/ui/app/components/send/account-list-item.js index b11527d95..64acde767 100644 --- a/ui/app/components/send/account-list-item.js +++ b/ui/app/components/send/account-list-item.js @@ -3,27 +3,34 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect const Identicon = require('../identicon') +const CurrencyDisplay = require('./currency-display') +const { conversionRateSelector } = require('../../selectors') inherits(AccountListItem, Component) function AccountListItem () { Component.call(this) } -module.exports = AccountListItem +function mapStateToProps(state) { + return { + conversionRate: conversionRateSelector(state) + } +} + +module.exports = connect(mapStateToProps)(AccountListItem) AccountListItem.prototype.render = function () { const { account, handleClick, icon = null, + conversionRate, } = this.props - const { identity, balancesToRender } = account - const { name, address } = identity - const { primary, secondary } = balancesToRender + const { name, address, balance } = account return h('div.account-list-item', { - onClick: () => handleClick(identity), + onClick: () => handleClick({ name, address, balance }), }, [ h('div.account-list-item__top-row', {}, [ @@ -35,7 +42,7 @@ AccountListItem.prototype.render = function () { diameter: 18, className: 'account-list-item__identicon', }, - ), + ), h('div.account-list-item__account-name', {}, name), @@ -43,9 +50,17 @@ AccountListItem.prototype.render = function () { ]), - h('div.account-list-item__account-primary-balance', {}, primary), - - h('div.account-list-item__account-secondary-balance', {}, secondary), + h(CurrencyDisplay, { + primaryCurrency: 'ETH', + convertedCurrency: 'USD', + value: balance, + conversionRate, + convertedPrefix: '$', + readOnly: true, + className: 'account-list-item__account-balances', + primaryBalanceClassName: 'account-list-item__account-primary-balance', + convertedBalanceClassName: 'account-list-item__account-secondary-balance', + }, name), ]) }
\ No newline at end of file diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js index 332d722ec..ed9847fdb 100644 --- a/ui/app/components/send/currency-display.js +++ b/ui/app/components/send/currency-display.js @@ -11,8 +11,7 @@ function CurrencyDisplay () { Component.call(this) this.state = { - minWidth: null, - currentScrollWidth: null, + value: null, } } @@ -29,28 +28,50 @@ function resetCaretIfPastEnd (value, event) { } } +CurrencyDisplay.prototype.handleChangeInHexWei = function (value) { + const { handleChange } = this.props + + const valueInHexWei = conversionUtil(value, { + fromNumericBase: 'dec', + toNumericBase: 'hex', + toDenomination: 'WEI', + }) + + handleChange(valueInHexWei) +} + CurrencyDisplay.prototype.render = function () { const { - className, + className = 'currency-display', + primaryBalanceClassName = 'currency-display__input', + convertedBalanceClassName = 'currency-display__converted-value', + conversionRate, primaryCurrency, convertedCurrency, - value = '', - placeholder = '0', - conversionRate, convertedPrefix = '', + placeholder = '0', readOnly = false, - handleChange, + value: initValue, } = this.props - const { minWidth } = this.state + const { value } = this.state + + const initValueToRender = conversionUtil(initValue, { + fromNumericBase: 'hex', + toNumericBase: 'dec', + fromDenomination: 'WEI', + numberOfDecimals: 6, + conversionRate, + }) - const convertedValue = conversionUtil(value, { + const convertedValue = conversionUtil(value || initValueToRender, { fromNumericBase: 'dec', fromCurrency: primaryCurrency, toCurrency: convertedCurrency, + numberOfDecimals: 2, conversionRate, }) - return h('div.currency-display', { + return h('div', { className, }, [ @@ -58,35 +79,39 @@ CurrencyDisplay.prototype.render = function () { h('div.currency-display__input-wrapper', [ - h('input.currency-display__input', { - value: `${value} ${primaryCurrency}`, + h('input', { + className: primaryBalanceClassName, + value: `${value || initValueToRender} ${primaryCurrency}`, placeholder: `${0} ${primaryCurrency}`, readOnly, onChange: (event) => { let newValue = event.target.value.split(' ')[0] if (newValue === '') { - handleChange('0') + this.setState({ value: '0' }) } else if (newValue.match(/^0[1-9]$/)) { - handleChange(newValue.match(/[1-9]/)[0]) + this.setState({ value: newValue.match(/[1-9]/)[0] }) } else if (newValue && !isValidInput(newValue)) { event.preventDefault() } else { - handleChange(newValue) + this.setState({ value: newValue }) } }, - onKeyUp: event => resetCaretIfPastEnd(value, event), - onClick: event => resetCaretIfPastEnd(value, event), + onBlur: event => this.handleChangeInHexWei(event.target.value.split(' ')[0]), + onKeyUp: event => resetCaretIfPastEnd(value || initValueToRender, event), + onClick: event => resetCaretIfPastEnd(value || initValueToRender, event), }), ]), ]), - h('div.currency-display__converted-value', {}, `${convertedPrefix}${convertedValue} ${convertedCurrency}`), + h('div', { + className: convertedBalanceClassName, + }, `${convertedPrefix}${convertedValue} ${convertedCurrency}`), ]) diff --git a/ui/app/components/send/from-dropdown.js b/ui/app/components/send/from-dropdown.js index fb0a00cc2..e8e1d43f0 100644 --- a/ui/app/components/send/from-dropdown.js +++ b/ui/app/components/send/from-dropdown.js @@ -14,7 +14,7 @@ function FromDropdown () { FromDropdown.prototype.getListItemIcon = function (currentAccount, selectedAccount) { const listItemIcon = h(`i.fa.fa-check.fa-lg`, { style: { color: '#02c9b1' } }) - return currentAccount.identity.address === selectedAccount.identity.address + return currentAccount.address === selectedAccount.address ? listItemIcon : null } diff --git a/ui/app/components/send/gas-fee-display-v2.js b/ui/app/components/send/gas-fee-display-v2.js new file mode 100644 index 000000000..226ae93f8 --- /dev/null +++ b/ui/app/components/send/gas-fee-display-v2.js @@ -0,0 +1,47 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const CurrencyDisplay = require('./currency-display'); + +const { multiplyCurrencies } = require('../../conversion-util') + +module.exports = GasFeeDisplay + +inherits(GasFeeDisplay, Component) +function GasFeeDisplay () { + Component.call(this) +} + +GasFeeDisplay.prototype.render = function () { + const { + conversionRate, + gasLimit, + gasPrice, + onClick, + } = this.props + + const readyToRender = Boolean(gasLimit && gasPrice) + + return h('div', [ + + readyToRender + ? h(CurrencyDisplay, { + primaryCurrency: 'ETH', + convertedCurrency: 'USD', + value: multiplyCurrencies(gasLimit, gasPrice, { toNumericBase: 'hex' }), + conversionRate, + convertedPrefix: '$', + readOnly: true, + }) + : h('div.currency-display', 'Loading...') + , + + h('div.send-v2__sliders-icon-container', { + onClick, + }, [ + h('i.fa.fa-sliders.send-v2__sliders-icon'), + ]) + + ]) +} + diff --git a/ui/app/components/send/gas-tooltip.js b/ui/app/components/send/gas-tooltip.js index bef419e48..46aff3499 100644 --- a/ui/app/components/send/gas-tooltip.js +++ b/ui/app/components/send/gas-tooltip.js @@ -73,7 +73,7 @@ GasTooltip.prototype.render = function () { step: 1, min: 0, placeholder: '0', - initValue: gasPrice, + value: gasPrice, onChange: (newPrice) => this.updateGasPrice(newPrice), }), h('div.gas-tooltip-input-label', { @@ -89,7 +89,7 @@ GasTooltip.prototype.render = function () { step: 1, min: 0, placeholder: '0', - initValue: gasLimit, + value: gasLimit, onChange: (newLimit) => this.updateGasLimit(newLimit), }), ]), diff --git a/ui/app/components/send/memo-textarea.js b/ui/app/components/send/memo-textarea.js new file mode 100644 index 000000000..4005b9493 --- /dev/null +++ b/ui/app/components/send/memo-textarea.js @@ -0,0 +1,33 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const Identicon = require('../identicon') + +module.exports = MemoTextArea + +inherits(MemoTextArea, Component) +function MemoTextArea () { + Component.call(this) +} + +MemoTextArea.prototype.render = function () { + const { memo, identities, onChange } = this.props + + return h('div.send-v2__memo-text-area', [ + + h('textarea.send-v2__memo-text-area__input', { + placeholder: 'Optional', + value: memo, + onChange, + // onBlur: () => { + // this.setErrorsFor('memo') + // }, + onFocus: event => { + // this.clearErrorsFor('memo') + }, + }), + + ]) + +} + diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js new file mode 100644 index 000000000..0c8dd5335 --- /dev/null +++ b/ui/app/components/send/send-v2-container.js @@ -0,0 +1,62 @@ +const connect = require('react-redux').connect +const actions = require('../../actions') +const abi = require('ethereumjs-abi') +const SendEther = require('../../send-v2') + +const { multiplyCurrencies } = require('../../conversion-util') + +const { + accountsWithSendEtherInfoSelector, + getCurrentAccountWithSendEtherInfo, + conversionRateSelector, + getSelectedToken, + getSelectedTokenExchangeRate, + getSelectedAddress, +} = require('../../selectors') + +module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther) + +function mapStateToProps (state) { + const selectedAddress = getSelectedAddress(state); + const selectedToken = getSelectedToken(state); + const tokenExchangeRates = state.metamask.tokenExchangeRates + const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state) + const conversionRate = conversionRateSelector(state) + + let data; + let primaryCurrency; + let tokenToUSDRate; + if (selectedToken) { + data = Array.prototype.map.call( + abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']), + x => ('00' + x.toString(16)).slice(-2) + ).join('') + + primaryCurrency = selectedToken.symbol + + tokenToUSDRate = multiplyCurrencies( + conversionRate, + selectedTokenExchangeRate, + { toNumericBase: 'dec' } + ) + } + + return { + selectedAccount: getCurrentAccountWithSendEtherInfo(state), + accounts: accountsWithSendEtherInfoSelector(state), + conversionRate, + selectedToken, + primaryCurrency, + data, + tokenToUSDRate, + } +} + +function mapDispatchToProps (dispatch) { + return { + showCustomizeGasModal: () => dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' })), + estimateGas: params => dispatch(actions.estimateGas(params)), + getGasPrice: () => dispatch(actions.getGasPrice()), + updateTokenExchangeRate: token => dispatch(actions.updateTokenExchangeRate(token)), + } +} diff --git a/ui/app/components/send/to-autocomplete.js b/ui/app/components/send/to-autocomplete.js index 3808bf496..1bf1e1907 100644 --- a/ui/app/components/send/to-autocomplete.js +++ b/ui/app/components/send/to-autocomplete.js @@ -11,7 +11,7 @@ function ToAutoComplete () { } ToAutoComplete.prototype.render = function () { - const { to, identities, onChange } = this.props + const { to, accounts, onChange } = this.props return h('div.send-v2__to-autocomplete', [ @@ -32,7 +32,7 @@ ToAutoComplete.prototype.render = function () { h('datalist#addresses', [ // Corresponds to the addresses owned. - ...Object.entries(identities).map(([key, { address, name }]) => { + ...Object.entries(accounts).map(([key, { address, name }]) => { return h('option', { value: address, label: name, |