Skip to content

Commit 20fc3c6

Browse files
release(runway): cherry-pick feat: cp-13.12.0 confirmations for sidepanel (#38394)
- feat: confirmations for sidepanel (#38375) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Enables the confirmation handler to listen from pages other than the home page. There's a list of exempted routes including the confirmation routes. Depends on #38361 <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/38375?quickstart=1) ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: feat: confirmations for sidepanel ## **Related issues** Fixes: ## **Manual testing steps** 1. Switch to sidepanel 2. Test requests and approvals ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] 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). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] 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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Enables confirmation routing across routes (incl. sidepanel), centralizes modal handling with a network menu close action, and updates manifests/builds for side panel support. > > - **UI**: > - **Confirmation routing**: `ConfirmationHandler` now runs outside `DEFAULT_ROUTE`, adds exempt routes, and closes modals before navigating. > - **Modals**: New `useModalState` hook and `Modals` container to render `NetworkListMenu`; routes integrate `Modals` and remove inline network menu handling. > - **App state**: Add `CLOSE_NETWORK_MENU` action/reducer; export `closeNetworkMenu`; add selector `selectIsNetworkMenuOpen`. > - **Cleanup**: Remove unused sidepanel/env and swaps props in `home.container`. > - **Manifests/Build**: > - Add `"sidePanel"` permission and Chrome `side_panel.default_path`. > - Set `IS_SIDEPANEL: true` for main build. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit a91ea96. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> [3c42ac3](3c42ac3) Co-authored-by: Francis Nepomuceno <n3ps@users.noreply.github.com>
1 parent 74e4bd9 commit 20fc3c6

File tree

12 files changed

+100
-32
lines changed

12 files changed

+100
-32
lines changed

app/manifest/v3/_base.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
"webRequest",
8989
"offscreen",
9090
"identity",
91+
"sidePanel",
9192
"contextMenus"
9293
],
9394
"sandbox": {

app/manifest/v3/chrome.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,8 @@
1010
"matches": ["http://*/*", "https://*/*"],
1111
"ids": ["*"]
1212
},
13-
"minimum_chrome_version": "115"
13+
"minimum_chrome_version": "115",
14+
"side_panel": {
15+
"default_path": "sidepanel.html"
16+
}
1417
}

builds.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ buildTypes:
3434
- REJECT_INVALID_SNAPS_PLATFORM_VERSION: true
3535
- IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/10.2.3/index.html
3636
- ACCOUNT_SNAPS_DIRECTORY_URL: https://snaps.metamask.io/account-management
37-
- IS_SIDEPANEL: false
37+
- IS_SIDEPANEL: true
3838
# for seedless onboarding (social login)
3939
- GOOGLE_PROD_CLIENT_ID
4040
- APPLE_PROD_CLIENT_ID

ui/ducks/app/app.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,11 @@ export default function reduceApp(
716716
),
717717
isNetworkMenuOpen: !appState.isNetworkMenuOpen,
718718
};
719+
case actionConstants.CLOSE_NETWORK_MENU:
720+
return {
721+
...appState,
722+
isNetworkMenuOpen: false,
723+
};
719724
case actionConstants.DELETE_METAMETRICS_DATA_MODAL_OPEN:
720725
return {
721726
...appState,

ui/hooks/useModalState.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useCallback } from 'react';
2+
import { useDispatch } from 'react-redux';
3+
import { closeNetworkMenu } from '../store/actions';
4+
5+
export function useModalState() {
6+
const dispatch = useDispatch();
7+
8+
const closeModals = useCallback(
9+
() => dispatch(closeNetworkMenu()),
10+
[dispatch],
11+
);
12+
13+
return {
14+
closeModals,
15+
};
16+
}

