diff options
Diffstat (limited to 'ui/app/components/send_/send-content')
57 files changed, 790 insertions, 0 deletions
diff --git a/ui/app/components/send_/send-content/send-amount-row/README.md b/ui/app/components/send_/send-content/send-amount-row/README.md new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/README.md diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.component.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.component.js new file mode 100644 index 000000000..59a1fd6db --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.component.js @@ -0,0 +1,54 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' + +export default class AmountMaxButton extends Component { + + static propTypes = { + tokenBalance: PropTypes.string, + gasTotal: PropTypes.string, + balance: PropTypes.string, + selectedToken: PropTypes.object, + setAmountToMax: PropTypes.func, + setMaxModeTo: PropTypes.func, + maxModeOn: PropTypes.bool, + }; + + setAmountToMax = function () { + const { + balance, + tokenBalance, + selectedToken, + gasTotal, + setAmountToMax, + } = this.props + + setAmountToMax({ + tokenBalance, + selectedToken, + gasTotal, + setAmountToMax, + }) + } + + render () { + const { setMaxModeTo } = this.props + + return ( + <div + className='send-v2__amount-max' + onClick={(event) => { + event.preventDefault() + setMaxModeTo(true) + this.setAmountToMax() + }} + > + {!maxModeOn ? this.context.t('max') : '' ])} + </div> + ); + } + +} + +AmountMaxButton.contextTypes = { + t: PropTypes.func, +} diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.container.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.container.js new file mode 100644 index 000000000..572e1fc46 --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.container.js @@ -0,0 +1,36 @@ +import { + getSelectedToken, + getGasTotal, + getTokenBalance, + getSendFromBalance, +} from '../../../send.selectors.js' +import { getMaxModeOn } from '../send-amount-row.selectors.js' +import { calcMaxAmount } from './amount-max-button.utils.js' +import { + updateSendAmount, + setMaxModeTo, +} from '../../../actions' +import AmountMaxButton from './amount-max-button.component' + +export default connect(mapStateToProps, mapDispatchToProps)(SendToRow) + +function mapStateToProps (state) { + + return { + selectedToken: getSelectedToken(state), + maxModeOn: getMaxModeOn(state), + gasTotal: getGasTotal(state), + tokenBalance: getTokenBalance(state), + balance: getSendFromBalance(state), + } +} + +function mapDispatchToProps (dispatch) { + return { + setAmountToMax: maxAmountDataObject => { + updateSendErrors({ amount: null }) + updateSendAmount(calcMaxAmount(maxAmountDataObject)) + } + setMaxModeTo: bool => dispatch(setMaxModeTo(bool)), + } +}
\ No newline at end of file diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js new file mode 100644 index 000000000..54aacc8d7 --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js @@ -0,0 +1,22 @@ +const { + multiplyCurrencies, + subtractCurrencies, +} = require('../../../../conversion-util') +const ethUtil = require('ethereumjs-util') + +function calcMaxAmount ({ balance, gasTotal, selectedToken, tokenBalance }) { + const { decimals } = selectedToken || {} + const multiplier = Math.pow(10, Number(decimals || 0)) + + return selectedToken + ? multiplyCurrencies(tokenBalance, multiplier, {toNumericBase: 'hex'}) + : subtractCurrencies( + ethUtil.addHexPrefix(balance), + ethUtil.addHexPrefix(gasTotal), + { toNumericBase: 'hex' } + ) +} + +module.exports = { + calcMaxAmount +} diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js new file mode 100644 index 000000000..78038f714 --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js @@ -0,0 +1,91 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component' +import AmountMaxButton from '../amount-max-button/amount-max-button.component' +import CurrencyDisplay from '../../../send/currency-display' + +export default class SendAmountRow extends Component { + + static propTypes = { + amountConversionRate: PropTypes.string, + conversionRate: PropTypes.string, + from: PropTypes.object, + gasTotal: PropTypes.string, + primaryCurrency: PropTypes.string, + selectedToken: PropTypes.object, + tokenBalance: PropTypes.string, + updateSendAmountError: PropTypes.func, + updateSendAmount: PropTypes.func, + setMaxModeTo: PropTypes.func + } + + validateAmount (amount) { + const { + amountConversionRate, + conversionRate, + from: { balance }, + gasTotal, + primaryCurrency, + selectedToken, + tokenBalance, + updateSendAmountError, + } = this.props + + updateSendAmountError({ + amount, + amountConversionRate, + balance, + conversionRate, + gasTotal, + primaryCurrency, + selectedToken, + tokenBalance, + }) + } + + handleAmountChange (amount) { + const { updateSendAmount, setMaxModeTo } = this.props + + setMaxModeTo(false) + this.validateAmount(amount) + updateSendAmount(amount) + } + + render () { + const { + amount, + amountConversionRate, + convertedCurrency, + inError, + gasTotal, + maxModeOn, + primaryCurrency = 'ETH', + selectedToken, + } = this.props + + return ( + <SendRowWrapper + label={`${this.context.t('amount')}:`} + showError={inError} + errorType={'amount'} + > + !inError && gasTotal && <AmountMaxButton /> + <CurrencyDisplay + inError={inError}, + primaryCurrency={primaryCurrency}, + convertedCurrency={convertedCurrency}, + selectedToken={selectedToken}, + value={amount || '0x0'}, + conversionRate={amountConversionRate}, + handleChange={this.handleAmountChange}, + > + </SendRowWrapper> + ); + } + +} + +SendAmountRow.contextTypes = { + t: PropTypes.func, +} + diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js new file mode 100644 index 000000000..098855a02 --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js @@ -0,0 +1,49 @@ +import { + getSelectedToken, + getPrimaryCurrency, + getAmountConversionRate, + getConvertedCurrency, + getSendAmount, + getGasTotal, + getSelectedBalance, + getTokenBalance, + getSendFromBalance, +} from '../../send.selectors.js' +import { + getMaxModeOn, + sendAmountIsInError, +} from './send-amount-row.selectors.js' +import { getAmountErrorObject } from './send-amount-row.utils.js' +import { + updateSendAmount, + setMaxModeTo, +} from '../../../actions' +import SendAmountRow from './send-amount-row.component' + +export default connect(mapStateToProps, mapDispatchToProps)(SendToRow) + +function mapStateToProps (state) { +updateSendTo +return { + selectedToken: getSelectedToken(state), + primaryCurrency: getPrimaryCurrency(state), + convertedCurrency: getConvertedCurrency(state), + amountConversionRate: getAmountConversionRate(state), + inError: sendAmountIsInError(state), + amount: getSendAmount(state), + maxModeOn: getMaxModeOn(state), + gasTotal: getGasTotal(state), + tokenBalance: getTokenBalance(state), + balance: getSendFromBalance(state), +} +} + +function mapDispatchToProps (dispatch) { + return { + updateSendAmountError: (amountDataObject) => { + dispatch(updateSendErrors(getAmountErrorObject(amountDataObject))) + }, + updateSendAmount: newAmount => dispatch(updateSendAmount(newAmount)), + setMaxModeTo: bool => dispatch(setMaxModeTo(bool)), + } +}
\ No newline at end of file diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.scss b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.scss new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.scss diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.selectors.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.selectors.js new file mode 100644 index 000000000..724f345af --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.selectors.js @@ -0,0 +1,14 @@ +const selectors = { + getMaxModeOn, + sendAmountIsInError, +} + +module.exports = selectors + +function getMaxModeOn (state) { + return state.metamask.send.maxModeOn +} + +function sendAmountIsInError (state) { + return Boolean(state.metamask.send.errors.amount) +} diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.utils.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.utils.js new file mode 100644 index 000000000..5b01b4594 --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.utils.js @@ -0,0 +1,55 @@ +const { isValidAddress } = require('../../../../util') + +function getAmountErrorObject ({ + amount, + balance, + amountConversionRate, + conversionRate, + primaryCurrency, + selectedToken, + gasTotal, + tokenBalance, +}) { + let insufficientFunds = false + if (gasTotal && conversionRate) { + insufficientFunds = !isBalanceSufficient({ + amount: selectedToken ? '0x0' : amount, + gasTotal, + balance, + primaryCurrency, + amountConversionRate, + conversionRate, + }) + } + + let inSufficientTokens = false + if (selectedToken && tokenBalance !== null) { + const { decimals } = selectedToken + inSufficientTokens = !isTokenBalanceSufficient({ + tokenBalance, + amount, + decimals, + }) + } + + const amountLessThanZero = conversionGreaterThan( + { value: 0, fromNumericBase: 'dec' }, + { value: amount, fromNumericBase: 'hex' }, + ) + + let amountError = null + + if (insufficientFunds) { + amountError = this.context.t('insufficientFunds') + } else if (insufficientTokens) { + amountError = this.context.t('insufficientTokens') + } else if (amountLessThanZero) { + amountError = this.context.t('negativeETH') + } + + return { amount: amountError } +} + +module.exports = { + getAmountErrorObject +} diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-selectors.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-selectors.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-selectors.test.js diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-utils.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-utils.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-utils.test.js diff --git a/ui/app/components/send_/send-content/send-content-README.md b/ui/app/components/send_/send-content/send-content-README.md new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-content-README.md diff --git a/ui/app/components/send_/send-content/send-content.component.js b/ui/app/components/send_/send-content/send-content.component.js new file mode 100644 index 000000000..ad6b4a982 --- /dev/null +++ b/ui/app/components/send_/send-content/send-content.component.js @@ -0,0 +1,23 @@ +import React, { Component } from 'react' +import PageContainerContent from '../../page-container/page-container-header.component' +import SendFromRow from './send-from-row/send-from-row.component' +import SendToRow from './send-to-row/send-to-row.component' +import SendAmountRow from './send-amount-row/send-amount-row.component' +import SendGasRow from './send-gas-row/send-gas-row.component' + +export default class SendContent extends Component { + + render () { + return ( + <PageContainerContent> + <div className='.send-v2__form'> + <SendFromRow /> + <SendToRow /> + <SendAmountRow /> + <SendGasRow /> + </div> + </PageContainerContent> + ); + } + +} diff --git a/ui/app/components/send_/send-content/send-content.scss b/ui/app/components/send_/send-content/send-content.scss new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-content.scss diff --git a/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown-README.md b/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown-README.md new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown-README.md diff --git a/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.component.js b/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.component.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.component.js diff --git a/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.scss b/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.scss new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/from-dropdown/from-dropdown.scss diff --git a/ui/app/components/send_/send-content/send-from-row/from-dropdown/tests/from-dropdown-component.test.js b/ui/app/components/send_/send-content/send-from-row/from-dropdown/tests/from-dropdown-component.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/from-dropdown/tests/from-dropdown-component.test.js diff --git a/ui/app/components/send_/send-content/send-from-row/send-from-row-README.md b/ui/app/components/send_/send-content/send-from-row/send-from-row-README.md new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/send-from-row-README.md diff --git a/ui/app/components/send_/send-content/send-from-row/send-from-row.component.js b/ui/app/components/send_/send-content/send-from-row/send-from-row.component.js new file mode 100644 index 000000000..b17f749a6 --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/send-from-row.component.js @@ -0,0 +1,64 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component' +import FromDropdown from '../../../send/from-dropdown' + +export default class SendFromRow extends Component { + + static propTypes = { + closeFromDropdown: PropTypes.func, + conversionRate: PropTypes.string, + from: PropTypes.string, + fromAccounts: PropTypes.array, + fromDropdownOpen: PropTypes.bool, + openFromDropdown: PropTypes.func, + tokenContract: PropTypes.object, + updateSendFrom: PropTypes.func, + updateSendTokenBalance: PropTypes.func, + }; + + async handleFromChange (newFrom) { + const { + updateSendFrom, + tokenContract, + updateSendTokenBalance, + } = this.props + + if (tokenContract) { + const usersToken = await tokenContract.balanceOf(newFrom.address) + updateSendTokenBalance(usersToken) + } + updateSendFrom(newFrom) + } + + render () { + const { + from, + fromAccounts, + conversionRate, + fromDropdownOpen, + tokenContract, + openFromDropdown, + closeFromDropdown, + } = this.props + + return ( + <SendRowWrapper label={`${this.context.t('from')}:`}> + <FromDropdown + dropdownOpen={fromDropdownOpen} + accounts={fromAccounts} + selectedAccount={from} + onSelect={newFrom => this.handleFromChange(newFrom)} + openDropdown={() => openFromDropdown()} + closeDropdown={() => closeFromDropdown()} + conversionRate={conversionRate} + /> + </SendRowWrapper> + ); + } + +} + +SendFromRow.contextTypes = { + t: PropTypes.func, +} diff --git a/ui/app/components/send_/send-content/send-from-row/send-from-row.container.js b/ui/app/components/send_/send-content/send-from-row/send-from-row.container.js new file mode 100644 index 000000000..eeeb51629 --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/send-from-row.container.js @@ -0,0 +1,44 @@ +import { + getSendFrom, + getConversionRate, + getSelectedTokenContract, + getCurrentAccountWithSendEtherInfo, + accountsWithSendEtherInfoSelector, +} from '../../send.selectors.js' +import { getFromDropdownOpen } from './send-from-row.selectors.js' +import { calcTokenUpdateAmount } from './send-from-row.utils.js' +import { + updateSendTokenBalance, + updateSendFrom, +} from '../../../actions' +import { + openFromDropdown, + closeFromDropdown, +} from '../../../ducks/send' +import SendFromRow from './send-from-row.component' + +export default connect(mapStateToProps, mapDispatchToProps)(SendFromRow) + +function mapStateToProps (state) { + return { + from: getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state), + fromAccounts: accountsWithSendEtherInfoSelector(state), + conversionRate: getConversionRate(state), + fromDropdownOpen: getFromDropdownOpen(state), + tokenContract: getSelectedTokenContract(state), + } +} + +function mapDispatchToProps (dispatch) { + return { + updateSendTokenBalance: (usersToken, selectedToken) => { + if (!usersToken) return + + const tokenBalance = calcTokenUpdateAmount(selectedToken, selectedToken) + dispatch(updateSendTokenBalance(tokenBalance)) + }, + updateSendFrom: newFrom => dispatch(updateSendFrom(newFrom)), + openFromDropdown: () => dispatch(()), + closeFromDropdown: () => dispatch(()), + } +} diff --git a/ui/app/components/send_/send-content/send-from-row/send-from-row.selectors.js b/ui/app/components/send_/send-content/send-from-row/send-from-row.selectors.js new file mode 100644 index 000000000..03ef4806b --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/send-from-row.selectors.js @@ -0,0 +1,9 @@ +const selectors = { + getFromDropdownOpen, +} + +module.exports = selectors + +function getFromDropdownOpen (state) { + return state.send.fromDropdownOpen +} diff --git a/ui/app/components/send_/send-content/send-from-row/send-from-row.utils.js b/ui/app/components/send_/send-content/send-from-row/send-from-row.utils.js new file mode 100644 index 000000000..2be25816f --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/send-from-row.utils.js @@ -0,0 +1,12 @@ +const { + calcTokenAmount, +} = require('../../token-util') + +function calcTokenUpdateAmount (usersToken, selectedToken) { + const { decimals } = selectedToken || {} + return calcTokenAmount(usersToken.balance.toString(), decimals) +} + +module.exports = { + calcTokenUpdateAmount +} diff --git a/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-component.test.js b/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-component.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-component.test.js diff --git a/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-container.test.js b/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-container.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-container.test.js diff --git a/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-selectors.test.js b/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-selectors.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-from-row/tests/send-from-row-selectors.test.js diff --git a/ui/app/components/send_/send-content/send-gas-row/README.md b/ui/app/components/send_/send-content/send-gas-row/README.md new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-gas-row/README.md diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js new file mode 100644 index 000000000..8c1f14f48 --- /dev/null +++ b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js @@ -0,0 +1,60 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component' +import GasFeeDisplay from '../../../send/gas-fee-display-v2' + +export default class SendGasRow extends Component { + + static propTypes = { + closeFromDropdown: PropTypes.func, + conversionRate: PropTypes.string, + from: PropTypes.string, + fromAccounts: PropTypes.array, + fromDropdownOpen: PropTypes.bool, + openFromDropdown: PropTypes.func, + tokenContract: PropTypes.object, + updateSendFrom: PropTypes.func, + updateSendTokenBalance: PropTypes.func, + }; + + async handleFromChange (newFrom) { + const { + updateSendFrom, + tokenContract, + updateSendTokenBalance, + } = this.props + + if (tokenContract) { + const usersToken = await tokenContract.balanceOf(newFrom.address) + updateSendTokenBalance(usersToken) + } + updateSendFrom(newFrom) + } + + render () { + const { + conversionRate, + convertedCurrency, + showCustomizeGasModal, + gasTotal, + gasLoadingError, + } = this.props + + return ( + <SendRowWrapper label={`${this.context.t('gasFee')}:`}> + <GasFeeDisplay + gasTotal={gasTotal}, + conversionRate={conversionRate}, + convertedCurrency={convertedCurrency}, + onClick={() => showCustomizeGasModal()}, + gasLoadingError={gasLoadingError}, + /> + </SendRowWrapper> + ); + } + +} + +SendGasRow.contextTypes = { + t: PropTypes.func, +} diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js new file mode 100644 index 000000000..7fb3a68be --- /dev/null +++ b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js @@ -0,0 +1,26 @@ +import { + getConversionRate, + getConvertedCurrency, + getGasTotal, +} from '../../send.selectors.js' +import { getGasLoadingError } from './send-gas-row.selectors.js' +import { calcTokenUpdateAmount } from './send-gas-row.utils.js' +import { showModal } from '../../../actions' +import SendGasRow from './send-from-row.component' + +export default connect(mapStateToProps, mapDispatchToProps)(SendGasRow) + +function mapStateToProps (state) { + return { + conversionRate: getConversionRate(state), + convertedCurrency: getConvertedCurrency(state), + gasTotal: getGasTotal(state), + gasLoadingError: getGasLoadingError(state), + } +} + +function mapDispatchToProps (dispatch) { + return { + showCustomizeGasModal: () => dispatch(showModal({ name: 'CUSTOMIZE_GAS' })), + } +} diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.scss b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.scss new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.scss diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.selectors.js b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.selectors.js new file mode 100644 index 000000000..d069ae8c6 --- /dev/null +++ b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.selectors.js @@ -0,0 +1,9 @@ +const selectors = { + sendGasIsInError, +} + +module.exports = selectors + +function sendGasIsInError (state) { + return state.send.errors.gasLoading +} diff --git a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js diff --git a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js diff --git a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-selectors.test.js b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-selectors.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-selectors.test.js diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message-README.md b/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message-README.md new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message-README.md diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js b/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js new file mode 100644 index 000000000..08f830cc5 --- /dev/null +++ b/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js @@ -0,0 +1,23 @@ +export default class SendRowErrorMessage extends Component { + + static propTypes = { + errors: PropTypes.object, + errorType: PropTypes.string, + }; + + render () { + const { errors, errorType } = this.props + const errorMessage = errors[errorType] + + return ( + errorMessage + ? <div className='send-v2__error'>{errorMessage}</div> + : null + ); + } + +} + +SendRowErrorMessage.contextTypes = { + t: PropTypes.func, +}
\ No newline at end of file diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.container.js b/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.container.js new file mode 100644 index 000000000..2278dbe63 --- /dev/null +++ b/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.container.js @@ -0,0 +1,11 @@ +import { getSendErrors } from '../../../send.selectors' +import SendRowErrorMessage from './send-row-error-message.component' + +export default connect(mapStateToProps)(SendRowErrorMessage) + +function mapStateToProps (state, ownProps) { + return { + errors: getSendErrors(state), + errorType: ownProps.errorType, + } +}
\ No newline at end of file diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.scss b/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.scss new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.scss diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper-README.md b/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper-README.md new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper-README.md diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.component.js b/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.component.js new file mode 100644 index 000000000..92382da01 --- /dev/null +++ b/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.component.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import SendRowErrorMessage from './send-row-error-message/send-row-error-message.container' + +export default class SendRowWrapper extends Component { + + static propTypes = { + label: PropTypes.string, + showError: PropTypes.bool, + children: PropTypes.node, + errorType: PropTypes.string, + }; + + render () { + const { + label, + errorType = '', + showError = false, + children, + } = this.props + + let formField = children[0] + let customLabelContent = null + + if (children.length === 2) { + formField = children[1] + customLabelContent = children[0] + } + + return ( + <div className="send-v2__form-row"> + <div className="send-v2__form-label"> + {label} + (showError && <SendRowErrorMessage errorType={errorType}/>) + {customLabelContent} + </div> + <div className="send-v2__form-field"> + {formField} + </div> + </div> + ); + } + +} + +SendRowWrapper.contextTypes = { + t: PropTypes.func, +} diff --git a/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.scss b/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.scss new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-row-wrapper/send-row-wrapper.scss diff --git a/ui/app/components/send_/send-content/send-row-wrapper/tests/send-row-wrapper-component.test.js b/ui/app/components/send_/send-content/send-row-wrapper/tests/send-row-wrapper-component.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-row-wrapper/tests/send-row-wrapper-component.test.js diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row-README.md b/ui/app/components/send_/send-content/send-to-row/send-to-row-README.md new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-to-row/send-to-row-README.md diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js b/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js new file mode 100644 index 000000000..5f81402d8 --- /dev/null +++ b/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js @@ -0,0 +1,66 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import SendRowWrapper from '../send-row-wrapper/send-row-wrapper.component' +import ToDropdown from '../../../ens-input' + +export default class SendToRow extends Component { + + static propTypes = { + to: PropTypes.string, + toAccounts: PropTypes.array, + toDropdownOpen: PropTypes.bool, + inError: PropTypes.bool, + updateSendTo: PropTypes.func, + updateSendToError: PropTypes.func, + openToDropdown: PropTypes.func, + closeToDropdown: PropTypes.func, + network: PropTypes.number, + }; + + handleToChange (to, nickname = '') { + const { updateSendTo, updateSendToError } = this.props + updateSendTo(to, nickname) + updateSendErrors(to) + } + + render () { + const { + from, + fromAccounts, + conversionRate, + fromDropdownOpen, + tokenContract, + openToDropdown, + closeToDropdown, + network, + inError, + } = this.props + + return ( + <SendRowWrapper + label={`${this.context.t('to')}:`} + showError={inError} + errorType={'to'} + > + <EnsInput + name={'address'} + placeholder={this.context.t('recipient Address')} + network={network}, + to={to}, + accounts={toAccounts} + dropdownOpen={toDropdownOpen} + openDropdown={() => openToDropdown()} + closeDropdown={() => closeToDropdown()} + onChange={this.handleToChange} + inError={inError} + /> + </SendRowWrapper> + ); + } + +} + +SendToRow.contextTypes = { + t: PropTypes.func, +} + diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.container.js b/ui/app/components/send_/send-content/send-to-row/send-to-row.container.js new file mode 100644 index 000000000..1c446c168 --- /dev/null +++ b/ui/app/components/send_/send-content/send-to-row/send-to-row.container.js @@ -0,0 +1,43 @@ +import { + getSendTo, + getToAccounts, + getCurrentNetwork, +} from '../../send.selectors.js' +import { + getToDropdownOpen, + sendToIsInError, +} from './send-to-row.selectors.js' +import { getToErrorObject } from './send-to-row.utils.js' +import { + updateSendErrors, + updateSendTo, +} from '../../../actions' +import { + openToDropdown, + closeToDropdown, +} from '../../../ducks/send' +import SendToRow from './send-to-row.component' + +export default connect(mapStateToProps, mapDispatchToProps)(SendToRow) + +function mapStateToProps (state) { + updateSendTo + return { + to: getSendTo(state), + toAccounts: getSendToAccounts(state), + toDropdownOpen: getToDropdownOpen(state), + inError: sendToIsInError(state), + network: getCurrentNetwork(state), + } +} + +function mapDispatchToProps (dispatch) { + return { + updateSendToError: (to) => { + dispatch(updateSendErrors(getToErrorObject(to))) + }, + updateSendTo: (to, nickname) => dispatch(updateSendTo(to, nickname)), + openToDropdown: () => dispatch(()), + closeToDropdown: () => dispatch(()), + } +}
\ No newline at end of file diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.selectors.js b/ui/app/components/send_/send-content/send-to-row/send-to-row.selectors.js new file mode 100644 index 000000000..c741aad84 --- /dev/null +++ b/ui/app/components/send_/send-content/send-to-row/send-to-row.selectors.js @@ -0,0 +1,14 @@ +const selectors = { + getToDropdownOpen, + sendToIsInError, +} + +module.exports = selectors + +function getToDropdownOpen (state) { + return state.send.toDropdownOpen +} + +function sendToIsInError (state) { + return Boolean(state.metamask.send.errors.to) +} diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js b/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js new file mode 100644 index 000000000..52bfde009 --- /dev/null +++ b/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js @@ -0,0 +1,17 @@ +const { isValidAddress } = require('../../../../util') + +function getToErrorObject (to) { + let toError = null + + if (!to) { + toError = 'required' + } else if (!isValidAddress(to)) { + toError = 'invalidAddressRecipient' + } + + return { to: toError } +} + +module.exports = { + getToErrorObject +} diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-container.test.js b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-container.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-container.test.js diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-selectors.test.js b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-selectors.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-selectors.test.js diff --git a/ui/app/components/send_/send-content/tests/send-content-component.test.js b/ui/app/components/send_/send-content/tests/send-content-component.test.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ui/app/components/send_/send-content/tests/send-content-component.test.js |