1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-23 02:10:12 +01:00
metamask-extension/ui/components/multichain/app-header/app-header.js
David Walsh b89630fdd2
Release MultiChain 0.5 (#18903)
* Show portfolio icon in ETH overview

* Show new copy button in QR code modal

* Show address copy button in wallet overview

* Update connected status component

* Remove legacy MenuBar

* Remove legacy ImportTokenLink

* Remove AssetListItem

* Remove DetectedTokensLink

* Remove legacy AppHeader

* Remove MULTICHAIN flag from builds.yml

* Remove legacy AccountMenu

* FIX: Token cell snapshot

* Add data-testid for Account Picker

* Remove multichain check in LoadingNetworkScreen

* Remove MULTICHAIN check for AccountDetailsModal

* Remove MULTICHAIN check for AssetList

* Update QR dimensions

* Remove MULTICHAIN declaration from metamaskrc.dist

* Implement PickerNetwork and NetworkListMenu in onboarding

* Remove legacy NetworkDropdown and Dropdown

* Remove documentation about legacy account menu

* FIX: Fixes route tests for missing data-testid=network-display

* Fix account-menu-icon data-testid

* Fix TokenCell test

* FIX Onboarding Flow tests

* Remove unused locales from AccountMenu removal

* E2E: Fix Import Secret Recovery Phrase: logs out of the vault

* E2E: Fix Show account details: should show the QR code for the account

* E2E: Fix add-account.spec.js

* E2E: Fix state-logs.spec.js

* E2E: Fix lock-account.spec.js

* E2E: Fix settings-general.spec.js

* E2E: Fix advanced-settings.spec.js

* E2E: Fix auto-lock.spec.js

* E2E: Fix backup-restore.spec.js

* E2E: Fix clear-activity.spec.js

* E2E: Fix settings-search.spec.js

* E2E: Fix encrypt-decrypt.spec.js

* E2E: Fix dapp-interactions.spec.js

* E2E: Fix test-snap-management.spec.js

* E2E: Fix add-custom-network.spec.js

* E2E: Fix from-import-ui.spec.js

* E2E: Fix provider-api.spec.js

* E2E: Fix chain-interactions.spec.js

* E2E: Fix custom-rpc-history.spec.js

* Remove network icon from overview components

* E2E: Fix user-actions-benchmark.js

* E2E: Fix benchmark.js

* E2E: Fix add-hide-token.spec.js

* E2E: Fix address-book.spec.js

* E2E: Fix custom-token-add-approve.spec.js

* E2E: Fix incremental-security.spec.js

* E2E: Fix metamask-responsive-ui.spec.js

* E2E: Onboarding.spec.js

* E2E: Fix permissions.spec.js

* E2E: Fix send-hex-address.spec.js

* E2E: Fix send-to-contract.spec.js

* Remove dead AccountOptionsMenu test

* E2E: Fix token-details.spec.js

* E2E: Fix switch-custom-network.spec.js

* E2E: Fix metamask-ui.spec.js

* Revert "UX Multichain: updated border top for activity list (#19176)"

This reverts commit 15598f2a23.

* E2Es: Fix test-snap-management.spec.js and test-snap-notification.spec.js

* E2Es: Fix add-account.spec.js after flaky test fixes

* e2e flaky test

* adds back the mmi options

* scss fix

* test fix

* removes unnecessary double quotes

* Prevent double logos on login screen

* Update ui/components/ui/list-item/index.scss

Co-authored-by: Nidhi Kumari <nidhi.kumari@consensys.net>

---------

Co-authored-by: seaona <mariona@gmx.es>
Co-authored-by: Antonio Regadas <antonio.regadas@consensys.net>
Co-authored-by: Nidhi Kumari <nidhi.kumari@consensys.net>
2023-06-01 16:14:38 -05:00

437 lines
16 KiB
JavaScript

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,
BLOCK_SIZES,
DISPLAY,
JustifyContent,
Size,
} from '../../../helpers/constants/design-system';
import {
AvatarNetwork,
ButtonIcon,
ButtonIconSize,
IconName,
IconSize,
PickerNetwork,
} 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,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getSelectedAddress,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
import { GlobalMenu, ProductTour, AccountPicker } from '..';
import Box from '../../ui/box/box';
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 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);
// Used to get the environment and connection status
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 isConfirmationPage = Boolean(
matchPath(location.pathname, {
path: CONFIRM_TRANSACTION_ROUTE,
exact: false,
}),
);
const isTransactionEditPage = [
SEND_STAGES.EDIT,
SEND_STAGES.DRAFT,
SEND_STAGES.ADD_RECIPIENT,
].includes(sendStage);
const isSwapsPage = Boolean(
matchPath(location.pathname, { path: SWAPS_ROUTE, exact: false }),
);
const isSwapsBuildQuotePage = Boolean(
matchPath(location.pathname, { path: BUILD_QUOTE_ROUTE, exact: false }),
);
const disablePickers =
isConfirmationPage ||
isTransactionEditPage ||
(isSwapsPage && !isSwapsBuildQuotePage);
const disableNetworkPicker = isSwapsPage || disablePickers;
// 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]);
return (
<>
{isUnlocked && !popupStatus ? (
<Box
display={[DISPLAY.NONE, DISPLAY.FLEX]}
alignItems={AlignItems.center}
margin={2}
className="multichain-app-header-logo"
data-testid="app-header-logo"
justifyContent={JustifyContent.center}
>
<MetafoxLogo
unsetIconHeight
onClick={async () => history.push(DEFAULT_ROUTE)}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
custodyImgSrc={custodianIcon}
isUnlocked={isUnlocked}
///: END:ONLY_INCLUDE_IN
/>
</Box>
) : null}
<Box
display={DISPLAY.FLEX}
className={classnames('multichain-app-header', {
'multichain-app-header-shadow': !isUnlocked || popupStatus,
})}
alignItems={AlignItems.center}
width={BLOCK_SIZES.FULL}
backgroundColor={
!isUnlocked || popupStatus
? BackgroundColor.backgroundDefault
: BackgroundColor.backgroundAlternative
}
>
<>
{isUnlocked ? (
<Box
className={classnames('multichain-app-header__contents', {
'multichain-app-header-shadow': isUnlocked && !popupStatus,
})}
alignItems={AlignItems.center}
width={BLOCK_SIZES.FULL}
backgroundColor={BackgroundColor.backgroundDefault}
padding={2}
paddingLeft={4}
paddingRight={4}
gap={2}
>
{popupStatus ? (
<Box className="multichain-app-header__contents__container">
<Tooltip title={currentNetwork?.nickname} position="right">
<AvatarNetwork
className="multichain-app-header__contents--avatar-network"
ref={menuRef}
as="button"
aria-label={t('networkMenu')}
padding={0}
name={currentNetwork?.nickname}
src={currentNetwork?.rpcPrefs?.imageUrl}
size={Size.SM}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
networkOpenCallback();
}}
display={[DISPLAY.FLEX, DISPLAY.NONE]} // show on popover hide on desktop
disabled={disableNetworkPicker}
/>
</Tooltip>
</Box>
) : (
<div>
<PickerNetwork
margin={2}
label={currentNetwork?.nickname}
src={currentNetwork?.rpcPrefs?.imageUrl}
onClick={(e) => {
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"
/>
</div>
)}
{showProductTour &&
popupStatus &&
multichainProductTourStep === 1 ? (
<ProductTour
className="multichain-app-header__product-tour"
anchorElement={menuRef.current}
title={t('switcherTitle')}
description={t('switcherTourDescription')}
currentStep="1"
totalSteps="3"
onClick={() =>
setMultichainProductTourStep(multichainProductTourStep + 1)
}
positionObj={productTourDirection === 'rtl' ? '0%' : '88%'}
productTourDirection={productTourDirection}
/>
) : null}
{identity ? (
<AccountPicker
address={identity.address}
name={identity.name}
onClick={() => {
dispatch(toggleAccountMenu());
trackEvent({
event: MetaMetricsEventName.NavAccountMenuOpened,
category: MetaMetricsEventCategory.Navigation,
properties: {
location: 'Home',
},
});
}}
disabled={disablePickers}
/>
) : null}
<Box
display={DISPLAY.FLEX}
alignItems={AlignItems.center}
justifyContent={JustifyContent.flexEnd}
>
<Box display={DISPLAY.FLEX} gap={4}>
{showStatus ? (
<Box ref={menuRef}>
<ConnectedStatusIndicator
onClick={() => {
history.push(CONNECTED_ACCOUNTS_ROUTE);
trackEvent({
event: MetaMetricsEventName.NavConnectedSitesOpened,
category: MetaMetricsEventCategory.Navigation,
});
}}
/>
</Box>
) : null}{' '}
{
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
custodianIcon && (
<Box
display={DISPLAY.FLEX}
alignItems={AlignItems.center}
className="custody-logo"
data-testid="custody-logo"
>
<img
src={custodianIcon}
className="custody-logo--icon"
alt=""
/>
</Box>
)
///: END:ONLY_INCLUDE_IN
}
{popupStatus && multichainProductTourStep === 2 ? (
<ProductTour
className="multichain-app-header__product-tour"
anchorElement={menuRef.current}
closeMenu={() => 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}
<Box
ref={menuRef}
display={DISPLAY.FLEX}
justifyContent={JustifyContent.flexEnd}
width={BLOCK_SIZES.FULL}
>
<ButtonIcon
iconName={IconName.MoreVertical}
data-testid="account-options-menu-button"
ariaLabel={t('accountOptions')}
onClick={() => {
trackEvent({
event: MetaMetricsEventName.NavAccountMenuOpened,
category: MetaMetricsEventCategory.Navigation,
properties: {
location: 'Home',
},
});
setAccountOptionsMenuOpen(true);
}}
size={ButtonIconSize.Sm}
iconProps={{ size: IconSize.Sm }}
/>
</Box>
</Box>
{accountOptionsMenuOpen ? (
<GlobalMenu
anchorElement={menuRef.current}
closeMenu={() => setAccountOptionsMenuOpen(false)}
/>
) : null}
{showProductTour &&
popupStatus &&
multichainProductTourStep === 3 ? (
<ProductTour
className="multichain-app-header__product-tour"
anchorElement={menuRef.current}
closeMenu={() => 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}
</Box>
</Box>
) : (
<Box
display={DISPLAY.FLEX}
className={classnames('multichain-app-header__lock-contents', {
'multichain-app-header-shadow': isUnlocked && !popupStatus,
})}
alignItems={AlignItems.center}
width={BLOCK_SIZES.FULL}
justifyContent={JustifyContent.spaceBetween}
backgroundColor={BackgroundColor.backgroundDefault}
padding={2}
gap={2}
>
<div>
<PickerNetwork
label={currentNetwork?.nickname}
src={currentNetwork?.rpcPrefs?.imageUrl}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
networkOpenCallback();
}}
className="multichain-app-header__contents__network-picker"
data-testid="network-display"
/>
</div>
<MetafoxLogo
unsetIconHeight
onClick={async () => {
history.push(DEFAULT_ROUTE);
}}
/>
</Box>
)}
</>
</Box>
</>
);
};
AppHeader.propTypes = {
/**
* The location object for the application
*/
location: PropTypes.object,
};