diff --git a/app/manifest/v3/_base.json b/app/manifest/v3/_base.json
index e517039aa2e4..fc810c5a8827 100644
--- a/app/manifest/v3/_base.json
+++ b/app/manifest/v3/_base.json
@@ -88,6 +88,7 @@
"webRequest",
"offscreen",
"identity",
+ "sidePanel",
"contextMenus"
],
"sandbox": {
diff --git a/app/manifest/v3/chrome.json b/app/manifest/v3/chrome.json
index 6ab1d3756485..73d6e1cfe49e 100644
--- a/app/manifest/v3/chrome.json
+++ b/app/manifest/v3/chrome.json
@@ -10,5 +10,8 @@
"matches": ["http://*/*", "https://*/*"],
"ids": ["*"]
},
- "minimum_chrome_version": "115"
+ "minimum_chrome_version": "115",
+ "side_panel": {
+ "default_path": "sidepanel.html"
+ }
}
diff --git a/builds.yml b/builds.yml
index f564ed0e89c3..52ff0fbf1853 100644
--- a/builds.yml
+++ b/builds.yml
@@ -34,7 +34,7 @@ buildTypes:
- REJECT_INVALID_SNAPS_PLATFORM_VERSION: true
- IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/iframe/10.2.3/index.html
- ACCOUNT_SNAPS_DIRECTORY_URL: https://snaps.metamask.io/account-management
- - IS_SIDEPANEL: false
+ - IS_SIDEPANEL: true
# for seedless onboarding (social login)
- GOOGLE_PROD_CLIENT_ID
- APPLE_PROD_CLIENT_ID
diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts
index 93c90313ff8e..3f6373637f87 100644
--- a/ui/ducks/app/app.ts
+++ b/ui/ducks/app/app.ts
@@ -716,6 +716,11 @@ export default function reduceApp(
),
isNetworkMenuOpen: !appState.isNetworkMenuOpen,
};
+ case actionConstants.CLOSE_NETWORK_MENU:
+ return {
+ ...appState,
+ isNetworkMenuOpen: false,
+ };
case actionConstants.DELETE_METAMETRICS_DATA_MODAL_OPEN:
return {
...appState,
diff --git a/ui/hooks/useModalState.ts b/ui/hooks/useModalState.ts
new file mode 100644
index 000000000000..2756f56ce257
--- /dev/null
+++ b/ui/hooks/useModalState.ts
@@ -0,0 +1,16 @@
+import { useCallback } from 'react';
+import { useDispatch } from 'react-redux';
+import { closeNetworkMenu } from '../store/actions';
+
+export function useModalState() {
+ const dispatch = useDispatch();
+
+ const closeModals = useCallback(
+ () => dispatch(closeNetworkMenu()),
+ [dispatch],
+ );
+
+ return {
+ closeModals,
+ };
+}
diff --git a/ui/pages/home/home.container.js b/ui/pages/home/home.container.js
index 17b78271e83c..0842ddb51fcb 100644
--- a/ui/pages/home/home.container.js
+++ b/ui/pages/home/home.container.js
@@ -26,7 +26,6 @@ import {
hasPendingApprovals,
getSelectedInternalAccount,
getEditedNetwork,
- selectPendingApprovalsForNavigation,
getShowUpdateModal,
getIsSocialLoginFlow,
getShowShieldEntryModal,
@@ -76,7 +75,6 @@ import { getIsBrowserDeprecated } from '../../helpers/utils/util';
import {
ENVIRONMENT_TYPE_NOTIFICATION,
ENVIRONMENT_TYPE_POPUP,
- ENVIRONMENT_TYPE_SIDEPANEL,
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES,
///: END:ONLY_INCLUDE_IF
@@ -99,8 +97,6 @@ const mapStateToProps = (state) => {
seedPhraseBackedUp,
connectedStatusPopoverHasBeenShown,
defaultHomeActiveTabName,
- swapsState,
- quotes,
dataCollectionForMarketing,
participateInMetaMetrics,
firstTimeFlowType,
@@ -111,13 +107,11 @@ const mapStateToProps = (state) => {
const { address: selectedAddress } = selectedAccount;
const totalUnapprovedCount = getTotalUnapprovedCount(state);
const swapsEnabled = getSwapsFeatureIsLive(state);
- const pendingApprovals = selectPendingApprovalsForNavigation(state);
const redirectAfterDefaultPage = getRedirectAfterDefaultPage(state);
const envType = getEnvironmentType();
const isPopup = envType === ENVIRONMENT_TYPE_POPUP;
const isNotification = envType === ENVIRONMENT_TYPE_NOTIFICATION;
- const isSidepanel = envType === ENVIRONMENT_TYPE_SIDEPANEL;
const originOfCurrentTab = getOriginOfCurrentTab(state);
const shouldShowWeb3ShimUsageNotification =
@@ -154,7 +148,6 @@ const mapStateToProps = (state) => {
isPopup,
isNotification,
dataCollectionForMarketing,
- isSidepanel,
selectedAddress,
totalUnapprovedCount,
participateInMetaMetrics,
@@ -163,14 +156,9 @@ const mapStateToProps = (state) => {
defaultHomeActiveTabName,
firstTimeFlowType,
completedOnboarding,
- haveSwapsQuotes: Boolean(Object.values(swapsState.quotes || {}).length),
- swapsFetchParams: swapsState.fetchParams,
- showAwaitingSwapScreen: swapsState.routeState === 'awaiting',
- haveBridgeQuotes: Boolean(Object.values(quotes || {}).length),
isMainnet: getIsMainnet(state),
originOfCurrentTab,
shouldShowWeb3ShimUsageNotification,
- pendingApprovals,
infuraBlocked: getInfuraBlocked(state),
announcementsToShow: getSortedAnnouncementsToShow(state).length > 0,
showWhatsNewPopup,
diff --git a/ui/pages/routes/confirmation-handler.tsx b/ui/pages/routes/confirmation-handler.tsx
index 9a7457367625..caccae33a022 100644
--- a/ui/pages/routes/confirmation-handler.tsx
+++ b/ui/pages/routes/confirmation-handler.tsx
@@ -7,7 +7,13 @@ import {
AWAITING_SWAP_ROUTE,
PREPARE_SWAP_ROUTE,
CROSS_CHAIN_SWAP_ROUTE,
- DEFAULT_ROUTE,
+ ACCOUNT_LIST_PAGE_ROUTE,
+ UNLOCK_ROUTE,
+ CONNECT_ROUTE,
+ CONFIRMATION_V_NEXT_ROUTE,
+ CONFIRM_TRANSACTION_ROUTE,
+ CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
+ CONFIRM_ADD_SUGGESTED_NFT_ROUTE,
} from '../../helpers/constants/routes';
import { getConfirmationRoute } from '../confirmations/hooks/useConfirmationNavigation';
// eslint-disable-next-line import/no-restricted-paths
@@ -28,6 +34,20 @@ import {
selectShowAwaitingSwapScreen,
} from '../../ducks/swaps/swaps';
import { useNavState } from '../../contexts/navigation-state';
+import { useModalState } from '../../hooks/useModalState';
+
+const EXEMPTED_ROUTES = [
+ ACCOUNT_LIST_PAGE_ROUTE,
+ AWAITING_SWAP_ROUTE,
+ PREPARE_SWAP_ROUTE,
+ CROSS_CHAIN_SWAP_ROUTE,
+ UNLOCK_ROUTE,
+ CONNECT_ROUTE,
+ CONFIRMATION_V_NEXT_ROUTE,
+ CONFIRM_TRANSACTION_ROUTE,
+ CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
+ CONFIRM_ADD_SUGGESTED_NFT_ROUTE,
+];
const SNAP_APPROVAL_TYPES = [
'wallet_installSnapResult',
@@ -44,6 +64,7 @@ export const ConfirmationHandler = () => {
const location = useLocation();
const { pathname } = location;
const navState = useNavState();
+ const { closeModals } = useModalState();
const envType = getEnvironmentType();
const isFullscreen = envType === ENVIRONMENT_TYPE_FULLSCREEN;
@@ -66,10 +87,13 @@ export const ConfirmationHandler = () => {
// Ported from home.component - checkStatusAndNavigate()
const checkStatusAndNavigate = useCallback(() => {
if (canRedirect && showAwaitingSwapScreen) {
+ closeModals();
navigate(AWAITING_SWAP_ROUTE);
} else if (canRedirect && (hasSwapsQuotes || swapsFetchParams)) {
+ closeModals();
navigate(PREPARE_SWAP_ROUTE);
} else if (canRedirect && hasBridgeQuotes) {
+ closeModals();
navigate(CROSS_CHAIN_SWAP_ROUTE + PREPARE_SWAP_ROUTE);
} else if (pendingApprovals.length || hasApprovalFlows) {
const url = getConfirmationRoute(
@@ -80,11 +104,13 @@ export const ConfirmationHandler = () => {
);
if (url) {
+ closeModals();
navigate(url, { replace: true });
}
}
}, [
canRedirect,
+ closeModals,
hasApprovalFlows,
hasBridgeQuotes,
hasSwapsQuotes,
@@ -94,13 +120,16 @@ export const ConfirmationHandler = () => {
swapsFetchParams,
]);
+ const isExemptedRoute = EXEMPTED_ROUTES.some((route) =>
+ pathname.startsWith(route),
+ );
+
const hasAllowedPopupRedirectApprovals = pendingApprovals.some((approval) =>
SNAP_APPROVAL_TYPES.includes(approval.type),
);
useEffect(() => {
- // Only run when on home/default page (for now)
- if (pathname !== DEFAULT_ROUTE) {
+ if (isExemptedRoute) {
return;
}
@@ -112,8 +141,8 @@ export const ConfirmationHandler = () => {
}, [
checkStatusAndNavigate,
hasAllowedPopupRedirectApprovals,
+ isExemptedRoute,
isFullscreen,
- pathname,
]);
return null;
diff --git a/ui/pages/routes/modals.tsx b/ui/pages/routes/modals.tsx
new file mode 100644
index 000000000000..2aa80810c670
--- /dev/null
+++ b/ui/pages/routes/modals.tsx
@@ -0,0 +1,25 @@
+import React, { useCallback } from 'react';
+import { useDispatch } from 'react-redux';
+import { useAppSelector } from '../../store/store';
+import { setEditedNetwork } from '../../store/actions';
+import { NetworkListMenu } from '../../components/multichain/network-list-menu';
+import { useModalState } from '../../hooks/useModalState';
+
+export const Modals = () => {
+ const dispatch = useDispatch();
+ const { closeModals } = useModalState();
+ const isNetworkMenuOpen = useAppSelector(
+ ({ appState }) => appState.isNetworkMenuOpen,
+ );
+
+ const handleClose = useCallback(() => {
+ closeModals();
+ dispatch(setEditedNetwork());
+ }, [closeModals, dispatch]);
+
+ const modal = isNetworkMenuOpen ? 'network' : undefined;
+
+ return (
+ <>{modal === 'network' && }>
+ );
+};
diff --git a/ui/pages/routes/routes.component.tsx b/ui/pages/routes/routes.component.tsx
index d5647ae56dd9..04fdd1a26ec2 100644
--- a/ui/pages/routes/routes.component.tsx
+++ b/ui/pages/routes/routes.component.tsx
@@ -23,7 +23,6 @@ import Loading from '../../components/ui/loading-screen';
import { Modal } from '../../components/app/modals';
import Alert from '../../components/ui/alert';
import {
- NetworkListMenu,
ImportNftsModal,
ImportTokensModal,
} from '../../components/multichain';
@@ -95,14 +94,12 @@ import {
setCurrentCurrency,
setLastActiveTime,
toggleAccountMenu,
- toggleNetworkMenu,
hideImportTokensModal,
hideDeprecatedNetworkModal,
automaticallySwitchNetwork,
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
hideKeyringRemovalResultModal,
///: END:ONLY_INCLUDE_IF
- setEditedNetwork,
} from '../../store/actions';
import { pageChanged } from '../../ducks/history/history';
import {
@@ -161,6 +158,7 @@ import {
setTheme,
} from './utils';
import { ConfirmationHandler } from './confirmation-handler';
+import { Modals } from './modals';
/**
* V5-to-v5-compat navigation function that bridges react-router-dom v5 history
@@ -525,9 +523,7 @@ export default function Routes() {
const isAccountMenuOpen = useAppSelector(
(state) => state.appState.isAccountMenuOpen,
);
- const isNetworkMenuOpen = useAppSelector(
- (state) => state.appState.isNetworkMenuOpen,
- );
+
const isImportTokensModalOpen = useAppSelector(
(state) => state.appState.importTokensModalOpen,
);
@@ -1198,14 +1194,7 @@ export default function Routes() {
)}
{isAccountMenuOpen ? accountListMenu : null}
- {isNetworkMenuOpen ? (
- {
- dispatch(toggleNetworkMenu());
- dispatch(setEditedNetwork());
- }}
- />
- ) : null}
+
{isImportNftsModalOpen ? (
dispatch(hideImportNftsModal())} />
@@ -1253,6 +1242,8 @@ export default function Routes() {
}>,
{ location },
)}
+
+
);
}
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index ac6d6f669572..06e1c0748ae1 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -1641,6 +1641,9 @@ const selectSnapId = (_state, snapId) => snapId;
*/
export const selectInstalledSnaps = (state) => state.metamask.snaps;
+export const selectIsNetworkMenuOpen = (state) =>
+ state.appState.isNetworkMenuOpen;
+
/**
* Retrieve registry data for requested Snap.
*
diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts
index 08145882f828..1ac2d8e6362f 100644
--- a/ui/store/actionConstants.ts
+++ b/ui/store/actionConstants.ts
@@ -83,6 +83,7 @@ export const SHOW_NFT_DETECTION_ENABLEMENT_TOAST =
export const TOGGLE_ACCOUNT_MENU = 'TOGGLE_ACCOUNT_MENU';
export const TOGGLE_NETWORK_MENU = 'TOGGLE_NETWORK_MENU';
+export const CLOSE_NETWORK_MENU = 'CLOSE_NETWORK_MENU';
export const SET_SELECTED_ACCOUNTS_FOR_DAPP_CONNECTIONS =
'SET_SELECTED_ACCOUNTS_FOR_DAPP_CONNECTIONS';
export const SET_SELECTED_NETWORKS_FOR_DAPP_CONNECTIONS =
diff --git a/ui/store/actions.ts b/ui/store/actions.ts
index 0ef91ba1990d..b3d0d5dcef01 100644
--- a/ui/store/actions.ts
+++ b/ui/store/actions.ts
@@ -4764,6 +4764,12 @@ export function toggleNetworkMenu(payload?: {
};
}
+export function closeNetworkMenu() {
+ return {
+ type: actionConstants.CLOSE_NETWORK_MENU,
+ };
+}
+
export function setParticipateInMetaMetrics(
participationPreference: boolean,
): ThunkAction<