From 4e32d32994a61f1f8a8839b4ac5b6d19308a53be Mon Sep 17 00:00:00 2001 From: Bryan Fullam Date: Mon, 13 Jan 2025 21:41:20 +0100 Subject: [PATCH] feat: persist bridge state through ToS click (#29366) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Currently, clicking on Terms takes you to an external page and resets your bridge progress. We want to follow the same UX as swaps, where if the user clicks on the ToS link, the extension will close, but when the user reopens the extension, they remain on the bridge page. Acceptance Criteria: When clicking on the ToS link, the extension closes. But when the user re-opens the extension, they see the bridge page with tokens inputted (but not amounts) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29366?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to bridge 2. Put in tokens/amounts to get quote 3. Click "terms" link 4. Reopen extension and see inputs persisted ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- ui/pages/bridge/index.test.tsx | 2 +- ui/pages/bridge/index.tsx | 2 -- .../bridge/prepare/bridge-input-group.tsx | 8 +++-- .../bridge/prepare/prepare-bridge-page.tsx | 29 +++++++++++++++++-- ui/pages/home/home.component.js | 11 ++++++- ui/pages/home/home.container.js | 2 ++ 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/ui/pages/bridge/index.test.tsx b/ui/pages/bridge/index.test.tsx index 7878126c47e1..e5f225108b88 100644 --- a/ui/pages/bridge/index.test.tsx +++ b/ui/pages/bridge/index.test.tsx @@ -90,6 +90,6 @@ describe('Bridge', () => { expect(getByText('Bridge')).toBeInTheDocument(); expect(container).toMatchSnapshot(); - expect(mockResetBridgeState).toHaveBeenCalledTimes(2); + expect(mockResetBridgeState).toHaveBeenCalledTimes(1); }); }); diff --git a/ui/pages/bridge/index.tsx b/ui/pages/bridge/index.tsx index fa815a0de2b9..15c3a0365cc9 100644 --- a/ui/pages/bridge/index.tsx +++ b/ui/pages/bridge/index.tsx @@ -67,8 +67,6 @@ const CrossChainSwap = () => { useEffect(() => { // Reset controller and inputs before unloading the page - resetControllerAndInputStates(); - window.addEventListener('beforeunload', resetControllerAndInputStates); return () => { diff --git a/ui/pages/bridge/prepare/bridge-input-group.tsx b/ui/pages/bridge/prepare/bridge-input-group.tsx index ac545fb834dd..6c75c703e22b 100644 --- a/ui/pages/bridge/prepare/bridge-input-group.tsx +++ b/ui/pages/bridge/prepare/bridge-input-group.tsx @@ -15,7 +15,11 @@ import { TabName } from '../../../components/multichain/asset-picker-amount/asse import { useI18nContext } from '../../../hooks/useI18nContext'; import { getLocale } from '../../../selectors'; import { getCurrentCurrency } from '../../../ducks/metamask/metamask'; -import { formatCurrencyAmount, formatTokenAmount } from '../utils/quote'; +import { + formatCurrencyAmount, + formatTokenAmount, + isNativeAddress, +} from '../utils/quote'; import { Column, Row } from '../layout'; import { Display, @@ -234,7 +238,7 @@ export const BridgeInputGroup = ({ : undefined} {onMaxButtonClick && token && - token.type !== AssetType.native && + !isNativeAddress(token.address) && balanceAmount && ( { }, [rotateSwitchTokens]); useEffect(() => { - // Reset controller and inputs on load - dispatch(resetBridgeState()); + if (activeQuote) { + // Get input data from active quote + const { srcAsset, destAsset, destChainId } = activeQuote.quote; + const quoteSrcToken = fromTokens[srcAsset.address.toLowerCase()]; + const quoteDestChainId = decimalToPrefixedHex(destChainId); + const quoteDestToken = toTokens[destAsset.address.toLowerCase()]; + + if (quoteSrcToken && quoteDestToken && quoteDestChainId) { + // Set inputs to values from active quote + dispatch(setFromTokenInputValue(null)); + dispatch( + setFromToken({ ...quoteSrcToken, image: quoteSrcToken.iconUrl }), + ); + dispatch(setToChainId(quoteDestChainId)); + dispatch( + setToToken({ ...quoteDestToken, image: quoteDestToken.iconUrl }), + ); + } + } else { + // Reset controller and inputs on load + dispatch(resetBridgeState()); + } }, []); // Scroll to bottom of the page when banners are shown diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index df4ebaca0776..3daed446261a 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -58,6 +58,7 @@ import { CONNECTED_ACCOUNTS_ROUTE, AWAITING_SWAP_ROUTE, PREPARE_SWAP_ROUTE, + CROSS_CHAIN_SWAP_ROUTE, ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) ONBOARDING_SECURE_YOUR_WALLET_ROUTE, ///: END:ONLY_INCLUDE_IF @@ -159,6 +160,7 @@ export default class Home extends PureComponent { onTabClick: PropTypes.func.isRequired, haveSwapsQuotes: PropTypes.bool.isRequired, showAwaitingSwapScreen: PropTypes.bool.isRequired, + haveBridgeQuotes: PropTypes.bool.isRequired, setDataCollectionForMarketing: PropTypes.func.isRequired, dataCollectionForMarketing: PropTypes.bool, swapsFetchParams: PropTypes.object, @@ -220,6 +222,7 @@ export default class Home extends PureComponent { const { closeNotificationPopup, haveSwapsQuotes, + haveBridgeQuotes, isNotification, pendingApprovals, showAwaitingSwapScreen, @@ -235,7 +238,10 @@ export default class Home extends PureComponent { pendingApprovals.length || (!isNotification && !stayOnHomePage && - (showAwaitingSwapScreen || haveSwapsQuotes || swapsFetchParams)) + (showAwaitingSwapScreen || + haveSwapsQuotes || + swapsFetchParams || + haveBridgeQuotes)) ) { this.state.redirecting = true; } @@ -289,6 +295,7 @@ export default class Home extends PureComponent { history, isNotification, haveSwapsQuotes, + haveBridgeQuotes, showAwaitingSwapScreen, swapsFetchParams, location, @@ -306,6 +313,8 @@ export default class Home extends PureComponent { history.push(AWAITING_SWAP_ROUTE); } else if (canRedirect && (haveSwapsQuotes || swapsFetchParams)) { history.push(PREPARE_SWAP_ROUTE); + } else if (canRedirect && haveBridgeQuotes) { + history.push(CROSS_CHAIN_SWAP_ROUTE + PREPARE_SWAP_ROUTE); } else if (pendingApprovals.length) { navigateToConfirmation( pendingApprovals[0].id, diff --git a/ui/pages/home/home.container.js b/ui/pages/home/home.container.js index cae73b180f4d..9d29c91f7a8f 100644 --- a/ui/pages/home/home.container.js +++ b/ui/pages/home/home.container.js @@ -96,6 +96,7 @@ const mapStateToProps = (state) => { connectedStatusPopoverHasBeenShown, defaultHomeActiveTabName, swapsState, + bridgeState, dataCollectionForMarketing, participateInMetaMetrics, firstTimeFlowType, @@ -161,6 +162,7 @@ const mapStateToProps = (state) => { haveSwapsQuotes: Boolean(Object.values(swapsState.quotes || {}).length), swapsFetchParams: swapsState.fetchParams, showAwaitingSwapScreen: swapsState.routeState === 'awaiting', + haveBridgeQuotes: Boolean(Object.values(bridgeState?.quotes || {}).length), isMainnet: getIsMainnet(state), originOfCurrentTab, shouldShowWeb3ShimUsageNotification,