ui/pages/home/home.container.js

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
hasPendingApprovals,
2727
getSelectedInternalAccount,
2828
getEditedNetwork,
29-
selectPendingApprovalsForNavigation,
3029
getShowUpdateModal,
3130
getIsSocialLoginFlow,
3231
getShowShieldEntryModal,
@@ -76,7 +75,6 @@ import { getIsBrowserDeprecated } from '../../helpers/utils/util';
7675
import {
7776
ENVIRONMENT_TYPE_NOTIFICATION,
7877
ENVIRONMENT_TYPE_POPUP,
79-
ENVIRONMENT_TYPE_SIDEPANEL,
8078
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
8179
SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES,
8280
///: END:ONLY_INCLUDE_IF
@@ -99,8 +97,6 @@ const mapStateToProps = (state) => {
9997
seedPhraseBackedUp,
10098
connectedStatusPopoverHasBeenShown,
10199
defaultHomeActiveTabName,
102-
swapsState,
103-
quotes,
104100
dataCollectionForMarketing,
105101
participateInMetaMetrics,
106102
firstTimeFlowType,
@@ -111,13 +107,11 @@ const mapStateToProps = (state) => {
111107
const { address: selectedAddress } = selectedAccount;
112108
const totalUnapprovedCount = getTotalUnapprovedCount(state);
113109
const swapsEnabled = getSwapsFeatureIsLive(state);
114-
const pendingApprovals = selectPendingApprovalsForNavigation(state);
115110
const redirectAfterDefaultPage = getRedirectAfterDefaultPage(state);
116111

117112
const envType = getEnvironmentType();
118113
const isPopup = envType === ENVIRONMENT_TYPE_POPUP;
119114
const isNotification = envType === ENVIRONMENT_TYPE_NOTIFICATION;
120-
const isSidepanel = envType === ENVIRONMENT_TYPE_SIDEPANEL;
121115

122116
const originOfCurrentTab = getOriginOfCurrentTab(state);
123117
const shouldShowWeb3ShimUsageNotification =
@@ -154,7 +148,6 @@ const mapStateToProps = (state) => {
154148
isPopup,
155149
isNotification,
156150
dataCollectionForMarketing,
157-
isSidepanel,
158151
selectedAddress,
159152
totalUnapprovedCount,
160153
participateInMetaMetrics,
@@ -163,14 +156,9 @@ const mapStateToProps = (state) => {
163156
defaultHomeActiveTabName,
164157
firstTimeFlowType,
165158
completedOnboarding,
166-
haveSwapsQuotes: Boolean(Object.values(swapsState.quotes || {}).length),
167-
swapsFetchParams: swapsState.fetchParams,
168-
showAwaitingSwapScreen: swapsState.routeState === 'awaiting',
169-
haveBridgeQuotes: Boolean(Object.values(quotes || {}).length),
170159
isMainnet: getIsMainnet(state),
171160
originOfCurrentTab,
172161
shouldShowWeb3ShimUsageNotification,
173-
pendingApprovals,
174162
infuraBlocked: getInfuraBlocked(state),
175163
announcementsToShow: getSortedAnnouncementsToShow(state).length > 0,
176164
showWhatsNewPopup,

ui/pages/routes/confirmation-handler.tsx

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ import {
77
AWAITING_SWAP_ROUTE,
88
PREPARE_SWAP_ROUTE,
99
CROSS_CHAIN_SWAP_ROUTE,
10-
DEFAULT_ROUTE,
10+
ACCOUNT_LIST_PAGE_ROUTE,
11+
UNLOCK_ROUTE,
12+
CONNECT_ROUTE,
13+
CONFIRMATION_V_NEXT_ROUTE,
14+
CONFIRM_TRANSACTION_ROUTE,
15+
CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
16+
CONFIRM_ADD_SUGGESTED_NFT_ROUTE,
1117
} from '../../helpers/constants/routes';
1218
import { getConfirmationRoute } from '../confirmations/hooks/useConfirmationNavigation';
1319
// eslint-disable-next-line import/no-restricted-paths
@@ -28,6 +34,20 @@ import {
2834
selectShowAwaitingSwapScreen,
2935
} from '../../ducks/swaps/swaps';
3036
import { useNavState } from '../../contexts/navigation-state';
37+
import { useModalState } from '../../hooks/useModalState';
38+
39+
const EXEMPTED_ROUTES = [
40+
ACCOUNT_LIST_PAGE_ROUTE,
41+
AWAITING_SWAP_ROUTE,
42+
PREPARE_SWAP_ROUTE,
43+
CROSS_CHAIN_SWAP_ROUTE,
44+
UNLOCK_ROUTE,
45+
CONNECT_ROUTE,
46+
CONFIRMATION_V_NEXT_ROUTE,
47+
CONFIRM_TRANSACTION_ROUTE,
48+
CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
49+
CONFIRM_ADD_SUGGESTED_NFT_ROUTE,
50+
];
3151

3252
const SNAP_APPROVAL_TYPES = [
3353
'wallet_installSnapResult',
@@ -44,6 +64,7 @@ export const ConfirmationHandler = () => {
4464
const location = useLocation();
4565
const { pathname } = location;
4666
const navState = useNavState();
67+
const { closeModals } = useModalState();
4768

4869
const envType = getEnvironmentType();
4970
const isFullscreen = envType === ENVIRONMENT_TYPE_FULLSCREEN;
@@ -66,10 +87,13 @@ export const ConfirmationHandler = () => {
6687
// Ported from home.component - checkStatusAndNavigate()
6788
const checkStatusAndNavigate = useCallback(() => {
6889
if (canRedirect && showAwaitingSwapScreen) {
90+
closeModals();
6991
navigate(AWAITING_SWAP_ROUTE);
7092
} else if (canRedirect && (hasSwapsQuotes || swapsFetchParams)) {
93+
closeModals();
7194
navigate(PREPARE_SWAP_ROUTE);
7295
} else if (canRedirect && hasBridgeQuotes) {
96+
closeModals();
7397
navigate(CROSS_CHAIN_SWAP_ROUTE + PREPARE_SWAP_ROUTE);
7498
} else if (pendingApprovals.length || hasApprovalFlows) {
7599
const url = getConfirmationRoute(
@@ -80,11 +104,13 @@ export const ConfirmationHandler = () => {
80104
);
81105

82106
if (url) {
107+
closeModals();
83108
navigate(url, { replace: true });
84109
}
85110
}
86111
}, [
87112
canRedirect,
113+
closeModals,
88114
hasApprovalFlows,
89115
hasBridgeQuotes,
90116
hasSwapsQuotes,
@@ -94,13 +120,16 @@ export const ConfirmationHandler = () => {
94120
swapsFetchParams,
95121
]);
96122

123+
const isExemptedRoute = EXEMPTED_ROUTES.some((route) =>
124+
pathname.startsWith(route),
125+
);
126+
97127
const hasAllowedPopupRedirectApprovals = pendingApprovals.some((approval) =>
98128
SNAP_APPROVAL_TYPES.includes(approval.type),
99129
);
100130

101131
useEffect(() => {
102-
// Only run when on home/default page (for now)
103-
if (pathname !== DEFAULT_ROUTE) {
132+
if (isExemptedRoute) {
104133
return;
105134
}
106135

@@ -112,8 +141,8 @@ export const ConfirmationHandler = () => {
112141
}, [
113142
checkStatusAndNavigate,
114143
hasAllowedPopupRedirectApprovals,
144+
isExemptedRoute,
115145
isFullscreen,
116-
pathname,
117146
]);
118147

119148
return null;

ui/pages/routes/modals.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React, { useCallback } from 'react';
2+
import { useDispatch } from 'react-redux';
3+
import { useAppSelector } from '../../store/store';
4+
import { setEditedNetwork } from '../../store/actions';
5+
import { NetworkListMenu } from '../../components/multichain/network-list-menu';
6+
import { useModalState } from '../../hooks/useModalState';
7+
8+
export const Modals = () => {
9+
const dispatch = useDispatch();
10+
const { closeModals } = useModalState();
11+
const isNetworkMenuOpen = useAppSelector(
12+
({ appState }) => appState.isNetworkMenuOpen,
13+
);
14+
15+
const handleClose = useCallback(() => {
16+
closeModals();
17+
dispatch(setEditedNetwork());
18+
}, [closeModals, dispatch]);
19+
20+
const modal = isNetworkMenuOpen ? 'network' : undefined;
21+
22+
return (
23+
<>{modal === 'network' && <NetworkListMenu onClose={handleClose} />}</>
24+
);
25+
};

ui/pages/routes/routes.component.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import Loading from '../../components/ui/loading-screen';
2323
import { Modal } from '../../components/app/modals';
2424
import Alert from '../../components/ui/alert';
2525
import {
26-
NetworkListMenu,
2726
ImportNftsModal,
2827
ImportTokensModal,
2928
} from '../../components/multichain';
@@ -95,14 +94,12 @@ import {
9594
setCurrentCurrency,
9695
setLastActiveTime,
9796
toggleAccountMenu,
98-
toggleNetworkMenu,
9997
hideImportTokensModal,
10098
hideDeprecatedNetworkModal,
10199
automaticallySwitchNetwork,
102100
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
103101
hideKeyringRemovalResultModal,
104102
///: END:ONLY_INCLUDE_IF
105-
setEditedNetwork,
106103
} from '../../store/actions';
107104
import { pageChanged } from '../../ducks/history/history';
108105
import {
@@ -161,6 +158,7 @@ import {
161158
setTheme,
162159
} from './utils';
163160
import { ConfirmationHandler } from './confirmation-handler';
161+
import { Modals } from './modals';
164162

165163
/**
166164
* V5-to-v5-compat navigation function that bridges react-router-dom v5 history
@@ -525,9 +523,7 @@ export default function Routes() {
525523
const isAccountMenuOpen = useAppSelector(
526524
(state) => state.appState.isAccountMenuOpen,
527525
);
528-
const isNetworkMenuOpen = useAppSelector(
529-
(state) => state.appState.isNetworkMenuOpen,
530-
);
526+
531527
const isImportTokensModalOpen = useAppSelector(
532528
(state) => state.appState.importTokensModalOpen,
533529
);
@@ -1198,14 +1194,7 @@ export default function Routes() {
11981194
<MultichainMetaFoxLogo />
11991195
)}
12001196
{isAccountMenuOpen ? accountListMenu : null}
1201-
{isNetworkMenuOpen ? (
1202-
<NetworkListMenu
1203-
onClose={() => {
1204-
dispatch(toggleNetworkMenu());
1205-
dispatch(setEditedNetwork());
1206-
}}
1207-
/>
1208-
) : null}
1197+
12091198
<NetworkConfirmationPopover />
12101199
{isImportNftsModalOpen ? (
12111200
<ImportNftsModal onClose={() => dispatch(hideImportNftsModal())} />
@@ -1253,6 +1242,8 @@ export default function Routes() {
12531242
}>,
12541243
{ location },
12551244
)}
1245+
1246+
<Modals />
12561247
</div>
12571248
);
12581249
}

ui/selectors/selectors.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,9 @@ const selectSnapId = (_state, snapId) => snapId;
16411641
*/
16421642
export const selectInstalledSnaps = (state) => state.metamask.snaps;
16431643

1644+
export const selectIsNetworkMenuOpen = (state) =>
1645+
state.appState.isNetworkMenuOpen;
1646+
16441647
/**
16451648
* Retrieve registry data for requested Snap.
16461649
*

0 commit comments

Comments
 (0)