From a020281c962ca71f3a58b32dfa0ec000b99cb667 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 1 Dec 2021 06:01:21 +0530 Subject: [PATCH] Implementing save button on advance gas fee modal (#12854) --- .../advanced-gas-fee-input-subtext.js | 2 +- .../advanced-gas-fee-input-subtext/index.scss | 2 +- .../advanced-gas-fee-inputs.js | 10 +- .../base-fee-input/base-fee-input.js | 155 ++++++++++++++++++ .../basefee-input.test.js | 24 +-- .../base-fee-input/index.js | 1 + .../base-fee-input/index.scss | 8 + .../advanced-gas-fee-inputs/basefee-input.js | 144 ---------------- .../advanced-gas-fee-inputs/index.scss | 14 +- .../priority-fee-input/index.js | 1 + .../priority-fee-input/priority-fee-input.js | 67 ++++++++ .../priority-fee-input.test.js} | 22 ++- .../priorityfee-input.js | 57 ------- .../advanced-gas-fee-popover.js | 33 ++-- .../advanced-gas-fee-popover.test.js | 71 ++++++++ .../advanced-gas-fee-save.js | 37 +++++ .../advanced-gas-fee-save/index.js | 1 + .../context/advanceGasFeePopover.js | 33 ++++ .../advanced-gas-fee-popover/context/index.js | 1 + ui/components/app/app-components.scss | 1 + .../edit-gas-item/index.scss | 2 +- .../transaction-detail.component.test.js | 1 + ui/components/ui/form-field/form-field.js | 4 - ui/components/ui/typography/typography.scss | 3 +- ui/hooks/gasFeeInput/useGasFeeInputs.js | 37 ++++- ui/hooks/gasFeeInput/useMaxFeePerGasInput.js | 9 +- .../useMaxPriorityFeePerGasInput.js | 13 +- .../gasFeeInput/useTransactionFunctions.js | 20 ++- .../confirm-transaction-base.component.js | 8 +- .../gas-details-item/gas-details-item.js | 4 +- .../gas-details-item/gas-details-item.scss | 4 - 31 files changed, 508 insertions(+), 281 deletions(-) create mode 100644 ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/base-fee-input.js rename ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/{ => base-fee-input}/basefee-input.test.js (78%) create mode 100644 ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index.js create mode 100644 ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index.scss delete mode 100644 ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/basefee-input.js create mode 100644 ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/index.js create mode 100644 ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/priority-fee-input.js rename ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/{priorityfee-input.test.js => priority-fee-input/priority-fee-input.test.js} (65%) delete mode 100644 ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priorityfee-input.js create mode 100644 ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-popover.test.js create mode 100644 ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-save/advanced-gas-fee-save.js create mode 100644 ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-save/index.js create mode 100644 ui/components/app/advanced-gas-fee-popover/context/advanceGasFeePopover.js create mode 100644 ui/components/app/advanced-gas-fee-popover/context/index.js diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-input-subtext/advanced-gas-fee-input-subtext.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-input-subtext/advanced-gas-fee-input-subtext.js index 015ed5f7e..40c003237 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-input-subtext/advanced-gas-fee-input-subtext.js +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-input-subtext/advanced-gas-fee-input-subtext.js @@ -25,7 +25,7 @@ const AdvancedGasFeeInputSubtext = ({ latest, historical }) => { }; AdvancedGasFeeInputSubtext.propTypes = { - latest: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + latest: PropTypes.string, historical: PropTypes.string, }; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-input-subtext/index.scss b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-input-subtext/index.scss index 17aba4ccd..45763668c 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-input-subtext/index.scss +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-input-subtext/index.scss @@ -3,7 +3,7 @@ align-items: center; margin-top: 2px; color: $ui-4; - font-size: $font-size-h7; + font-size: $font-size-h8; &__label { font-weight: bold; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/advanced-gas-fee-inputs.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/advanced-gas-fee-inputs.js index 654a414c5..c191d36f6 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/advanced-gas-fee-inputs.js +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/advanced-gas-fee-inputs.js @@ -1,14 +1,14 @@ import React from 'react'; import Box from '../../../ui/box'; -import BasefeeInput from './basefee-input'; -import PriorityFeeInput from './priorityfee-input'; +import BaseFeeInput from './base-fee-input'; +import PriorityFeeInput from './priority-fee-input'; const AdvancedGasFeeInputs = () => { return ( - - -
+ + +
); diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/base-fee-input.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/base-fee-input.js new file mode 100644 index 000000000..1e7739bf3 --- /dev/null +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/base-fee-input.js @@ -0,0 +1,155 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; + +import { PRIORITY_LEVELS } from '../../../../../../shared/constants/gas'; +import { + divideCurrencies, + multiplyCurrencies, +} from '../../../../../../shared/modules/conversion.utils'; +import { PRIMARY, SECONDARY } from '../../../../../helpers/constants/common'; +import { decGWEIToHexWEI } from '../../../../../helpers/utils/conversions.util'; +import { getAdvancedGasFeeValues } from '../../../../../selectors'; +import { useGasFeeContext } from '../../../../../contexts/gasFee'; +import { useI18nContext } from '../../../../../hooks/useI18nContext'; +import { useUserPreferencedCurrency } from '../../../../../hooks/useUserPreferencedCurrency'; +import { useCurrencyDisplay } from '../../../../../hooks/useCurrencyDisplay'; +import Button from '../../../../ui/button'; +import Box from '../../../../ui/box'; +import FormField from '../../../../ui/form-field'; +import I18nValue from '../../../../ui/i18n-value'; + +import { useAdvanceGasFeePopoverContext } from '../../context'; +import AdvancedGasFeeInputSubtext from '../../advanced-gas-fee-input-subtext'; + +const divideCurrencyValues = (value, baseFee) => { + if (baseFee === 0) { + return 0; + } + return divideCurrencies(value, baseFee, { + numberOfDecimals: 2, + dividendBase: 10, + divisorBase: 10, + }).toNumber(); +}; + +const multiplyCurrencyValues = (baseFee, value, numberOfDecimals) => + multiplyCurrencies(baseFee, value, { + numberOfDecimals, + multiplicandBase: 10, + multiplierBase: 10, + }).toNumber(); + +const BaseFeeInput = () => { + const t = useI18nContext(); + const { gasFeeEstimates, estimateUsed, maxFeePerGas } = useGasFeeContext(); + const { setDirty, setMaxFeePerGas } = useAdvanceGasFeePopoverContext(); + const { estimatedBaseFee } = gasFeeEstimates; + const { + numberOfDecimals: numberOfDecimalsPrimary, + } = useUserPreferencedCurrency(PRIMARY); + const { + currency, + numberOfDecimals: numberOfDecimalsFiat, + } = useUserPreferencedCurrency(SECONDARY); + + const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues); + + const [editingInGwei, setEditingInGwei] = useState(false); + + const [maxBaseFeeGWEI, setMaxBaseFeeGWEI] = useState(() => { + if ( + estimateUsed !== PRIORITY_LEVELS.CUSTOM && + advancedGasFeeValues?.maxBaseFee + ) { + return multiplyCurrencyValues( + estimatedBaseFee, + advancedGasFeeValues.maxBaseFee, + numberOfDecimalsPrimary, + ); + } + return maxFeePerGas; + }); + + const [maxBaseFeeMultiplier, setMaxBaseFeeMultiplier] = useState(() => { + if ( + estimateUsed !== PRIORITY_LEVELS.CUSTOM && + advancedGasFeeValues?.maxBaseFee + ) { + return advancedGasFeeValues.maxBaseFee; + } + return divideCurrencyValues(maxFeePerGas, estimatedBaseFee); + }); + + const [, { value: baseFeeInFiat }] = useCurrencyDisplay( + decGWEIToHexWEI(maxBaseFeeGWEI), + { currency, numberOfDecimalsFiat }, + ); + + const updateBaseFee = useCallback( + (value) => { + let baseFeeInGWEI; + let baseFeeMultiplierValue; + if (editingInGwei) { + baseFeeInGWEI = value; + baseFeeMultiplierValue = divideCurrencyValues(value, estimatedBaseFee); + } else { + baseFeeInGWEI = multiplyCurrencyValues( + estimatedBaseFee, + value, + numberOfDecimalsPrimary, + ); + baseFeeMultiplierValue = value; + } + setMaxBaseFeeGWEI(baseFeeInGWEI); + setMaxBaseFeeMultiplier(baseFeeMultiplierValue); + setDirty(true); + }, + [ + editingInGwei, + estimatedBaseFee, + numberOfDecimalsPrimary, + setMaxBaseFeeGWEI, + setMaxBaseFeeMultiplier, + setDirty, + ], + ); + + useEffect(() => { + setMaxFeePerGas(maxBaseFeeGWEI); + }, [maxBaseFeeGWEI, setMaxFeePerGas]); + + return ( + + setEditingInGwei(!editingInGwei)} + > + + + } + value={editingInGwei ? maxBaseFeeGWEI : maxBaseFeeMultiplier} + detailText={ + editingInGwei + ? `${maxBaseFeeMultiplier}x ${`≈ ${baseFeeInFiat}`}` + : `${maxBaseFeeGWEI} GWEI ${`≈ ${baseFeeInFiat}`}` + } + numeric + /> + + + ); +}; + +export default BaseFeeInput; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/basefee-input.test.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/basefee-input.test.js similarity index 78% rename from ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/basefee-input.test.js rename to ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/basefee-input.test.js index 1818f5a07..7cef8ac15 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/basefee-input.test.js +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/basefee-input.test.js @@ -1,21 +1,23 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; -import mockEstimates from '../../../../../test/data/mock-estimates.json'; -import mockState from '../../../../../test/data/mock-state.json'; -import { renderWithProvider } from '../../../../../test/lib/render-helpers'; -import configureStore from '../../../../store/store'; -import { GasFeeContextProvider } from '../../../../contexts/gasFee'; +import { GAS_ESTIMATE_TYPES } from '../../../../../../shared/constants/gas'; +import { renderWithProvider } from '../../../../../../test/lib/render-helpers'; +import mockEstimates from '../../../../../../test/data/mock-estimates.json'; +import mockState from '../../../../../../test/data/mock-state.json'; +import { GasFeeContextProvider } from '../../../../../contexts/gasFee'; +import configureStore from '../../../../../store/store'; -import { GAS_ESTIMATE_TYPES } from '../../../../../shared/constants/gas'; -import BasefeeInput from './basefee-input'; +import { AdvanceGasFeePopoverContextProvider } from '../../context'; +import BaseFeeInput from './base-fee-input'; -jest.mock('../../../../store/actions', () => ({ +jest.mock('../../../../../store/actions', () => ({ disconnectGasFeeEstimatePoller: jest.fn(), getGasFeeEstimatesAndStartPolling: jest .fn() .mockImplementation(() => Promise.resolve()), addPollingTokenToAppState: jest.fn(), + removePollingTokenFromAppState: jest.fn(), })); const render = (txProps) => { @@ -42,13 +44,15 @@ const render = (txProps) => { ...txProps, }} > - + + + , store, ); }; -describe('BasefeeInput', () => { +describe('BaseFeeInput', () => { it('should renders advancedGasFee.baseFee value if current estimate used is not custom', () => { render({ userFeeLevel: 'high', diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index.js new file mode 100644 index 000000000..61d1de89b --- /dev/null +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index.js @@ -0,0 +1 @@ +export { default } from './base-fee-input'; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index.scss b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index.scss new file mode 100644 index 000000000..871c43df9 --- /dev/null +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index.scss @@ -0,0 +1,8 @@ +.base-fee-input { + a.base-fee-input__edit-link { + display: inline; + font-size: $font-size-h7; + padding: 0; + white-space: nowrap; + } +} diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/basefee-input.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/basefee-input.js deleted file mode 100644 index d448393a7..000000000 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/basefee-input.js +++ /dev/null @@ -1,144 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import { useSelector } from 'react-redux'; - -import { PRIORITY_LEVELS } from '../../../../../shared/constants/gas'; -import { - divideCurrencies, - multiplyCurrencies, -} from '../../../../../shared/modules/conversion.utils'; -import { PRIMARY, SECONDARY } from '../../../../helpers/constants/common'; -import { decGWEIToHexWEI } from '../../../../helpers/utils/conversions.util'; -import { useGasFeeContext } from '../../../../contexts/gasFee'; -import { useI18nContext } from '../../../../hooks/useI18nContext'; -import { useUserPreferencedCurrency } from '../../../../hooks/useUserPreferencedCurrency'; -import { useCurrencyDisplay } from '../../../../hooks/useCurrencyDisplay'; -import Button from '../../../ui/button'; -import FormField from '../../../ui/form-field'; -import I18nValue from '../../../ui/i18n-value'; - -import AdvancedGasFeeInputSubtext from '../advanced-gas-fee-input-subtext'; -import { getAdvancedGasFeeValues } from '../../../../selectors'; - -const divideCurrencyValues = (value, baseFee) => { - if (baseFee === 0) { - return 0; - } - return divideCurrencies(value, baseFee, { - numberOfDecimals: 2, - dividendBase: 10, - divisorBase: 10, - }).toNumber(); -}; - -const multiplyCurrencyValues = (baseFee, value, numberOfDecimals) => - multiplyCurrencies(baseFee, value, { - numberOfDecimals, - multiplicandBase: 10, - multiplierBase: 10, - }).toNumber(); - -const BasefeeInput = () => { - const t = useI18nContext(); - const { gasFeeEstimates, estimateUsed, maxFeePerGas } = useGasFeeContext(); - const { estimatedBaseFee } = gasFeeEstimates; - const { - numberOfDecimals: numberOfDecimalsPrimary, - } = useUserPreferencedCurrency(PRIMARY); - const { - currency, - numberOfDecimals: numberOfDecimalsFiat, - } = useUserPreferencedCurrency(SECONDARY); - - const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues); - - const [editingInGwei, setEditingInGwei] = useState(false); - - const [maxBaseFeeGWEI, setMaxBaseFeeGWEI] = useState(() => { - if ( - estimateUsed !== PRIORITY_LEVELS.CUSTOM && - advancedGasFeeValues?.maxBaseFee - ) { - return multiplyCurrencyValues( - estimatedBaseFee, - advancedGasFeeValues.maxBaseFee, - numberOfDecimalsPrimary, - ); - } - return maxFeePerGas; - }); - - const [maxBaseFeeMultiplier, setMaxBaseFeeMultiplier] = useState(() => { - if ( - estimateUsed !== PRIORITY_LEVELS.CUSTOM && - advancedGasFeeValues?.maxBaseFee - ) { - return advancedGasFeeValues.maxBaseFee; - } - return divideCurrencyValues(maxFeePerGas, estimatedBaseFee); - }); - - const [, { value: baseFeeInFiat }] = useCurrencyDisplay( - decGWEIToHexWEI(maxBaseFeeGWEI), - { currency, numberOfDecimalsFiat }, - ); - - const updateBaseFee = useCallback( - (value) => { - if (editingInGwei) { - setMaxBaseFeeGWEI(value); - setMaxBaseFeeMultiplier(divideCurrencyValues(value, estimatedBaseFee)); - } else { - setMaxBaseFeeMultiplier(value); - setMaxBaseFeeGWEI( - multiplyCurrencyValues( - estimatedBaseFee, - value, - numberOfDecimalsPrimary, - ), - ); - } - }, - [ - editingInGwei, - estimatedBaseFee, - numberOfDecimalsPrimary, - setMaxBaseFeeGWEI, - setMaxBaseFeeMultiplier, - ], - ); - - return ( - setEditingInGwei(!editingInGwei)} - > - - - } - value={editingInGwei ? maxBaseFeeGWEI : maxBaseFeeMultiplier} - detailText={ - editingInGwei - ? `${maxBaseFeeMultiplier}x ${`≈ ${baseFeeInFiat}`}` - : `${maxBaseFeeGWEI} GWEI ${`≈ ${baseFeeInFiat}`}` - } - numeric - inputDetails={ - - } - /> - ); -}; - -export default BasefeeInput; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/index.scss b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/index.scss index 489722155..55a27eafb 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/index.scss +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/index.scss @@ -1,20 +1,12 @@ -.advanced-gas-fee-input { - a.advanced-gas-fee-input__edit-link { - display: inline; - font-size: $font-size-h7; - padding: 0; - white-space: nowrap; +.advanced-gas-fee-inputs { + .form-field { + margin-bottom: 4px; } .form-field__heading-title > h6 { font-size: $font-size-h7; } - &__border { - padding-bottom: 16px; - border-bottom: 1px solid $Grey-100; - } - &__separator { border-top: 1px solid $ui-grey; margin: 24px 0 16px 0; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/index.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/index.js new file mode 100644 index 000000000..0fddc8c30 --- /dev/null +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/index.js @@ -0,0 +1 @@ +export { default } from './priority-fee-input'; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/priority-fee-input.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/priority-fee-input.js new file mode 100644 index 000000000..e1d717fd8 --- /dev/null +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/priority-fee-input.js @@ -0,0 +1,67 @@ +import React, { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; + +import { PRIORITY_LEVELS } from '../../../../../../shared/constants/gas'; +import { SECONDARY } from '../../../../../helpers/constants/common'; +import { decGWEIToHexWEI } from '../../../../../helpers/utils/conversions.util'; +import { getAdvancedGasFeeValues } from '../../../../../selectors'; +import { useCurrencyDisplay } from '../../../../../hooks/useCurrencyDisplay'; +import { useGasFeeContext } from '../../../../../contexts/gasFee'; +import { useI18nContext } from '../../../../../hooks/useI18nContext'; +import { useUserPreferencedCurrency } from '../../../../../hooks/useUserPreferencedCurrency'; +import FormField from '../../../../ui/form-field'; + +import { useAdvanceGasFeePopoverContext } from '../../context'; +import AdvancedGasFeeInputSubtext from '../../advanced-gas-fee-input-subtext'; + +const PriorityFeeInput = () => { + const t = useI18nContext(); + const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues); + const { + setDirty, + setMaxPriorityFeePerGas, + } = useAdvanceGasFeePopoverContext(); + const { estimateUsed, maxPriorityFeePerGas } = useGasFeeContext(); + + const [priorityFee, setPriorityFee] = useState(() => { + if ( + estimateUsed !== PRIORITY_LEVELS.CUSTOM && + advancedGasFeeValues?.priorityFee + ) + return advancedGasFeeValues.priorityFee; + return maxPriorityFeePerGas; + }); + + const { currency, numberOfDecimals } = useUserPreferencedCurrency(SECONDARY); + + const [, { value: priorityFeeInFiat }] = useCurrencyDisplay( + decGWEIToHexWEI(priorityFee), + { currency, numberOfDecimals }, + ); + + const updatePriorityFee = (value) => { + setPriorityFee(value); + setDirty(true); + }; + + useEffect(() => { + setMaxPriorityFeePerGas(priorityFee); + }, [priorityFee, setMaxPriorityFeePerGas]); + + return ( + <> + + + + ); +}; + +export default PriorityFeeInput; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priorityfee-input.test.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/priority-fee-input.test.js similarity index 65% rename from ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priorityfee-input.test.js rename to ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/priority-fee-input.test.js index 9240b83b8..1a8733ba5 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priorityfee-input.test.js +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priority-fee-input/priority-fee-input.test.js @@ -1,20 +1,22 @@ import React from 'react'; -import mockEstimates from '../../../../../test/data/mock-estimates.json'; -import mockState from '../../../../../test/data/mock-state.json'; -import { renderWithProvider } from '../../../../../test/lib/render-helpers'; -import configureStore from '../../../../store/store'; -import { GasFeeContextProvider } from '../../../../contexts/gasFee'; +import { GAS_ESTIMATE_TYPES } from '../../../../../../shared/constants/gas'; +import { renderWithProvider } from '../../../../../../test/lib/render-helpers'; +import mockEstimates from '../../../../../../test/data/mock-estimates.json'; +import mockState from '../../../../../../test/data/mock-state.json'; +import { GasFeeContextProvider } from '../../../../../contexts/gasFee'; +import configureStore from '../../../../../store/store'; -import { GAS_ESTIMATE_TYPES } from '../../../../../shared/constants/gas'; -import PriprityfeeInput from './priorityfee-input'; +import { AdvanceGasFeePopoverContextProvider } from '../../context'; +import PriorityfeeInput from './priority-fee-input'; -jest.mock('../../../../store/actions', () => ({ +jest.mock('../../../../../store/actions', () => ({ disconnectGasFeeEstimatePoller: jest.fn(), getGasFeeEstimatesAndStartPolling: jest .fn() .mockImplementation(() => Promise.resolve()), addPollingTokenToAppState: jest.fn(), + removePollingTokenFromAppState: jest.fn(), })); const render = (txProps) => { @@ -41,7 +43,9 @@ const render = (txProps) => { ...txProps, }} > - + + + , store, ); diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priorityfee-input.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priorityfee-input.js deleted file mode 100644 index 6e75f1b45..000000000 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-inputs/priorityfee-input.js +++ /dev/null @@ -1,57 +0,0 @@ -import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; - -import { PRIORITY_LEVELS } from '../../../../../shared/constants/gas'; -import { SECONDARY } from '../../../../helpers/constants/common'; -import { decGWEIToHexWEI } from '../../../../helpers/utils/conversions.util'; -import { getAdvancedGasFeeValues } from '../../../../selectors'; -import { useCurrencyDisplay } from '../../../../hooks/useCurrencyDisplay'; -import { useGasFeeContext } from '../../../../contexts/gasFee'; -import { useI18nContext } from '../../../../hooks/useI18nContext'; -import { useUserPreferencedCurrency } from '../../../../hooks/useUserPreferencedCurrency'; -import FormField from '../../../ui/form-field'; - -import AdvancedGasFeeInputSubtext from '../advanced-gas-fee-input-subtext'; - -const PriorityFeeInput = () => { - const t = useI18nContext(); - const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues); - - const { estimateUsed, maxPriorityFeePerGas } = useGasFeeContext(); - - const [priorityFee, setPriorityFee] = useState(() => { - if ( - estimateUsed !== PRIORITY_LEVELS.CUSTOM && - advancedGasFeeValues?.priorityFee - ) - return advancedGasFeeValues.priorityFee; - return maxPriorityFeePerGas; - }); - - const { currency, numberOfDecimals } = useUserPreferencedCurrency(SECONDARY); - - const [, { value: priorityFeeInFiat }] = useCurrencyDisplay( - decGWEIToHexWEI(priorityFee), - { currency, numberOfDecimals }, - ); - - return ( - - } - /> - ); -}; - -export default PriorityFeeInput; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-popover.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-popover.js index 5a673afda..cda544c81 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-popover.js +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-popover.js @@ -2,13 +2,12 @@ import React from 'react'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { useTransactionModalContext } from '../../../contexts/transaction-modal'; - import Box from '../../ui/box'; -import Button from '../../ui/button'; -import I18nValue from '../../ui/i18n-value'; import Popover from '../../ui/popover'; +import { AdvanceGasFeePopoverContextProvider } from './context'; import AdvancedGasFeeInputs from './advanced-gas-fee-inputs'; +import AdvancedGasFeeSaveButton from './advanced-gas-fee-save'; const AdvancedGasFeePopover = () => { const t = useI18nContext(); @@ -21,21 +20,19 @@ const AdvancedGasFeePopover = () => { if (currentModal !== 'advancedGasFee') return null; return ( - closeModal('advancedGasFee')} - onClose={closeAllModals} - footer={ - - } - > - - - - + + closeModal('advancedGasFee')} + onClose={closeAllModals} + footer={} + > + + + + + ); }; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-popover.test.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-popover.test.js new file mode 100644 index 000000000..dd7f0f7a7 --- /dev/null +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-popover.test.js @@ -0,0 +1,71 @@ +import React from 'react'; +import { fireEvent, screen } from '@testing-library/react'; + +import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas'; +import { renderWithProvider } from '../../../../test/lib/render-helpers'; +import mockEstimates from '../../../../test/data/mock-estimates.json'; +import mockState from '../../../../test/data/mock-state.json'; +import { GasFeeContextProvider } from '../../../contexts/gasFee'; +import configureStore from '../../../store/store'; + +import AdvancedGasFeePopover from './advanced-gas-fee-popover'; + +jest.mock('../../../store/actions', () => ({ + disconnectGasFeeEstimatePoller: jest.fn(), + getGasFeeEstimatesAndStartPolling: jest + .fn() + .mockImplementation(() => Promise.resolve()), + addPollingTokenToAppState: jest.fn(), + removePollingTokenFromAppState: jest.fn(), +})); + +jest.mock('../../../contexts/transaction-modal', () => ({ + useTransactionModalContext: () => ({ + closeModal: () => undefined, + currentModal: 'advancedGasFee', + }), +})); + +const render = () => { + const store = configureStore({ + metamask: { + ...mockState.metamask, + accounts: { + [mockState.metamask.selectedAddress]: { + address: mockState.metamask.selectedAddress, + balance: '0x1F4', + }, + }, + advancedGasFee: { priorityFee: 100 }, + featureFlags: { advancedInlineGas: true }, + gasFeeEstimates: + mockEstimates[GAS_ESTIMATE_TYPES.FEE_MARKET].gasFeeEstimates, + }, + }); + + return renderWithProvider( + + + , + store, + ); +}; + +describe('AdvancedGasFeePopover', () => { + it('should renders save button disabled by default', () => { + render(); + expect(screen.queryByRole('button', { name: 'Save' })).toBeDisabled(); + }); + + it('should enable save button as input value is changed', () => { + render(); + fireEvent.change(document.getElementsByTagName('input')[0], { + target: { value: 4 }, + }); + expect(screen.queryByRole('button', { name: 'Save' })).not.toBeDisabled(); + }); +}); diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-save/advanced-gas-fee-save.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-save/advanced-gas-fee-save.js new file mode 100644 index 000000000..d8efd7c7c --- /dev/null +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-save/advanced-gas-fee-save.js @@ -0,0 +1,37 @@ +import React from 'react'; + +import { PRIORITY_LEVELS } from '../../../../../shared/constants/gas'; +import { useTransactionModalContext } from '../../../../contexts/transaction-modal'; +import { useGasFeeContext } from '../../../../contexts/gasFee'; +import Button from '../../../ui/button'; +import I18nValue from '../../../ui/i18n-value'; + +import { useAdvanceGasFeePopoverContext } from '../context'; +import { decGWEIToHexWEI } from '../../../../../shared/modules/conversion.utils'; + +const AdvancedGasFeeSaveButton = () => { + const { closeModal } = useTransactionModalContext(); + const { updateTransaction } = useGasFeeContext(); + const { + isDirty, + maxFeePerGas, + maxPriorityFeePerGas, + } = useAdvanceGasFeePopoverContext(); + + const onSave = () => { + updateTransaction( + PRIORITY_LEVELS.CUSTOM, + decGWEIToHexWEI(maxFeePerGas), + decGWEIToHexWEI(maxPriorityFeePerGas), + ); + closeModal('advancedGasFee'); + }; + + return ( + + ); +}; + +export default AdvancedGasFeeSaveButton; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-save/index.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-save/index.js new file mode 100644 index 000000000..082c24190 --- /dev/null +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-save/index.js @@ -0,0 +1 @@ +export { default } from './advanced-gas-fee-save'; diff --git a/ui/components/app/advanced-gas-fee-popover/context/advanceGasFeePopover.js b/ui/components/app/advanced-gas-fee-popover/context/advanceGasFeePopover.js new file mode 100644 index 000000000..a6bca0a33 --- /dev/null +++ b/ui/components/app/advanced-gas-fee-popover/context/advanceGasFeePopover.js @@ -0,0 +1,33 @@ +import React, { createContext, useContext, useState } from 'react'; +import PropTypes from 'prop-types'; + +export const AdvanceGasFeePopoverContext = createContext({}); + +export const AdvanceGasFeePopoverContextProvider = ({ children }) => { + const [maxFeePerGas, setMaxFeePerGas] = useState(); + const [maxPriorityFeePerGas, setMaxPriorityFeePerGas] = useState(); + const [isDirty, setDirty] = useState(); + + return ( + + {children} + + ); +}; + +export function useAdvanceGasFeePopoverContext() { + return useContext(AdvanceGasFeePopoverContext); +} + +AdvanceGasFeePopoverContextProvider.propTypes = { + children: PropTypes.node.isRequired, +}; diff --git a/ui/components/app/advanced-gas-fee-popover/context/index.js b/ui/components/app/advanced-gas-fee-popover/context/index.js new file mode 100644 index 000000000..f9181147b --- /dev/null +++ b/ui/components/app/advanced-gas-fee-popover/context/index.js @@ -0,0 +1 @@ +export * from './advanceGasFeePopover'; diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss index 961ed1c84..a3f4df407 100644 --- a/ui/components/app/app-components.scss +++ b/ui/components/app/app-components.scss @@ -53,4 +53,5 @@ @import 'loading-network-screen/index'; @import 'advanced-gas-fee-popover/index'; @import 'advanced-gas-fee-popover/advanced-gas-fee-inputs/index'; +@import 'advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index'; @import 'advanced-gas-fee-popover/advanced-gas-fee-input-subtext/index'; diff --git a/ui/components/app/edit-gas-fee-popover/edit-gas-item/index.scss b/ui/components/app/edit-gas-fee-popover/edit-gas-item/index.scss index 1d7ed379c..f29b80fce 100644 --- a/ui/components/app/edit-gas-fee-popover/edit-gas-item/index.scss +++ b/ui/components/app/edit-gas-fee-popover/edit-gas-item/index.scss @@ -11,7 +11,7 @@ height: 32px; width: 100%; - &--selected { + &-selected { background-color: $ui-1; } diff --git a/ui/components/app/transaction-detail/transaction-detail.component.test.js b/ui/components/app/transaction-detail/transaction-detail.component.test.js index a1f899413..7209c872e 100644 --- a/ui/components/app/transaction-detail/transaction-detail.component.test.js +++ b/ui/components/app/transaction-detail/transaction-detail.component.test.js @@ -44,6 +44,7 @@ const render = ({ componentProps, contextProps } = {}) => { console.log('on edit'); }} rows={[]} + userAcknowledgedGasMissing {...componentProps} /> , diff --git a/ui/components/ui/form-field/form-field.js b/ui/components/ui/form-field/form-field.js index 79386680c..5cd1fd5b6 100644 --- a/ui/components/ui/form-field/form-field.js +++ b/ui/components/ui/form-field/form-field.js @@ -30,7 +30,6 @@ export default function FormField({ password, allowDecimals, disabled, - inputDetails, }) { return (
)} - {inputDetails}
); @@ -129,7 +127,6 @@ FormField.propTypes = { password: PropTypes.bool, allowDecimals: PropTypes.bool, disabled: PropTypes.bool, - inputDetails: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), }; FormField.defaultProps = { @@ -146,5 +143,4 @@ FormField.defaultProps = { password: false, allowDecimals: true, disabled: false, - inputDetails: '', }; diff --git a/ui/components/ui/typography/typography.scss b/ui/components/ui/typography/typography.scss index c92fc6eb2..ea1cbaff3 100644 --- a/ui/components/ui/typography/typography.scss +++ b/ui/components/ui/typography/typography.scss @@ -4,7 +4,8 @@ .typography { @include design-system.Paragraph; - & b { + & b, + & strong { font-weight: 700; } diff --git a/ui/hooks/gasFeeInput/useGasFeeInputs.js b/ui/hooks/gasFeeInput/useGasFeeInputs.js index fadf9f7e7..49864193b 100644 --- a/ui/hooks/gasFeeInput/useGasFeeInputs.js +++ b/ui/hooks/gasFeeInput/useGasFeeInputs.js @@ -23,6 +23,9 @@ import { useMaxPriorityFeePerGasInput } from './useMaxPriorityFeePerGasInput'; import { useGasEstimates } from './useGasEstimates'; import { useTransactionFunctions } from './useTransactionFunctions'; +// eslint-disable-next-line prefer-destructuring +const EIP_1559_V2 = process.env.EIP_1559_V2; + /** * @typedef {Object} GasFeeInputReturnType * @property {DecGweiString} [maxFeePerGas] - the maxFeePerGas input value. @@ -118,10 +121,16 @@ export function useGasFeeInputs( * so that transaction is source of truth whenever possible. */ useEffect(() => { - if (transaction?.userFeeLevel) { + if (EIP_1559_V2 && transaction?.userFeeLevel) { setEstimateUsed(transaction?.userFeeLevel); + setInternalEstimateToUse(transaction?.userFeeLevel); } - }, [setEstimateUsed, transaction]); + }, [ + setEstimateUsed, + setInternalEstimateToUse, + transaction, + userPrefersAdvancedGas, + ]); const [gasLimit, setGasLimit] = useState(() => Number(hexToDecimal(transaction?.txParams?.gas ?? '0x0')), @@ -143,6 +152,7 @@ export function useGasFeeInputs( maxFeePerGasFiat, setMaxFeePerGas, } = useMaxFeePerGasInput({ + EIP_1559_V2, estimateToUse, gasEstimateType, gasFeeEstimates, @@ -156,6 +166,7 @@ export function useGasFeeInputs( maxPriorityFeePerGasFiat, setMaxPriorityFeePerGas, } = useMaxPriorityFeePerGasInput({ + EIP_1559_V2, estimateToUse, gasEstimateType, gasFeeEstimates, @@ -214,8 +225,12 @@ export function useGasFeeInputs( } }, [minimumGasLimit, gasErrors.gasLimit, transaction]); - const { updateTransactionUsingGasFeeEstimates } = useTransactionFunctions({ + const { + updateTransaction, + updateTransactionUsingGasFeeEstimates, + } = useTransactionFunctions({ defaultEstimateToUse, + gasFeeEstimates, gasLimit, transaction, }); @@ -302,6 +317,22 @@ export function useGasFeeInputs( hasSimulationError, supportsEIP1559, supportsEIP1559V2: supportsEIP1559 && EIP_1559_V2_ENABLED, + updateTransaction, updateTransactionUsingGasFeeEstimates, }; } + +/** + * In EIP_1559_V2 implementation as used by useGasfeeInputContext() the use of this hook is evolved. + * It is no longer used to keep transient state of advance gas fee inputs. + * Transient state of inputs is maintained locally in /ui/components/app/advance-gas-fee-popover component. + * + * This hook is used now as source of shared data about transaction, it shares details of gas fee in transaction, + * estimate used, is EIP-1559 supported and other details. It also have methods to update transaction. + * + * Transaction is used as single source of truth and as transaction is updated the fields shared by hook are + * also updated using useEffect hook. + * + * It will be useful to plan a task to create a new hook of this shared information from this hook. + * Methods like setEstimateToUse, onManualChange are deprecated in context of EIP_1559_V2 implementation. + */ diff --git a/ui/hooks/gasFeeInput/useMaxFeePerGasInput.js b/ui/hooks/gasFeeInput/useMaxFeePerGasInput.js index ab3d10122..452c0611e 100644 --- a/ui/hooks/gasFeeInput/useMaxFeePerGasInput.js +++ b/ui/hooks/gasFeeInput/useMaxFeePerGasInput.js @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas'; @@ -34,6 +34,7 @@ const getMaxFeePerGasFromTransaction = (transaction) => { * method to update the setMaxFeePerGas. */ export function useMaxFeePerGasInput({ + EIP_1559_V2, estimateToUse, gasEstimateType, gasFeeEstimates, @@ -65,6 +66,12 @@ export function useMaxFeePerGasInput({ return null; }); + useEffect(() => { + if (EIP_1559_V2) { + setMaxFeePerGas(maxFeePerGasFromTransaction); + } + }, [EIP_1559_V2, maxFeePerGasFromTransaction, setMaxFeePerGas]); + let gasSettings = { gasLimit: decimalToHex(gasLimit), }; diff --git a/ui/hooks/gasFeeInput/useMaxPriorityFeePerGasInput.js b/ui/hooks/gasFeeInput/useMaxPriorityFeePerGasInput.js index 1e9a13690..54aeb1093 100644 --- a/ui/hooks/gasFeeInput/useMaxPriorityFeePerGasInput.js +++ b/ui/hooks/gasFeeInput/useMaxPriorityFeePerGasInput.js @@ -1,5 +1,5 @@ import { useSelector } from 'react-redux'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { addHexPrefix } from 'ethereumjs-util'; @@ -34,6 +34,7 @@ const getMaxPriorityFeePerGasFromTransaction = (transaction) => { * method to update the maxPriorityFeePerGas. */ export function useMaxPriorityFeePerGasInput({ + EIP_1559_V2, estimateToUse, gasEstimateType, gasFeeEstimates, @@ -61,6 +62,16 @@ export function useMaxPriorityFeePerGasInput({ return null; }); + useEffect(() => { + if (EIP_1559_V2) { + setMaxPriorityFeePerGas(maxPriorityFeePerGasFromTransaction); + } + }, [ + EIP_1559_V2, + maxPriorityFeePerGasFromTransaction, + setMaxPriorityFeePerGas, + ]); + const maxPriorityFeePerGasToUse = maxPriorityFeePerGas ?? getGasFeeEstimate( diff --git a/ui/hooks/gasFeeInput/useTransactionFunctions.js b/ui/hooks/gasFeeInput/useTransactionFunctions.js index d4f094240..ab3830266 100644 --- a/ui/hooks/gasFeeInput/useTransactionFunctions.js +++ b/ui/hooks/gasFeeInput/useTransactionFunctions.js @@ -2,11 +2,15 @@ import { useCallback } from 'react'; import { useDispatch } from 'react-redux'; import { PRIORITY_LEVELS } from '../../../shared/constants/gas'; -import { decimalToHex } from '../../helpers/utils/conversions.util'; +import { + decimalToHex, + decGWEIToHexWEI, +} from '../../helpers/utils/conversions.util'; import { updateTransaction as updateTransactionFn } from '../../store/actions'; export const useTransactionFunctions = ({ defaultEstimateToUse, + gasFeeEstimates, gasLimit, transaction, }) => { @@ -54,11 +58,19 @@ export const useTransactionFunctions = ({ maxPriorityFeePerGas, ); } else { - updateTransaction(gasFeeEstimateToUse); + const { + suggestedMaxFeePerGas, + suggestedMaxPriorityFeePerGas, + } = gasFeeEstimates[gasFeeEstimateToUse]; + updateTransaction( + gasFeeEstimateToUse, + decGWEIToHexWEI(suggestedMaxFeePerGas), + decGWEIToHexWEI(suggestedMaxPriorityFeePerGas), + ); } }, - [transaction?.dappSuggestedGasFees, updateTransaction], + [gasFeeEstimates, transaction?.dappSuggestedGasFees, updateTransaction], ); - return { updateTransactionUsingGasFeeEstimates }; + return { updateTransaction, updateTransactionUsingGasFeeEstimates }; }; diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index e0e2f03c2..4547528aa 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -516,9 +516,9 @@ export default class ConfirmTransactionBase extends Component { subText={ !isMultiLayerFeeNetwork && ( <> - + {t('editGasSubTextFeeLabel')} - +
- + {t('editGasSubTextAmountLabel')} - + {renderTotalMaxAmount()} } diff --git a/ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.js b/ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.js index 0c00be4e9..72f9a780e 100644 --- a/ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.js +++ b/ui/pages/confirm-transaction-base/gas-details-item/gas-details-item.js @@ -102,10 +102,10 @@ const GasDetailsItem = ({ })} > - + {estimateUsed === 'high' && '⚠ '} - +