diff options
Diffstat (limited to 'ui/app/components/send')
-rw-r--r-- | ui/app/components/send/account-list-item.js | 73 | ||||
-rw-r--r-- | ui/app/components/send/currency-display.js | 116 | ||||
-rw-r--r-- | ui/app/components/send/currency-toggle.js | 44 | ||||
-rw-r--r-- | ui/app/components/send/eth-fee-display.js | 37 | ||||
-rw-r--r-- | ui/app/components/send/from-dropdown.js | 72 | ||||
-rw-r--r-- | ui/app/components/send/gas-fee-display-v2.js | 44 | ||||
-rw-r--r-- | ui/app/components/send/gas-fee-display.js | 62 | ||||
-rw-r--r-- | ui/app/components/send/gas-tooltip.js | 100 | ||||
-rw-r--r-- | ui/app/components/send/memo-textarea.js | 33 | ||||
-rw-r--r-- | ui/app/components/send/send-constants.js | 33 | ||||
-rw-r--r-- | ui/app/components/send/send-utils.js | 68 | ||||
-rw-r--r-- | ui/app/components/send/send-v2-container.js | 85 | ||||
-rw-r--r-- | ui/app/components/send/to-autocomplete.js | 114 | ||||
-rw-r--r-- | ui/app/components/send/usd-fee-display.js | 35 |
14 files changed, 916 insertions, 0 deletions
diff --git a/ui/app/components/send/account-list-item.js b/ui/app/components/send/account-list-item.js new file mode 100644 index 000000000..1ad3f69c1 --- /dev/null +++ b/ui/app/components/send/account-list-item.js @@ -0,0 +1,73 @@ +const Component = require('react').Component +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, getCurrentCurrency } = require('../../selectors') + +inherits(AccountListItem, Component) +function AccountListItem () { + Component.call(this) +} + +function mapStateToProps (state) { + return { + conversionRate: conversionRateSelector(state), + currentCurrency: getCurrentCurrency(state), + } +} + +module.exports = connect(mapStateToProps)(AccountListItem) + +AccountListItem.prototype.render = function () { + const { + className, + account, + handleClick, + icon = null, + conversionRate, + currentCurrency, + displayBalance = true, + displayAddress = false, + } = this.props + + const { name, address, balance } = account || {} + + return h('div.account-list-item', { + className, + onClick: () => handleClick({ name, address, balance }), + }, [ + + h('div.account-list-item__top-row', {}, [ + + h( + Identicon, + { + address, + diameter: 18, + className: 'account-list-item__identicon', + }, + ), + + h('div.account-list-item__account-name', {}, name || address), + + icon && h('div.account-list-item__icon', [icon]), + + ]), + + displayAddress && name && h('div.account-list-item__account-address', address), + + displayBalance && h(CurrencyDisplay, { + primaryCurrency: 'ETH', + convertedCurrency: currentCurrency, + value: balance, + conversionRate, + readOnly: true, + className: 'account-list-item__account-balances', + primaryBalanceClassName: 'account-list-item__account-primary-balance', + convertedBalanceClassName: 'account-list-item__account-secondary-balance', + }, name), + + ]) +} diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js new file mode 100644 index 000000000..819fee0a0 --- /dev/null +++ b/ui/app/components/send/currency-display.js @@ -0,0 +1,116 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const CurrencyInput = require('../currency-input') +const { conversionUtil, multiplyCurrencies } = require('../../conversion-util') + +module.exports = CurrencyDisplay + +inherits(CurrencyDisplay, Component) +function CurrencyDisplay () { + Component.call(this) +} + +function toHexWei (value) { + return conversionUtil(value, { + fromNumericBase: 'dec', + toNumericBase: 'hex', + toDenomination: 'WEI', + }) +} + +CurrencyDisplay.prototype.getAmount = function (value) { + const { selectedToken } = this.props + const { decimals } = selectedToken || {} + const multiplier = Math.pow(10, Number(decimals || 0)) + + const sendAmount = multiplyCurrencies(value, multiplier, {toNumericBase: 'hex'}) + + return selectedToken + ? sendAmount + : toHexWei(value) +} + +CurrencyDisplay.prototype.getValueToRender = function () { + const { selectedToken, conversionRate, value } = this.props + + const { decimals, symbol } = selectedToken || {} + const multiplier = Math.pow(10, Number(decimals || 0)) + + return selectedToken + ? conversionUtil(value, { + fromNumericBase: 'hex', + toCurrency: symbol, + conversionRate: multiplier, + invertConversionRate: true, + }) + : conversionUtil(value, { + fromNumericBase: 'hex', + toNumericBase: 'dec', + fromDenomination: 'WEI', + numberOfDecimals: 6, + conversionRate, + }) +} + +CurrencyDisplay.prototype.render = function () { + const { + className = 'currency-display', + primaryBalanceClassName = 'currency-display__input', + convertedBalanceClassName = 'currency-display__converted-value', + conversionRate, + primaryCurrency, + convertedCurrency, + readOnly = false, + inError = false, + handleChange, + } = this.props + + const valueToRender = this.getValueToRender() + + let convertedValue = conversionUtil(valueToRender, { + fromNumericBase: 'dec', + fromCurrency: primaryCurrency, + toCurrency: convertedCurrency, + numberOfDecimals: 2, + conversionRate, + }) + convertedValue = Number(convertedValue).toFixed(2) + + return h('div', { + className, + style: { + borderColor: inError ? 'red' : null, + }, + onClick: () => this.currencyInput.focus(), + }, [ + + h('div.currency-display__primary-row', [ + + h('div.currency-display__input-wrapper', [ + + h(CurrencyInput, { + className: primaryBalanceClassName, + value: `${valueToRender}`, + placeholder: '0', + readOnly, + onInputChange: newValue => { + handleChange(this.getAmount(newValue)) + }, + inputRef: input => { this.currencyInput = input }, + }), + + h('span.currency-display__currency-symbol', primaryCurrency), + + ]), + + ]), + + h('div', { + className: convertedBalanceClassName, + }, `${convertedValue} ${convertedCurrency.toUpperCase()}`), + + ]) + +} + diff --git a/ui/app/components/send/currency-toggle.js b/ui/app/components/send/currency-toggle.js new file mode 100644 index 000000000..7aaccd490 --- /dev/null +++ b/ui/app/components/send/currency-toggle.js @@ -0,0 +1,44 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const classnames = require('classnames') + +module.exports = CurrencyToggle + +inherits(CurrencyToggle, Component) +function CurrencyToggle () { + Component.call(this) +} + +const defaultCurrencies = [ 'ETH', 'USD' ] + +CurrencyToggle.prototype.renderToggles = function () { + const { onClick, activeCurrency } = this.props + const [currencyA, currencyB] = this.props.currencies || defaultCurrencies + + return [ + h('span', { + className: classnames('currency-toggle__item', { + 'currency-toggle__item--selected': currencyA === activeCurrency, + }), + onClick: () => onClick(currencyA), + }, [ currencyA ]), + '<>', + h('span', { + className: classnames('currency-toggle__item', { + 'currency-toggle__item--selected': currencyB === activeCurrency, + }), + onClick: () => onClick(currencyB), + }, [ currencyB ]), + ] +} + +CurrencyToggle.prototype.render = function () { + const currencies = this.props.currencies || defaultCurrencies + + return h('span.currency-toggle', currencies.length + ? this.renderToggles() + : [] + ) +} + diff --git a/ui/app/components/send/eth-fee-display.js b/ui/app/components/send/eth-fee-display.js new file mode 100644 index 000000000..9eda5ec62 --- /dev/null +++ b/ui/app/components/send/eth-fee-display.js @@ -0,0 +1,37 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const EthBalance = require('../eth-balance') +const { getTxFeeBn } = require('../../util') + +module.exports = EthFeeDisplay + +inherits(EthFeeDisplay, Component) +function EthFeeDisplay () { + Component.call(this) +} + +EthFeeDisplay.prototype.render = function () { + const { + activeCurrency, + conversionRate, + gas, + gasPrice, + blockGasLimit, + } = this.props + + return h(EthBalance, { + value: getTxFeeBn(gas, gasPrice, blockGasLimit), + currentCurrency: activeCurrency, + conversionRate, + showFiat: false, + hideTooltip: true, + styleOveride: { + color: '#5d5d5d', + fontSize: '16px', + fontFamily: 'DIN OT', + lineHeight: '22.4px', + }, + }) +} + diff --git a/ui/app/components/send/from-dropdown.js b/ui/app/components/send/from-dropdown.js new file mode 100644 index 000000000..0686fbe73 --- /dev/null +++ b/ui/app/components/send/from-dropdown.js @@ -0,0 +1,72 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const AccountListItem = require('./account-list-item') + +module.exports = FromDropdown + +inherits(FromDropdown, Component) +function FromDropdown () { + Component.call(this) +} + +FromDropdown.prototype.getListItemIcon = function (currentAccount, selectedAccount) { + const listItemIcon = h(`i.fa.fa-check.fa-lg`, { style: { color: '#02c9b1' } }) + + return currentAccount.address === selectedAccount.address + ? listItemIcon + : null +} + +FromDropdown.prototype.renderDropdown = function () { + const { + accounts, + selectedAccount, + closeDropdown, + onSelect, + } = this.props + + return h('div', {}, [ + + h('div.send-v2__from-dropdown__close-area', { + onClick: closeDropdown, + }), + + h('div.send-v2__from-dropdown__list', {}, [ + + ...accounts.map(account => h(AccountListItem, { + className: 'account-list-item__dropdown', + account, + handleClick: () => { + onSelect(account) + closeDropdown() + }, + icon: this.getListItemIcon(account, selectedAccount), + })), + + ]), + + ]) +} + +FromDropdown.prototype.render = function () { + const { + selectedAccount, + openDropdown, + dropdownOpen, + } = this.props + + return h('div.send-v2__from-dropdown', {}, [ + + h(AccountListItem, { + account: selectedAccount, + handleClick: openDropdown, + icon: h(`i.fa.fa-caret-down.fa-lg`, { style: { color: '#dedede' } }), + }), + + dropdownOpen && this.renderDropdown(), + + ]) + +} + 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..0c4c3f7a9 --- /dev/null +++ b/ui/app/components/send/gas-fee-display-v2.js @@ -0,0 +1,44 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const CurrencyDisplay = require('./currency-display') + +module.exports = GasFeeDisplay + +inherits(GasFeeDisplay, Component) +function GasFeeDisplay () { + Component.call(this) +} + +GasFeeDisplay.prototype.render = function () { + const { + conversionRate, + gasTotal, + onClick, + primaryCurrency = 'ETH', + convertedCurrency, + } = this.props + + return h('div.send-v2__gas-fee-display', [ + + gasTotal + ? h(CurrencyDisplay, { + primaryCurrency, + convertedCurrency, + value: gasTotal, + conversionRate, + convertedPrefix: '$', + readOnly: true, + }) + : h('div.currency-display', 'Loading...'), + + h('button.send-v2__sliders-icon-container', { + onClick, + disabled: !gasTotal, + }, [ + h('i.fa.fa-sliders.send-v2__sliders-icon'), + ]), + + ]) +} + diff --git a/ui/app/components/send/gas-fee-display.js b/ui/app/components/send/gas-fee-display.js new file mode 100644 index 000000000..a9a3f3f49 --- /dev/null +++ b/ui/app/components/send/gas-fee-display.js @@ -0,0 +1,62 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const USDFeeDisplay = require('./usd-fee-display') +const EthFeeDisplay = require('./eth-fee-display') +const { getTxFeeBn, formatBalance, shortenBalance } = require('../../util') + +module.exports = GasFeeDisplay + +inherits(GasFeeDisplay, Component) +function GasFeeDisplay () { + Component.call(this) +} + +GasFeeDisplay.prototype.getTokenValue = function () { + const { + tokenExchangeRate, + gas, + gasPrice, + blockGasLimit, + } = this.props + + const value = formatBalance(getTxFeeBn(gas, gasPrice, blockGasLimit), 6, true) + const [ethNumber] = value.split(' ') + + return shortenBalance(Number(ethNumber) / tokenExchangeRate, 6) +} + +GasFeeDisplay.prototype.render = function () { + const { + activeCurrency, + conversionRate, + gas, + gasPrice, + blockGasLimit, + } = this.props + + switch (activeCurrency) { + case 'USD': + return h(USDFeeDisplay, { + activeCurrency, + conversionRate, + gas, + gasPrice, + blockGasLimit, + }) + case 'ETH': + return h(EthFeeDisplay, { + activeCurrency, + conversionRate, + gas, + gasPrice, + blockGasLimit, + }) + default: + return h('div.token-gas', [ + h('div.token-gas__amount', this.getTokenValue()), + h('div.token-gas__symbol', activeCurrency), + ]) + } +} + diff --git a/ui/app/components/send/gas-tooltip.js b/ui/app/components/send/gas-tooltip.js new file mode 100644 index 000000000..46aff3499 --- /dev/null +++ b/ui/app/components/send/gas-tooltip.js @@ -0,0 +1,100 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const InputNumber = require('../input-number.js') + +module.exports = GasTooltip + +inherits(GasTooltip, Component) +function GasTooltip () { + Component.call(this) + this.state = { + gasLimit: 0, + gasPrice: 0, + } + + this.updateGasPrice = this.updateGasPrice.bind(this) + this.updateGasLimit = this.updateGasLimit.bind(this) + this.onClose = this.onClose.bind(this) +} + +GasTooltip.prototype.componentWillMount = function () { + const { gasPrice = 0, gasLimit = 0} = this.props + + this.setState({ + gasPrice: parseInt(gasPrice, 16) / 1000000000, + gasLimit: parseInt(gasLimit, 16), + }) +} + +GasTooltip.prototype.updateGasPrice = function (newPrice) { + const { onFeeChange } = this.props + const { gasLimit } = this.state + + this.setState({ gasPrice: newPrice }) + onFeeChange({ + gasLimit: gasLimit.toString(16), + gasPrice: (newPrice * 1000000000).toString(16), + }) +} + +GasTooltip.prototype.updateGasLimit = function (newLimit) { + const { onFeeChange } = this.props + const { gasPrice } = this.state + + this.setState({ gasLimit: newLimit }) + onFeeChange({ + gasLimit: newLimit.toString(16), + gasPrice: (gasPrice * 1000000000).toString(16), + }) +} + +GasTooltip.prototype.onClose = function (e) { + e.stopPropagation() + this.props.onClose() +} + +GasTooltip.prototype.render = function () { + const { gasPrice, gasLimit } = this.state + + return h('div.gas-tooltip', {}, [ + h('div.gas-tooltip-close-area', { + onClick: this.onClose, + }), + h('div.customize-gas-tooltip-container', {}, [ + h('div.customize-gas-tooltip', {}, [ + h('div.gas-tooltip-header.gas-tooltip-label', {}, ['Customize Gas']), + h('div.gas-tooltip-input-label', {}, [ + h('span.gas-tooltip-label', {}, ['Gas Price']), + h('i.fa.fa-info-circle'), + ]), + h(InputNumber, { + unitLabel: 'GWEI', + step: 1, + min: 0, + placeholder: '0', + value: gasPrice, + onChange: (newPrice) => this.updateGasPrice(newPrice), + }), + h('div.gas-tooltip-input-label', { + style: { + 'marginTop': '81px', + }, + }, [ + h('span.gas-tooltip-label', {}, ['Gas Limit']), + h('i.fa.fa-info-circle'), + ]), + h(InputNumber, { + unitLabel: 'UNITS', + step: 1, + min: 0, + placeholder: '0', + value: gasLimit, + onChange: (newLimit) => this.updateGasLimit(newLimit), + }), + ]), + h('div.gas-tooltip-arrow', {}), + ]), + ]) +} + diff --git a/ui/app/components/send/memo-textarea.js b/ui/app/components/send/memo-textarea.js new file mode 100644 index 000000000..f4bb24bf8 --- /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-constants.js b/ui/app/components/send/send-constants.js new file mode 100644 index 000000000..b3ee0899a --- /dev/null +++ b/ui/app/components/send/send-constants.js @@ -0,0 +1,33 @@ +const ethUtil = require('ethereumjs-util') +const { conversionUtil, multiplyCurrencies } = require('../../conversion-util') + +const MIN_GAS_PRICE_HEX = (100000000).toString(16) +const MIN_GAS_PRICE_DEC = '100000000' +const MIN_GAS_LIMIT_DEC = '21000' +const MIN_GAS_LIMIT_HEX = (parseInt(MIN_GAS_LIMIT_DEC)).toString(16) + +const MIN_GAS_PRICE_GWEI = ethUtil.addHexPrefix(conversionUtil(MIN_GAS_PRICE_HEX, { + fromDenomination: 'WEI', + toDenomination: 'GWEI', + fromNumericBase: 'hex', + toNumericBase: 'hex', + numberOfDecimals: 1, +})) + +const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, { + toNumericBase: 'hex', + multiplicandBase: 16, + multiplierBase: 16, +}) + +const TOKEN_TRANSFER_FUNCTION_SIGNATURE = '0xa9059cbb' + +module.exports = { + MIN_GAS_PRICE_GWEI, + MIN_GAS_PRICE_HEX, + MIN_GAS_PRICE_DEC, + MIN_GAS_LIMIT_HEX, + MIN_GAS_LIMIT_DEC, + MIN_GAS_TOTAL, + TOKEN_TRANSFER_FUNCTION_SIGNATURE, +} diff --git a/ui/app/components/send/send-utils.js b/ui/app/components/send/send-utils.js new file mode 100644 index 000000000..d8211930d --- /dev/null +++ b/ui/app/components/send/send-utils.js @@ -0,0 +1,68 @@ +const { + addCurrencies, + conversionUtil, + conversionGTE, +} = require('../../conversion-util') +const { + calcTokenAmount, +} = require('../../token-util') + +function isBalanceSufficient ({ + amount = '0x0', + gasTotal = '0x0', + balance, + primaryCurrency, + amountConversionRate, + conversionRate, +}) { + const totalAmount = addCurrencies(amount, gasTotal, { + aBase: 16, + bBase: 16, + toNumericBase: 'hex', + }) + + const balanceIsSufficient = conversionGTE( + { + value: balance, + fromNumericBase: 'hex', + fromCurrency: primaryCurrency, + conversionRate, + }, + { + value: totalAmount, + fromNumericBase: 'hex', + conversionRate: amountConversionRate, + fromCurrency: primaryCurrency, + }, + ) + + return balanceIsSufficient +} + +function isTokenBalanceSufficient ({ + amount = '0x0', + tokenBalance, + decimals, +}) { + const amountInDec = conversionUtil(amount, { + fromNumericBase: 'hex', + }) + + const tokenBalanceIsSufficient = conversionGTE( + { + value: tokenBalance, + fromNumericBase: 'dec', + }, + { + value: calcTokenAmount(amountInDec, decimals), + fromNumericBase: 'dec', + }, + ) + + return tokenBalanceIsSufficient +} + +module.exports = { + isBalanceSufficient, + isTokenBalanceSufficient, +} 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..1106902b7 --- /dev/null +++ b/ui/app/components/send/send-v2-container.js @@ -0,0 +1,85 @@ +const connect = require('react-redux').connect +const actions = require('../../actions') +const abi = require('ethereumjs-abi') +const SendEther = require('../../send-v2') + +const { + accountsWithSendEtherInfoSelector, + getCurrentAccountWithSendEtherInfo, + conversionRateSelector, + getSelectedToken, + getSelectedAddress, + getAddressBook, + getSendFrom, + getCurrentCurrency, + getSelectedTokenToFiatRate, + getSelectedTokenContract, +} = require('../../selectors') + +module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther) + +function mapStateToProps (state) { + const fromAccounts = accountsWithSendEtherInfoSelector(state) + const selectedAddress = getSelectedAddress(state) + const selectedToken = getSelectedToken(state) + const conversionRate = conversionRateSelector(state) + + let data + let primaryCurrency + let tokenToFiatRate + if (selectedToken) { + data = Array.prototype.map.call( + abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']), + x => ('00' + x.toString(16)).slice(-2) + ).join('') + + primaryCurrency = selectedToken.symbol + + tokenToFiatRate = getSelectedTokenToFiatRate(state) + } + + return { + ...state.metamask.send, + from: getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state), + fromAccounts, + toAccounts: [...fromAccounts, ...getAddressBook(state)], + conversionRate, + selectedToken, + primaryCurrency, + convertedCurrency: getCurrentCurrency(state), + data, + amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate, + tokenContract: getSelectedTokenContract(state), + unapprovedTxs: state.metamask.unapprovedTxs, + network: state.metamask.network, + } +} + +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)), + signTokenTx: (tokenAddress, toAddress, amount, txData) => ( + dispatch(actions.signTokenTx(tokenAddress, toAddress, amount, txData)) + ), + signTx: txParams => dispatch(actions.signTx(txParams)), + updateAndApproveTx: txParams => dispatch(actions.updateAndApproveTx(txParams)), + updateTx: txData => dispatch(actions.updateTransaction(txData)), + setSelectedAddress: address => dispatch(actions.setSelectedAddress(address)), + addToAddressBook: address => dispatch(actions.addToAddressBook(address)), + updateGasTotal: newTotal => dispatch(actions.updateGasTotal(newTotal)), + updateGasPrice: newGasPrice => dispatch(actions.updateGasPrice(newGasPrice)), + updateGasLimit: newGasLimit => dispatch(actions.updateGasLimit(newGasLimit)), + updateSendTokenBalance: tokenBalance => dispatch(actions.updateSendTokenBalance(tokenBalance)), + updateSendFrom: newFrom => dispatch(actions.updateSendFrom(newFrom)), + updateSendTo: newTo => dispatch(actions.updateSendTo(newTo)), + updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)), + updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)), + updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)), + goHome: () => dispatch(actions.goHome()), + clearSend: () => dispatch(actions.clearSend()), + setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)), + } +} diff --git a/ui/app/components/send/to-autocomplete.js b/ui/app/components/send/to-autocomplete.js new file mode 100644 index 000000000..e0cdd0a58 --- /dev/null +++ b/ui/app/components/send/to-autocomplete.js @@ -0,0 +1,114 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const AccountListItem = require('./account-list-item') + +module.exports = ToAutoComplete + +inherits(ToAutoComplete, Component) +function ToAutoComplete () { + Component.call(this) + + this.state = { accountsToRender: [] } +} + +ToAutoComplete.prototype.getListItemIcon = function (listItemAddress, toAddress) { + const listItemIcon = h(`i.fa.fa-check.fa-lg`, { style: { color: '#02c9b1' } }) + + return toAddress && listItemAddress === toAddress + ? listItemIcon + : null +} + +ToAutoComplete.prototype.renderDropdown = function () { + const { + closeDropdown, + onChange, + to, + } = this.props + const { accountsToRender } = this.state + + return accountsToRender.length && h('div', {}, [ + + h('div.send-v2__from-dropdown__close-area', { + onClick: closeDropdown, + }), + + h('div.send-v2__from-dropdown__list', {}, [ + + ...accountsToRender.map(account => h(AccountListItem, { + account, + className: 'account-list-item__dropdown', + handleClick: () => { + onChange(account.address) + closeDropdown() + }, + icon: this.getListItemIcon(account.address, to), + displayBalance: false, + displayAddress: true, + })), + + ]), + + ]) +} + +ToAutoComplete.prototype.handleInputEvent = function (event = {}, cb) { + const { + to, + accounts, + closeDropdown, + openDropdown, + } = this.props + + const matchingAccounts = accounts.filter(({ address }) => address.match(to || '')) + const matches = matchingAccounts.length + + if (!matches || matchingAccounts[0].address === to) { + this.setState({ accountsToRender: [] }) + event.target && event.target.select() + closeDropdown() + } else { + this.setState({ accountsToRender: matchingAccounts }) + openDropdown() + } + cb && cb(event.target.value) +} + +ToAutoComplete.prototype.componentDidUpdate = function (nextProps, nextState) { + if (this.props.to !== nextProps.to) { + this.handleInputEvent() + } +} + +ToAutoComplete.prototype.render = function () { + const { + to, + dropdownOpen, + onChange, + inError, + } = this.props + + return h('div.send-v2__to-autocomplete', {}, [ + + h('input.send-v2__to-autocomplete__input', { + placeholder: 'Recipient Address', + className: inError ? `send-v2__error-border` : '', + value: to, + onChange: event => onChange(event.target.value), + onFocus: event => this.handleInputEvent(event), + style: { + borderColor: inError ? 'red' : null, + }, + }), + + !to && h(`i.fa.fa-caret-down.fa-lg.send-v2__to-autocomplete__down-caret`, { + style: { color: '#dedede' }, + onClick: () => this.handleInputEvent(), + }), + + dropdownOpen && this.renderDropdown(), + + ]) +} + diff --git a/ui/app/components/send/usd-fee-display.js b/ui/app/components/send/usd-fee-display.js new file mode 100644 index 000000000..4cf31a493 --- /dev/null +++ b/ui/app/components/send/usd-fee-display.js @@ -0,0 +1,35 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const FiatValue = require('../fiat-value') +const { getTxFeeBn } = require('../../util') + +module.exports = USDFeeDisplay + +inherits(USDFeeDisplay, Component) +function USDFeeDisplay () { + Component.call(this) +} + +USDFeeDisplay.prototype.render = function () { + const { + activeCurrency, + conversionRate, + gas, + gasPrice, + blockGasLimit, + } = this.props + + return h(FiatValue, { + value: getTxFeeBn(gas, gasPrice, blockGasLimit), + conversionRate, + currentCurrency: activeCurrency, + style: { + color: '#5d5d5d', + fontSize: '16px', + fontFamily: 'DIN OT', + lineHeight: '22.4px', + }, + }) +} + |