routes.component.tsx

Created Diff never expires
308 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
587 lines
433 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
716 lines
/* eslint-disable jsdoc/check-tag-names */
/* eslint-disable import/no-useless-path-segments */
/* eslint-disable import/extensions */
import classnames from 'classnames';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import React, { Suspense, useCallback, useEffect, useRef } from 'react';
import React, { Component, Suspense } from 'react';
import { useDispatch } from 'react-redux';
import { Route, Switch } from 'react-router-dom';
import { Route, Switch, useHistory, useLocation } from 'react-router-dom';
import IdleTimer from 'react-idle-timer';
import IdleTimer from 'react-idle-timer';
import type { ApprovalType } from '@metamask/controller-utils';


import { useAppSelector } from '../../store/store';
import Authenticated from '../../helpers/higher-order-components/authenticated';
import Authenticated from '../../helpers/higher-order-components/authenticated';
import Initialized from '../../helpers/higher-order-components/initialized';
import Initialized from '../../helpers/higher-order-components/initialized';
import PermissionsConnect from '../permissions-connect';
import PermissionsConnect from '../permissions-connect';
import Loading from '../../components/ui/loading-screen';
import Loading from '../../components/ui/loading-screen';
import LoadingNetwork from '../../components/app/loading-network-screen';
import LoadingNetwork from '../../components/app/loading-network-screen';
import { Modal } from '../../components/app/modals';
import { Modal } from '../../components/app/modals';
import Alert from '../../components/ui/alert';
import Alert from '../../components/ui/alert';
import {
import {
AppHeader,
AppHeader,
AccountListMenu,
AccountListMenu,
NetworkListMenu,
NetworkListMenu,
AccountDetails,
AccountDetails,
ImportNftsModal,
ImportNftsModal,
ImportTokensModal,
ImportTokensModal,
} from '../../components/multichain';
} from '../../components/multichain';
import Alerts from '../../components/app/alerts';
import Alerts from '../../components/app/alerts';


