diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 2363bc51dc2d..8c9b810bdfa1 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -289,6 +289,9 @@
"addNfts": {
"message": "Add NFTs"
},
+ "addRpcUrl": {
+ "message": "Add RPC URL"
+ },
"addSnapAccountToggle": {
"message": "Enable \"Add account Snap (Beta)\""
},
@@ -1354,6 +1357,9 @@
"decryptRequest": {
"message": "Decrypt request"
},
+ "defaultRpcUrl": {
+ "message": "Default RPC URL"
+ },
"delete": {
"message": "Delete"
},
@@ -1629,6 +1635,9 @@
"enabled": {
"message": "Enabled"
},
+ "enabledNetworks": {
+ "message": "Enabled networks"
+ },
"encryptionPublicKeyNotice": {
"message": "$1 would like your public encryption key. By consenting, this site will be able to compose encrypted messages to you.",
"description": "$1 is the web3 site name"
@@ -3871,6 +3880,10 @@
"popularCustomNetworks": {
"message": "Popular custom networks"
},
+ "popularNetworkAddToolTip": {
+ "message": "Some of these networks rely on third parties. The connections may be less reliable or enable third-parties to track activity. $1",
+ "description": "$1 is Learn more link"
+ },
"portfolio": {
"message": "Portfolio"
},
diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js
index dfa04ab73236..5dc85389f449 100644
--- a/app/scripts/lib/setupSentry.js
+++ b/app/scripts/lib/setupSentry.js
@@ -629,6 +629,13 @@ export default function setupSentry({ release, getState }) {
tracesSampleRate: 0.01,
beforeSend: (report) => rewriteReport(report, getState),
beforeBreadcrumb: beforeBreadcrumb(getState),
+ // Client reports are automatically sent when a page's visibility changes to
+ // "hidden", but cancelled (with an Error) that gets logged to the console.
+ // Our test infra sometimes reports these errors as unexpected failures,
+ // which results in test flakiness. We don't use these client reports, so
+ // we can safely turn them off by setting the `sendClientReports` option to
+ // `false`.
+ sendClientReports: false,
});
/**
diff --git a/test/e2e/default-fixture.js b/test/e2e/default-fixture.js
index 98413671caca..b3075f3319ca 100644
--- a/test/e2e/default-fixture.js
+++ b/test/e2e/default-fixture.js
@@ -107,6 +107,8 @@ function defaultFixture(inputChainId = CHAIN_IDS.LOCALHOST) {
'__FIXTURE_SUBSTITUTION__currentDateInMilliseconds',
showTestnetMessageInDropdown: true,
trezorModel: null,
+ newPrivacyPolicyToastClickedOrClosed: true,
+ newPrivacyPolicyToastShownDate: Date.now(),
usedNetworks: {
[CHAIN_IDS.MAINNET]: true,
[CHAIN_IDS.LINEA_MAINNET]: true,
diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json
index 8ab0a3b93cd0..dddbe2b554f4 100644
--- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json
+++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json
@@ -39,8 +39,8 @@
"showAccountBanner": true,
"trezorModel": null,
"onboardingDate": "object",
- "newPrivacyPolicyToastClickedOrClosed": "object",
- "newPrivacyPolicyToastShownDate": "object",
+ "newPrivacyPolicyToastClickedOrClosed": "boolean",
+ "newPrivacyPolicyToastShownDate": "number",
"hadAdvancedGasFeesSetPriorToMigration92_3": false,
"nftsDropdownState": {},
"termsOfUseLastAgreed": "number",
diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json
index 93650970b9a9..96dc9ca07e48 100644
--- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json
+++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json
@@ -75,8 +75,8 @@
"showAccountBanner": true,
"trezorModel": null,
"onboardingDate": "object",
- "newPrivacyPolicyToastClickedOrClosed": "object",
- "newPrivacyPolicyToastShownDate": "object",
+ "newPrivacyPolicyToastClickedOrClosed": "boolean",
+ "newPrivacyPolicyToastShownDate": "number",
"hadAdvancedGasFeesSetPriorToMigration92_3": false,
"nftsDropdownState": {},
"termsOfUseLastAgreed": "number",
diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json
index 60379a1d0811..dd90d1071776 100644
--- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json
+++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json
@@ -46,6 +46,8 @@
"0x5": true,
"0x539": true
},
+ "newPrivacyPolicyToastClickedOrClosed": "boolean",
+ "newPrivacyPolicyToastShownDate": "number",
"snapsInstallPrivacyWarningShown": true
},
"CurrencyController": {
diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json
index 48392c6b4db3..fc4bb1d65eed 100644
--- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json
+++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json
@@ -46,6 +46,8 @@
"0x5": true,
"0x539": true
},
+ "newPrivacyPolicyToastClickedOrClosed": "boolean",
+ "newPrivacyPolicyToastShownDate": "number",
"snapsInstallPrivacyWarningShown": true
},
"CurrencyController": {
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 022ad7f055b0..6c276e326062 100644
--- a/ui/components/multichain/network-list-menu/network-list-menu.js
+++ b/ui/components/multichain/network-list-menu/network-list-menu.js
@@ -67,12 +67,23 @@ import {
getIsUnlocked,
} from '../../../ducks/metamask/metamask';
import { getLocalNetworkMenuRedesignFeatureFlag } from '../../../helpers/utils/feature-flags';
+import AddNetworkModal from '../../../pages/onboarding-flow/add-network-modal';
import PopularNetworkList from './popular-network-list/popular-network-list';
import NetworkListSearch from './network-list-search/network-list-search';
+const ACTION_MODES = {
+ // Displays the search box and network list
+ LIST: 'list',
+ // Displays the Add form
+ ADD: 'add',
+ // Displays the Edit form
+ EDIT: 'edit',
+};
+
export const NetworkListMenu = ({ onClose }) => {
const t = useI18nContext();
+ const [actionMode, setActionMode] = useState(ACTION_MODES.LIST);
const nonTestNetworks = useSelector(getNonTestNetworks);
const testNetworks = useSelector(getTestNetworks);
const showTestNetworks = useSelector(getShowTestNetworks);
@@ -99,6 +110,13 @@ export const NetworkListMenu = ({ onClose }) => {
const isUnlocked = useSelector(getIsUnlocked);
+ let title = t('networkMenuHeading');
+ if (actionMode === ACTION_MODES.ADD) {
+ title = t('addCustomNetwork');
+ } else if (actionMode === ACTION_MODES.EDIT) {
+ title = currentNetwork.nickname;
+ }
+
const orderedNetworksList = useSelector(getOrderedNetworksList);
const networkConfigurationChainIds = Object.values(networkConfigurations).map(
@@ -315,6 +333,11 @@ export const NetworkListMenu = ({ onClose }) => {
}
};
+ const headerAdditionalProps =
+ actionMode === ACTION_MODES.LIST
+ ? {}
+ : { onBack: () => setActionMode(ACTION_MODES.LIST) };
+
return (
@@ -332,145 +355,165 @@ export const NetworkListMenu = ({ onClose }) => {
paddingRight={4}
paddingBottom={6}
onClose={onClose}
+ {...headerAdditionalProps}
>
- {t('networkMenuHeading')}
+ {title}
- <>
-
- {showBanner ? (
-
-
-
- }
- onClose={() => hideNetworkBanner()}
- description={t('dragAndDropBanner')}
+ {actionMode === ACTION_MODES.LIST ? (
+ <>
+
- ) : null}
-
- {searchResults.length === 0 && focusSearch ? (
-
- {t('noNetworksFound')}
-
- ) : (
-
-
- {(provided) => (
-
- {searchResults.map((network, index) => {
- const isCurrentNetwork =
- currentNetwork.id === network.id;
-
- const canDeleteNetwork =
- isUnlocked && !isCurrentNetwork && network.removable;
-
- const networkListItem = generateNetworkListItem({
- network,
- isCurrentNetwork,
- canDeleteNetwork,
- });
-
- return (
-
- {(providedDrag) => (
-
- {networkListItem}
-
- )}
-
- );
- })}
- {provided.placeholder}
-
- )}
-
-
- )}
- {networkMenuRedesign ? (
-
+
+
+ }
+ onClose={() => hideNetworkBanner()}
+ description={t('dragAndDropBanner')}
/>
) : null}
-
- {t('showTestnetNetworks')}
-
-
- {showTestNetworks || currentlyOnTestNetwork ? (
-
- {generateMenuItems(searchTestNetworkResults)}
+
+
+ {t('enabledNetworks')}
- ) : null}
-
-
- {
- if (isFullScreen) {
- if (completedOnboarding) {
- history.push(ADD_POPULAR_CUSTOM_NETWORK);
- } else {
- dispatch(showModal({ name: 'ONBOARDING_ADD_NETWORK' }));
+ {searchResults.length === 0 && focusSearch ? (
+
+ {t('noNetworksFound')}
+
+ ) : (
+
+
+ {(provided) => (
+
+ {searchResults.map((network, index) => {
+ const isCurrentNetwork =
+ currentNetwork.id === network.id;
+
+ const canDeleteNetwork =
+ isUnlocked &&
+ !isCurrentNetwork &&
+ network.removable;
+
+ const networkListItem = generateNetworkListItem({
+ network,
+ isCurrentNetwork,
+ canDeleteNetwork,
+ });
+
+ return (
+
+ {(providedDrag) => (
+
+ {networkListItem}
+
+ )}
+
+ );
+ })}
+ {provided.placeholder}
+
+ )}
+
+
+ )}
+ {networkMenuRedesign ? (
+
+ ) : null}
+
+ {t('showTestnetNetworks')}
+
+
+ {showTestNetworks || currentlyOnTestNetwork ? (
+
+ {generateMenuItems(searchTestNetworkResults)}
+
+ ) : null}
+
+
+ {
+ if (!networkMenuRedesign) {
+ if (isFullScreen) {
+ if (completedOnboarding) {
+ history.push(ADD_POPULAR_CUSTOM_NETWORK);
+ } else {
+ dispatch(showModal({ name: 'ONBOARDING_ADD_NETWORK' }));
+ }
+ } else {
+ global.platform.openExtensionInBrowser(
+ ADD_POPULAR_CUSTOM_NETWORK,
+ );
+ }
+ dispatch(toggleNetworkMenu());
+ return;
}
- } else {
- global.platform.openExtensionInBrowser(
- ADD_POPULAR_CUSTOM_NETWORK,
- );
- }
- dispatch(toggleNetworkMenu());
- trackEvent({
- event: MetaMetricsEventName.AddNetworkButtonClick,
- category: MetaMetricsEventCategory.Network,
- });
- }}
- >
- {t('addNetwork')}
-
-
- >
+ trackEvent({
+ event: MetaMetricsEventName.AddNetworkButtonClick,
+ category: MetaMetricsEventCategory.Network,
+ });
+ setActionMode(ACTION_MODES.ADD);
+ }}
+ >
+ {t('addNetwork')}
+
+
+ >
+ ) : (
+
+ )}
);
diff --git a/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.tsx b/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.tsx
index 6d2cd02f5170..cbe1a99fd69f 100644
--- a/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.tsx
+++ b/ui/components/multichain/network-list-menu/popular-network-list/popular-network-list.tsx
@@ -9,6 +9,11 @@ import {
Button,
AvatarNetworkSize,
ButtonVariant,
+ IconName,
+ Icon,
+ IconSize,
+ ButtonLinkSize,
+ ButtonLink,
} from '../../../component-library';
import { MetaMetricsNetworkEventSource } from '../../../../../shared/constants/metametrics';
import {
@@ -26,8 +31,11 @@ import {
Display,
JustifyContent,
TextColor,
+ IconColor,
} from '../../../../helpers/constants/design-system';
import { RPCDefinition } from '../../../../../shared/constants/network';
+import Tooltip from '../../../ui/tooltip';
+import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url';
const PopularNetworkList = ({
searchAddNetworkResults,
@@ -53,7 +61,45 @@ const PopularNetworkList = ({
display={Display.Flex}
justifyContent={JustifyContent.spaceBetween}
>
- {t('additionalNetworks')}
+
+ {t('additionalNetworks')}
+
+ {t('popularNetworkAddToolTip', [
+
+ {
+ global.platform.openTab({
+ url: ZENDESK_URLS.UNKNOWN_NETWORK,
+ });
+ }}
+ >
+ {t('learnMoreUpperCase')}
+
+ ,
+ ,
+ ])}
+
+ }
+ trigger="mouseenter"
+ >
+
+
+
+
+
)}
diff --git a/ui/helpers/utils/feature-flags.js b/ui/helpers/utils/feature-flags.js
index 765bbcf532f9..1956ee75c4bc 100644
--- a/ui/helpers/utils/feature-flags.js
+++ b/ui/helpers/utils/feature-flags.js
@@ -1,3 +1,3 @@
export function getLocalNetworkMenuRedesignFeatureFlag() {
- return window.metamaskFeatureFlags.networkMenuRedesign;
+ return window.metamaskFeatureFlags?.networkMenuRedesign;
}
diff --git a/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap b/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap
index ace866477762..e422b5d8216b 100644
--- a/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap
+++ b/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap
@@ -223,15 +223,10 @@ exports[`Add Network Modal should render 1`] = `
-
- {!viewOnly && (
- <>
- {deletable && (
-
+ )}
);
};
diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.test.js b/ui/pages/settings/networks-tab/networks-form/networks-form.test.js
index a491dee64184..e4b338a8a580 100644
--- a/ui/pages/settings/networks-tab/networks-form/networks-form.test.js
+++ b/ui/pages/settings/networks-tab/networks-form/networks-form.test.js
@@ -43,6 +43,11 @@ const propNetworkDisplay = {
addNewNetwork: false,
};
+jest.mock('../../../../helpers/utils/feature-flags', () => ({
+ ...jest.requireActual('../../../../helpers/utils/feature-flags'),
+ getLocalNetworkMenuRedesignFeatureFlag: () => false,
+}));
+
describe('NetworkForm Component', () => {
beforeAll(() => {
nock.disableNetConnect();
diff --git a/ui/pages/settings/networks-tab/networks-form/rpc-url-editor.tsx b/ui/pages/settings/networks-tab/networks-form/rpc-url-editor.tsx
new file mode 100644
index 000000000000..99cd98776c07
--- /dev/null
+++ b/ui/pages/settings/networks-tab/networks-form/rpc-url-editor.tsx
@@ -0,0 +1,144 @@
+import React, { useRef, useState } from 'react';
+import classnames from 'classnames';
+import {
+ Box,
+ ButtonIcon,
+ ButtonIconSize,
+ Icon,
+ IconName,
+ IconSize,
+ Popover,
+ PopoverPosition,
+ Text,
+} from '../../../../components/component-library';
+import {
+ AlignItems,
+ BackgroundColor,
+ BorderColor,
+ BorderRadius,
+ Display,
+ IconColor,
+ JustifyContent,
+ TextColor,
+ TextVariant,
+} from '../../../../helpers/constants/design-system';
+import { useI18nContext } from '../../../../hooks/useI18nContext';
+
+export const RpcUrlEditor = ({ currentRpcUrl }: { currentRpcUrl: string }) => {
+ // TODO: real endpoints
+ const dummyRpcUrls = [
+ currentRpcUrl,
+ 'https://dummy.mainnet.public.blastapi.io',
+ 'https://dummy.io/v3/blockchain/node/dummy',
+ ];
+
+ const t = useI18nContext();
+ const rpcDropdown = useRef(null);
+ const [isOpen, setIsOpen] = useState(false);
+ const [currentRpcEndpoint, setCurrentRpcEndpoint] = useState(currentRpcUrl);
+
+ return (
+ <>
+
+ {t('defaultRpcUrl')}
+
+ setIsOpen(!isOpen)}
+ className="networks-tab__rpc-dropdown"
+ display={Display.Flex}
+ justifyContent={JustifyContent.spaceBetween}
+ borderRadius={BorderRadius.MD}
+ borderColor={BorderColor.borderDefault}
+ borderWidth={1}
+ padding={2}
+ ref={rpcDropdown}
+ >
+ {currentRpcEndpoint}
+
+
+
+ {dummyRpcUrls.map((rpcEndpoint) => (
+ setCurrentRpcEndpoint(rpcEndpoint)}
+ className={classnames('networks-tab__rpc-item', {
+ 'networks-tab__rpc-item--selected':
+ rpcEndpoint === currentRpcEndpoint,
+ })}
+ >
+ {rpcEndpoint === currentRpcEndpoint && (
+
+ )}
+
+ {rpcEndpoint}
+
+ alert('TODO: delete confirmation modal')}
+ />
+
+ ))}
+ alert('TODO: add RPC modal')}
+ padding={4}
+ display={Display.Flex}
+ alignItems={AlignItems.center}
+ className="networks-tab__rpc-item"
+ >
+
+
+ {t('addRpcUrl')}
+
+
+
+ >
+ );
+};
+
+export default RpcUrlEditor;
diff --git a/ui/pages/settings/networks-tab/networks-tab.test.js b/ui/pages/settings/networks-tab/networks-tab.test.js
index fe985de9ebe4..cead29634a70 100644
--- a/ui/pages/settings/networks-tab/networks-tab.test.js
+++ b/ui/pages/settings/networks-tab/networks-tab.test.js
@@ -20,6 +20,11 @@ const mockState = {
},
};
+jest.mock('../../../helpers/utils/feature-flags', () => ({
+ ...jest.requireActual('../../../helpers/utils/feature-flags'),
+ getLocalNetworkMenuRedesignFeatureFlag: () => false,
+}));
+
const renderComponent = (props) => {
const store = configureMockStore([])(mockState);
return renderWithProvider(, store);