diff options
author | Dan J Miller <danjm.com@gmail.com> | 2019-02-06 08:24:28 +0800 |
---|---|---|
committer | Dan Finlay <542863+danfinlay@users.noreply.github.com> | 2019-02-06 08:24:28 +0800 |
commit | 38b91f63a21d1563cf88307e280f52836df005db (patch) | |
tree | 4632fe335ab9ff05df98f6739891a00a5229d90c /ui/app/components/send/send-content | |
parent | c28fa312503b7c868bfcfceb42b3a79c0f25d492 (diff) | |
download | tangerine-wallet-browser-38b91f63a21d1563cf88307e280f52836df005db.tar tangerine-wallet-browser-38b91f63a21d1563cf88307e280f52836df005db.tar.gz tangerine-wallet-browser-38b91f63a21d1563cf88307e280f52836df005db.tar.bz2 tangerine-wallet-browser-38b91f63a21d1563cf88307e280f52836df005db.tar.lz tangerine-wallet-browser-38b91f63a21d1563cf88307e280f52836df005db.tar.xz tangerine-wallet-browser-38b91f63a21d1563cf88307e280f52836df005db.tar.zst tangerine-wallet-browser-38b91f63a21d1563cf88307e280f52836df005db.zip |
Add togglable advanced gas controls on send and confirm screens (#6112)
* Extract advanced gas input controls to their own component
* Add advanced inline gas toggle to settings
* Add optional advanced inline gas to send send screen
* Adds optional advanced gas inputs to the confirm screen
* Add info modals for advanced gas inputs.
* Fix translation of advance gas toggle description.
* Lint and unit test fixes for inline-advanced-gas-inputs
* Increase margin above advanced options button on send screen
* Move methods from constructor to property syntax in advanced-gas-inputs.component
Diffstat (limited to 'ui/app/components/send/send-content')
3 files changed, 153 insertions, 30 deletions
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 index 8d305dd4f..50337e0bf 100644 --- 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 @@ -3,6 +3,7 @@ import PropTypes from 'prop-types' import SendRowWrapper from '../send-row-wrapper/' import GasFeeDisplay from './gas-fee-display/gas-fee-display.component' import GasPriceButtonGroup from '../../../gas-customization/gas-price-button-group' +import AdvancedGasInputs from '../../../gas-customization/advanced-gas-inputs' export default class SendGasRow extends Component { @@ -13,54 +14,94 @@ export default class SendGasRow extends Component { gasLoadingError: PropTypes.bool, gasTotal: PropTypes.string, showCustomizeGasModal: PropTypes.func, + setGasPrice: PropTypes.func, + setGasLimit: PropTypes.func, gasPriceButtonGroupProps: PropTypes.object, gasButtonGroupShown: PropTypes.bool, + advancedInlineGasShown: PropTypes.bool, resetGasButtons: PropTypes.func, + gasPrice: PropTypes.number, + gasLimit: PropTypes.number, + insufficientBalance: PropTypes.bool, } static contextTypes = { t: PropTypes.func, } - render () { + renderAdvancedOptionsButton () { + const { showCustomizeGasModal } = this.props + return <div className="advanced-gas-options-btn" onClick={() => showCustomizeGasModal()}> + { this.context.t('advancedOptions') } + </div> + } + + renderContent () { const { conversionRate, convertedCurrency, gasLoadingError, gasTotal, - gasFeeError, showCustomizeGasModal, gasPriceButtonGroupProps, gasButtonGroupShown, + advancedInlineGasShown, resetGasButtons, + setGasPrice, + setGasLimit, + gasPrice, + gasLimit, + insufficientBalance, } = this.props + const gasPriceButtonGroup = <div> + <GasPriceButtonGroup + className="gas-price-button-group--small" + showCheck={false} + {...gasPriceButtonGroupProps} + /> + { this.renderAdvancedOptionsButton() } + </div> + const gasFeeDisplay = <GasFeeDisplay + conversionRate={conversionRate} + convertedCurrency={convertedCurrency} + gasLoadingError={gasLoadingError} + gasTotal={gasTotal} + onReset={resetGasButtons} + onClick={() => showCustomizeGasModal()} + /> + const advancedGasInputs = <div> + <AdvancedGasInputs + updateCustomGasPrice={newGasPrice => setGasPrice(newGasPrice, gasLimit)} + updateCustomGasLimit={newGasLimit => setGasLimit(newGasLimit, gasPrice)} + customGasPrice={gasPrice} + customGasLimit={gasLimit} + insufficientBalance={insufficientBalance} + customPriceIsSafe={true} + isSpeedUp={false} + /> + { this.renderAdvancedOptionsButton() } + </div> + + if (advancedInlineGasShown) { + return advancedGasInputs + } else if (gasButtonGroupShown) { + return gasPriceButtonGroup + } else { + return gasFeeDisplay + } + } + + render () { + const { gasFeeError } = this.props + return ( <SendRowWrapper label={`${this.context.t('transactionFee')}:`} showError={gasFeeError} errorType={'gasFee'} > - {gasButtonGroupShown - ? <div> - <GasPriceButtonGroup - className="gas-price-button-group--small" - showCheck={false} - {...gasPriceButtonGroupProps} - /> - <div className="advanced-gas-options-btn" onClick={() => showCustomizeGasModal()}> - { this.context.t('advancedOptions') } - </div> - </div> - : <GasFeeDisplay - conversionRate={conversionRate} - convertedCurrency={convertedCurrency} - gasLoadingError={gasLoadingError} - gasTotal={gasTotal} - onReset={resetGasButtons} - onClick={() => showCustomizeGasModal()} - />} - + { this.renderContent() } </SendRowWrapper> ) } 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 index 977f8ab3c..b32928b75 100644 --- 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 @@ -4,32 +4,59 @@ import { getCurrentCurrency, getGasTotal, getGasPrice, + getGasLimit, + getSendAmount, } from '../../send.selectors.js' import { + isBalanceSufficient, + calcGasTotal, +} from '../../send.utils.js' +import { getBasicGasEstimateLoadingStatus, getRenderableEstimateDataForSmallButtonsFromGWEI, getDefaultActiveButtonIndex, } from '../../../../selectors/custom-gas' import { + decGWEIToHexWEI, + decimalToHex, + convertGasPriceForInputs, + convertGasLimitForInputs, +} from '../../../../helpers/conversions.util' +import { showGasButtonGroup, } from '../../../../ducks/send.duck' import { resetCustomData, } from '../../../../ducks/gas.duck' import { getGasLoadingError, gasFeeIsInError, getGasButtonGroupShown } from './send-gas-row.selectors.js' -import { showModal, setGasPrice } from '../../../../actions' +import { showModal, setGasPrice, setGasLimit, setGasTotal } from '../../../../actions' +import { getAdvancedInlineGasShown, getCurrentEthBalance } from '../../../../selectors' import SendGasRow from './send-gas-row.component' export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(SendGasRow) function mapStateToProps (state) { const gasButtonInfo = getRenderableEstimateDataForSmallButtonsFromGWEI(state) - const activeButtonIndex = getDefaultActiveButtonIndex(gasButtonInfo, getGasPrice(state)) + const gasPrice = getGasPrice(state) + const activeButtonIndex = getDefaultActiveButtonIndex(gasButtonInfo, gasPrice) + const renderableGasPrice = convertGasPriceForInputs(gasPrice) + const renderableGasLimit = convertGasLimitForInputs(getGasLimit(state)) + + const gasTotal = getGasTotal(state) + const conversionRate = getConversionRate(state) + const balance = getCurrentEthBalance(state) + + const insufficientBalance = !isBalanceSufficient({ + amount: getSendAmount(state), + gasTotal, + balance, + conversionRate, + }) return { - conversionRate: getConversionRate(state), + conversionRate, convertedCurrency: getCurrentCurrency(state), - gasTotal: getGasTotal(state), + gasTotal, gasFeeError: gasFeeIsInError(state), gasLoadingError: getGasLoadingError(state), gasPriceButtonGroupProps: { @@ -39,13 +66,26 @@ function mapStateToProps (state) { gasButtonInfo, }, gasButtonGroupShown: getGasButtonGroupShown(state), + advancedInlineGasShown: getAdvancedInlineGasShown(state), + gasPrice: renderableGasPrice, + gasLimit: renderableGasLimit, + insufficientBalance, } } function mapDispatchToProps (dispatch) { return { showCustomizeGasModal: () => dispatch(showModal({ name: 'CUSTOMIZE_GAS', hideBasic: true })), - setGasPrice: newPrice => dispatch(setGasPrice(newPrice)), + setGasPrice: (newPrice, gasLimit) => { + newPrice = decGWEIToHexWEI(newPrice) + dispatch(setGasPrice(newPrice)) + dispatch(setGasTotal(calcGasTotal(gasLimit, newPrice))) + }, + setGasLimit: (newLimit, gasPrice) => { + newLimit = decimalToHex(newLimit) + dispatch(setGasLimit(newLimit)) + dispatch(setGasTotal(calcGasTotal(newLimit, gasPrice))) + }, showGasButtonGroup: () => dispatch(showGasButtonGroup()), resetCustomData: () => dispatch(resetCustomData()), } @@ -74,5 +114,6 @@ function mergeProps (stateProps, dispatchProps, ownProps) { dispatchSetGasPrice(gasButtonInfo[1].priceInHexWei) dispatchShowGasButtonGroup() }, + setGasPrice: dispatchSetGasPrice, } } 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 index f0c82e4f7..439f2ef6a 100644 --- 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 @@ -9,6 +9,8 @@ let mergeProps const actionSpies = { showModal: sinon.spy(), setGasPrice: sinon.spy(), + setGasTotal: sinon.spy(), + setGasLimit: sinon.spy(), } const sendDuckSpies = { @@ -28,11 +30,26 @@ proxyquire('../send-gas-row.container.js', { return () => ({}) }, }, + '../../../../selectors': { + getCurrentEthBalance: (s) => `mockCurrentEthBalance:${s}`, + getAdvancedInlineGasShown: (s) => `mockAdvancedInlineGasShown:${s}`, + }, '../../send.selectors.js': { getConversionRate: (s) => `mockConversionRate:${s}`, getCurrentCurrency: (s) => `mockConvertedCurrency:${s}`, getGasTotal: (s) => `mockGasTotal:${s}`, getGasPrice: (s) => `mockGasPrice:${s}`, + getGasLimit: (s) => `mockGasLimit:${s}`, + getSendAmount: (s) => `mockSendAmount:${s}`, + }, + '../../send.utils.js': { + isBalanceSufficient: ({ + amount, + gasTotal, + balance, + conversionRate, + }) => `${amount}:${gasTotal}:${balance}:${conversionRate}`, + calcGasTotal: (gasLimit, gasPrice) => gasLimit + gasPrice, }, './send-gas-row.selectors.js': { getGasLoadingError: (s) => `mockGasLoadingError:${s}`, @@ -47,6 +64,12 @@ proxyquire('../send-gas-row.container.js', { }, '../../../../ducks/send.duck': sendDuckSpies, '../../../../ducks/gas.duck': gasDuckSpies, + '../../../../helpers/conversions.util': { + convertGasPriceForInputs: str => str + '*', + convertGasLimitForInputs: str => str + '**', + decGWEIToHexWEI: str => '0x' + str + '000', + decimalToHex: str => '0x' + str, + }, }) describe('send-gas-row container', () => { @@ -67,6 +90,10 @@ describe('send-gas-row container', () => { gasButtonInfo: `mockGasButtonInfo:mockState`, }, gasButtonGroupShown: `mockGetGasButtonGroupShown:mockState`, + advancedInlineGasShown: 'mockAdvancedInlineGasShown:mockState', + gasLimit: 'mockGasLimit:mockState**', + gasPrice: 'mockGasPrice:mockState*', + insufficientBalance: false, }) }) @@ -79,6 +106,7 @@ describe('send-gas-row container', () => { beforeEach(() => { dispatchSpy = sinon.spy() mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy) + actionSpies.setGasTotal.resetHistory() }) describe('showCustomizeGasModal()', () => { @@ -94,10 +122,23 @@ describe('send-gas-row container', () => { describe('setGasPrice()', () => { it('should dispatch an action', () => { - mapDispatchToPropsObject.setGasPrice('mockNewPrice') - assert(dispatchSpy.calledOnce) + mapDispatchToPropsObject.setGasPrice('mockNewPrice', 'mockLimit') + assert(dispatchSpy.calledTwice) assert(actionSpies.setGasPrice.calledOnce) - assert.equal(actionSpies.setGasPrice.getCall(0).args[0], 'mockNewPrice') + assert.equal(actionSpies.setGasPrice.getCall(0).args[0], '0xmockNewPrice000') + assert(actionSpies.setGasTotal.calledOnce) + assert.equal(actionSpies.setGasTotal.getCall(0).args[0], 'mockLimit0xmockNewPrice000') + }) + }) + + describe('setGasLimit()', () => { + it('should dispatch an action', () => { + mapDispatchToPropsObject.setGasLimit('mockNewLimit', 'mockPrice') + assert(dispatchSpy.calledTwice) + assert(actionSpies.setGasLimit.calledOnce) + assert.equal(actionSpies.setGasLimit.getCall(0).args[0], '0xmockNewLimit') + assert(actionSpies.setGasTotal.calledOnce) + assert.equal(actionSpies.setGasTotal.getCall(0).args[0], '0xmockNewLimitmockPrice') }) }) |