From 9416d1ca7120ef214f3e3a9f845be3b0a4864d25 Mon Sep 17 00:00:00 2001
From: Brad Decker <git@braddecker.dev>
Date: Mon, 12 Jul 2021 14:35:54 -0500
Subject: [PATCH] provide skeleton for submitting gas form (#11496)

---
 app/_locales/en/messages.json                 |  3 +
 .../confirm-page-container.component.js       | 10 +++-
 .../edit-gas-popover.component.js             | 54 +++++++++++++++++-
 .../app/sidebars/sidebar.component.js         | 18 ------
 .../transaction-list-item.component.js        | 31 ++++++++--
 ui/hooks/useCancelTransaction.js              | 17 +++++-
 ui/hooks/useCancelTransaction.test.js         | 12 ++--
 ui/hooks/useRetryTransaction.js               | 56 ++++++++++++-------
 ui/hooks/useRetryTransaction.test.js          | 12 ++--
 .../confirm-transaction-base.component.js     |  1 +
 10 files changed, 158 insertions(+), 56 deletions(-)

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 {
             )}
           </PageContainerFooter>
         )}
-        {editingGas && <EditGasPopover onClose={handleCloseEditGas} />}
+        {editingGas && (
+          <EditGasPopover
+            onClose={handleCloseEditGas}
+            transaction={currentTransaction}
+          />
+        )}
       </div>
     );
   }
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={
         <>
-          <Button type="primary" onClick={closePopover}>
+          <Button type="primary" onClick={onSubmit}>
             {footerButtonText}
           </Button>
         </>
@@ -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 (
-      <EditGasPopover
-        popoverTitle={t('speedUpPopoverTitle')}
-        editGasDisplayProps={{ alwaysShowForm: true, type: 'speed-up' }}
-        confirmButtonText={t('submit')}
-      />
-    );
-  }
-
   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 (
       <div>
         <ReactCSSTransitionGroup
diff --git a/ui/components/app/transaction-list-item/transaction-list-item.component.js b/ui/components/app/transaction-list-item/transaction-list-item.component.js
index 98635cce4..01cd5cbb7 100644
--- a/ui/components/app/transaction-list-item/transaction-list-item.component.js
+++ b/ui/components/app/transaction-list-item/transaction-list-item.component.js
@@ -18,6 +18,8 @@ import {
   TRANSACTION_GROUP_CATEGORIES,
   TRANSACTION_STATUSES,
 } from '../../../../shared/constants/transaction';
+import EditGasPopover from '../edit-gas-popover';
+import { EDIT_GAS_MODE } from '../edit-gas-popover/edit-gas-popover.component';
 
 export default function TransactionListItem({
   transactionGroup,
@@ -32,10 +34,15 @@ export default function TransactionListItem({
     initialTransaction: { id },
     primaryTransaction: { err, status },
   } = transactionGroup;
-  const [cancelEnabled, cancelTransaction] = useCancelTransaction(
-    transactionGroup,
-  );
-  const retryTransaction = useRetryTransaction(transactionGroup);
+  const [
+    cancelEnabled,
+    { cancelTransaction, showCancelEditGasPopover, closeCancelEditGasPopover },
+  ] = useCancelTransaction(transactionGroup);
+  const {
+    retryTransaction,
+    showRetryEditGasPopover,
+    closeRetryEditGasPopover,
+  } = useRetryTransaction(transactionGroup);
   const shouldShowSpeedUp = useShouldShowSpeedUp(
     transactionGroup,
     isEarliestNonce,
@@ -203,6 +210,22 @@ export default function TransactionListItem({
           cancelDisabled={!cancelEnabled}
         />
       )}
+      {process.env.SHOW_EIP_1559_UI && showRetryEditGasPopover && (
+        <EditGasPopover
+          popoverTitle={t('cancelPopoverTitle')}
+          onClose={closeRetryEditGasPopover}
+          mode={EDIT_GAS_MODE.SPEED_UP}
+          transaction={transactionGroup.primaryTransaction}
+        />
+      )}
+      {process.env.SHOW_EIP_1559_UI && showCancelEditGasPopover && (
+        <EditGasPopover
+          popoverTitle={t('speedUpPopoverTitle')}
+          onClose={closeCancelEditGasPopover}
+          mode={EDIT_GAS_MODE.CANCEL}
+          transaction={transactionGroup.primaryTransaction}
+        />
+      )}
     </>
   );
 }
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}
       />
     );
   }