import React, { useContext, useState, useRef, useCallback } from 'react'; import classnames from 'classnames'; import PropTypes from 'prop-types'; import browser from 'webextension-polyfill'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory, matchPath } from 'react-router-dom'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { MetaMetricsEventCategory, MetaMetricsEventName, } from '../../../../shared/constants/metametrics'; import { BUILD_QUOTE_ROUTE, CONFIRM_TRANSACTION_ROUTE, CONNECTED_ACCOUNTS_ROUTE, DEFAULT_ROUTE, SWAPS_ROUTE, } from '../../../helpers/constants/routes'; import { AlignItems, BackgroundColor, BlockSize, Display, JustifyContent, } from '../../../helpers/constants/design-system'; import { ButtonIcon, ButtonIconSize, IconName, PickerNetwork, Box, } from '../../component-library'; ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) import { getCustodianIconForAddress } from '../../../selectors/institutional/selectors'; ///: END:ONLY_INCLUDE_IN import { getCurrentChainId, getCurrentNetwork, getOnboardedInThisUISession, getOriginOfCurrentTab, getSelectedIdentity, getShowProductTour, getTestNetworkBackgroundColor, ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) getSelectedAddress, ///: END:ONLY_INCLUDE_IN } from '../../../selectors'; import { GlobalMenu, ProductTour, AccountPicker } from '..'; import { hideProductTour, toggleAccountMenu, toggleNetworkMenu, } from '../../../store/actions'; import MetafoxLogo from '../../ui/metafox-logo'; import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import ConnectedStatusIndicator from '../../app/connected-status-indicator'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getCompletedOnboarding } from '../../../ducks/metamask/metamask'; import { getSendStage, SEND_STAGES } from '../../../ducks/send'; import Tooltip from '../../ui/tooltip'; export const AppHeader = ({ location }) => { const trackEvent = useContext(MetaMetricsContext); const [accountOptionsMenuOpen, setAccountOptionsMenuOpen] = useState(false); const [multichainProductTourStep, setMultichainProductTourStep] = useState(1); const menuRef = useRef(false); const origin = useSelector(getOriginOfCurrentTab); const history = useHistory(); const isHomePage = location.pathname === DEFAULT_ROUTE; const isUnlocked = useSelector((state) => state.metamask.isUnlocked); const t = useI18nContext(); const chainId = useSelector(getCurrentChainId); ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) const selectedAddress = useSelector(getSelectedAddress); const custodianIcon = useSelector((state) => getCustodianIconForAddress(state, selectedAddress), ); ///: END:ONLY_INCLUDE_IN // Used for account picker const identity = useSelector(getSelectedIdentity); const dispatch = useDispatch(); const completedOnboarding = useSelector(getCompletedOnboarding); const onboardedInThisUISession = useSelector(getOnboardedInThisUISession); const showProductTourPopup = useSelector(getShowProductTour); // Used for network icon / dropdown const currentNetwork = useSelector(getCurrentNetwork); const testNetworkBackgroundColor = useSelector(getTestNetworkBackgroundColor); const popupStatus = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP; const showStatus = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP && origin && origin !== browser.runtime.id; const showProductTour = completedOnboarding && !onboardedInThisUISession && showProductTourPopup; const productTourDirection = document .querySelector('[dir]') ?.getAttribute('dir'); // Disable the network and account pickers if the user is in // a critical flow const sendStage = useSelector(getSendStage); const isTransactionEditPage = [ SEND_STAGES.EDIT, SEND_STAGES.DRAFT, SEND_STAGES.ADD_RECIPIENT, ].includes(sendStage); const isConfirmationPage = Boolean( matchPath(location.pathname, { path: CONFIRM_TRANSACTION_ROUTE, exact: false, }), ); const isSwapsPage = Boolean( matchPath(location.pathname, { path: SWAPS_ROUTE, exact: false }), ); const isSwapsBuildQuotePage = Boolean( matchPath(location.pathname, { path: BUILD_QUOTE_ROUTE, exact: false }), ); const hasUnapprovedTransactions = useSelector( (state) => Object.keys(state.metamask.unapprovedTxs).length > 0, ); const disableAccountPicker = isConfirmationPage || (isSwapsPage && !isSwapsBuildQuotePage); const disableNetworkPicker = isSwapsPage || isTransactionEditPage || isConfirmationPage || hasUnapprovedTransactions; // Callback for network dropdown const networkOpenCallback = useCallback(() => { dispatch(toggleNetworkMenu()); trackEvent({ event: MetaMetricsEventName.NavNetworkMenuOpened, category: MetaMetricsEventCategory.Navigation, properties: { location: 'App header', chain_id: chainId, }, }); }, [chainId, dispatch, trackEvent]); // This is required to ensure send and confirmation screens // look as desired const headerBottomMargin = !popupStatus && disableNetworkPicker ? 4 : 0; return ( <> {isUnlocked && !popupStatus ? ( history.push(DEFAULT_ROUTE)} ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) custodyImgSrc={custodianIcon} isUnlocked={isUnlocked} ///: END:ONLY_INCLUDE_IN /> ) : null} <> {isUnlocked ? ( {popupStatus ? ( { e.stopPropagation(); e.preventDefault(); networkOpenCallback(); }} display={[Display.Flex, Display.None]} // show on popover hide on desktop disabled={disableNetworkPicker} /> ) : (
{ e.stopPropagation(); e.preventDefault(); networkOpenCallback(); }} display={[Display.None, Display.Flex]} // show on desktop hide on popover className="multichain-app-header__contents__network-picker" disabled={disableNetworkPicker} data-testid="network-display" />
)} {showProductTour && popupStatus && isHomePage && multichainProductTourStep === 1 ? ( setMultichainProductTourStep(multichainProductTourStep + 1) } positionObj={productTourDirection === 'rtl' ? '0%' : '88%'} productTourDirection={productTourDirection} /> ) : null} {identity ? ( { dispatch(toggleAccountMenu()); trackEvent({ event: MetaMetricsEventName.NavAccountMenuOpened, category: MetaMetricsEventCategory.Navigation, properties: { location: 'Home', }, }); }} disabled={disableAccountPicker} /> ) : null} {showStatus ? ( { history.push(CONNECTED_ACCOUNTS_ROUTE); trackEvent({ event: MetaMetricsEventName.NavConnectedSitesOpened, category: MetaMetricsEventCategory.Navigation, }); }} /> ) : null}{' '} { ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) custodianIcon && ( ) ///: END:ONLY_INCLUDE_IN } {popupStatus && multichainProductTourStep === 2 ? ( setAccountOptionsMenuOpen(false)} prevIcon title={t('permissionsTitle')} description={t('permissionsTourDescription')} currentStep="2" totalSteps="3" prevClick={() => setMultichainProductTourStep( multichainProductTourStep - 1, ) } onClick={() => setMultichainProductTourStep( multichainProductTourStep + 1, ) } positionObj={ productTourDirection === 'rtl' ? '76%' : '12%' } productTourDirection={productTourDirection} /> ) : null} { trackEvent({ event: MetaMetricsEventName.NavAccountMenuOpened, category: MetaMetricsEventCategory.Navigation, properties: { location: 'Home', }, }); setAccountOptionsMenuOpen(true); }} size={ButtonIconSize.Sm} /> {accountOptionsMenuOpen ? ( setAccountOptionsMenuOpen(false)} /> ) : null} {showProductTour && popupStatus && multichainProductTourStep === 3 ? ( setAccountOptionsMenuOpen(false)} prevIcon title={t('globalTitle')} description={t('globalTourDescription')} currentStep="3" totalSteps="3" prevClick={() => setMultichainProductTourStep( multichainProductTourStep - 1, ) } onClick={() => { hideProductTour(); }} positionObj={productTourDirection === 'rtl' ? '88%' : '0%'} productTourDirection={productTourDirection} /> ) : null}
) : (
{ e.stopPropagation(); e.preventDefault(); networkOpenCallback(); }} className="multichain-app-header__contents__network-picker" data-testid="network-display" />
{ history.push(DEFAULT_ROUTE); }} />
)}
); }; AppHeader.propTypes = { /** * The location object for the application */ location: PropTypes.object, };