diff options
Diffstat (limited to 'ui/app/pages')
13 files changed, 163 insertions, 45 deletions
diff --git a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js index 3c4e6dcac..c6a05cf0f 100644 --- a/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/app/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -9,6 +9,7 @@ import { DEFAULT_ROUTE, CONFIRM_TRANSACTION_ROUTE } from '../../helpers/constant import { INSUFFICIENT_FUNDS_ERROR_KEY, TRANSACTION_ERROR_KEY, + GAS_LIMIT_TOO_LOW_ERROR_KEY, } from '../../helpers/constants/error-keys' import { CONFIRMED_STATUS, DROPPED_STATUS } from '../../helpers/constants/transactions' import UserPreferencedCurrencyDisplay from '../../components/app/user-preferenced-currency-display' @@ -134,6 +135,7 @@ export default class ConfirmTransactionBase extends Component { value: amount, } = {}, } = {}, + customGas, } = this.props const insufficientBalance = balance && !isBalanceSufficient({ @@ -150,6 +152,13 @@ export default class ConfirmTransactionBase extends Component { } } + if (customGas.gasLimit < 21000) { + return { + valid: false, + errorKey: GAS_LIMIT_TOO_LOW_ERROR_KEY, + } + } + if (simulationFails) { return { valid: true, diff --git a/ui/app/pages/create-account/connect-hardware/index.js b/ui/app/pages/create-account/connect-hardware/index.js index 5a91a2725..80a160205 100644 --- a/ui/app/pages/create-account/connect-hardware/index.js +++ b/ui/app/pages/create-account/connect-hardware/index.js @@ -8,8 +8,6 @@ const ConnectScreen = require('./connect-screen') const AccountList = require('./account-list') const { DEFAULT_ROUTE } = require('../../../helpers/constants/routes') const { formatBalance } = require('../../../helpers/utils/util') -const { getPlatform } = require('../../../../../app/scripts/lib/util') -const { PLATFORM_FIREFOX } = require('../../../../../app/scripts/lib/enums') class ConnectHardwareForm extends Component { constructor (props) { @@ -51,12 +49,6 @@ class ConnectHardwareForm extends Component { } connectToHardwareWallet = (device) => { - // Ledger hardware wallets are not supported on firefox - if (getPlatform() === PLATFORM_FIREFOX && device === 'ledger') { - this.setState({ browserSupported: false, error: null}) - return null - } - if (this.state.accounts.length) { return null } diff --git a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js index e256d1442..7901ccef6 100644 --- a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js +++ b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.component.js @@ -1,16 +1,21 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' +import classnames from 'classnames' export default class AmountMaxButton extends Component { static propTypes = { balance: PropTypes.string, + buttonDataLoading: PropTypes.bool, + clearMaxAmount: PropTypes.func, + inError: PropTypes.bool, gasTotal: PropTypes.string, maxModeOn: PropTypes.bool, selectedToken: PropTypes.object, setAmountToMax: PropTypes.func, setMaxModeTo: PropTypes.func, tokenBalance: PropTypes.string, + } static contextTypes = { @@ -35,8 +40,8 @@ export default class AmountMaxButton extends Component { }) } - onMaxClick = (event) => { - const { setMaxModeTo } = this.props + onMaxClick = () => { + const { setMaxModeTo, clearMaxAmount, maxModeOn } = this.props const { metricsEvent } = this.context metricsEvent({ @@ -46,25 +51,25 @@ export default class AmountMaxButton extends Component { name: 'Clicked "Amount Max"', }, }) - - event.preventDefault() - setMaxModeTo(true) - this.setMaxAmount() + if (!maxModeOn) { + setMaxModeTo(true) + this.setMaxAmount() + } else { + setMaxModeTo(false) + clearMaxAmount() + } } render () { - return this.props.maxModeOn - ? null - : ( - <div> - <span - className="send-v2__amount-max" - onClick={this.onMaxClick} - > - {this.context.t('max')} - </span> - </div> + const { maxModeOn, buttonDataLoading, inError } = this.props + + return ( + <div className={'send-v2__amount-max'} onClick={buttonDataLoading || inError ? null : this.onMaxClick}> + <input type="checkbox" checked={maxModeOn} /> + <div className={classnames('send-v2__amount-max__button', { 'send-v2__amount-max__button__disabled': buttonDataLoading || inError })}> + {this.context.t('max')} + </div> + </div> ) } - } diff --git a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js index cd48a105f..e444589a1 100644 --- a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js +++ b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js @@ -5,6 +5,7 @@ import { getSendFromBalance, getTokenBalance, } from '../../../send.selectors.js' +import { getBasicGasEstimateLoadingStatus } from '../../../../../selectors/custom-gas' import { getMaxModeOn } from './amount-max-button.selectors.js' import { calcMaxAmount } from './amount-max-button.utils.js' import { @@ -22,6 +23,7 @@ function mapStateToProps (state) { return { balance: getSendFromBalance(state), + buttonDataLoading: getBasicGasEstimateLoadingStatus(state), gasTotal: getGasTotal(state), maxModeOn: getMaxModeOn(state), selectedToken: getSelectedToken(state), @@ -35,6 +37,9 @@ function mapDispatchToProps (dispatch) { dispatch(updateSendErrors({ amount: null })) dispatch(updateSendAmount(calcMaxAmount(maxAmountDataObject))) }, + clearMaxAmount: () => { + dispatch(updateSendAmount('0')) + }, setMaxModeTo: bool => dispatch(setMaxModeTo(bool)), } } diff --git a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js index a6cb29d4c..f986b26bb 100644 --- a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js +++ b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js @@ -65,7 +65,7 @@ describe('AmountMaxButton Component', function () { assert(wrapper.exists('.send-v2__amount-max')) }) - it('should call setMaxModeTo and setMaxAmount when the send-v2__amount-max div is clicked', () => { + it('should call setMaxModeTo and setMaxAmount when the checkbox is checked', () => { const { onClick, } = wrapper.find('.send-v2__amount-max').props() @@ -81,11 +81,6 @@ describe('AmountMaxButton Component', function () { ) }) - it('should not render anything when maxModeOn is true', () => { - wrapper.setProps({ maxModeOn: true }) - assert.ok(!wrapper.exists('.send-v2__amount-max')) - }) - it('should render the expected text when maxModeOn is false', () => { wrapper.setProps({ maxModeOn: false }) assert.equal(wrapper.find('.send-v2__amount-max').text(), 'max_t') diff --git a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js index a75ed5e8f..dcee8fda0 100644 --- a/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js +++ b/ui/app/pages/send/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js @@ -29,6 +29,7 @@ proxyquire('../amount-max-button.container.js', { }, './amount-max-button.selectors.js': { getMaxModeOn: (s) => `mockMaxModeOn:${s}` }, './amount-max-button.utils.js': { calcMaxAmount: (mockObj) => mockObj.val + 1 }, + '../../../../../selectors/custom-gas': { getBasicGasEstimateLoadingStatus: (s) => `mockButtonDataLoading:${s}`}, '../../../../../store/actions': actionSpies, '../../../../../ducks/send/send.duck': duckActionSpies, }) @@ -40,6 +41,7 @@ describe('amount-max-button container', () => { it('should map the correct properties to props', () => { assert.deepEqual(mapStateToProps('mockState'), { balance: 'mockBalance:mockState', + buttonDataLoading: 'mockButtonDataLoading:mockState', gasTotal: 'mockGasTotal:mockState', maxModeOn: 'mockMaxModeOn:mockState', selectedToken: 'mockSelectedToken:mockState', diff --git a/ui/app/pages/send/send-content/send-amount-row/send-amount-row.component.js b/ui/app/pages/send/send-content/send-amount-row/send-amount-row.component.js index c0241ea91..10e90c419 100644 --- a/ui/app/pages/send/send-content/send-amount-row/send-amount-row.component.js +++ b/ui/app/pages/send/send-content/send-amount-row/send-amount-row.component.js @@ -110,7 +110,7 @@ export default class SendAmountRow extends Component { showError={inError} errorType={'amount'} > - {!inError && gasTotal && <AmountMaxButton />} + {gasTotal && <AmountMaxButton inError={inError} />} { this.renderInput() } </SendRowWrapper> ) diff --git a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js index 1b850ac57..4c09ed564 100644 --- a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js +++ b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.component.js @@ -8,14 +8,19 @@ import AdvancedGasInputs from '../../../../components/app/gas-customization/adva export default class SendGasRow extends Component { static propTypes = { + balance: PropTypes.string, conversionRate: PropTypes.number, convertedCurrency: PropTypes.string, gasFeeError: PropTypes.bool, gasLoadingError: PropTypes.bool, gasTotal: PropTypes.string, + maxModeOn: PropTypes.bool, showCustomizeGasModal: PropTypes.func, + selectedToken: PropTypes.object, + setAmountToMax: PropTypes.func, setGasPrice: PropTypes.func, setGasLimit: PropTypes.func, + tokenBalance: PropTypes.string, gasPriceButtonGroupProps: PropTypes.object, gasButtonGroupShown: PropTypes.bool, advancedInlineGasShown: PropTypes.bool, @@ -47,6 +52,23 @@ export default class SendGasRow extends Component { </div> } + setMaxAmount () { + const { + balance, + gasTotal, + selectedToken, + setAmountToMax, + tokenBalance, + } = this.props + + setAmountToMax({ + balance, + gasTotal, + selectedToken, + tokenBalance, + }) + } + renderContent () { const { conversionRate, @@ -57,6 +79,7 @@ export default class SendGasRow extends Component { gasPriceButtonGroupProps, gasButtonGroupShown, advancedInlineGasShown, + maxModeOn, resetGasButtons, setGasPrice, setGasLimit, @@ -71,7 +94,7 @@ export default class SendGasRow extends Component { className="gas-price-button-group--small" showCheck={false} {...gasPriceButtonGroupProps} - handleGasPriceSelection={(...args) => { + handleGasPriceSelection={async (...args) => { metricsEvent({ eventOpts: { category: 'Transactions', @@ -79,7 +102,10 @@ export default class SendGasRow extends Component { name: 'Changed Gas Button', }, }) - gasPriceButtonGroupProps.handleGasPriceSelection(...args) + await gasPriceButtonGroupProps.handleGasPriceSelection(...args) + if (maxModeOn) { + this.setMaxAmount() + } }} /> { this.renderAdvancedOptionsButton() } @@ -89,7 +115,12 @@ export default class SendGasRow extends Component { convertedCurrency={convertedCurrency} gasLoadingError={gasLoadingError} gasTotal={gasTotal} - onReset={resetGasButtons} + onReset={() => { + resetGasButtons() + if (maxModeOn) { + this.setMaxAmount() + } + }} onClick={() => showCustomizeGasModal()} /> const advancedGasInputs = <div> diff --git a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js index c4daa98af..10eaa50b8 100644 --- a/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js +++ b/ui/app/pages/send/send-content/send-gas-row/send-gas-row.container.js @@ -6,11 +6,17 @@ import { getGasPrice, getGasLimit, getSendAmount, + getSendFromBalance, + getTokenBalance, } from '../../send.selectors.js' import { + getMaxModeOn, +} from '../send-amount-row/amount-max-button/amount-max-button.selectors' +import { isBalanceSufficient, calcGasTotal, } from '../../send.utils.js' +import { calcMaxAmount } from '../send-amount-row/amount-max-button/amount-max-button.utils' import { getBasicGasEstimateLoadingStatus, getRenderableEstimateDataForSmallButtonsFromGWEI, @@ -18,6 +24,7 @@ import { } from '../../../../selectors/custom-gas' import { showGasButtonGroup, + updateSendErrors, } from '../../../../ducks/send/send.duck' import { resetCustomData, @@ -25,10 +32,11 @@ import { setCustomGasLimit, } from '../../../../ducks/gas/gas.duck' import { getGasLoadingError, gasFeeIsInError, getGasButtonGroupShown } from './send-gas-row.selectors.js' -import { showModal, setGasPrice, setGasLimit, setGasTotal } from '../../../../store/actions' +import { showModal, setGasPrice, setGasLimit, setGasTotal, updateSendAmount } from '../../../../store/actions' import { getAdvancedInlineGasShown, getCurrentEthBalance, getSelectedToken } from '../../../../selectors/selectors' import SendGasRow from './send-gas-row.component' + export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(SendGasRow) function mapStateToProps (state) { @@ -49,6 +57,7 @@ function mapStateToProps (state) { }) return { + balance: getSendFromBalance(state), conversionRate, convertedCurrency: getCurrentCurrency(state), gasTotal, @@ -65,6 +74,9 @@ function mapStateToProps (state) { gasPrice, gasLimit, insufficientBalance, + maxModeOn: getMaxModeOn(state), + selectedToken: getSelectedToken(state), + tokenBalance: getTokenBalance(state), } } @@ -85,6 +97,10 @@ function mapDispatchToProps (dispatch) { dispatch(setGasTotal(calcGasTotal(newLimit, gasPrice))) } }, + setAmountToMax: maxAmountDataObject => { + dispatch(updateSendErrors({ amount: null })) + dispatch(updateSendAmount(calcMaxAmount(maxAmountDataObject))) + }, showGasButtonGroup: () => dispatch(showGasButtonGroup()), resetCustomData: () => dispatch(resetCustomData()), } diff --git a/ui/app/pages/send/send-content/send-gas-row/tests/send-gas-row-container.test.js b/ui/app/pages/send/send-content/send-gas-row/tests/send-gas-row-container.test.js index ddc6ea985..4acb310f8 100644 --- a/ui/app/pages/send/send-content/send-gas-row/tests/send-gas-row-container.test.js +++ b/ui/app/pages/send/send-content/send-gas-row/tests/send-gas-row-container.test.js @@ -44,6 +44,11 @@ proxyquire('../send-gas-row.container.js', { getGasPrice: (s) => `mockGasPrice:${s}`, getGasLimit: (s) => `mockGasLimit:${s}`, getSendAmount: (s) => `mockSendAmount:${s}`, + getSendFromBalance: (s) => `mockBalance:${s}`, + getTokenBalance: (s) => `mockTokenBalance:${s}`, + }, + '../send-amount-row/amount-max-button/amount-max-button.selectors': { + getMaxModeOn: (s) => `mockMaxModeOn:${s}`, }, '../../send.utils.js': { isBalanceSufficient: ({ @@ -75,6 +80,7 @@ describe('send-gas-row container', () => { it('should map the correct properties to props', () => { assert.deepEqual(mapStateToProps('mockState'), { + balance: 'mockBalance:mockState', conversionRate: 'mockConversionRate:mockState', convertedCurrency: 'mockConvertedCurrency:mockState', gasTotal: 'mockGasTotal:mockState', @@ -91,6 +97,9 @@ describe('send-gas-row container', () => { gasLimit: 'mockGasLimit:mockState', gasPrice: 'mockGasPrice:mockState', insufficientBalance: false, + maxModeOn: 'mockMaxModeOn:mockState', + selectedToken: false, + tokenBalance: 'mockTokenBalance:mockState', }) }) diff --git a/ui/app/pages/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js b/ui/app/pages/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js index 61bc7bab7..0be01996a 100644 --- a/ui/app/pages/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js +++ b/ui/app/pages/send/send-content/send-row-wrapper/send-row-error-message/send-row-error-message.component.js @@ -1,5 +1,6 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' +import classnames from 'classnames' export default class SendRowErrorMessage extends Component { @@ -19,7 +20,7 @@ export default class SendRowErrorMessage extends Component { return ( errorMessage - ? <div className="send-v2__error">{this.context.t(errorMessage)}</div> + ? <div className={classnames('send-v2__error', {'send-v2__error-amount': errorType === 'amount'})}>{this.context.t(errorMessage)}</div> : null ) } diff --git a/ui/app/pages/send/send-content/send-row-wrapper/send-row-wrapper.component.js b/ui/app/pages/send/send-content/send-row-wrapper/send-row-wrapper.component.js index 94309bd96..075b86633 100644 --- a/ui/app/pages/send/send-content/send-row-wrapper/send-row-wrapper.component.js +++ b/ui/app/pages/send/send-content/send-row-wrapper/send-row-wrapper.component.js @@ -18,7 +18,7 @@ export default class SendRowWrapper extends Component { t: PropTypes.func, }; - render () { + renderAmountFormRow () { const { children, errorType = '', @@ -34,7 +34,39 @@ export default class SendRowWrapper extends Component { <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-container"> + <div className="send-v2__form-field"> + {formField} + </div> + <div> + {showError && <SendRowErrorMessage errorType={errorType} />} + {!showError && showWarning && <SendRowWarningMessage warningType={warningType} />} + </div> + </div> + </div> + ) + } + + renderFormRow () { + const { + children, + errorType = '', + label, + showError = false, + showWarning = false, + warningType = '', + } = this.props + + const formField = Array.isArray(children) ? children[1] || children[0] : children + const customLabelContent = (Array.isArray(children) && children.length) > 1 ? children[0] : null + + return ( + <div className="send-v2__form-row"> + <div className="send-v2__form-label"> + {label} + {showError && <SendRowErrorMessage errorType={errorType} />} {!showError && showWarning && <SendRowWarningMessage warningType={warningType} />} {customLabelContent} </div> @@ -45,4 +77,14 @@ export default class SendRowWrapper extends Component { ) } + render () { + const { + errorType = '', + } = this.props + + return ( + errorType === 'amount' ? this.renderAmountFormRow() : this.renderFormRow() + ) + } + } diff --git a/ui/app/pages/send/to-autocomplete/to-autocomplete.js b/ui/app/pages/send/to-autocomplete/to-autocomplete.js index 328a5b62b..11f86acf3 100644 --- a/ui/app/pages/send/to-autocomplete/to-autocomplete.js +++ b/ui/app/pages/send/to-autocomplete/to-autocomplete.js @@ -1,6 +1,7 @@ const Component = require('react').Component const PropTypes = require('prop-types') const h = require('react-hyperscript') +const copyToClipboard = require('copy-to-clipboard') const inherits = require('util').inherits const AccountListItem = require('../account-list-item/account-list-item.component').default const connect = require('react-redux').connect @@ -93,24 +94,34 @@ ToAutoComplete.prototype.componentDidUpdate = function (nextProps) { ToAutoComplete.prototype.render = function () { const { to, + recipient, dropdownOpen, onChange, inError, qrScanner, } = this.props - return h('div.send-v2__to-autocomplete', {}, [ + const isRecipientToDiff = recipient && recipient !== to + + return h('div.send-v2__to-autocomplete', {style: { + borderColor: inError ? 'red' : null, + }}, [ h(`input.send-v2__to-autocomplete__input${qrScanner ? '.with-qr' : ''}`, { placeholder: this.context.t('recipientAddress'), className: inError ? `send-v2__error-border` : '', - value: to, + value: recipient, onChange: event => onChange(event.target.value), onFocus: event => this.handleInputEvent(event), - style: { - borderColor: inError ? 'red' : null, - }, }), + isRecipientToDiff && h(Tooltip, {title: this.context.t('copyToClipboard')}, + h('div.send-v2__to-autocomplete__resolved', { + onClick: (event) => { + event.preventDefault() + event.stopPropagation() + copyToClipboard(to) + }, + }, to)), qrScanner && h(Tooltip, { title: this.context.t('scanQrCode'), position: 'bottom', |