import {
import {
ASSET_ROUTE,
ASSET_ROUTE,
CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
CONFIRM_ADD_SUGGESTED_NFT_ROUTE,
CONFIRM_ADD_SUGGESTED_NFT_ROUTE,
CONFIRM_TRANSACTION_ROUTE,
CONFIRM_TRANSACTION_ROUTE,
CONNECT_ROUTE,
CONNECT_ROUTE,
DEFAULT_ROUTE,
DEFAULT_ROUTE,
LOCK_ROUTE,
LOCK_ROUTE,
NEW_ACCOUNT_ROUTE,
NEW_ACCOUNT_ROUTE,
RESTORE_VAULT_ROUTE,
RESTORE_VAULT_ROUTE,
REVEAL_SEED_ROUTE,
REVEAL_SEED_ROUTE,
SEND_ROUTE,
SEND_ROUTE,
SWAPS_ROUTE,
SWAPS_ROUTE,
SETTINGS_ROUTE,
SETTINGS_ROUTE,
UNLOCK_ROUTE,
UNLOCK_ROUTE,
CONFIRMATION_V_NEXT_ROUTE,
CONFIRMATION_V_NEXT_ROUTE,
ONBOARDING_ROUTE,
ONBOARDING_ROUTE,
CONNECTIONS,
CONNECTIONS,
PERMISSIONS,
PERMISSIONS,
REVIEW_PERMISSIONS,
REVIEW_PERMISSIONS,
SNAPS_ROUTE,
SNAPS_ROUTE,
SNAPS_VIEW_ROUTE,
SNAPS_VIEW_ROUTE,
NOTIFICATIONS_ROUTE,
NOTIFICATIONS_ROUTE,
NOTIFICATIONS_SETTINGS_ROUTE,
NOTIFICATIONS_SETTINGS_ROUTE,
CROSS_CHAIN_SWAP_ROUTE,
CROSS_CHAIN_SWAP_ROUTE,
CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE,
CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE,
IMPORT_SRP_ROUTE,
IMPORT_SRP_ROUTE,
DEFI_ROUTE,
DEFI_ROUTE,
DEEP_LINK_ROUTE,
DEEP_LINK_ROUTE,
SMART_ACCOUNT_UPDATE,
SMART_ACCOUNT_UPDATE,
WALLET_DETAILS_ROUTE,
WALLET_DETAILS_ROUTE,
ACCOUNT_DETAILS_ROUTE,
ACCOUNT_DETAILS_ROUTE,
ACCOUNT_DETAILS_QR_CODE_ROUTE,
ACCOUNT_DETAILS_QR_CODE_ROUTE,
ACCOUNT_LIST_PAGE_ROUTE,
} from '../../helpers/constants/routes';
} from '../../helpers/constants/routes';
import {
getProviderConfig,
isNetworkLoading as getIsNetworkLoading,
} from '../../../shared/modules/selectors/networks';
import {
getNetworkIdentifier,
getPreferences,
getTheme,
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
getUnapprovedConfirmations,
///: END:ONLY_INCLUDE_IF
getShowExtensionInFullSizeView,
getNetworkToAutomaticallySwitchTo,
getNumberOfAllUnapprovedTransactionsAndMessages,
getSelectedInternalAccount,
oldestPendingConfirmationSelector,
getUnapprovedTransactions,
getPendingApprovals,
getIsMultichainAccountsState1Enabled,
} from '../../selectors';
import {
hideImportNftsModal,
hideIpfsModal,
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 {
getCompletedOnboarding,
getIsUnlocked,
} from '../../ducks/metamask/metamask';
import { useI18nContext } from '../../hooks/useI18nContext';
import { DEFAULT_AUTO_LOCK_TIME_LIMIT } from '../../../shared/constants/preferences';
import { getShouldShowSeedPhraseReminder } from '../../selectors/multi-srp/multi-srp';


import {
import {
ENVIRONMENT_TYPE_NOTIFICATION,
ENVIRONMENT_TYPE_NOTIFICATION,
ENVIRONMENT_TYPE_POPUP,
ENVIRONMENT_TYPE_POPUP,
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES,
SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES,
///: END:ONLY_INCLUDE_IF
///: END:ONLY_INCLUDE_IF
} from '../../../shared/constants/app';
} from '../../../shared/constants/app';
// TODO: Remove restricted import
// TODO: Remove restricted import
// eslint-disable-next-line import/no-restricted-paths
// eslint-disable-next-line import/no-restricted-paths
import { getEnvironmentType } from '../../../app/scripts/lib/util';
import { getEnvironmentType } from '../../../app/scripts/lib/util';
import QRHardwarePopover from '../../components/app/qr-hardware-popover';
import QRHardwarePopover from '../../components/app/qr-hardware-popover';
import DeprecatedNetworks from '../../components/ui/deprecated-networks/deprecated-networks';
import DeprecatedNetworks from '../../components/ui/deprecated-networks/deprecated-networks';
import { Box } from '../../components/component-library';
import { Box } from '../../components/component-library';
import { ToggleIpfsModal } from '../../components/app/assets/nfts/nft-default-image/toggle-ipfs-modal';
import { ToggleIpfsModal } from '../../components/app/assets/nfts/nft-default-image/toggle-ipfs-modal';
import { BasicConfigurationModal } from '../../components/app/basic-configuration-modal';
import { BasicConfigurationModal } from '../../components/app/basic-configuration-modal';
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
import KeyringSnapRemovalResult from '../../components/app/modals/keyring-snap-removal-modal';
import KeyringSnapRemovalResult from '../../components/app/modals/keyring-snap-removal-modal';
///: END:ONLY_INCLUDE_IF
///: END:ONLY_INCLUDE_IF
import { MultichainAccountListMenu } from '../../components/multichain-accounts/multichain-account-list-menu';


import { DeprecatedNetworkModal } from '../settings/deprecated-network-modal/DeprecatedNetworkModal';
import { DeprecatedNetworkModal } from '../settings/deprecated-network-modal/DeprecatedNetworkModal';
import { MultichainMetaFoxLogo } from '../../components/multichain/app-header/multichain-meta-fox-logo';
import { MultichainMetaFoxLogo } from '../../components/multichain/app-header/multichain-meta-fox-logo';
import NetworkConfirmationPopover from '../../components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover';
import NetworkConfirmationPopover from '../../components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover';
import { ToastMaster } from '../../components/app/toast-master/toast-master';
import { ToastMaster } from '../../components/app/toast-master/toast-master';
import { mmLazy } from '../../helpers/utils/mm-lazy';
import { type DynamicImportType, mmLazy } from '../../helpers/utils/mm-lazy';
import CrossChainSwapTxDetails from '../bridge/transaction-details/transaction-details';
import CrossChainSwapTxDetails from '../bridge/transaction-details/transaction-details';
import {
import {
isCorrectDeveloperTransactionType,
isCorrectDeveloperTransactionType,
isCorrectSignatureApprovalType,
isCorrectSignatureApprovalType,
} from '../../../shared/lib/confirmation.utils';
} from '../../../shared/lib/confirmation.utils';
import { MultichainAccountListMenu } from '../../components/multichain-accounts/multichain-account-list-menu';
import { type Confirmation } from '../confirmations/types/confirm';
import { SmartAccountUpdate } from '../confirmations/components/confirm/smart-account-update';
import { SmartAccountUpdate } from '../confirmations/components/confirm/smart-account-update';
import { MultichainAccountDetails } from '../multichain-accounts/account-details';
import { MultichainAccountDetails } from '../multichain-accounts/account-details';
import { AddressQRCode } from '../multichain-accounts/address-qr-code';
import { AddressQRCode } from '../multichain-accounts/address-qr-code';
import { AccountList } from '../multichain-accounts/account-list';
import {
import {
getConnectingLabel,
getConnectingLabel,
hideAppHeader,
isConfirmTransactionRoute,
isConfirmTransactionRoute,
setTheme,
setTheme,
showAppHeader,
showAppHeader,
} from './utils';
} from './utils';


// TODO: Fix `as unknown as` casting once `mmLazy` is updated to handle named exports, wrapped components, and other React module types.
// Casting is preferable over `@ts-expect-error` annotations in this case,
// because it doesn't suppress competing error messages e.g. "Cannot find module..."

// Begin Lazy Routes
// Begin Lazy Routes
const OnboardingFlow = mmLazy(
const OnboardingFlow = mmLazy(
() => import('../onboarding-flow/onboarding-flow'),
(() =>
import(
'../onboarding-flow/onboarding-flow.js'
)) as unknown as DynamicImportType,
);
);
const Lock = mmLazy(() => import('../lock'));
const Lock = mmLazy(
const UnlockPage = mmLazy(() => import('../unlock-page'));
(() => import('../lock/index.js')) as unknown as DynamicImportType,
const RestoreVaultPage = mmLazy(() => import('../keychains/restore-vault'));
);
const ImportSrpPage = mmLazy(() => import('../multi-srp/import-srp'));
const UnlockPage = mmLazy(
const RevealSeedConfirmation = mmLazy(() => import('../keychains/reveal-seed'));
(() => import('../unlock-page/index.js')) as unknown as DynamicImportType,
const Settings = mmLazy(() => import('../settings'));
);
const NotificationsSettings = mmLazy(() => import('../notifications-settings'));
const RestoreVaultPage = mmLazy(
const NotificationDetails = mmLazy(() => import('../notification-details'));
(() =>
const Notifications = mmLazy(() => import('../notifications'));
import('../keychains/restore-vault.js')) as unknown as DynamicImportType,
const SnapList = mmLazy(() => import('../snaps/snaps-list'));
);
const SnapView = mmLazy(() => import('../snaps/snap-view'));
const ImportSrpPage = mmLazy(
// TODO: This is a named export. Fix incorrect type casting once `mmLazy` is updated to handle non-default export types.
(() =>
import('../multi-srp/import-srp/index.ts')) as unknown as DynamicImportType,
);
const RevealSeedConfirmation = mmLazy(
(() => import('../keychains/reveal-seed.js')) as unknown as DynamicImportType,
);
const Settings = mmLazy(
(() => import('../settings/index.js')) as unknown as DynamicImportType,
);
const NotificationsSettings = mmLazy(
(() =>
import(
'../notifications-settings/index.js'
)) as unknown as DynamicImportType,
);
const NotificationDetails = mmLazy(
(() =>
import('../notification-details/index.js')) as unknown as DynamicImportType,
);
const Notifications = mmLazy(
(() => import('../notifications/index.js')) as unknown as DynamicImportType,
);
const SnapList = mmLazy(
(() =>
import('../snaps/snaps-list/index.js')) as unknown as DynamicImportType,
);
const SnapView = mmLazy(
(() => import('../snaps/snap-view/index.js')) as unknown as DynamicImportType,
);
const ConfirmTransaction = mmLazy(
const ConfirmTransaction = mmLazy(
() => import('../confirmations/confirm-transaction'),
(() =>
import(
'../confirmations/confirm-transaction/index.js'
)) as unknown as DynamicImportType,
);
);
const SendPage = mmLazy(() => import('../../components/multichain/pages/send'));
const SendPage = mmLazy(
const Swaps = mmLazy(() => import('../swaps'));
// TODO: This is a named export. Fix incorrect type casting once `mmLazy` is updated to handle non-default export types.
const CrossChainSwap = mmLazy(() => import('../bridge'));
(() =>
import(
'../../components/multichain/pages/send/index.js'
)) as unknown as DynamicImportType,
);
const Swaps = mmLazy(
(() => import('../swaps/index.js')) as unknown as DynamicImportType,
);
const CrossChainSwap = mmLazy(
(() => import('../bridge/index.tsx')) as unknown as DynamicImportType,
);
const ConfirmAddSuggestedTokenPage = mmLazy(
const ConfirmAddSuggestedTokenPage = mmLazy(
() => import('../confirm-add-suggested-token'),
(() =>
import(
'../confirm-add-suggested-token/index.js'
)) as unknown as DynamicImportType,
);
);
const ConfirmAddSuggestedNftPage = mmLazy(
const ConfirmAddSuggestedNftPage = mmLazy(
() => import('../confirm-add-suggested-nft'),
(() =>
import(
'../confirm-add-suggested-nft/index.js'
)) as unknown as DynamicImportType,
);
);
const ConfirmationPage = mmLazy(() => import('../confirmations/confirmation'));
const ConfirmationPage = mmLazy(
(() =>
import(
'../confirmations/confirmation/index.js'
)) as unknown as DynamicImportType,
);
const CreateAccountPage = mmLazy(
const CreateAccountPage = mmLazy(
() => import('../create-account/create-account.component'),
(() =>
import(
'../create-account/create-account.component.js'
)) as unknown as DynamicImportType,
);
);
const NftFullImage = mmLazy(
const NftFullImage = mmLazy(
() => import('../../components/app/assets/nfts/nft-details/nft-full-image'),
(() =>
import(
'../../components/app/assets/nfts/nft-details/nft-full-image.tsx'
)) as unknown as DynamicImportType,
);
);
const Asset = mmLazy(() => import('../asset'));
const Asset = mmLazy(
const DeFiPage = mmLazy(() => import('../defi'));
(() => import('../asset/index.js')) as unknown as DynamicImportType,
);
const DeFiPage = mmLazy(
(() => import('../defi/index.ts')) as unknown as DynamicImportType,
);
const PermissionsPage = mmLazy(
const PermissionsPage = mmLazy(
() =>
// TODO: This is a named export. Fix incorrect type casting once `mmLazy` is updated to handle non-default export types.
(() =>
import(
import(
'../../components/multichain/pages/permissions-page/permissions-page'
'../../components/multichain/pages/permissions-page/permissions-page.js'
),
)) as unknown as DynamicImportType,
);
);
const Connections = mmLazy(
const Connections = mmLazy(
() => import('../../components/multichain/pages/connections'),
// TODO: This is a named export. Fix incorrect type casting once `mmLazy` is updated to handle non-default export types.
(() =>
import(
'../../components/multichain/pages/connections/index.js'
)) as unknown as DynamicImportType,
);
);
const ReviewPermissions = mmLazy(
const ReviewPermissions = mmLazy(
() =>
// TODO: This is a named export. Fix incorrect type casting once `mmLazy` is updated to handle non-default export types.
(() =>
import(
import(
'../../components/multichain/pages/review-permissions-page/review-permissions-page'
'../../components/multichain/pages/review-permissions-page/review-permissions-page.tsx'
),
)) as unknown as DynamicImportType,
);
);
const Home = mmLazy(() => import('../home'));


const DeepLink = mmLazy(() => import('../deep-link/deep-link'));
const Home = mmLazy(
(() => import('../home/index.js')) as unknown as DynamicImportType,
);

const DeepLink = mmLazy(
// TODO: This is a named export. Fix incorrect type casting once `mmLazy` is updated to handle non-default export types.
(() => import('../deep-link/deep-link.tsx')) as unknown as DynamicImportType,
);
const WalletDetails = mmLazy(
const WalletDetails = mmLazy(
() => import('../multichain-accounts/wallet-details'),
(() =>
import(
'../multichain-accounts/wallet-details/index.ts'
)) as unknown as DynamicImportType,
);
);
// End Lazy Routes
// End Lazy Routes


export default class Routes extends Component {
// eslint-disable-next-line @typescript-eslint/naming-convention
static propTypes = {
export default function Routes() {
currentCurrency: PropTypes.string,
const dispatch = useDispatch();
setCurrentCurrencyToUSD: PropTypes.func,
const history = useHistory();
isLoading: PropTypes.bool,
const location = useLocation();
loadingMessage: PropTypes.string,
alertMessage: PropTypes.string,
textDirection: PropTypes.string,
isNetworkLoading: PropTypes.bool,
alertOpen: PropTypes.bool,
isUnlocked: PropTypes.bool,
setLastActiveTime: PropTypes.func,
history: PropTypes.object,
location: PropTypes.object,
autoLockTimeLimit: PropTypes.number,
privacyMode: PropTypes.bool,
pageChanged: PropTypes.func.isRequired,
browserEnvironmentOs: PropTypes.string,
browserEnvironmentBrowser: PropTypes.string,
theme: PropTypes.string,
showExtensionInFullSizeView: PropTypes.bool,
shouldShowSeedPhraseReminder: PropTypes.bool,
forgottenPassword: PropTypes.bool,
completedOnboarding: PropTypes.bool,
isAccountMenuOpen: PropTypes.bool,
toggleAccountMenu: PropTypes.func,
isNetworkMenuOpen: PropTypes.bool,
networkMenuClose: PropTypes.func,
accountDetailsAddress: PropTypes.string,
isImportNftsModalOpen: PropTypes.bool.isRequired,
hideImportNftsModal: PropTypes.func.isRequired,
isIpfsModalOpen: PropTypes.bool.isRequired,
isBasicConfigurationModalOpen: PropTypes.bool.isRequired,
hideIpfsModal: PropTypes.func.isRequired,
isImportTokensModalOpen: PropTypes.bool.isRequired,
hideImportTokensModal: PropTypes.func.isRequired,
isDeprecatedNetworkModalOpen: PropTypes.bool.isRequired,
hideDeprecatedNetworkModal: PropTypes.func.isRequired,
networkToAutomaticallySwitchTo: PropTypes.object,
automaticallySwitchNetwork: PropTypes.func.isRequired,
totalUnapprovedConfirmationCount: PropTypes.number.isRequired,
currentExtensionPopupId: PropTypes.number,
oldestPendingApproval: PropTypes.object,
pendingApprovals: PropTypes.arrayOf(PropTypes.object).isRequired,
transactionsMetadata: PropTypes.object.isRequired,
isMultichainAccountsState1Enabled: PropTypes.bool.isRequired,
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
isShowKeyringSnapRemovalResultModal: PropTypes.bool.isRequired,
hideShowKeyringSnapRemovalResultModal: PropTypes.func.isRequired,
pendingConfirmations: PropTypes.array.isRequired,
///: END:ONLY_INCLUDE_IF
};


static contextTypes = {
const alertOpen = useAppSelector((state) => state.appState.alertOpen);
t: PropTypes.func,
const alertMessage = useAppSelector((state) => state.appState.alertMessage);
metricsEvent: PropTypes.func,
const isLoading = useAppSelector((state) => state.appState.isLoading);
};
const loadingMessage = useAppSelector(
(state) => state.appState.loadingMessage,
);
const { autoLockTimeLimit = DEFAULT_AUTO_LOCK_TIME_LIMIT, privacyMode } =
useAppSelector(getPreferences);
const completedOnboarding = useAppSelector(getCompletedOnboarding);


componentDidUpdate(prevProps) {
// If there is more than one connected account to activeTabOrigin,
const {
// *BUT* the current account is not one of them, show the banner
theme,
const account = useAppSelector(getSelectedInternalAccount);
networkToAutomaticallySwitchTo,
const isNetworkLoading = useAppSelector(getIsNetworkLoading);
totalUnapprovedConfirmationCount,

isUnlocked,
const networkToAutomaticallySwitchTo = useAppSelector(
currentExtensionPopupId,
getNetworkToAutomaticallySwitchTo,
} = this.props;
);
if (theme !== prevProps.theme) {
const oldestPendingApproval = useAppSelector(
setTheme(theme);
oldestPendingConfirmationSelector,
}
);
const pendingApprovals = useAppSelector(getPendingApprovals);
const transactionsMetadata = useAppSelector(getUnapprovedTransactions);

const shouldShowSeedPhraseReminder = useAppSelector((state) =>
getShouldShowSeedPhraseReminder(state, account),
);

const textDirection = useAppSelector((state) => state.metamask.textDirection);
const isUnlocked = useAppSelector(getIsUnlocked);
const currentCurrency = useAppSelector(
(state) => state.metamask.currentCurrency,
);
const os = useAppSelector((state) => state.metamask.browserEnvironment?.os);
const browser = useAppSelector(
(state) => state.metamask.browserEnvironment?.browser,
);
const providerId = useAppSelector(getNetworkIdentifier);
const { type: providerType } = useAppSelector(getProviderConfig);
const theme = useAppSelector(getTheme);
const showExtensionInFullSizeView = useAppSelector(
getShowExtensionInFullSizeView,
);
const forgottenPassword = useAppSelector(
(state) => state.metamask.forgottenPassword,
);
const isAccountMenuOpen = useAppSelector(
(state) => state.appState.isAccountMenuOpen,
);
const isNetworkMenuOpen = useAppSelector(
(state) => state.appState.isNetworkMenuOpen,
);
const isImportTokensModalOpen = useAppSelector(
(state) => state.appState.importTokensModalOpen,
);
const isBasicConfigurationModalOpen = useAppSelector(
(state) => state.appState.showBasicFunctionalityModal,
);
const isDeprecatedNetworkModalOpen = useAppSelector(
(state) => state.appState.deprecatedNetworkModalOpen,
);
const accountDetailsAddress = useAppSelector(
(state) => state.appState.accountDetailsAddress,
);
const isImportNftsModalOpen = useAppSelector(
(state) => state.appState.importNftsModal.open,
);
const isIpfsModalOpen = useAppSelector(
(state) => state.appState.showIpfsModalOpen,
);
const totalUnapprovedConfirmationCount = useAppSelector(
getNumberOfAllUnapprovedTransactionsAndMessages,
);
const currentExtensionPopupId = useAppSelector(
(state) => state.metamask.currentExtensionPopupId,
);

///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
const isShowKeyringSnapRemovalResultModal = useAppSelector(
(state) => state.appState.showKeyringRemovalSnapModal,
);
const pendingConfirmations = useAppSelector(getUnapprovedConfirmations);
const hideShowKeyringSnapRemovalResultModal = () =>
dispatch(hideKeyringRemovalResultModal());
///: END:ONLY_INCLUDE_IF

const isMultichainAccountsState1Enabled = useAppSelector(
getIsMultichainAccountsState1Enabled,
);

const prevPropsRef = useRef({ isUnlocked, totalUnapprovedConfirmationCount });

useEffect(() => {
const prevProps = prevPropsRef.current;


// Automatically switch the network if the user
// Automatically switch the network if the user
// no longer has unapproved transactions and they
// no longer has unapproved transactions and they
// should be on a different network for the
// should be on a different network for the
// currently active tab's dapp
// currently active tab's dapp
if (
if (
networkToAutomaticallySwitchTo &&
networkToAutomaticallySwitchTo &&
totalUnapprovedConfirmationCount === 0 &&
totalUnapprovedConfirmationCount === 0 &&
(prevProps.totalUnapprovedConfirmationCount > 0 ||
(prevProps.totalUnapprovedConfirmationCount > 0 ||
(prevProps.isUnlocked === false && isUnlocked))
(prevProps.isUnlocked === false && isUnlocked))
) {
) {
this.props.automaticallySwitchNetwork(networkToAutomaticallySwitchTo);
dispatch(automaticallySwitchNetwork(networkToAutomaticallySwitchTo));
}
}


prevPropsRef.current = { isUnlocked, totalUnapprovedConfirmationCount };
}, [
networkToAutomaticallySwitchTo,
isUnlocked,
totalUnapprovedConfirmationCount,
dispatch,
]);

useEffect(() => {
// Terminate the popup when another popup is opened
// Terminate the popup when another popup is opened
// if the user is using RPC queueing
// if the user is using RPC queueing
if (
if (
currentExtensionPopupId !== undefined &&
currentExtensionPopupId !== undefined &&
'metamask' in global &&
typeof global.metamask === 'object' &&
global.metamask &&
'id' in global.metamask &&
global.metamask.id !== undefined &&
global.metamask.id !== undefined &&
currentExtensionPopupId !== global.metamask.id
currentExtensionPopupId !== global.metamask.id
) {
) {
window.close();
window.close();
}
}
}
}, [currentExtensionPopupId]);

UNSAFE_componentWillMount() {
const {
currentCurrency,
pageChanged,
setCurrentCurrencyToUSD,
history,
showExtensionInFullSizeView,
} = this.props;


useEffect(() => {
const windowType = getEnvironmentType();
const windowType = getEnvironmentType();
if (showExtensionInFullSizeView && windowType === ENVIRONMENT_TYPE_POPUP) {
const { openExtensionInBrowser } = globalThis.platform ?? {};
global.platform.openExtensionInBrowser();
if (
}
showExtensionInFullSizeView &&

windowType === ENVIRONMENT_TYPE_POPUP &&
if (!currentCurrency) {
openExtensionInBrowser
setCurrentCurrencyToUSD();
) {
openExtensionInBrowser();
}
}
}, [showExtensionInFullSizeView]);


history.listen((locationObj, action) => {
useEffect(() => {
const unlisten = history.listen((locationObj: Location, action: 'PUSH') => {
if (action === 'PUSH') {
if (action === 'PUSH') {
pageChanged(locationObj.pathname);
dispatch(pageChanged(locationObj.pathname));
}
}
});
});


setTheme(this.props.theme);
return () => {
}
unlisten();
};
}, [history, dispatch]);


renderRoutes() {
useEffect(() => {
const { autoLockTimeLimit, setLastActiveTime, forgottenPassword } =
setTheme(theme);
this.props;
}, [theme]);

useEffect(() => {
if (!currentCurrency) {
dispatch(setCurrentCurrency('usd'));
}
}, [currentCurrency, dispatch]);

const renderRoutes = useCallback(() => {
const RestoreVaultComponent = forgottenPassword ? Route : Initialized;
const RestoreVaultComponent = forgottenPassword ? Route : Initialized;


const routes = (
const routes = (
<Suspense fallback={null}>
<Suspense fallback={null}>
{/* since the loading time is less than 200ms, we decided not to show a spinner fallback or anything */}
{/* since the loading time is less than 200ms, we decided not to show a spinner fallback or anything */}
<Switch>
<Switch>
{/** @ts-expect-error TODO: Replace `component` prop with `element` once `react-router` is upgraded to v6 */}
<Route path={ONBOARDING_ROUTE} component={OnboardingFlow} />
<Route path={ONBOARDING_ROUTE} component={OnboardingFlow} />
{/** @ts-expect-error TODO: Replace `component` prop with `element` once `react-router` is upgraded to v6 */}
<Route path={LOCK_ROUTE} component={Lock} exact />
<Route path={LOCK_ROUTE} component={Lock} exact />
<Initialized path={UNLOCK_ROUTE} component={UnlockPage} exact />
<Initialized path={UNLOCK_ROUTE} component={UnlockPage} exact />
{/** @ts-expect-error TODO: Replace `component` prop with `element` once `react-router` is upgraded to v6 */}
<Route path={DEEP_LINK_ROUTE} component={DeepLink} />
<Route path={DEEP_LINK_ROUTE} component={DeepLink} />
<RestoreVaultComponent
<RestoreVaultComponent
path={RESTORE_VAULT_ROUTE}
path={RESTORE_VAULT_ROUTE}
component={RestoreVaultPage}
component={RestoreVaultPage}
exact
exact
/>
/>
<Authenticated
<Authenticated
path={SMART_ACCOUNT_UPDATE}
path={SMART_ACCOUNT_UPDATE}
component={SmartAccountUpdate}
component={SmartAccountUpdate}
/>
/>
<Authenticated
<Authenticated
// `:keyringId` is optional here, if not provided, this will fallback
// `:keyringId` is optional here, if not provided, this will fallback
// to the main seed phrase.
// to the main seed phrase.
path={`${REVEAL_SEED_ROUTE}/:keyringId?`}
path={`${REVEAL_SEED_ROUTE}/:keyringId?`}
component={RevealSeedConfirmation}
component={RevealSeedConfirmation}
/>
/>
<Authenticated path={IMPORT_SRP_ROUTE} component={ImportSrpPage} />
<Authenticated path={IMPORT_SRP_ROUTE} component={ImportSrpPage} />
<Authenticated path={SETTINGS_ROUTE} component={Settings} />
<Authenticated path={SETTINGS_ROUTE} component={Settings} />
<Authenticated
<Authenticated
path={NOTIFICATIONS_SETTINGS_ROUTE}
path={NOTIFICATIONS_SETTINGS_ROUTE}
component={NotificationsSettings}
component={NotificationsSettings}
/>
/>
<Authenticated
<Authenticated
path={`${NOTIFICATIONS_ROUTE}/:uuid`}
path={`${NOTIFICATIONS_ROUTE}/:uuid`}
component={NotificationDetails}
component={NotificationDetails}
/>
/>
<Authenticated path={NOTIFICATIONS_ROUTE} component={Notifications} />
<Authenticated path={NOTIFICATIONS_ROUTE} component={Notifications} />
<Authenticated exact path={SNAPS_ROUTE} component={SnapList} />
<Authenticated path={SNAPS_ROUTE} component={SnapList} exact />
<Authenticated path={SNAPS_VIEW_ROUTE} component={SnapView} />
<Authenticated path={SNAPS_VIEW_ROUTE} component={SnapView} />
<Authenticated
<Authenticated
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?`}
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?`}
component={ConfirmTransaction}
component={ConfirmTransaction}
/>
/>
<Authenticated path={SEND_ROUTE} component={SendPage} exact />
<Authenticated path={SEND_ROUTE} component={SendPage} exact />
<Authenticated path={SWAPS_ROUTE} component={Swaps} />
<Authenticated path={SWAPS_ROUTE} component={Swaps} />
<Authenticated
<Authenticated
path={`${CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE}/:srcTxMetaId`}
path={`${CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE}/:srcTxMetaId`}
component={CrossChainSwapTxDetails}
component={CrossChainSwapTxDetails}
exact
exact
/>
/>
<Authenticated
<Authenticated
path={CROSS_CHAIN_SWAP_ROUTE}
path={CROSS_CHAIN_SWAP_ROUTE}
component={CrossChainSwap}
component={CrossChainSwap}
/>
/>
<Authenticated
<Authenticated
path={CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE}
path={CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE}
component={ConfirmAddSuggestedTokenPage}
component={ConfirmAddSuggestedTokenPage}
exact
exact
/>
/>
<Authenticated
<Authenticated
path={CONFIRM_ADD_SUGGESTED_NFT_ROUTE}
path={CONFIRM_ADD_SUGGESTED_NFT_ROUTE}
component={ConfirmAddSuggestedNftPage}
component={ConfirmAddSuggestedNftPage}
exact
exact
/>
/>
<Authenticated
<Authenticated
path={`${CONFIRMATION_V_NEXT_ROUTE}/:id?`}
path={`${CONFIRMATION_V_NEXT_ROUTE}/:id?`}
component={ConfirmationPage}
component={ConfirmationPage}
/>
/>
<Authenticated
<Authenticated
path={NEW_ACCOUNT_ROUTE}
path={NEW_ACCOUNT_ROUTE}
component={CreateAccountPage}
component={CreateAccountPage}
/>
/>
<Authenticated
<Authenticated
path={`${CONNECT_ROUTE}/:id`}
path={`${CONNECT_ROUTE}/:id`}
component={PermissionsConnect}
component={PermissionsConnect}
/>
/>
<Authenticated
<Authenticated
path={`${ASSET_ROUTE}/image/:asset/:id`}
path={`${ASSET_ROUTE}/image/:asset/:id`}
component={NftFullImage}
component={NftFullImage}
/>
/>
<Authenticated
<Authenticated
path={`${ASSET_ROUTE}/:chainId/:asset/:id`}
path={`${ASSET_ROUTE}/:chainId/:asset/:id`}
component={Asset}
component={Asset}
/>
/>
<Authenticated
<Authenticated
path={`${ASSET_ROUTE}/:chainId/:asset/`}
path={`${ASSET_ROUTE}/:chainId/:asset/`}
component={Asset}
component={Asset}
/>
/>
<Authenticated path={`${ASSET_ROUTE}/:chainId`} component={Asset} />
<Authenticated path={`${ASSET_ROUTE}/:chainId`} component={Asset} />
<Authenticated
<Authenticated
path={`${DEFI_ROUTE}/:chainId/:protocolId`}
path={`${DEFI_ROUTE}/:chainId/:protocolId`}
component={DeFiPage}
component={DeFiPage}
/>
/>
<Authenticated
<Authenticated
path={`${CONNECTIONS}/:origin`}
path={`${CONNECTIONS}/:origin`}
component={Connections}
component={Connections}
/>
/>
<Authenticated path={PERMISSIONS} component={PermissionsPage} exact />
<Authenticated path={PERMISSIONS} component={PermissionsPage} exact />
<Authenticated
<Authenticated
path={`${REVIEW_PERMISSIONS}/:origin`}
path={`${REVIEW_PERMISSIONS}/:origin`}
component={ReviewPermissions}
component={ReviewPermissions}
exact
exact
/>
/>
<Authenticated
<Authenticated
path={ACCOUNT_LIST_PAGE_ROUTE}
component={AccountList}
exact
/>
<Authenticated
path={WALLET_DETAILS_ROUTE}
path={WALLET_DETAILS_ROUTE}
component={WalletDetails}
component={WalletDetails}
exact
exact
/>
/>
<Authenticated
<Authenticated
path={`${ACCOUNT_DETAILS_ROUTE}/:address`}
path={`${ACCOUNT_DETAILS_ROUTE}/:address`}
component={MultichainAccountDetails}
component={MultichainAccountDetails}
exact
exact
/>
/>
<Authenticated
<Authenticated
path={`${ACCOUNT_DETAILS_QR_CODE_ROUTE}/:address`}
path={`${ACCOUNT_DETAILS_QR_CODE_ROUTE}/:address`}
component={AddressQRCode}
component={AddressQRCode}
exact
exact
/>
/>

<Authenticated path={DEFAULT_ROUTE} component={Home} />
<Authenticated path={DEFAULT_ROUTE} component={Home} />
</Switch>
</Switch>
</Suspense>
</Suspense>
);
);


if (autoLockTimeLimit > 0) {
if (autoLockTimeLimit > 0) {
return (
return (
<IdleTimer onAction={setLastActiveTime} throttle={1000}>
<IdleTimer
onAction={() => dispatch(setLastActiveTime())}
throttle={1000}
>
{routes}
{routes}
</IdleTimer>
</IdleTimer>
);
);
}
}


return routes;
return routes;
}
}, [autoLockTimeLimit, forgottenPassword, dispatch]);


renderAccountDetails() {
const t = useI18nContext();
const { accountDetailsAddress, isMultichainAccountsState1Enabled } =

this.props;
const renderAccountDetails = () => {
if (!accountDetailsAddress || isMultichainAccountsState1Enabled) {
if (!accountDetailsAddress || isMultichainAccountsState1Enabled) {
return null;
return null;
}
}
return <AccountDetails address={accountDetailsAddress} />;
return <AccountDetails address={accountDetailsAddress} />;
}
};

render() {
const {
isLoading,
isUnlocked,
alertMessage,
textDirection,
loadingMessage,
isNetworkLoading,
browserEnvironmentOs: os,
browserEnvironmentBrowser: browser,
shouldShowSeedPhraseReminder,
completedOnboarding,
isAccountMenuOpen,
toggleAccountMenu,
isNetworkMenuOpen,
isImportTokensModalOpen,
isDeprecatedNetworkModalOpen,
location,
isImportNftsModalOpen,
hideImportNftsModal,
isIpfsModalOpen,
isBasicConfigurationModalOpen,
hideIpfsModal,
hideImportTokensModal,
hideDeprecatedNetworkModal,
networkMenuClose,
privacyMode,
oldestPendingApproval,
pendingApprovals,
transactionsMetadata,
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
isShowKeyringSnapRemovalResultModal,
hideShowKeyringSnapRemovalResultModal,
pendingConfirmations,
///: END:ONLY_INCLUDE_IF
} = this.props;

const loadMessage =
loadingMessage || isNetworkLoading
? getConnectingLabel(loadingMessage, this.props, this.context)
: null;


const windowType = getEnvironmentType();
const loadMessage =
loadingMessage || isNetworkLoading
? getConnectingLabel(loadingMessage, { providerType, providerId }, { t })
: null;


const shouldShowNetworkDeprecationWarning =
const windowType = getEnvironmentType();
windowType !== ENVIRONMENT_TYPE_NOTIFICATION &&
isUnlocked &&
!shouldShowSeedPhraseReminder;


const paramsConfirmationId = location.pathname.split(
const shouldShowNetworkDeprecationWarning =
'/confirm-transaction/',
windowType !== ENVIRONMENT_TYPE_NOTIFICATION &&
)[1];
isUnlocked &&
const confirmationId = paramsConfirmationId ?? oldestPendingApproval?.id;
!shouldShowSeedPhraseReminder;
const pendingApproval = pendingApprovals.find(
(approval) => approval.id === confirmationId,
);
const isCorrectApprovalType = isCorrectSignatureApprovalType(
pendingApproval?.type,
);
const isCorrectTransactionType = isCorrectDeveloperTransactionType(
transactionsMetadata[confirmationId]?.type,
);


const isShowingDeepLinkRoute = location.pathname === DEEP_LINK_ROUTE;
const paramsConfirmationId: string = location.pathname.split(
'/confirm-transaction/',
)[1];
const confirmationId = paramsConfirmationId ?? oldestPendingApproval?.id;
const pendingApproval = pendingApprovals.find(
(approval) => approval.id === confirmationId,
);
const isCorrectApprovalType = isCorrectSignatureApprovalType(
pendingApproval?.type as ApprovalType | undefined,
);
const isCorrectTransactionType = isCorrectDeveloperTransactionType(
transactionsMetadata[confirmationId]?.type,
);


let isLoadingShown =
const isShowingDeepLinkRoute = location.pathname === DEEP_LINK_ROUTE;
isLoading &&
completedOnboarding &&
// In the redesigned screens, we hide the general loading spinner and the
// loading states are on a component by component basis.
!isCorrectApprovalType &&
!isCorrectTransactionType &&
// We don't want to show the loading screen on the deep link route, as it
// is already a fullscreen interface.
!isShowingDeepLinkRoute;


const isLoadingShown =
isLoading &&
completedOnboarding &&
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
isLoadingShown =
!pendingConfirmations.some(
isLoading &&
(confirmation: Confirmation) =>
completedOnboarding &&
confirmation.type ===
!pendingConfirmations.some(
SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES.showSnapAccountRedirect,
(confirmation) =>
) &&
confirmation.type ===
SNAP_MANAGE_ACCOUNTS_CONFIRMATION_TYPES.showSnapAccountRedirect,
) &&
// In the redesigned screens, we hide the general loading spinner and the
// loading states are on a component by component basis.
!isCorrectApprovalType &&
!isCorrectTransactionType &&
// We don't want to show the loading spinner on the deep link route, as it
// is already a fullscreen interface.
!isShowingDeepLinkRoute;
///: END:ONLY_INCLUDE_IF
///: END:ONLY_INCLUDE_IF

// In the redesigned screens, we hide the general loading spinner and the
const accountListMenu = this.props.isMultichainAccountsState1Enabled ? (
// loading states are on a component by component basis.
<MultichainAccountListMenu
!isCorrectApprovalType &&
onClose={toggleAccountMenu}
!isCorrectTransactionType &&
privacyMode={privacyMode}
// We don't want to show the loading screen on the deep link route, as it
/>
// is already a fullscreen interface.
) : (
!isShowingDeepLinkRoute;
<AccountListMenu onClose={toggleAccountMenu} privacyMode={privacyMode} />
);


return (
const accountListMenu = isMultichainAccountsState1Enabled ? (
<div
<MultichainAccountListMenu
className={classnames('app', {
onClose={() => dispatch(toggleAccountMenu())}
[`os-${os}`]: os,
privacyMode={privacyMode}
[`browser-${browser}`]: browser,
/>
})}
) : (
dir={textDirection}
<AccountListMenu
>
onClose={() => dispatch(toggleAccountMenu())}
{shouldShowNetworkDeprecationWarning ? <DeprecatedNetworks /> : null}
privacyMode={privacyMode}
<QRHardwarePopover />
/>
<Modal />
);
<Alert visible={this.props.alertOpen} msg={alertMessage} />
{showAppHeader(this.props) && <AppHeader location={location} />}
{isConfirmTransactionRoute(this.pathname) && <MultichainMetaFoxLogo />}
{isAccountMenuOpen ? accountListMenu : null}
{isNetworkMenuOpen ? (
<NetworkListMenu onClose={networkMenuClose} />
) : null}
<NetworkConfirmationPopover />
{this.renderAccountDetails()}
{isImportNftsModalOpen ? (
<ImportNftsModal onClose={hideImportNftsModal} />
) : null}


{isIpfsModalOpen ? <ToggleIpfsModal onClose={hideIpfsModal} /> : null}
return (
{isBasicConfigurationModalOpen ? <BasicConfigurationModal /> : null}
<div
{isImportTokensModalOpen ? (
className={classnames('app', {
<ImportTokensModal onClose={hideImportTokensModal} />
[`os-${os}`]: Boolean(os),
) : null}
[`browser-${browser}`]: Boolean(browser),
{isDeprecatedNetworkModalOpen ? (
})}
<DeprecatedNetworkModal onClose={hideDeprecatedNetworkModal} />
dir={textDirection}
) : null}
>
{
{shouldShowNetworkDeprecationWarning ? <DeprecatedNetworks /> : null}
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
<QRHardwarePopover />
isShowKeyringSnapRemovalResultModal && (
<Modal />
<KeyringSnapRemovalResult
<Alert visible={alertOpen} msg={alertMessage} />
isOpen={isShowKeyringSnapRemovalResultModal}
{process.env.REMOVE_GNS
onClose={hideShowKeyringSnapRemovalResultModal}
? showAppHeader({ location }) && <AppHeader location={location} />
/>
: !hideAppHeader({ location }) && <AppHeader location={location} />}
)
{isConfirmTransactionRoute(location.pathname) && (
///: END:ONLY_INCLUDE_IF
<MultichainMetaFoxLogo />
}
)}
<Box className="main-container-wrapper">
{isAccountMenuOpen ? accountListMenu : null}
{isLoadingShown ? <Loading loadingMessage={loadMessage} /> : null}
{isNetworkMenuOpen ? (
{!isLoading &&
<NetworkListMenu
isUnlocked &&
onClose={() => {
isNetworkLoading &&
dispatch(toggleNetworkMenu());
completedOnboarding &&
dispatch(setEditedNetwork());
!isShowingDeepLinkRoute ? (
}}
<LoadingNetwork />
/>
) : null}
) : null}
{this.renderRoutes()}
<NetworkConfirmationPopover />
</Box>
{renderAccountDetails()}
{isUnlocked ? <Alerts history={this.props.history} /> : null}
{isImportNftsModalOpen ? (
<ToastMaster />
<ImportNftsModal onClose={() => dispatch(hideImportNftsModal())} />
</div>
) : null}
);
}
}


{isIpfsModalOpen ? (
<ToggleIpfsModal onClose={() => dispatch(hideIpfsModal())} />
) : null}
{isBasicConfigurati