diff --git a/app/scripts/background.js b/app/scripts/background.js index 622e888f9..048d494f8 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -208,6 +208,7 @@ browser.runtime.onConnectExternal.addListener(async (...args) => { * @property {boolean} isInitialized - Whether the first vault has been created. * @property {boolean} isUnlocked - Whether the vault is currently decrypted and accounts are available for selection. * @property {boolean} isAccountMenuOpen - Represents whether the main account selection UI is currently displayed. + * @property {boolean} isNetworkMenuOpen - Represents whether the main network selection UI is currently displayed. * @property {object} identities - An object matching lower-case hex addresses to Identity objects with "address" and "name" (nickname) keys. * @property {object} unapprovedTxs - An object mapping transaction hashes to unapproved transactions. * @property {object} networkConfigurations - A list of network configurations, containing RPC provider details (eg chainId, rpcUrl, rpcPreferences). diff --git a/ui/components/app/menu-bar/menu-bar.js b/ui/components/app/menu-bar/menu-bar.js index fac87c26f..08da8208f 100644 --- a/ui/components/app/menu-bar/menu-bar.js +++ b/ui/components/app/menu-bar/menu-bar.js @@ -16,7 +16,6 @@ import { getOriginOfCurrentTab } from '../../../selectors'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { ButtonIcon } from '../../component-library/button-icon/deprecated'; import { ICON_NAMES } from '../../component-library/icon/deprecated'; -import { GlobalMenu } from '../../multichain/global-menu'; import AccountOptionsMenu from './account-options-menu'; export default function MenuBar() { @@ -34,7 +33,7 @@ export default function MenuBar() { return (
- {showStatus ? ( // TODO: Move the connection status menu icon to the correct position in header once we implement the new header + {showStatus ? ( history.push(CONNECTED_ACCOUNTS_ROUTE)} /> @@ -58,18 +57,12 @@ export default function MenuBar() { }} /> - {accountOptionsMenuOpen && - (process.env.MULTICHAIN ? ( - setAccountOptionsMenuOpen(false)} - /> - ) : ( - setAccountOptionsMenuOpen(false)} - /> - ))} + {accountOptionsMenuOpen && ( + setAccountOptionsMenuOpen(false)} + /> + )}
); } diff --git a/ui/components/multichain/account-list-menu/account-list-menu.js b/ui/components/multichain/account-list-menu/account-list-menu.js index fcb7d1046..7ffbcf5fa 100644 --- a/ui/components/multichain/account-list-menu/account-list-menu.js +++ b/ui/components/multichain/account-list-menu/account-list-menu.js @@ -103,7 +103,6 @@ export const AccountListMenu = ({ onClose }) => { }, }); dispatch(setSelectedAccount(account.address)); - onClose(); }} identity={account} key={account.address} diff --git a/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap b/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap new file mode 100644 index 000000000..d4d1cc8da --- /dev/null +++ b/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap @@ -0,0 +1,224 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`App Header should match snapshot 1`] = ` +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+`; diff --git a/ui/components/multichain/app-header/app-header.js b/ui/components/multichain/app-header/app-header.js new file mode 100644 index 000000000..d7f278530 --- /dev/null +++ b/ui/components/multichain/app-header/app-header.js @@ -0,0 +1,215 @@ +import React, { useContext, useState, useRef } from 'react'; +import PropTypes from 'prop-types'; +import browser from 'webextension-polyfill'; +import { useDispatch, useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import { MetaMetricsContext } from '../../../contexts/metametrics'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, +} from '../../../../shared/constants/metametrics'; +import { + CONNECTED_ACCOUNTS_ROUTE, + DEFAULT_ROUTE, +} from '../../../helpers/constants/routes'; + +import { + AlignItems, + BackgroundColor, + BLOCK_SIZES, + DISPLAY, + JustifyContent, + Size, +} from '../../../helpers/constants/design-system'; +import { AvatarNetwork, Button, PickerNetwork } from '../../component-library'; +import { ButtonIcon } from '../../component-library/button-icon/deprecated'; +import { ICON_NAMES } from '../../component-library/icon/deprecated'; +import { + getCurrentNetwork, + getOriginOfCurrentTab, + getSelectedIdentity, +} from '../../../selectors'; +import { GlobalMenu, AccountPicker } from '..'; + +import Box from '../../ui/box/box'; +import { 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'; + +export const AppHeader = ({ onClick }) => { + const trackEvent = useContext(MetaMetricsContext); + const [accountOptionsMenuOpen, setAccountOptionsMenuOpen] = useState(false); + const menuRef = useRef(false); + const origin = useSelector(getOriginOfCurrentTab); + const history = useHistory(); + const isUnlocked = useSelector((state) => state.metamask.isUnlocked); + + // Used for account picker + const identity = useSelector(getSelectedIdentity); + const dispatch = useDispatch(); + + // 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; + + return ( + <> + {isUnlocked && !popupStatus ? ( + + { + if (onClick) { + await onClick(); + } + history.push(DEFAULT_ROUTE); + }} + /> + + ) : null} + + <> + {isUnlocked ? ( + + {popupStatus ? ( + + ) : ( + dispatch(toggleNetworkMenu())} + /> + )} + + dispatch(toggleAccountMenu())} + /> + + {showStatus ? ( + history.push(CONNECTED_ACCOUNTS_ROUTE)} + /> + ) : null} + + { + trackEvent({ + event: MetaMetricsEventName.NavAccountMenuOpened, + category: MetaMetricsEventCategory.Navigation, + properties: { + location: 'Home', + }, + }); + setAccountOptionsMenuOpen(true); + }} + /> + + + {accountOptionsMenuOpen ? ( + setAccountOptionsMenuOpen(false)} + /> + ) : null} + + ) : ( + + dispatch(toggleNetworkMenu())} + /> + { + if (onClick) { + await onClick(); + } + history.push(DEFAULT_ROUTE); + }} + /> + + )} + + + + ); +}; + +AppHeader.propTypes = { + /** + * The onClick handler to be passed to the MetaMask Logo in the App Header + */ + onClick: PropTypes.func, +}; diff --git a/ui/components/multichain/app-header/app-header.scss b/ui/components/multichain/app-header/app-header.scss new file mode 100644 index 000000000..7ab95825a --- /dev/null +++ b/ui/components/multichain/app-header/app-header.scss @@ -0,0 +1,74 @@ +.multichain-app-header { + $height-screen-sm-max: 100%; + $width-screen-sm-min: 85vw; + $width-screen-md-min: 80vw; + $width-screen-lg-min: 62vw; + + flex-flow: column nowrap; + z-index: 55; + min-height: 64px; + + &__contents { + display: grid; + grid-template-columns: 1fr 2fr 1fr; + height: 64px; + + @include screen-sm-max { + height: $height-screen-sm-max; + } + + @include screen-sm-min { + width: $width-screen-sm-min; + } + + @include screen-md-min { + width: $width-screen-md-min; + } + + @include screen-lg-min { + width: $width-screen-lg-min; + } + + &--avatar-network { + background-color: transparent; + width: min-content; + padding: 8px; + + &:hover, + &:active { + box-shadow: none; + background: transparent; + } + } + } + + &__lock-contents { + flex-flow: row nowrap; + height: 64px; + + @include screen-sm-max { + height: $height-screen-sm-max; + } + + @include screen-sm-min { + width: $width-screen-sm-min; + } + + @include screen-md-min { + width: $width-screen-md-min; + } + + @include screen-lg-min { + width: $width-screen-lg-min; + } + } +} + +.multichain-app-header-shadow { + box-shadow: var(--shadow-size-md) var(--color-shadow-default); +} + +.multichain-app-header-logo { + height: 75px; + flex: 0 0 auto; +} diff --git a/ui/components/multichain/app-header/app-header.stories.js b/ui/components/multichain/app-header/app-header.stories.js new file mode 100644 index 000000000..c8ae6a810 --- /dev/null +++ b/ui/components/multichain/app-header/app-header.stories.js @@ -0,0 +1,71 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from '../../../store/store'; +import testData from '../../../../.storybook/test-data'; +import { AppHeader } from '.'; + +const store = configureStore(testData); + +export default { + title: 'Components/Multichain/AppHeader', + decorators: [(story) => {story()}], + component: AppHeader, + argTypes: { + onClick: { + action: 'onClick', + }, + }, +}; +const customNetworkUnlockedData = { + ...testData, + metamask: { + ...testData.metamask, + preferences: { + showTestNetworks: true, + }, + isUnlocked: true, + networkConfigurations: { + ...testData.metamask.networkConfigurations, + }, + }, +}; +const customNetworkUnlockedStore = configureStore(customNetworkUnlockedData); + +const customNetworkLockedData = { + ...testData, + metamask: { + ...testData.metamask, + preferences: { + showTestNetworks: true, + }, + isUnlocked: false, + networkConfigurations: { + ...testData.metamask.networkConfigurations, + }, + }, +}; +const customNetworkLockedStore = configureStore(customNetworkLockedData); + +const Template = (args) => { + return ; +}; + +export const FullScreenAndUnlockedStory = Template.bind({}); + +FullScreenAndUnlockedStory.decorators = [ + (Story) => ( + + + + ), +]; + +export const FullScreenAndLockedStory = Template.bind({}); + +FullScreenAndLockedStory.decorators = [ + (Story) => ( + + + + ), +]; diff --git a/ui/components/multichain/app-header/app-header.test.js b/ui/components/multichain/app-header/app-header.test.js new file mode 100644 index 000000000..c69caf12f --- /dev/null +++ b/ui/components/multichain/app-header/app-header.test.js @@ -0,0 +1,107 @@ +import React from 'react'; +import configureStore from 'redux-mock-store'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { renderWithProvider } from '../../../../test/lib/render-helpers'; +import { AppHeader } from '.'; + +describe('App Header', () => { + it('should match snapshot', () => { + const mockState = { + activeTab: { + title: 'Eth Sign Tests', + origin: 'https://remix.ethereum.org', + protocol: 'https:', + url: 'https://remix.ethereum.org/', + }, + metamask: { + provider: { + chainId: CHAIN_IDS.GOERLI, + }, + accounts: { + '0x7250739de134d33ec7ab1ee592711e15098c9d2d': { + address: '0x7250739de134d33ec7ab1ee592711e15098c9d2d', + }, + '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5': { + address: '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + }, + }, + preferences: { + showTestNetworks: true, + }, + cachedBalances: {}, + subjects: { + 'https://remix.ethereum.org': { + permissions: { + eth_accounts: { + caveats: [ + { + type: 'restrictReturnedAccounts', + value: [ + '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + '0x7250739de134d33ec7ab1ee592711e15098c9d2d', + ], + }, + ], + date: 1586359844177, + id: '3aa65a8b-3bcb-4944-941b-1baa5fe0ed8b', + invoker: 'https://remix.ethereum.org', + parentCapability: 'eth_accounts', + }, + }, + }, + 'peepeth.com': { + permissions: { + eth_accounts: { + caveats: [ + { + type: 'restrictReturnedAccounts', + value: ['0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5'], + }, + ], + date: 1585676177970, + id: '840d72a0-925f-449f-830a-1aa1dd5ce151', + invoker: 'peepeth.com', + parentCapability: 'eth_accounts', + }, + }, + }, + }, + identities: { + '0x7250739de134d33ec7ab1ee592711e15098c9d2d': { + address: '0x7250739de134d33ec7ab1ee592711e15098c9d2d', + name: 'Really Long Name That Should Be Truncated', + }, + '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5': { + address: '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + lastSelected: 1586359844192, + name: 'Account 1', + }, + }, + keyrings: [ + { + accounts: [ + '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + '0x7250739de134d33ec7ab1ee592711e15098c9d2d', + ], + }, + ], + permissionHistory: { + 'https://remix.ethereum.org': { + eth_accounts: { + accounts: { + '0x7250739de134d33ec7ab1ee592711e15098c9d2d': 1586359844192, + '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5': 1586359844192, + }, + lastApproved: 1586359844192, + }, + }, + }, + }, + }; + + const mockStore = configureStore(); + const store = mockStore(mockState); + const { container } = renderWithProvider(, store); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/ui/components/multichain/app-header/index.js b/ui/components/multichain/app-header/index.js new file mode 100644 index 000000000..1126d287c --- /dev/null +++ b/ui/components/multichain/app-header/index.js @@ -0,0 +1 @@ +export { AppHeader } from './app-header'; diff --git a/ui/components/multichain/index.js b/ui/components/multichain/index.js index a4703b158..c9cf2efe1 100644 --- a/ui/components/multichain/index.js +++ b/ui/components/multichain/index.js @@ -2,6 +2,7 @@ export { AccountListItem } from './account-list-item'; export { AccountListItemMenu } from './account-list-item-menu'; export { AccountListMenu } from './account-list-menu'; export { AccountPicker } from './account-picker'; +export { AppHeader } from './app-header'; export { DetectedTokensBanner } from './detected-token-banner'; export { GlobalMenu } from './global-menu'; export { MultichainImportTokenLink } from './multichain-import-token-link'; diff --git a/ui/components/multichain/multichain-components.scss b/ui/components/multichain/multichain-components.scss index 63b19d690..75b42ef2a 100644 --- a/ui/components/multichain/multichain-components.scss +++ b/ui/components/multichain/multichain-components.scss @@ -8,6 +8,7 @@ @import 'account-list-item/index'; @import 'account-list-menu/index'; @import 'account-picker/index'; +@import 'app-header/app-header'; @import 'multichain-connected-site-menu/index'; @import 'account-list-menu/'; @import 'multichain-token-list-item/multichain-token-list-item'; diff --git a/ui/components/multichain/network-list-menu/network-list-menu.js b/ui/components/multichain/network-list-menu/network-list-menu.js index 0cf1818ca..64a84f9da 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -10,6 +10,7 @@ import { showModal, setShowTestNetworks, setProviderType, + toggleNetworkMenu, } from '../../../store/actions'; import { CHAIN_IDS, TEST_CHAINS } from '../../../../shared/constants/network'; import { @@ -30,7 +31,7 @@ import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app'; const UNREMOVABLE_CHAIN_IDS = [CHAIN_IDS.MAINNET, ...TEST_CHAINS]; -export const NetworkListMenu = ({ closeMenu }) => { +export const NetworkListMenu = ({ onClose }) => { const t = useI18nContext(); const networks = useSelector(getAllNetworks); const showTestNetworks = useSelector(getShowTestNetworks); @@ -42,7 +43,7 @@ export const NetworkListMenu = ({ closeMenu }) => { const isFullScreen = environmentType === ENVIRONMENT_TYPE_FULLSCREEN; return ( - + <> {networks.map((network) => { @@ -58,16 +59,17 @@ export const NetworkListMenu = ({ closeMenu }) => { key={network.id || network.chainId} selected={isCurrentNetwork} onClick={() => { + dispatch(toggleNetworkMenu()); if (network.providerType) { dispatch(setProviderType(network.providerType)); } else { dispatch(setActiveNetwork(network.id)); } - closeMenu(); }} onDeleteClick={ canDeleteNetwork ? () => { + dispatch(toggleNetworkMenu()); dispatch( showModal({ name: 'CONFIRM_DELETE_NETWORK', @@ -75,7 +77,6 @@ export const NetworkListMenu = ({ closeMenu }) => { onConfirm: () => undefined, }), ); - closeMenu(); } : null } @@ -104,6 +105,7 @@ export const NetworkListMenu = ({ closeMenu }) => { : global.platform.openExtensionInBrowser( ADD_POPULAR_CUSTOM_NETWORK, ); + dispatch(toggleNetworkMenu()); }} > {t('addNetwork')} @@ -118,5 +120,5 @@ NetworkListMenu.propTypes = { /** * Executes when the menu should be closed */ - closeMenu: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, }; diff --git a/ui/components/multichain/network-list-menu/network-list-menu.stories.js b/ui/components/multichain/network-list-menu/network-list-menu.stories.js index 0629cd8e6..f2c5fabfb 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.stories.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.stories.js @@ -42,8 +42,8 @@ export default { title: 'Components/Multichain/NetworkListMenu', component: NetworkListMenu, argTypes: { - closeMenu: { - action: 'closeMenu', + onClose: { + action: 'onClose', }, }, }; diff --git a/ui/components/multichain/network-list-menu/network-list-menu.test.js b/ui/components/multichain/network-list-menu/network-list-menu.test.js index e87876f39..cd6dae498 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.test.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.test.js @@ -11,9 +11,11 @@ import { NetworkListMenu } from '.'; const mockSetShowTestNetworks = jest.fn(); const mockSetProviderType = jest.fn(); +const mockToggleNetworkMenu = jest.fn(); jest.mock('../../../store/actions.ts', () => ({ setShowTestNetworks: () => mockSetShowTestNetworks, setProviderType: () => mockSetProviderType, + toggleNetworkMenu: () => mockToggleNetworkMenu, })); const render = (showTestNetworks = false) => { @@ -25,7 +27,7 @@ const render = (showTestNetworks = false) => { }, }, }); - return renderWithProvider(, store); + return renderWithProvider(, store); }; describe('NetworkListMenu', () => { @@ -56,6 +58,7 @@ describe('NetworkListMenu', () => { it('switches networks when an item is clicked', () => { const { getByText } = render(); fireEvent.click(getByText(MAINNET_DISPLAY_NAME)); + expect(mockToggleNetworkMenu).toHaveBeenCalled(); expect(mockSetProviderType).toHaveBeenCalled(); }); }); diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index cd3adaca8..7ffa78625 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -26,6 +26,7 @@ const initialState = { isInitialized: false, isUnlocked: false, isAccountMenuOpen: false, + isNetworkMenuOpen: false, identities: {}, unapprovedTxs: {}, networkConfigurations: {}, @@ -102,6 +103,12 @@ export default function reduceMetamask(state = initialState, action) { isAccountMenuOpen: !metamaskState.isAccountMenuOpen, }; + case actionConstants.TOGGLE_NETWORK_MENU: + return { + ...metamaskState, + isNetworkMenuOpen: !metamaskState.isNetworkMenuOpen, + }; + case actionConstants.UPDATE_TRANSACTION_PARAMS: { const { id: txId, value } = action; let { currentNetworkTxList } = metamaskState; diff --git a/ui/ducks/metamask/metamask.test.js b/ui/ducks/metamask/metamask.test.js index 2ca94f71b..92241d589 100644 --- a/ui/ducks/metamask/metamask.test.js +++ b/ui/ducks/metamask/metamask.test.js @@ -157,6 +157,17 @@ describe('MetaMask Reducers', () => { expect(state.isAccountMenuOpen).toStrictEqual(true); }); + it('toggles network menu', () => { + const state = reduceMetamask( + {}, + { + type: actionConstants.TOGGLE_NETWORK_MENU, + }, + ); + + expect(state.isNetworkMenuOpen).toStrictEqual(true); + }); + it('updates value of tx by id', () => { const oldState = { currentNetworkTxList: [ diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index c525bd161..8f874e7db 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -641,7 +641,7 @@ export default class Home extends PureComponent { ? this.renderPopover() : null}
- + {process.env.MULTICHAIN ? null : }
diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index c5804d146..fae501142 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -31,6 +31,11 @@ import AccountMenu from '../../components/app/account-menu'; import { Modal } from '../../components/app/modals'; import Alert from '../../components/ui/alert'; import AppHeader from '../../components/app/app-header'; +import { + AppHeader as MultichainAppHeader, + AccountListMenu, + NetworkListMenu, +} from '../../components/multichain'; import UnlockPage from '../unlock-page'; import Alerts from '../../components/app/alerts'; import Asset from '../asset'; @@ -90,7 +95,6 @@ import { SEND_STAGES } from '../../ducks/send'; import DeprecatedTestNetworks from '../../components/ui/deprecated-test-networks/deprecated-test-networks'; import NewNetworkInfo from '../../components/ui/new-network-info/new-network-info'; import { ThemeType } from '../../../shared/constants/preferences'; -import { AccountListMenu } from '../../components/multichain'; export default class Routes extends Component { static propTypes = { @@ -128,6 +132,8 @@ export default class Routes extends Component { completedOnboarding: PropTypes.bool, isAccountMenuOpen: PropTypes.bool, toggleAccountMenu: PropTypes.func, + isNetworkMenuOpen: PropTypes.bool, + toggleNetworkMenu: PropTypes.func, }; static contextTypes = { @@ -436,6 +442,8 @@ export default class Routes extends Component { completedOnboarding, isAccountMenuOpen, toggleAccountMenu, + isNetworkMenuOpen, + toggleNetworkMenu, } = this.props; const loadMessage = loadingMessage || isNetworkLoading @@ -478,24 +486,30 @@ export default class Routes extends Component { - {!this.hideAppHeader() && ( - - )} + {!this.hideAppHeader() && + (process.env.MULTICHAIN ? ( + + ) : ( + + ))} {this.showOnboardingHeader() && } {completedOnboarding ? : null} {process.env.MULTICHAIN ? null : } {process.env.MULTICHAIN && isAccountMenuOpen ? ( toggleAccountMenu()} /> ) : null} + {process.env.MULTICHAIN && isNetworkMenuOpen ? ( + toggleNetworkMenu()} /> + ) : null}
{isLoading ? : null} {!isLoading && isNetworkLoading ? : null} diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index c132bcfa8..3f5c857ba 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -19,6 +19,7 @@ import { setLastActiveTime, setMouseUserState, toggleAccountMenu, + toggleNetworkMenu, } from '../../store/actions'; import { pageChanged } from '../../ducks/history/history'; import { prepareToLeaveSwaps } from '../../ducks/swaps/swaps'; @@ -57,6 +58,7 @@ function mapStateToProps(state) { isCurrentProviderCustom: isCurrentProviderCustom(state), completedOnboarding, isAccountMenuOpen: state.metamask.isAccountMenuOpen, + isNetworkMenuOpen: state.metamask.isNetworkMenuOpen, }; } @@ -70,6 +72,7 @@ function mapDispatchToProps(dispatch) { pageChanged: (path) => dispatch(pageChanged(path)), prepareToLeaveSwaps: () => dispatch(prepareToLeaveSwaps()), toggleAccountMenu: () => dispatch(toggleAccountMenu()), + toggleNetworkMenu: () => dispatch(toggleNetworkMenu()), }; } diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 1b48a1e84..44d2b914d 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -1111,6 +1111,13 @@ export function getNetworkConfigurations(state) { return state.metamask.networkConfigurations; } +export function getCurrentNetwork(state) { + const allNetworks = getAllNetworks(state); + const currentChainId = getCurrentChainId(state); + + return allNetworks.find((network) => network.chainId === currentChainId); +} + export function getAllNetworks(state) { const networkConfigurations = getNetworkConfigurations(state) || {}; const showTestnetNetworks = getShowTestNetworks(state); diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts index b58276ba9..34e1e9dfc 100644 --- a/ui/store/actionConstants.ts +++ b/ui/store/actionConstants.ts @@ -48,6 +48,7 @@ export const SHOW_LOADING = 'SHOW_LOADING_INDICATION'; export const HIDE_LOADING = 'HIDE_LOADING_INDICATION'; export const TOGGLE_ACCOUNT_MENU = 'TOGGLE_ACCOUNT_MENU'; +export const TOGGLE_NETWORK_MENU = 'TOGGLE_NETWORK_MENU'; // preferences export const UPDATE_CUSTOM_NONCE = 'UPDATE_CUSTOM_NONCE'; diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 97be01ad5..fe47604c9 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -3122,6 +3122,12 @@ export function toggleAccountMenu() { }; } +export function toggleNetworkMenu() { + return { + type: actionConstants.TOGGLE_NETWORK_MENU, + }; +} + export function setParticipateInMetaMetrics( participationPreference: boolean, ): ThunkAction<