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<