diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 1a73a1a57..d84e28438 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -289,6 +289,9 @@ "cancel": { "message": "Cancel" }, + "cancelPopoverTitle": { + "message": "Cancel transaction" + }, "cancellationGasFee": { "message": "Cancellation Gas Fee" }, diff --git a/ui/components/app/confirm-page-container/confirm-page-container.component.js b/ui/components/app/confirm-page-container/confirm-page-container.component.js index 8e3b8d858..8c81e5b39 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container.component.js +++ b/ui/components/app/confirm-page-container/confirm-page-container.component.js @@ -63,6 +63,8 @@ export default class ConfirmPageContainer extends Component { disabled: PropTypes.bool, editingGas: PropTypes.bool, handleCloseEditGas: PropTypes.func, + // Gas Popover + currentTransaction: PropTypes.object.isRequired, }; render() { @@ -110,6 +112,7 @@ export default class ConfirmPageContainer extends Component { ethGasPriceWarning, editingGas, handleCloseEditGas, + currentTransaction, } = this.props; const renderAssetImage = contentComponent || !identiconAddress; @@ -188,7 +191,12 @@ export default class ConfirmPageContainer extends Component { )} )} - {editingGas && } + {editingGas && ( + + )} ); } diff --git a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js index f27f6525d..740668416 100644 --- a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js +++ b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js @@ -8,12 +8,26 @@ import EditGasDisplay from '../edit-gas-display'; import EditGasDisplayEducation from '../edit-gas-display-education'; import { I18nContext } from '../../../contexts/i18n'; -import { hideModal, hideSidebar } from '../../../store/actions'; +import { + createCancelTransaction, + createSpeedUpTransaction, + hideModal, + hideSidebar, + updateTransaction, +} from '../../../store/actions'; + +export const EDIT_GAS_MODE = { + SPEED_UP: 'speed-up', + CANCEL: 'cancel', + MODIFY_IN_PLACE: 'modify-in-place', +}; export default function EditGasPopover({ popoverTitle, confirmButtonText, editGasDisplayProps, + transaction, + mode, onClose, }) { const t = useContext(I18nContext); @@ -37,6 +51,40 @@ export default function EditGasPopover({ } }, [showSidebar, onClose, dispatch]); + const onSubmit = useCallback(() => { + if (!transaction || !mode) { + closePopover(); + } + switch (mode) { + case EDIT_GAS_MODE.CANCEL: + dispatch( + createCancelTransaction(transaction.id, { + /** new gas settings */ + }), + ); + break; + case EDIT_GAS_MODE.SPEED_UP: + dispatch( + createSpeedUpTransaction(transaction.id, { + /** new gas settings */ + }), + ); + break; + case EDIT_GAS_MODE.MODIFY_IN_PLACE: + dispatch( + updateTransaction({ + ...transaction, + txParams: { ...transaction.txParams /** ...newGasSettings */ }, + }), + ); + break; + default: + break; + } + + closePopover(); + }, [transaction, mode, dispatch, closePopover]); + const title = showEducationContent ? t('editGasEducationModalTitle') : popoverTitle || t('editGasTitle'); @@ -51,7 +99,7 @@ export default function EditGasPopover({ } footer={ <> - + {footerButtonText} > @@ -77,6 +125,8 @@ EditGasPopover.propTypes = { confirmButtonText: PropTypes.string, showEducationButton: PropTypes.bool, onClose: PropTypes.func, + transaction: PropTypes.object, + mode: PropTypes.oneOf(Object.values(EDIT_GAS_MODE)), }; EditGasPopover.defaultProps = { diff --git a/ui/components/app/sidebars/sidebar.component.js b/ui/components/app/sidebars/sidebar.component.js index 1efd03c41..4ed71461b 100644 --- a/ui/components/app/sidebars/sidebar.component.js +++ b/ui/components/app/sidebars/sidebar.component.js @@ -4,8 +4,6 @@ import ReactCSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'; import CustomizeGas from '../gas-customization/gas-modal-page-container'; import { MILLISECOND } from '../../../../shared/constants/time'; -import EditGasPopover from '../edit-gas-popover/edit-gas-popover.component'; - export default class Sidebar extends Component { static propTypes = { sidebarOpen: PropTypes.bool, @@ -54,18 +52,6 @@ export default class Sidebar extends Component { } } - renderGasPopover() { - const { t } = this.context; - - return ( - - ); - } - componentDidUpdate(prevProps) { if (!prevProps.sidebarShouldClose && this.props.sidebarShouldClose) { this.props.hideSidebar(); @@ -77,10 +63,6 @@ export default class Sidebar extends Component { const showSidebar = sidebarOpen && !sidebarShouldClose; - if (showSidebar && process.env.SHOW_EIP_1559_UI) { - return this.renderGasPopover(); - } - return ( )} + {process.env.SHOW_EIP_1559_UI && showRetryEditGasPopover && ( + + )} + {process.env.SHOW_EIP_1559_UI && showCancelEditGasPopover && ( + + )} > ); } diff --git a/ui/hooks/useCancelTransaction.js b/ui/hooks/useCancelTransaction.js index 5c88d76a8..e0ee0cd4d 100644 --- a/ui/hooks/useCancelTransaction.js +++ b/ui/hooks/useCancelTransaction.js @@ -1,5 +1,5 @@ import { useDispatch, useSelector } from 'react-redux'; -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; import { showModal, showSidebar } from '../store/actions'; import { isBalanceSufficient } from '../pages/send/send.utils'; import { getSelectedAccount, getIsMainnet } from '../selectors'; @@ -34,9 +34,19 @@ export function useCancelTransaction(transactionGroup) { const conversionRate = useSelector(getConversionRate); const isMainnet = useSelector(getIsMainnet); const hideBasic = !(isMainnet || process.env.IN_TEST); + + const [showCancelEditGasPopover, setShowCancelEditGasPopover] = useState( + false, + ); + + const closeCancelEditGasPopover = () => setShowCancelEditGasPopover(false); + const cancelTransaction = useCallback( (event) => { event.stopPropagation(); + if (process.env.SHOW_EIP_1559_UI) { + return setShowCancelEditGasPopover(true); + } if (isLegacyTransaction(primaryTransaction)) { // To support the current process of cancelling or speeding up // a transaction, we have to inform the custom gas state of the new @@ -88,5 +98,8 @@ export function useCancelTransaction(transactionGroup) { conversionRate, }); - return [hasEnoughCancelGas, cancelTransaction]; + return [ + hasEnoughCancelGas, + { cancelTransaction, showCancelEditGasPopover, closeCancelEditGasPopover }, + ]; } diff --git a/ui/hooks/useCancelTransaction.test.js b/ui/hooks/useCancelTransaction.test.js index 8dd4e495b..711866bb4 100644 --- a/ui/hooks/useCancelTransaction.test.js +++ b/ui/hooks/useCancelTransaction.test.js @@ -58,8 +58,10 @@ describe('useCancelTransaction', function () { const { result } = renderHook(() => useCancelTransaction(transactionGroup), ); - expect(typeof result.current[1]).toStrictEqual('function'); - result.current[1]({ + expect(typeof result.current[1].cancelTransaction).toStrictEqual( + 'function', + ); + result.current[1].cancelTransaction({ preventDefault: () => undefined, stopPropagation: () => undefined, }); @@ -134,8 +136,10 @@ describe('useCancelTransaction', function () { const { result } = renderHook(() => useCancelTransaction(transactionGroup), ); - expect(typeof result.current[1]).toStrictEqual('function'); - result.current[1]({ + expect(typeof result.current[1].cancelTransaction).toStrictEqual( + 'function', + ); + result.current[1].cancelTransaction({ preventDefault: () => undefined, stopPropagation: () => undefined, }); diff --git a/ui/hooks/useRetryTransaction.js b/ui/hooks/useRetryTransaction.js index db1bf6205..f19c1000a 100644 --- a/ui/hooks/useRetryTransaction.js +++ b/ui/hooks/useRetryTransaction.js @@ -1,6 +1,6 @@ import { useDispatch, useSelector } from 'react-redux'; -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; import { showSidebar } from '../store/actions'; import { fetchBasicGasEstimates, @@ -11,11 +11,20 @@ import { getIsMainnet } from '../selectors'; import { isLegacyTransaction } from '../../shared/modules/transaction.utils'; import { useMetricEvent } from './useMetricEvent'; import { useIncrementedGasFees } from './useIncrementedGasFees'; + +/** + * @typedef {Object} RetryTransactionReturnValue + * @property {(event: Event) => void} retryTransaction - open edit gas popover + * to begin setting retry gas fees + * @property {boolean} showRetryEditGasPopover - Whether to show the popover + * @property {() => void} closeRetryEditGasPopover - close the popover. + */ + /** * Provides a reusable hook that, given a transactionGroup, will return * a method for beginning the retry process * @param {Object} transactionGroup - the transaction group - * @return {Function} + * @return {RetryTransactionReturnValue} */ export function useRetryTransaction(transactionGroup) { const { primaryTransaction } = transactionGroup; @@ -30,30 +39,35 @@ export function useRetryTransaction(transactionGroup) { }, }); const dispatch = useDispatch(); + const [showRetryEditGasPopover, setShowRetryEditGasPopover] = useState(false); + + const closeRetryEditGasPopover = () => setShowRetryEditGasPopover(false); const retryTransaction = useCallback( async (event) => { event.stopPropagation(); trackMetricsEvent(); - if (process.env.SHOW_EIP_1559_UI === true) { + if (process.env.SHOW_EIP_1559_UI) { + setShowRetryEditGasPopover(true); + } else { await dispatch(fetchBasicGasEstimates); - } - if (isLegacyTransaction(primaryTransaction)) { - // To support the current process of cancelling or speeding up - // a transaction, we have to inform the custom gas state of the new - // gasPrice to start at. - dispatch(setCustomGasPriceForRetry(customGasSettings.gasPrice)); - dispatch(setCustomGasLimit(primaryTransaction.txParams.gas)); - } + if (isLegacyTransaction(primaryTransaction)) { + // To support the current process of cancelling or speeding up + // a transaction, we have to inform the custom gas state of the new + // gasPrice to start at. + dispatch(setCustomGasPriceForRetry(customGasSettings.gasPrice)); + dispatch(setCustomGasLimit(primaryTransaction.txParams.gas)); + } - dispatch( - showSidebar({ - transitionName: 'sidebar-left', - type: 'customize-gas', - props: { transaction: primaryTransaction, hideBasic }, - }), - ); + dispatch( + showSidebar({ + transitionName: 'sidebar-left', + type: 'customize-gas', + props: { transaction: primaryTransaction, hideBasic }, + }), + ); + } }, [ dispatch, @@ -64,5 +78,9 @@ export function useRetryTransaction(transactionGroup) { ], ); - return retryTransaction; + return { + retryTransaction, + showRetryEditGasPopover, + closeRetryEditGasPopover, + }; } diff --git a/ui/hooks/useRetryTransaction.test.js b/ui/hooks/useRetryTransaction.test.js index 3c1f78401..b6b554a75 100644 --- a/ui/hooks/useRetryTransaction.test.js +++ b/ui/hooks/useRetryTransaction.test.js @@ -53,8 +53,8 @@ describe('useRetryTransaction', () => { const { result } = renderHook(() => useRetryTransaction(retryEnabledTransaction, true), ); - const retry = result.current; - retry(event); + const { retryTransaction } = result.current; + retryTransaction(event); expect(trackEvent.calledOnce).toStrictEqual(true); }); @@ -62,8 +62,8 @@ describe('useRetryTransaction', () => { const { result } = renderHook(() => useRetryTransaction(retryEnabledTransaction, true), ); - const retry = result.current; - await retry(event); + const { retryTransaction } = result.current; + await retryTransaction(event); expect( dispatch.calledWith( showSidebar({ @@ -108,8 +108,8 @@ describe('useRetryTransaction', () => { const { result } = renderHook(() => useRetryTransaction(cancelledTransaction, true), ); - const retry = result.current; - await retry(event); + const { retryTransaction } = result.current; + await retryTransaction(event); expect( dispatch.calledWith( showSidebar({ 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 6af9a95aa..e953adc1c 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -817,6 +817,7 @@ export default class ConfirmTransactionBase extends Component { ethGasPriceWarning={ethGasPriceWarning} editingGas={editingGas} handleCloseEditGas={() => this.handleCloseNewGasPopover()} + currentTransaction={txData} /> ); }