From 4ff3d6bc9bf665f2bec11d4fb4c5dd5b74355a0d Mon Sep 17 00:00:00 2001
From: David Walsh
Date: Mon, 10 Jun 2024 20:39:20 -0500
Subject: [PATCH 01/14] Network Menu Redesign: Add 3-dot menu
---
.../multichain/network-list-item/index.scss | 12 ---
.../network-list-item/network-list-item.js | 94 +++++++++++++++++--
2 files changed, 85 insertions(+), 21 deletions(-)
diff --git a/ui/components/multichain/network-list-item/index.scss b/ui/components/multichain/network-list-item/index.scss
index a7a1e6aa158e..ae68ea52dc3c 100644
--- a/ui/components/multichain/network-list-item/index.scss
+++ b/ui/components/multichain/network-list-item/index.scss
@@ -14,14 +14,6 @@
color: inherit;
}
- &:hover,
- &:focus,
- &:focus-within {
- .multichain-network-list-item__delete {
- visibility: visible;
- }
- }
-
&__network-name {
width: 100%;
flex: 1;
@@ -44,8 +36,4 @@
top: 4px;
left: 4px;
}
-
- &__delete {
- visibility: hidden;
- }
}
diff --git a/ui/components/multichain/network-list-item/network-list-item.js b/ui/components/multichain/network-list-item/network-list-item.js
index 6fe9336f434a..0783d1e50d84 100644
--- a/ui/components/multichain/network-list-item/network-list-item.js
+++ b/ui/components/multichain/network-list-item/network-list-item.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef } from 'react';
+import React, { useCallback, useEffect, useRef, useState } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import {
@@ -10,19 +10,23 @@ import {
Display,
IconColor,
JustifyContent,
- Size,
TextColor,
} from '../../../helpers/constants/design-system';
import {
AvatarNetwork,
Box,
ButtonIcon,
+ ButtonIconSize,
IconName,
+ Popover,
+ PopoverPosition,
+ PopoverRole,
Text,
} from '../../component-library';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { getAvatarNetworkColor } from '../../../helpers/utils/accounts';
import Tooltip from '../../ui/tooltip/tooltip';
+import { MenuItem } from '../../ui/menu';
const MAXIMUM_CHARACTERS_WITHOUT_TOOLTIP = 20;
@@ -33,9 +37,36 @@ export const NetworkListItem = ({
focus = true,
onClick,
onDeleteClick,
+ onEditClick,
}) => {
const t = useI18nContext();
const networkRef = useRef();
+ const menuRef = useRef(null);
+
+ const [networkOptionsMenuOpen, setNetworkOptionsMenuOpen] = useState(false);
+
+ // Handle click outside of the popover to close it
+ const popoverDialogRef = useRef(null);
+
+ const handleClickOutside = useCallback(
+ (event) => {
+ if (
+ popoverDialogRef?.current &&
+ !popoverDialogRef.current.contains(event.target)
+ ) {
+ setNetworkOptionsMenuOpen(false);
+ }
+ },
+ [setNetworkOptionsMenuOpen],
+ );
+
+ useEffect(() => {
+ document.addEventListener('mousedown', handleClickOutside);
+
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [handleClickOutside]);
useEffect(() => {
if (networkRef.current && focus) {
@@ -103,19 +134,60 @@ export const NetworkListItem = ({
)}
- {onDeleteClick ? (
+ {onDeleteClick || onEditClick ? (
{
+ e.preventDefault();
e.stopPropagation();
- onDeleteClick();
+
+ console.log(`Opening menu for ${name}`);
+ setNetworkOptionsMenuOpen(true);
}}
+ size={ButtonIconSize.Sm}
/>
) : null}
+ setNetworkOptionsMenuOpen(false)}
+ role={PopoverRole.Dialog}
+ position={PopoverPosition.Bottom}
+ offset={[0, 0]}
+ >
+ {onEditClick ? (
+ {
+ e.stopPropagation();
+
+ // Pass network info?
+ onEditClick();
+ }}
+ data-testid="network-list-item-options-edit"
+ >
+ {t('edit')}
+
+ ) : null}
+ {onDeleteClick ? (
+ {
+ e.stopPropagation();
+
+ // Pass network info?
+ onDeleteClick();
+ }}
+ data-testid="network-list-item-options-delete"
+ color={TextColor.errorDefault}
+ >
+ {t('delete')}
+
+ ) : null}
+
);
};
@@ -141,6 +213,10 @@ NetworkListItem.propTypes = {
* Executes when the delete icon is clicked
*/
onDeleteClick: PropTypes.func,
+ /**
+ * Executes when the edit icon is clicked
+ */
+ onEditClick: PropTypes.func,
/**
* Represents if the network item should be keyboard selected
*/
From adb9efab0852fbd4fc3841c67f90766f4e25a89a Mon Sep 17 00:00:00 2001
From: salimtb
Date: Wed, 12 Jun 2024 19:24:58 +0200
Subject: [PATCH 02/14] fix: resolve conflicts
---
.../network-list-menu/network-list-menu.js | 402 ++++++++++--------
1 file changed, 214 insertions(+), 188 deletions(-)
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 6c276e326062..52c0d729a23a 100644
--- a/ui/components/multichain/network-list-menu/network-list-menu.js
+++ b/ui/components/multichain/network-list-menu/network-list-menu.js
@@ -162,7 +162,6 @@ export const NetworkListMenu = ({ onClose }) => {
}, [dispatch, currentlyOnTestNetwork]);
const [searchQuery, setSearchQuery] = useState('');
- const [focusSearch, setFocusSearch] = useState(false);
const onboardedInThisUISession = useSelector(getOnboardedInThisUISession);
const showNetworkBanner = useSelector(getShowNetworkBanner);
const showBanner =
@@ -196,9 +195,9 @@ export const NetworkListMenu = ({ onClose }) => {
? items
: [...notExistingNetworkConfigurations];
- let searchTestNetworkResults = [...testNetworks];
+ const isSearching = searchQuery !== '';
- if (focusSearch && searchQuery !== '') {
+ if (isSearching) {
const fuse = new Fuse(searchResults, {
threshold: 0.2,
location: 0,
@@ -218,25 +217,11 @@ export const NetworkListMenu = ({ onClose }) => {
keys: ['nickname', 'chainId', 'ticker'],
});
- const fuseForTestsNetworks = new Fuse(searchTestNetworkResults, {
- threshold: 0.2,
- location: 0,
- distance: 100,
- maxPatternLength: 32,
- minMatchCharLength: 1,
- shouldSort: true,
- keys: ['nickname', 'chainId', 'ticker'],
- });
-
fuse.setCollection(searchResults);
fuseForPopularNetworks.setCollection(searchAddNetworkResults);
- fuseForTestsNetworks.setCollection(searchTestNetworkResults);
-
const fuseResults = fuse.search(searchQuery);
const fuseForPopularNetworksResults =
fuseForPopularNetworks.search(searchQuery);
- const fuseForTestsNetworksResults =
- fuseForTestsNetworks.search(searchQuery);
searchResults = searchResults.filter((network) =>
fuseResults.includes(network),
@@ -244,65 +229,25 @@ export const NetworkListMenu = ({ onClose }) => {
searchAddNetworkResults = searchAddNetworkResults.filter((network) =>
fuseForPopularNetworksResults.includes(network),
);
- searchTestNetworkResults = searchTestNetworkResults.filter((network) =>
- fuseForTestsNetworksResults.includes(network),
- );
}
- const generateNetworkListItem = ({
- network,
- isCurrentNetwork,
- canDeleteNetwork,
- }) => {
- return (
- {
- dispatch(toggleNetworkMenu());
- if (network.providerType) {
- dispatch(setProviderType(network.providerType));
- } else {
- dispatch(setActiveNetwork(network.id));
- }
-
- // If presently on a dapp, communicate a change to
- // the dapp via silent switchEthereumChain that the
- // network has changed due to user action
- if (useRequestQueue && selectedTabOrigin) {
- setNetworkClientIdForDomain(selectedTabOrigin, network.id);
- }
+ const getOnDeleteCallback = (networkId) => {
+ return () => {
+ dispatch(toggleNetworkMenu());
+ dispatch(
+ showModal({
+ name: 'CONFIRM_DELETE_NETWORK',
+ target: networkId,
+ onConfirm: () => undefined,
+ }),
+ );
+ };
+ };
- trackEvent({
- event: MetaMetricsEventName.NavNetworkSwitched,
- category: MetaMetricsEventCategory.Network,
- properties: {
- location: 'Network Menu',
- chain_id: currentChainId,
- from_network: currentChainId,
- to_network: network.chainId,
- },
- });
- }}
- onDeleteClick={
- canDeleteNetwork
- ? () => {
- dispatch(toggleNetworkMenu());
- dispatch(
- showModal({
- name: 'CONFIRM_DELETE_NETWORK',
- target: network.id,
- onConfirm: () => undefined,
- }),
- );
- }
- : null
- }
- />
- );
+ const getOnEditCallback = (networkId) => {
+ return () => {
+ console.log('Get onEditCallback for: ', networkId);
+ };
};
const generateMenuItems = (desiredNetworks) => {
@@ -314,11 +259,45 @@ export const NetworkListMenu = ({ onClose }) => {
const canDeleteNetwork =
isUnlocked && !isCurrentNetwork && network.removable;
- return generateNetworkListItem({
- network,
- isCurrentNetwork,
- canDeleteNetwork,
- });
+ return (
+ {
+ dispatch(toggleNetworkMenu());
+ if (network.providerType) {
+ dispatch(setProviderType(network.providerType));
+ } else {
+ dispatch(setActiveNetwork(network.id));
+ }
+ trackEvent({
+ event: MetaMetricsEventName.NavNetworkSwitched,
+ category: MetaMetricsEventCategory.Network,
+ properties: {
+ location: 'Network Menu',
+ chain_id: currentChainId,
+ from_network: currentChainId,
+ to_network: network.chainId,
+ },
+ });
+ }}
+ onDeleteClick={
+ canDeleteNetwork
+ ? () => {
+ dispatch(toggleNetworkMenu());
+ getOnDeleteCallback(network.id);
+ }
+ : null
+ }
+ onEditClick={() => {
+ dispatch(toggleNetworkMenu());
+ getOnEditCallback(network.id);
+ }}
+ />
+ );
});
};
@@ -364,121 +343,168 @@ export const NetworkListMenu = ({ onClose }) => {
- {showBanner ? (
-
-
-
- }
- onClose={() => hideNetworkBanner()}
- description={t('dragAndDropBanner')}
- />
- ) : null}
+
-
- {t('enabledNetworks')}
-
- {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)}
+
+ {searchResults.length === 0 && isSearching ? (
+
+ {t('noNetworksFound')}
+
+ ) : (
+
+
+ {(provided) => (
+
+ {searchResults.map((network, index) => {
+ const isCurrentNetwork =
+ currentNetwork.id === network.id;
+
+ const canDeleteNetwork =
+ isUnlocked &&
+ !isCurrentNetwork &&
+ network.removable;
+
+ return (
+
+ {(providedDrag) => (
+
+ {
+ dispatch(toggleNetworkMenu());
+ if (network.providerType) {
+ dispatch(
+ setProviderType(
+ network.providerType,
+ ),
+ );
+ } else {
+ dispatch(
+ setActiveNetwork(network.id),
+ );
+ }
+
+ // If presently on a dapp, communicate a change to
+ // the dapp via silent switchEthereumChain that the
+ // network has changed due to user action
+ if (
+ useRequestQueue &&
+ selectedTabOrigin
+ ) {
+ setNetworkClientIdForDomain(
+ selectedTabOrigin,
+ network.id,
+ );
+ }
+
+ trackEvent({
+ event:
+ MetaMetricsEventName.NavNetworkSwitched,
+ category:
+ MetaMetricsEventCategory.Network,
+ properties: {
+ location: 'Network Menu',
+ chain_id: currentChainId,
+ from_network: currentChainId,
+ to_network: network.chainId,
+ },
+ });
+ }}
+ onDeleteClick={
+ canDeleteNetwork
+ ? () => {
+ dispatch(toggleNetworkMenu());
+ getOnDeleteCallback(network.id);
+ }
+ : null
+ }
+ onEditClick={() => {
+ dispatch(toggleNetworkMenu());
+ getOnEditCallback(network.id);
+ }}
+ />
+
+ )}
+
+ );
+ })}
+ {provided.placeholder}
+
+ )}
+
+
+ )}
+ {networkMenuRedesign ? (
+
+ ) : null}
+
+ {t('showTestnetNetworks')}
+
- ) : null}
+ {showTestNetworks || currentlyOnTestNetwork ? (
+
+ {generateMenuItems(testNetworks)}
+
+ ) : null}
+
+
{
>
) : (
-
+
)}
From 04a4c0287acb513b7dc2aae225b845e6378d1aa1 Mon Sep 17 00:00:00 2001
From: David Walsh
Date: Mon, 10 Jun 2024 21:21:34 -0500
Subject: [PATCH 03/14] Add NetworkListItemMenu
---
app/_locales/en/messages.json | 3 +
.../multichain/multichain-components.scss | 1 +
.../network-list-item-menu/index.js | 1 +
.../network-list-item-menu/index.scss | 8 +
.../network-list-item-menu.js | 152 ++++++
.../network-list-item.test.js.snap | 7 +-
.../network-list-item/network-list-item.js | 86 +---
.../network-list-item.test.js | 10 +-
.../network-list-menu/network-list-menu.js | 441 +++++++++---------
ui/helpers/utils/network-helper.test.ts | 55 ++-
ui/helpers/utils/network-helper.ts | 16 +
.../add-network-modal/index.js | 24 +-
.../networks-form/networks-form.js | 32 +-
13 files changed, 530 insertions(+), 306 deletions(-)
create mode 100644 ui/components/multichain/network-list-item-menu/index.js
create mode 100644 ui/components/multichain/network-list-item-menu/index.scss
create mode 100644 ui/components/multichain/network-list-item-menu/network-list-item-menu.js
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index be8313a1fb4a..78f2f87ecb29 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -2885,6 +2885,9 @@
"networkNameZkSyncEra": {
"message": "zkSync Era"
},
+ "networkOptions": {
+ "message": "Network options"
+ },
"networkProvider": {
"message": "Network provider"
},
diff --git a/ui/components/multichain/multichain-components.scss b/ui/components/multichain/multichain-components.scss
index 8917088b40b4..c4b043c69b1f 100644
--- a/ui/components/multichain/multichain-components.scss
+++ b/ui/components/multichain/multichain-components.scss
@@ -19,6 +19,7 @@
@import 'connected-site-menu';
@import 'token-list-item';
@import 'network-list-item';
+@import 'network-list-item-menu';
@import 'network-list-menu';
@import 'product-tour-popover';
@import 'nft-item';
diff --git a/ui/components/multichain/network-list-item-menu/index.js b/ui/components/multichain/network-list-item-menu/index.js
new file mode 100644
index 000000000000..b21bbd464bd7
--- /dev/null
+++ b/ui/components/multichain/network-list-item-menu/index.js
@@ -0,0 +1 @@
+export { NetworkListItemMenu } from './network-list-item-menu';
diff --git a/ui/components/multichain/network-list-item-menu/index.scss b/ui/components/multichain/network-list-item-menu/index.scss
new file mode 100644
index 000000000000..62685b151700
--- /dev/null
+++ b/ui/components/multichain/network-list-item-menu/index.scss
@@ -0,0 +1,8 @@
+@use "design-system";
+
+.multichain-network-list-item-menu__popover {
+ z-index: design-system.$popover-in-modal-z-index;
+ overflow: hidden;
+ min-width: 225px;
+ max-width: 225px;
+}
diff --git a/ui/components/multichain/network-list-item-menu/network-list-item-menu.js b/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
new file mode 100644
index 000000000000..6618cee57610
--- /dev/null
+++ b/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
@@ -0,0 +1,152 @@
+import React, { useCallback, useEffect, useRef } from 'react';
+import PropTypes from 'prop-types';
+import { useI18nContext } from '../../../hooks/useI18nContext';
+import {
+ IconName,
+ ModalFocus,
+ Popover,
+ PopoverPosition,
+ PopoverRole,
+ Text,
+} from '../../component-library';
+import { MenuItem } from '../../ui/menu';
+import { IconColor, TextColor } from '../../../helpers/constants/design-system';
+
+export const NetworkListItemMenu = ({
+ anchorElement,
+ onClose,
+ onEditClick,
+ onDeleteClick,
+ isOpen,
+}) => {
+ const t = useI18nContext();
+
+ // Handle Tab key press for accessibility inside the popover and will close the popover on the last MenuItem
+ const lastItemRef = useRef(null);
+ const accountDetailsItemRef = useRef(null);
+ const removeAccountItemRef = useRef(null);
+ const removeJWTItemRef = useRef(null);
+
+ // Checks the MenuItems from the bottom to top to set lastItemRef on the last MenuItem that is not disabled
+ useEffect(() => {
+ if (removeJWTItemRef.current) {
+ lastItemRef.current = removeJWTItemRef.current;
+ } else if (removeAccountItemRef.current) {
+ lastItemRef.current = removeAccountItemRef.current;
+ } else {
+ lastItemRef.current = accountDetailsItemRef.current;
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [
+ removeJWTItemRef.current,
+ removeAccountItemRef.current,
+ accountDetailsItemRef.current,
+ ]);
+
+ const handleKeyDown = useCallback(
+ (event) => {
+ if (event.key === 'Tab' && event.target === lastItemRef.current) {
+ // If Tab is pressed at the last item to close popover and focus to next element in DOM
+ onClose();
+ }
+ },
+ [onClose],
+ );
+
+ // Handle click outside of the popover to close it
+ const popoverDialogRef = useRef(null);
+
+ const handleClickOutside = useCallback(
+ (event) => {
+ if (
+ popoverDialogRef?.current &&
+ !popoverDialogRef.current.contains(event.target)
+ ) {
+ onClose();
+ }
+ },
+ [onClose],
+ );
+
+ useEffect(() => {
+ document.addEventListener('mousedown', handleClickOutside);
+
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [handleClickOutside]);
+
+ return (
+
+
+
+ {onEditClick ? (
+ {
+ e.stopPropagation();
+
+ // Pass network info?
+ onEditClick();
+ }}
+ data-testid="network-list-item-options-edit"
+ >
+ {t('edit')}
+
+ ) : null}
+ {onDeleteClick ? (
+ {
+ e.stopPropagation();
+
+ // Pass network info?
+ onDeleteClick();
+ }}
+ data-testid="network-list-item-options-delete"
+ >
+ {t('delete')}
+
+ ) : null}
+
+
+
+ );
+};
+
+NetworkListItemMenu.propTypes = {
+ /**
+ * Element that the menu should display next to
+ */
+ anchorElement: PropTypes.instanceOf(window.Element),
+ /**
+ * Function that executes when the menu is closed
+ */
+ onClose: PropTypes.func.isRequired,
+ /**
+ * Function that executes when the Edit menu item is clicked
+ */
+ onEditClick: PropTypes.func,
+ /**
+ * Function that executes when the Delete menu item is closed
+ */
+ onDeleteClick: PropTypes.func,
+ /**
+ * Represents if the menu is open or not
+ *
+ * @type {boolean}
+ */
+ isOpen: PropTypes.bool.isRequired,
+};
diff --git a/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap b/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap
index df225a439aed..4d3f902a14de 100644
--- a/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap
+++ b/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap
@@ -26,12 +26,13 @@ exports[`NetworkListItem renders properly 1`] = `
diff --git a/ui/components/multichain/network-list-item/network-list-item.js b/ui/components/multichain/network-list-item/network-list-item.js
index 0783d1e50d84..74cd19314729 100644
--- a/ui/components/multichain/network-list-item/network-list-item.js
+++ b/ui/components/multichain/network-list-item/network-list-item.js
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect, useRef, useState } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import {
@@ -8,7 +8,6 @@ import {
BorderRadius,
Color,
Display,
- IconColor,
JustifyContent,
TextColor,
} from '../../../helpers/constants/design-system';
@@ -18,15 +17,12 @@ import {
ButtonIcon,
ButtonIconSize,
IconName,
- Popover,
- PopoverPosition,
- PopoverRole,
Text,
} from '../../component-library';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { getAvatarNetworkColor } from '../../../helpers/utils/accounts';
import Tooltip from '../../ui/tooltip/tooltip';
-import { MenuItem } from '../../ui/menu';
+import { NetworkListItemMenu } from '../network-list-item-menu';
const MAXIMUM_CHARACTERS_WITHOUT_TOOLTIP = 20;
@@ -41,32 +37,14 @@ export const NetworkListItem = ({
}) => {
const t = useI18nContext();
const networkRef = useRef();
- const menuRef = useRef(null);
- const [networkOptionsMenuOpen, setNetworkOptionsMenuOpen] = useState(false);
-
- // Handle click outside of the popover to close it
- const popoverDialogRef = useRef(null);
-
- const handleClickOutside = useCallback(
- (event) => {
- if (
- popoverDialogRef?.current &&
- !popoverDialogRef.current.contains(event.target)
- ) {
- setNetworkOptionsMenuOpen(false);
- }
- },
- [setNetworkOptionsMenuOpen],
- );
-
- useEffect(() => {
- document.addEventListener('mousedown', handleClickOutside);
+ const [networkListItemMenuElement, setNetworkListItemMenuElement] =
+ useState();
+ const setNetworkListItemMenuRef = (ref) => {
+ setNetworkListItemMenuElement(ref);
+ };
- return () => {
- document.removeEventListener('mousedown', handleClickOutside);
- };
- }, [handleClickOutside]);
+ const [networkOptionsMenuOpen, setNetworkOptionsMenuOpen] = useState(false);
useEffect(() => {
if (networkRef.current && focus) {
@@ -137,57 +115,23 @@ export const NetworkListItem = ({
{onDeleteClick || onEditClick ? (
{
- e.preventDefault();
e.stopPropagation();
-
- console.log(`Opening menu for ${name}`);
setNetworkOptionsMenuOpen(true);
}}
size={ButtonIconSize.Sm}
/>
) : null}
- setNetworkOptionsMenuOpen(false)}
- role={PopoverRole.Dialog}
- position={PopoverPosition.Bottom}
- offset={[0, 0]}
- >
- {onEditClick ? (
- {
- e.stopPropagation();
-
- // Pass network info?
- onEditClick();
- }}
- data-testid="network-list-item-options-edit"
- >
- {t('edit')}
-
- ) : null}
- {onDeleteClick ? (
- {
- e.stopPropagation();
-
- // Pass network info?
- onDeleteClick();
- }}
- data-testid="network-list-item-options-delete"
- color={TextColor.errorDefault}
- >
- {t('delete')}
-
- ) : null}
-
+ />
);
};
diff --git a/ui/components/multichain/network-list-item/network-list-item.test.js b/ui/components/multichain/network-list-item/network-list-item.test.js
index 4c4215d02b10..994e77bc18b7 100644
--- a/ui/components/multichain/network-list-item/network-list-item.test.js
+++ b/ui/components/multichain/network-list-item/network-list-item.test.js
@@ -65,16 +65,18 @@ describe('NetworkListItem', () => {
it('executes onDeleteClick when the delete button is clicked', () => {
const onDeleteClick = jest.fn();
const onClick = jest.fn();
- const { container } = render(
+
+ const { getByTestId } = render(
,
);
- fireEvent.click(
- container.querySelector('.multichain-network-list-item__delete'),
- );
+
+ fireEvent.click(getByTestId('network-list-item-options-button'));
+
+ fireEvent.click(getByTestId('network-list-item-options-delete'));
expect(onDeleteClick).toHaveBeenCalledTimes(1);
expect(onClick).toHaveBeenCalledTimes(0);
});
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 52c0d729a23a..5b3f2dadbc9f 100644
--- a/ui/components/multichain/network-list-menu/network-list-menu.js
+++ b/ui/components/multichain/network-list-menu/network-list-menu.js
@@ -84,6 +84,8 @@ export const NetworkListMenu = ({ onClose }) => {
const t = useI18nContext();
const [actionMode, setActionMode] = useState(ACTION_MODES.LIST);
+ const [modalTitle, setModalTitle] = useState(t('networkMenuHeading'));
+ const [networkToEdit, setNetworkToEdit] = useState(null);
const nonTestNetworks = useSelector(getNonTestNetworks);
const testNetworks = useSelector(getTestNetworks);
const showTestNetworks = useSelector(getShowTestNetworks);
@@ -110,13 +112,6 @@ 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(
@@ -244,9 +239,15 @@ export const NetworkListMenu = ({ onClose }) => {
};
};
- const getOnEditCallback = (networkId) => {
+ const getOnEditCallback = (network) => {
return () => {
- console.log('Get onEditCallback for: ', networkId);
+ const networkToUse = {
+ ...network,
+ label: network.nickname,
+ };
+ setModalTitle(network.nickname);
+ setNetworkToEdit(networkToUse);
+ setActionMode(ACTION_MODES.EDIT);
};
};
@@ -285,17 +286,9 @@ export const NetworkListMenu = ({ onClose }) => {
});
}}
onDeleteClick={
- canDeleteNetwork
- ? () => {
- dispatch(toggleNetworkMenu());
- getOnDeleteCallback(network.id);
- }
- : null
+ canDeleteNetwork ? getOnDeleteCallback(network.id) : null
}
- onEditClick={() => {
- dispatch(toggleNetworkMenu());
- getOnEditCallback(network.id);
- }}
+ onEditClick={getOnEditCallback(network)}
/>
);
});
@@ -312,6 +305,211 @@ export const NetworkListMenu = ({ onClose }) => {
}
};
+ const renderListNetworks = () => {
+ if (actionMode === ACTION_MODES.LIST) {
+ return (
+ <>
+
+
+
+ {showBanner ? (
+
+
+
+ }
+ onClose={() => hideNetworkBanner()}
+ description={t('dragAndDropBanner')}
+ />
+ ) : null}
+
+ {searchResults.length === 0 && isSearching ? (
+
+ {t('noNetworksFound')}
+
+ ) : (
+
+
+ {(provided) => (
+
+ {searchResults.map((network, index) => {
+ const isCurrentNetwork =
+ currentNetwork.id === network.id;
+
+ const canDeleteNetwork =
+ isUnlocked &&
+ !isCurrentNetwork &&
+ network.removable;
+
+ return (
+
+ {(providedDrag) => (
+
+ {
+ dispatch(toggleNetworkMenu());
+ if (network.providerType) {
+ dispatch(
+ setProviderType(network.providerType),
+ );
+ } else {
+ dispatch(setActiveNetwork(network.id));
+ }
+
+ // If presently on a dapp, communicate a change to
+ // the dapp via silent switchEthereumChain that the
+ // network has changed due to user action
+ if (
+ useRequestQueue &&
+ selectedTabOrigin
+ ) {
+ setNetworkClientIdForDomain(
+ selectedTabOrigin,
+ network.id,
+ );
+ }
+
+ trackEvent({
+ event:
+ MetaMetricsEventName.NavNetworkSwitched,
+ category:
+ MetaMetricsEventCategory.Network,
+ properties: {
+ location: 'Network Menu',
+ chain_id: currentChainId,
+ from_network: currentChainId,
+ to_network: network.chainId,
+ },
+ });
+ }}
+ onDeleteClick={
+ canDeleteNetwork
+ ? getOnDeleteCallback(network.id)
+ : null
+ }
+ onEditClick={getOnEditCallback(network)}
+ />
+
+ )}
+
+ );
+ })}
+ {provided.placeholder}
+
+ )}
+
+
+ )}
+ {networkMenuRedesign ? (
+
+ ) : null}
+
+ {t('showTestnetNetworks')}
+
+
+ {showTestNetworks || currentlyOnTestNetwork ? (
+
+ {generateMenuItems(testNetworks)}
+
+ ) : 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;
+ }
+ trackEvent({
+ event: MetaMetricsEventName.AddNetworkButtonClick,
+ category: MetaMetricsEventCategory.Network,
+ });
+ setActionMode(ACTION_MODES.ADD);
+ setModalTitle(t('addCustomNetwork'));
+ }}
+ >
+ {t('addNetwork')}
+
+
+ >
+ );
+ } else if (actionMode === ACTION_MODES.ADD) {
+ return ;
+ }
+ return (
+
+ );
+ };
+
const headerAdditionalProps =
actionMode === ACTION_MODES.LIST
? {}
@@ -336,210 +534,9 @@ export const NetworkListMenu = ({ onClose }) => {
onClose={onClose}
{...headerAdditionalProps}
>
- {title}
+ {modalTitle}
- {actionMode === ACTION_MODES.LIST ? (
- <>
-
-
-
- {showBanner ? (
-
-
-
- }
- onClose={() => hideNetworkBanner()}
- description={t('dragAndDropBanner')}
- />
- ) : null}
-
- {searchResults.length === 0 && isSearching ? (
-
- {t('noNetworksFound')}
-
- ) : (
-
-
- {(provided) => (
-
- {searchResults.map((network, index) => {
- const isCurrentNetwork =
- currentNetwork.id === network.id;
-
- const canDeleteNetwork =
- isUnlocked &&
- !isCurrentNetwork &&
- network.removable;
-
- return (
-
- {(providedDrag) => (
-
- {
- dispatch(toggleNetworkMenu());
- if (network.providerType) {
- dispatch(
- setProviderType(
- network.providerType,
- ),
- );
- } else {
- dispatch(
- setActiveNetwork(network.id),
- );
- }
-
- // If presently on a dapp, communicate a change to
- // the dapp via silent switchEthereumChain that the
- // network has changed due to user action
- if (
- useRequestQueue &&
- selectedTabOrigin
- ) {
- setNetworkClientIdForDomain(
- selectedTabOrigin,
- network.id,
- );
- }
-
- trackEvent({
- event:
- MetaMetricsEventName.NavNetworkSwitched,
- category:
- MetaMetricsEventCategory.Network,
- properties: {
- location: 'Network Menu',
- chain_id: currentChainId,
- from_network: currentChainId,
- to_network: network.chainId,
- },
- });
- }}
- onDeleteClick={
- canDeleteNetwork
- ? () => {
- dispatch(toggleNetworkMenu());
- getOnDeleteCallback(network.id);
- }
- : null
- }
- onEditClick={() => {
- dispatch(toggleNetworkMenu());
- getOnEditCallback(network.id);
- }}
- />
-
- )}
-
- );
- })}
- {provided.placeholder}
-
- )}
-
-
- )}
- {networkMenuRedesign ? (
-
- ) : null}
-
- {t('showTestnetNetworks')}
-
-
- {showTestNetworks || currentlyOnTestNetwork ? (
-
- {generateMenuItems(testNetworks)}
-
- ) : 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;
- }
- trackEvent({
- event: MetaMetricsEventName.AddNetworkButtonClick,
- category: MetaMetricsEventCategory.Network,
- });
- setActionMode(ACTION_MODES.ADD);
- }}
- >
- {t('addNetwork')}
-
-
- >
- ) : (
-
- )}
+ {renderListNetworks()}
);
diff --git a/ui/helpers/utils/network-helper.test.ts b/ui/helpers/utils/network-helper.test.ts
index fef6711dbd93..188cd235953a 100644
--- a/ui/helpers/utils/network-helper.test.ts
+++ b/ui/helpers/utils/network-helper.test.ts
@@ -1,4 +1,8 @@
-import { getMatchedChain, getMatchedSymbols } from './network-helper';
+import {
+ getMatchedChain,
+ getMatchedNames,
+ getMatchedSymbols,
+} from './network-helper';
describe('netwotkHelper', () => {
describe('getMatchedChain', () => {
@@ -76,4 +80,53 @@ describe('netwotkHelper', () => {
expect(result).toEqual([]);
});
});
+
+ describe('getMatchedName', () => {
+ it('should return an array of symbols that match the given decimalChainId', () => {
+ const chains = [
+ {
+ chainId: '1',
+ name: 'Ethereum',
+ nativeCurrency: { symbol: 'ETH', name: 'Ethereum' },
+ },
+ {
+ chainId: '3',
+ name: 'tEthereum',
+ nativeCurrency: { symbol: 'tETH', name: 'tEthereum' },
+ },
+ {
+ chainId: '1',
+ name: 'WEthereum',
+ nativeCurrency: { symbol: 'WETH', name: 'WEthereum' },
+ },
+ ];
+ const decimalChainId = '1';
+ const expected = ['Ethereum', 'WEthereum'];
+
+ const result = getMatchedNames(decimalChainId, chains);
+
+ expect(result).toEqual(expect.arrayContaining(expected));
+ expect(result.length).toBe(expected.length);
+ });
+
+ it('should return an empty array if no symbols match the given decimalChainId', () => {
+ const chains = [
+ {
+ chainId: '1',
+ name: 'Ethereum',
+ nativeCurrency: { symbol: 'ETH', name: 'Ethereum' },
+ },
+ {
+ chainId: '3',
+ name: 'tEthereum',
+ nativeCurrency: { symbol: 'tETH', name: 'tEthereum' },
+ },
+ ];
+ const decimalChainId = '2'; // No matching chainId
+
+ const result = getMatchedNames(decimalChainId, chains);
+
+ expect(result).toEqual([]);
+ });
+ });
});
diff --git a/ui/helpers/utils/network-helper.ts b/ui/helpers/utils/network-helper.ts
index 840dc8dfbf8a..960f2979cc0e 100644
--- a/ui/helpers/utils/network-helper.ts
+++ b/ui/helpers/utils/network-helper.ts
@@ -26,3 +26,19 @@ export const getMatchedSymbols = (
return accumulator;
}, []);
};
+
+export const getMatchedNames = (
+ decimalChainId: string,
+ safeChainsList: {
+ chainId: string;
+ name: string;
+ nativeCurrency: { symbol: string; name: string };
+ }[],
+): string[] => {
+ return safeChainsList.reduce((accumulator, currentNetwork) => {
+ if (currentNetwork.chainId.toString() === decimalChainId) {
+ accumulator.push(currentNetwork?.name);
+ }
+ return accumulator;
+ }, []);
+};
diff --git a/ui/pages/onboarding-flow/add-network-modal/index.js b/ui/pages/onboarding-flow/add-network-modal/index.js
index 9ee4f9d80c51..c031739b68b6 100644
--- a/ui/pages/onboarding-flow/add-network-modal/index.js
+++ b/ui/pages/onboarding-flow/add-network-modal/index.js
@@ -12,16 +12,24 @@ import {
TypographyVariant,
FONT_WEIGHT,
} from '../../../helpers/constants/design-system';
-
import NetworksForm from '../../settings/networks-tab/networks-form/networks-form';
-export default function AddNetworkModal({ showHeader = true }) {
+export default function AddNetworkModal({
+ showHeader = false,
+ isNewNetworkFlow = false,
+ addNewNetwork = true,
+ networkToEdit = null,
+}) {
const dispatch = useDispatch();
const t = useI18nContext();
const closeCallback = () =>
dispatch(hideModal({ name: 'ONBOARDING_ADD_NETWORK' }));
+ const additionalProps = networkToEdit
+ ? { selectedNetwork: networkToEdit }
+ : {};
+
return (
<>
{showHeader ? (
@@ -36,12 +44,14 @@ export default function AddNetworkModal({ showHeader = true }) {
) : null}
>
);
@@ -49,8 +59,14 @@ export default function AddNetworkModal({ showHeader = true }) {
AddNetworkModal.propTypes = {
showHeader: PropTypes.bool,
+ isNewNetworkFlow: PropTypes.bool,
+ addNewNetwork: PropTypes.bool,
+ networkToEdit: PropTypes.object,
};
AddNetworkModal.defaultProps = {
- showHeader: true,
+ showHeader: false,
+ isNewNetworkFlow: false,
+ addNewNetwork: true,
+ networkToEdit: null,
};
diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.js b/ui/pages/settings/networks-tab/networks-form/networks-form.js
index e66481453d32..54018aa0fe1e 100644
--- a/ui/pages/settings/networks-tab/networks-form/networks-form.js
+++ b/ui/pages/settings/networks-tab/networks-form/networks-form.js
@@ -10,6 +10,8 @@ import React, {
useState,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
+import { ORIGIN_METAMASK } from '@metamask/approval-controller';
+import { ApprovalType } from '@metamask/controller-utils';
import { isWebUrl } from '../../../../../app/scripts/lib/util';
import {
MetaMetricsEventCategory,
@@ -18,6 +20,7 @@ import {
} from '../../../../../shared/constants/metametrics';
import {
BUILT_IN_NETWORKS,
+ CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP,
CHAIN_IDS,
CHAINLIST_CURRENCY_SYMBOLS_MAP_NETWORK_COLLISION,
FEATURED_RPCS,
@@ -40,6 +43,7 @@ import { usePrevious } from '../../../../hooks/usePrevious';
import { useSafeChainsListValidationSelector } from '../../../../selectors';
import {
editAndSetNetworkConfiguration,
+ requestUserApproval,
setNewNetworkAdded,
setSelectedNetworkConfigurationId,
showDeprecatedNetworkModal,
@@ -617,6 +621,30 @@ const NetworksForm = ({
const onSubmit = async () => {
setIsSubmitting(true);
+ if (networkMenuRedesign && addNewNetwork) {
+ dispatch(toggleNetworkMenu());
+ await dispatch(
+ requestUserApproval({
+ origin: ORIGIN_METAMASK,
+ type: ApprovalType.AddEthereumChain,
+ requestData: {
+ chainId: prefixChainId(chainId),
+ rpcUrl,
+ ticker,
+ imageUrl:
+ CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[prefixChainId(chainId)] ?? '',
+ chainName: networkName,
+ rpcPrefs: {
+ ...rpcPrefs,
+ blockExplorerUrl: blockExplorerUrl || rpcPrefs?.blockExplorerUrl,
+ },
+ referrer: ORIGIN_METAMASK,
+ source: MetaMetricsNetworkEventSource.NewAddNetworkFlow,
+ },
+ }),
+ );
+ return;
+ }
try {
const formChainId = chainId.trim().toLowerCase();
const prefixedChainId = prefixChainId(formChainId);
@@ -890,7 +918,9 @@ const NetworksForm = ({
disabled={isSubmitDisabled}
onClick={() => {
onSubmit();
- dispatch(toggleNetworkMenu());
+ if (!networkMenuRedesign || !addNewNetwork) {
+ dispatch(toggleNetworkMenu());
+ }
}}
size={ButtonPrimarySize.Lg}
width={BlockSize.Full}
From 13afd6e7f8b1bbe2b5fb59e1ccf8950c37921898 Mon Sep 17 00:00:00 2001
From: salimtb
Date: Thu, 13 Jun 2024 15:09:30 +0200
Subject: [PATCH 04/14] feat: add toast after edit
---
app/_locales/en/messages.json | 3 +++
ui/ducks/app/app.ts | 9 +++++++
ui/pages/home/home.component.js | 26 +++++++++++++++++++
ui/pages/home/home.container.js | 6 +++++
.../networks-form/networks-form.js | 4 +++
ui/selectors/selectors.js | 4 +++
ui/selectors/selectors.test.js | 14 ++++++++++
ui/store/actionConstants.ts | 1 +
ui/store/actions.test.js | 19 ++++++++++++++
ui/store/actions.ts | 12 +++++++++
10 files changed, 98 insertions(+)
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 78f2f87ecb29..989f66071c41 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -2956,6 +2956,9 @@
"newNetworkAdded": {
"message": "“$1” was successfully added!"
},
+ "newNetworkEdited": {
+ "message": "“$1” was successfully edited!"
+ },
"newNftAddedMessage": {
"message": "NFT was successfully added!"
},
diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts
index 5c63ac25dcb7..db1e67f662c9 100644
--- a/ui/ducks/app/app.ts
+++ b/ui/ducks/app/app.ts
@@ -82,6 +82,7 @@ type AppState = {
newNftAddedMessage: string;
removeNftMessage: string;
newNetworkAddedName: string;
+ editedNetwork: string;
newNetworkAddedConfigurationId: string;
selectedNetworkConfigurationId: string;
sendInputCurrencySwitched: boolean;
@@ -164,6 +165,7 @@ const initialState: AppState = {
newNftAddedMessage: '',
removeNftMessage: '',
newNetworkAddedName: '',
+ editedNetwork: '',
newNetworkAddedConfigurationId: '',
selectedNetworkConfigurationId: '',
sendInputCurrencySwitched: false,
@@ -490,6 +492,13 @@ export default function reduceApp(
newNetworkAddedConfigurationId: networkConfigurationId,
};
}
+ case actionConstants.SET_EDIT_NETWORK: {
+ const { nickname } = action.payload;
+ return {
+ ...appState,
+ editedNetwork: nickname,
+ };
+ }
case actionConstants.SET_NEW_TOKENS_IMPORTED:
return {
...appState,
diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js
index aba0f4f57d59..00dc74464006 100644
--- a/ui/pages/home/home.component.js
+++ b/ui/pages/home/home.component.js
@@ -181,6 +181,7 @@ export default class Home extends PureComponent {
showOutdatedBrowserWarning: PropTypes.bool.isRequired,
setOutdatedBrowserWarningLastShown: PropTypes.func.isRequired,
newNetworkAddedName: PropTypes.string,
+ editedNetwork: PropTypes.string,
// This prop is used in the `shouldCloseNotificationPopup` function
// eslint-disable-next-line react/no-unused-prop-types
isSigningQRHardwareTransaction: PropTypes.bool.isRequired,
@@ -194,6 +195,7 @@ export default class Home extends PureComponent {
setNewTokensImported: PropTypes.func.isRequired,
setNewTokensImportedError: PropTypes.func.isRequired,
clearNewNetworkAdded: PropTypes.func,
+ clearEditedNetwork: PropTypes.func,
setActiveNetwork: PropTypes.func,
// eslint-disable-next-line react/no-unused-prop-types
setTokenAutodetectModal: PropTypes.func,
@@ -469,6 +471,7 @@ export default class Home extends PureComponent {
newNftAddedMessage,
setNewNftAddedMessage,
newNetworkAddedName,
+ editedNetwork,
removeNftMessage,
setRemoveNftMessage,
newTokensImported,
@@ -477,6 +480,7 @@ export default class Home extends PureComponent {
setNewTokensImportedError,
newNetworkAddedConfigurationId,
clearNewNetworkAdded,
+ clearEditedNetwork,
setActiveNetwork,
} = this.props;
@@ -591,6 +595,28 @@ export default class Home extends PureComponent {
}
/>
) : null}
+ {console.log('IM HERE ***********', editedNetwork)}
+ {editedNetwork ? (
+
+
+
+ {t('newNetworkEdited', [editedNetwork])}
+
+ clearEditedNetwork()}
+ className="home__new-network-notification-close"
+ />
+
+ }
+ />
+ ) : null}
{newTokensImported ? (
{
getIsBrowserDeprecated() && getShowOutdatedBrowserWarning(state),
seedPhraseBackedUp,
newNetworkAddedName: getNewNetworkAdded(state),
+ editedNetwork: getEditedNetwork(state),
isSigningQRHardwareTransaction: getIsSigningQRHardwareTransaction(state),
newNftAddedMessage: getNewNftAddedMessage(state),
removeNftMessage: getRemoveNftMessage(state),
@@ -266,6 +269,9 @@ const mapDispatchToProps = (dispatch) => {
clearNewNetworkAdded: () => {
dispatch(setNewNetworkAdded({}));
},
+ clearEditedNetwork: () => {
+ dispatch(setEditedNetwork({}));
+ },
setActiveNetwork: (networkConfigurationId) => {
dispatch(setActiveNetwork(networkConfigurationId));
},
diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.js b/ui/pages/settings/networks-tab/networks-form/networks-form.js
index 54018aa0fe1e..50ca91c31a18 100644
--- a/ui/pages/settings/networks-tab/networks-form/networks-form.js
+++ b/ui/pages/settings/networks-tab/networks-form/networks-form.js
@@ -44,6 +44,7 @@ import { useSafeChainsListValidationSelector } from '../../../../selectors';
import {
editAndSetNetworkConfiguration,
requestUserApproval,
+ setEditedNetwork,
setNewNetworkAdded,
setSelectedNetworkConfigurationId,
showDeprecatedNetworkModal,
@@ -704,6 +705,9 @@ const NetworksForm = ({
token_symbol: ticker,
},
});
+ if (networkMenuRedesign) {
+ dispatch(setEditedNetwork({ nickname: networkName }));
+ }
}
if (
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index 55c9576d1bce..d484ce026ab0 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -2036,6 +2036,10 @@ export function getNewNetworkAdded(state) {
return state.appState.newNetworkAddedName;
}
+export function getEditedNetwork(state) {
+ return state.appState.editedNetwork;
+}
+
export function getNetworksTabSelectedNetworkConfigurationId(state) {
return state.appState.selectedNetworkConfigurationId;
}
diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js
index f40a8497a86b..1287aff781b6 100644
--- a/ui/selectors/selectors.test.js
+++ b/ui/selectors/selectors.test.js
@@ -510,6 +510,20 @@ describe('Selectors', () => {
});
});
+ describe('#getEditedNetwork', () => {
+ it('returns undefined if getEditedNetwork is undefined', () => {
+ expect(selectors.getNewNetworkAdded({ appState: {} })).toBeUndefined();
+ });
+
+ it('returns getEditedNetwork', () => {
+ expect(
+ selectors.getEditedNetwork({
+ appState: { editedNetwork: 'test-chain' },
+ }),
+ ).toStrictEqual('test-chain');
+ });
+ });
+
describe('#getRpcPrefsForCurrentProvider', () => {
it('returns an empty object if state.metamask.providerConfig is empty', () => {
expect(
diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts
index 658e5b30c296..251196f70a73 100644
--- a/ui/store/actionConstants.ts
+++ b/ui/store/actionConstants.ts
@@ -118,6 +118,7 @@ export const SET_SHOW_TOKEN_AUTO_DETECT_MODAL_UPGRADE =
export const SET_SELECTED_NETWORK_CONFIGURATION_ID =
'SET_SELECTED_NETWORK_CONFIGURATION_ID';
export const SET_NEW_NETWORK_ADDED = 'SET_NEW_NETWORK_ADDED';
+export const SET_EDIT_NETWORK = 'SET_EDIT_NETWORK';
export const SET_NEW_NFT_ADDED_MESSAGE = 'SET_NEW_NFT_ADDED_MESSAGE';
export const SET_REMOVE_NFT_MESSAGE = 'SET_REMOVE_NFT_MESSAGE';
diff --git a/ui/store/actions.test.js b/ui/store/actions.test.js
index d153d1888018..813153d33ca3 100644
--- a/ui/store/actions.test.js
+++ b/ui/store/actions.test.js
@@ -1389,6 +1389,25 @@ describe('Actions', () => {
});
});
+ describe('#setEditedNetwork', () => {
+ it('sets appState.setEditedNetwork to provided value', async () => {
+ const store = mockStore();
+
+ const newNetworkAddedDetails = {
+ nickname: 'test-chain',
+ };
+
+ store.dispatch(actions.setEditedNetwork(newNetworkAddedDetails));
+
+ const resultantActions = store.getActions();
+
+ expect(resultantActions[0]).toStrictEqual({
+ type: 'SET_EDIT_NETWORK',
+ payload: newNetworkAddedDetails,
+ });
+ });
+ });
+
describe('#addToAddressBook', () => {
it('calls setAddressBook', async () => {
const store = mockStore();
diff --git a/ui/store/actions.ts b/ui/store/actions.ts
index c57776fd45ea..cc0dd1507bc3 100644
--- a/ui/store/actions.ts
+++ b/ui/store/actions.ts
@@ -4149,6 +4149,18 @@ export function setNewNetworkAdded({
};
}
+export function setEditedNetwork({
+ nickname,
+}: {
+ networkConfigurationId: string;
+ nickname: string;
+}): PayloadAction {
+ return {
+ type: actionConstants.SET_EDIT_NETWORK,
+ payload: { nickname },
+ };
+}
+
export function setNewNftAddedMessage(
newNftAddedMessage: string,
): PayloadAction {
From ab714b544c39810e7351e7282b4e5a6e51dd484b Mon Sep 17 00:00:00 2001
From: salimtb
Date: Thu, 13 Jun 2024 15:12:49 +0200
Subject: [PATCH 05/14] feat: add feature flag for menu
---
.../network-list-item/network-list-item.js | 53 ++++++++++++++-----
ui/pages/home/home.component.js | 5 +-
2 files changed, 43 insertions(+), 15 deletions(-)
diff --git a/ui/components/multichain/network-list-item/network-list-item.js b/ui/components/multichain/network-list-item/network-list-item.js
index 74cd19314729..4e76258584bc 100644
--- a/ui/components/multichain/network-list-item/network-list-item.js
+++ b/ui/components/multichain/network-list-item/network-list-item.js
@@ -1,6 +1,7 @@
import React, { useEffect, useRef, useState } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
+import { useSelector } from 'react-redux';
import {
AlignItems,
BackgroundColor,
@@ -10,6 +11,8 @@ import {
Display,
JustifyContent,
TextColor,
+ Size,
+ IconColor,
} from '../../../helpers/constants/design-system';
import {
AvatarNetwork,
@@ -23,6 +26,7 @@ import { useI18nContext } from '../../../hooks/useI18nContext';
import { getAvatarNetworkColor } from '../../../helpers/utils/accounts';
import Tooltip from '../../ui/tooltip/tooltip';
import { NetworkListItemMenu } from '../network-list-item-menu';
+import { getLocalNetworkMenuRedesignFeatureFlag } from '../../../helpers/utils/feature-flags';
const MAXIMUM_CHARACTERS_WITHOUT_TOOLTIP = 20;
@@ -43,9 +47,42 @@ export const NetworkListItem = ({
const setNetworkListItemMenuRef = (ref) => {
setNetworkListItemMenuElement(ref);
};
-
const [networkOptionsMenuOpen, setNetworkOptionsMenuOpen] = useState(false);
+ const networkMenuRedesign = useSelector(
+ getLocalNetworkMenuRedesignFeatureFlag,
+ );
+
+ const renderButton = () => {
+ if (networkMenuRedesign) {
+ return onDeleteClick || onEditClick ? (
+ {
+ e.stopPropagation();
+ setNetworkOptionsMenuOpen(true);
+ }}
+ size={ButtonIconSize.Sm}
+ />
+ ) : null;
+ }
+ return onDeleteClick ? (
+ {
+ e.stopPropagation();
+ onDeleteClick();
+ }}
+ />
+ ) : null;
+ };
useEffect(() => {
if (networkRef.current && focus) {
networkRef.current.focus();
@@ -112,19 +149,7 @@ export const NetworkListItem = ({
)}
- {onDeleteClick || onEditClick ? (
- {
- e.stopPropagation();
- setNetworkOptionsMenuOpen(true);
- }}
- size={ButtonIconSize.Sm}
- />
- ) : null}
+ {renderButton()}
) : null}
- {console.log('IM HERE ***********', editedNetwork)}
{editedNetwork ? (
From d22da9ff6af3e40566cb3a6deb48f8070763e24b Mon Sep 17 00:00:00 2001
From: salimtb
Date: Thu, 20 Jun 2024 11:16:27 +0200
Subject: [PATCH 06/14] fix: fix suggested name form
---
.../networks-form/networks-form.js | 118 +++++++++++++++++-
1 file changed, 117 insertions(+), 1 deletion(-)
diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.js b/ui/pages/settings/networks-tab/networks-form/networks-form.js
index 50ca91c31a18..00d967c43cc0 100644
--- a/ui/pages/settings/networks-tab/networks-form/networks-form.js
+++ b/ui/pages/settings/networks-tab/networks-form/networks-form.js
@@ -73,6 +73,7 @@ import {
} from '../../../../helpers/constants/design-system';
import {
getMatchedChain,
+ getMatchedNames,
getMatchedSymbols,
} from '../../../../helpers/utils/network-helper';
import { getLocalNetworkMenuRedesignFeatureFlag } from '../../../../helpers/utils/feature-flags';
@@ -123,6 +124,7 @@ const NetworksForm = ({
const t = useI18nContext();
const dispatch = useDispatch();
const DEFAULT_SUGGESTED_TICKER = [];
+ const DEFAULT_SUGGESTED_NAME = [];
const { label, labelKey, viewOnly, rpcPrefs } = selectedNetwork;
const selectedNetworkName =
label || (labelKey && t(getNetworkLabelKey(labelKey)));
@@ -144,6 +146,7 @@ const NetworksForm = ({
);
const [isEditing, setIsEditing] = useState(Boolean(addNewNetwork));
const [previousNetwork, setPreviousNetwork] = useState(selectedNetwork);
+ const [suggestedNames, setSuggestedNames] = useState(DEFAULT_SUGGESTED_NAME);
const trackEvent = useContext(MetaMetricsContext);
@@ -205,6 +208,7 @@ const NetworksForm = ({
setErrors({});
setWarnings({});
setSuggestedTicker([]);
+ setSuggestedNames([]);
setIsSubmitting(false);
setIsEditing(false);
setPreviousNetwork(selectedNetwork);
@@ -326,6 +330,33 @@ const NetworksForm = ({
setSuggestedTicker([...matchedSymbol]);
}, []);
+ const autoSuggestName = useCallback((formChainId) => {
+ const decimalChainId = getDisplayChainId(formChainId);
+ if (decimalChainId.trim() === '' || safeChainsList.current.length === 0) {
+ setSuggestedNames([]);
+ return;
+ }
+ const matchedChain = safeChainsList.current?.find(
+ (chain) => chain.chainId.toString() === decimalChainId,
+ );
+
+ const matchedNames = safeChainsList.current?.reduce(
+ (accumulator, currentNetwork) => {
+ if (currentNetwork.chainId.toString() === decimalChainId) {
+ accumulator.push(currentNetwork?.name);
+ }
+ return accumulator;
+ },
+ [],
+ );
+
+ if (matchedChain === undefined) {
+ setSuggestedNames([]);
+ return;
+ }
+ setSuggestedNames([...matchedNames]);
+ }, []);
+
const hasErrors = () => {
return Object.keys(errors).some((key) => {
const error = errors[key];
@@ -466,6 +497,7 @@ const NetworksForm = ({
};
}
autoSuggestTicker(formChainId);
+ autoSuggestName(formChainId);
return null;
},
[rpcUrl, networksToRender, t],
@@ -528,6 +560,57 @@ const NetworksForm = ({
[t],
);
+ const validateNetworkName = useCallback(
+ async (formChainId, formName) => {
+ let warningKey;
+ let warningMessage;
+ const decimalChainId = getDisplayChainId(formChainId);
+
+ if (!decimalChainId || !formName) {
+ setSuggestedNames([]);
+ return null;
+ }
+
+ if (safeChainsList.current.length === 0) {
+ warningKey = 'failedToFetchTickerSymbolData';
+ warningMessage = t('failedToFetchTickerSymbolData');
+ } else {
+ const matchedChain = getMatchedChain(
+ decimalChainId,
+ safeChainsList.current,
+ );
+
+ const matchedNames = getMatchedNames(
+ decimalChainId,
+ safeChainsList.current,
+ );
+ setSuggestedNames([...matchedNames]);
+
+ if (matchedChain === undefined) {
+ warningKey = 'failedToFetchTickerSymbolData';
+ warningMessage = t('failedToFetchTickerSymbolData');
+ } else if (
+ !matchedNames.some(
+ (name) => name.toLowerCase() === formName.toLowerCase(),
+ )
+ ) {
+ warningKey = 'wrongNetworkName';
+ warningMessage = t('wrongNetworkName');
+ }
+ }
+
+ if (warningKey) {
+ return {
+ key: warningKey,
+ msg: warningMessage,
+ };
+ }
+
+ return null;
+ },
+ [t],
+ );
+
const validateRPCUrl = useCallback(
(url) => {
const [
@@ -585,6 +668,7 @@ const NetworksForm = ({
const { error: chainIdError, warning: chainIdWarning } =
(await validateChainId(chainId)) || {};
const tickerWarning = await validateTickerSymbol(chainId, ticker);
+ const nameWarning = await validateNetworkName(chainId, networkName);
const blockExplorerError = validateBlockExplorerURL(blockExplorerUrl);
const rpcUrlError = validateRPCUrl(rpcUrl);
setErrors({
@@ -597,6 +681,7 @@ const NetworksForm = ({
...warnings,
chainId: chainIdWarning,
ticker: tickerWarning,
+ networkName: nameWarning,
});
}
@@ -609,6 +694,7 @@ const NetworksForm = ({
ticker,
blockExplorerUrl,
viewOnly,
+ networkName,
label,
previousRpcUrl,
previousChainId,
@@ -618,6 +704,7 @@ const NetworksForm = ({
validateChainId,
validateTickerSymbol,
validateRPCUrl,
+ validateNetworkName,
]);
const onSubmit = async () => {
@@ -806,7 +893,36 @@ const NetworksForm = ({
disabled={viewOnly}
dataTestId="network-form-network-name"
/>
- {window.metamaskFeatureFlags?.networkMenuRedesign ? (
+ {suggestedNames &&
+ !suggestedNames.some(
+ (nameSuggested) => nameSuggested === networkName,
+ ) ? (
+
+ {t('suggestedTokenName')}
+ {suggestedNames.map((suggestedName, i) => (
+ {
+ setNetworkName(suggestedName);
+ }}
+ paddingLeft={1}
+ paddingRight={1}
+ style={{ verticalAlign: 'baseline' }}
+ key={i}
+ >
+ {suggestedName}
+
+ ))}
+
+ ) : null}
+ {networkMenuRedesign ? (
) : (
Date: Thu, 20 Jun 2024 11:40:01 +0200
Subject: [PATCH 07/14] fix: fix toast css
---
ui/pages/home/home.component.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js
index d92f92476073..d43616dc230d 100644
--- a/ui/pages/home/home.component.js
+++ b/ui/pages/home/home.component.js
@@ -85,7 +85,6 @@ import BetaHomeFooter from './beta/beta-home-footer.component';
///: END:ONLY_INCLUDE_IF
///: BEGIN:ONLY_INCLUDE_IF(build-flask)
import FlaskHomeFooter from './flask/flask-home-footer.component';
-import { setEditedNetwork } from '../../store/actions';
///: END:ONLY_INCLUDE_IF
function shouldCloseNotificationPopup({
@@ -490,7 +489,7 @@ export default class Home extends PureComponent {
setRemoveNftMessage('');
setNewTokensImported(''); // Added this so we dnt see the notif if user does not close it
setNewTokensImportedError('');
- setEditedNetwork({});
+ clearEditedNetwork({});
};
const autoHideDelay = 5 * SECOND;
@@ -600,7 +599,7 @@ export default class Home extends PureComponent {
{editedNetwork ? (
Date: Thu, 20 Jun 2024 11:45:25 +0200
Subject: [PATCH 08/14] fix: fix PR comments
---
.../network-list-item-menu.js | 37 ++-----------------
1 file changed, 3 insertions(+), 34 deletions(-)
diff --git a/ui/components/multichain/network-list-item-menu/network-list-item-menu.js b/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
index 6618cee57610..52a579513ffa 100644
--- a/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
+++ b/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
@@ -29,9 +29,7 @@ export const NetworkListItemMenu = ({
// Checks the MenuItems from the bottom to top to set lastItemRef on the last MenuItem that is not disabled
useEffect(() => {
- if (removeJWTItemRef.current) {
- lastItemRef.current = removeJWTItemRef.current;
- } else if (removeAccountItemRef.current) {
+ if (removeAccountItemRef.current) {
lastItemRef.current = removeAccountItemRef.current;
} else {
lastItemRef.current = accountDetailsItemRef.current;
@@ -43,42 +41,13 @@ export const NetworkListItemMenu = ({
accountDetailsItemRef.current,
]);
- const handleKeyDown = useCallback(
- (event) => {
- if (event.key === 'Tab' && event.target === lastItemRef.current) {
- // If Tab is pressed at the last item to close popover and focus to next element in DOM
- onClose();
- }
- },
- [onClose],
- );
-
// Handle click outside of the popover to close it
const popoverDialogRef = useRef(null);
- const handleClickOutside = useCallback(
- (event) => {
- if (
- popoverDialogRef?.current &&
- !popoverDialogRef.current.contains(event.target)
- ) {
- onClose();
- }
- },
- [onClose],
- );
-
- useEffect(() => {
- document.addEventListener('mousedown', handleClickOutside);
-
- return () => {
- document.removeEventListener('mousedown', handleClickOutside);
- };
- }, [handleClickOutside]);
-
return (
-
+
{onEditClick ? (
Date: Thu, 20 Jun 2024 12:01:42 +0200
Subject: [PATCH 09/14] fix: fix suggested name display
---
app/_locales/en/messages.json | 3 +++
ui/pages/settings/networks-tab/networks-form/networks-form.js | 2 ++
2 files changed, 5 insertions(+)
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 989f66071c41..6307c797e954 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -5246,6 +5246,9 @@
"message": "Suggested by $1",
"description": "$1 is the snap name"
},
+ "suggestedTokenName": {
+ "message": "Suggested name:"
+ },
"suggestedTokenSymbol": {
"message": "Suggested ticker symbol:"
},
diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.js b/ui/pages/settings/networks-tab/networks-form/networks-form.js
index 00d967c43cc0..28d89a8b39ab 100644
--- a/ui/pages/settings/networks-tab/networks-form/networks-form.js
+++ b/ui/pages/settings/networks-tab/networks-form/networks-form.js
@@ -894,6 +894,7 @@ const NetworksForm = ({
dataTestId="network-form-network-name"
/>
{suggestedNames &&
+ suggestedNames.length > 0 &&
!suggestedNames.some(
(nameSuggested) => nameSuggested === networkName,
) ? (
@@ -955,6 +956,7 @@ const NetworksForm = ({
data-testid="network-form-ticker"
helpText={
suggestedTicker &&
+ suggestedTicker.length > 0 &&
!suggestedTicker.some(
(symbolSuggested) => symbolSuggested === ticker,
) ? (
From 2ef606ab31558b7aff4ce9ebac41c06050c8ab77 Mon Sep 17 00:00:00 2001
From: salimtb
Date: Thu, 20 Jun 2024 13:23:02 +0200
Subject: [PATCH 10/14] fix: fix linter + tests
---
.../network-list-item-menu.js | 2 +-
.../network-list-item.test.js.snap | 7 ++-
.../network-list-item.test.js | 44 +++++++++++++++
.../network-list-menu/network-list-menu.js | 35 +++++++++---
.../add-network-modal.test.js.snap | 54 ++++++-------------
5 files changed, 91 insertions(+), 51 deletions(-)
diff --git a/ui/components/multichain/network-list-item-menu/network-list-item-menu.js b/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
index 52a579513ffa..a1a0f9ad6632 100644
--- a/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
+++ b/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect, useRef } from 'react';
+import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { useI18nContext } from '../../../hooks/useI18nContext';
import {
diff --git a/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap b/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap
index 4d3f902a14de..df225a439aed 100644
--- a/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap
+++ b/ui/components/multichain/network-list-item/__snapshots__/network-list-item.test.js.snap
@@ -26,13 +26,12 @@ exports[`NetworkListItem renders properly 1`] = `
diff --git a/ui/components/multichain/network-list-item/network-list-item.test.js b/ui/components/multichain/network-list-item/network-list-item.test.js
index 994e77bc18b7..323e432e0e33 100644
--- a/ui/components/multichain/network-list-item/network-list-item.test.js
+++ b/ui/components/multichain/network-list-item/network-list-item.test.js
@@ -1,10 +1,12 @@
/* eslint-disable jest/require-top-level-describe */
import React from 'react';
import { fireEvent, render } from '@testing-library/react';
+import { useSelector } from 'react-redux';
import {
MATIC_TOKEN_IMAGE_URL,
POLYGON_DISPLAY_NAME,
} from '../../../../shared/constants/network';
+import { getLocalNetworkMenuRedesignFeatureFlag } from '../../../helpers/utils/feature-flags';
import { NetworkListItem } from '.';
const DEFAULT_PROPS = {
@@ -15,13 +17,35 @@ const DEFAULT_PROPS = {
onDeleteClick: () => undefined,
};
+jest.mock('react-redux', () => ({
+ ...jest.requireActual('react-redux'),
+ useSelector: jest.fn(),
+}));
+
+const generateUseSelectorRouter = (opts) => (selector) => {
+ if (selector === getLocalNetworkMenuRedesignFeatureFlag) {
+ return opts.networkMenuRedesign ?? false;
+ }
+ return undefined;
+};
+
describe('NetworkListItem', () => {
it('renders properly', () => {
+ useSelector.mockImplementation(
+ generateUseSelectorRouter({
+ networkMenuRedesign: false,
+ }),
+ );
const { container } = render( );
expect(container).toMatchSnapshot();
});
it('does not render the delete icon when no onDeleteClick is clicked', () => {
+ useSelector.mockImplementation(
+ generateUseSelectorRouter({
+ networkMenuRedesign: false,
+ }),
+ );
const { container } = render(
,
);
@@ -31,6 +55,11 @@ describe('NetworkListItem', () => {
});
it('shows as selected when selected', () => {
+ useSelector.mockImplementation(
+ generateUseSelectorRouter({
+ networkMenuRedesign: false,
+ }),
+ );
const { container } = render(
,
);
@@ -42,6 +71,11 @@ describe('NetworkListItem', () => {
});
it('renders a tooltip when the network name is very long', () => {
+ useSelector.mockImplementation(
+ generateUseSelectorRouter({
+ networkMenuRedesign: false,
+ }),
+ );
const { container } = render(
{
});
it('executes onClick when the item is clicked', () => {
+ useSelector.mockImplementation(
+ generateUseSelectorRouter({
+ networkMenuRedesign: false,
+ }),
+ );
const onClick = jest.fn();
const { container } = render(
,
@@ -63,6 +102,11 @@ describe('NetworkListItem', () => {
});
it('executes onDeleteClick when the delete button is clicked', () => {
+ useSelector.mockImplementation(
+ generateUseSelectorRouter({
+ networkMenuRedesign: true,
+ }),
+ );
const onDeleteClick = jest.fn();
const onClick = jest.fn();
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 5b3f2dadbc9f..2df12ec8605d 100644
--- a/ui/components/multichain/network-list-menu/network-list-menu.js
+++ b/ui/components/multichain/network-list-menu/network-list-menu.js
@@ -157,6 +157,7 @@ export const NetworkListMenu = ({ onClose }) => {
}, [dispatch, currentlyOnTestNetwork]);
const [searchQuery, setSearchQuery] = useState('');
+ const [focusSearch, setFocusSearch] = useState(false);
const onboardedInThisUISession = useSelector(getOnboardedInThisUISession);
const showNetworkBanner = useSelector(getShowNetworkBanner);
const showBanner =
@@ -190,9 +191,9 @@ export const NetworkListMenu = ({ onClose }) => {
? items
: [...notExistingNetworkConfigurations];
- const isSearching = searchQuery !== '';
+ let searchTestNetworkResults = [...testNetworks];
- if (isSearching) {
+ if (focusSearch && searchQuery !== '') {
const fuse = new Fuse(searchResults, {
threshold: 0.2,
location: 0,
@@ -212,11 +213,25 @@ export const NetworkListMenu = ({ onClose }) => {
keys: ['nickname', 'chainId', 'ticker'],
});
+ const fuseForTestsNetworks = new Fuse(searchTestNetworkResults, {
+ threshold: 0.2,
+ location: 0,
+ distance: 100,
+ maxPatternLength: 32,
+ minMatchCharLength: 1,
+ shouldSort: true,
+ keys: ['nickname', 'chainId', 'ticker'],
+ });
+
fuse.setCollection(searchResults);
fuseForPopularNetworks.setCollection(searchAddNetworkResults);
+ fuseForTestsNetworks.setCollection(searchTestNetworkResults);
+
const fuseResults = fuse.search(searchQuery);
const fuseForPopularNetworksResults =
fuseForPopularNetworks.search(searchQuery);
+ const fuseForTestsNetworksResults =
+ fuseForTestsNetworks.search(searchQuery);
searchResults = searchResults.filter((network) =>
fuseResults.includes(network),
@@ -224,6 +239,9 @@ export const NetworkListMenu = ({ onClose }) => {
searchAddNetworkResults = searchAddNetworkResults.filter((network) =>
fuseForPopularNetworksResults.includes(network),
);
+ searchTestNetworkResults = searchTestNetworkResults.filter((network) =>
+ fuseForTestsNetworksResults.includes(network),
+ );
}
const getOnDeleteCallback = (networkId) => {
@@ -265,8 +283,8 @@ export const NetworkListMenu = ({ onClose }) => {
name={network.nickname}
iconSrc={network?.rpcPrefs?.imageUrl}
key={network.id}
- selected={isCurrentNetwork}
- focus={isCurrentNetwork && !isSearching}
+ selected={isCurrentNetwork && !focusSearch}
+ focus={isCurrentNetwork && !focusSearch}
onClick={() => {
dispatch(toggleNetworkMenu());
if (network.providerType) {
@@ -312,6 +330,7 @@ export const NetworkListMenu = ({ onClose }) => {
@@ -339,7 +358,7 @@ export const NetworkListMenu = ({ onClose }) => {
/>
) : null}
- {searchResults.length === 0 && isSearching ? (
+ {searchResults.length === 0 && focusSearch ? (
{
name={network.nickname}
iconSrc={network?.rpcPrefs?.imageUrl}
key={network.id}
- selected={isCurrentNetwork}
- focus={isCurrentNetwork && !isSearching}
+ selected={isCurrentNetwork && !focusSearch}
+ focus={isCurrentNetwork && !focusSearch}
onClick={() => {
dispatch(toggleNetworkMenu());
if (network.providerType) {
@@ -458,7 +477,7 @@ export const NetworkListMenu = ({ onClose }) => {
{showTestNetworks || currentlyOnTestNetwork ? (
- {generateMenuItems(testNetworks)}
+ {generateMenuItems(searchTestNetworkResults)}
) : null}
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 e422b5d8216b..05128f7c75ca 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
@@ -67,36 +67,26 @@ exports[`Add Network Modal should render 1`] = `
+
+ Default RPC URL
+
-
-
- Suggested ticker symbol:
-
-
`;
-
-exports[`Add Network Modal should render 2`] = `
`;
From 667e58a533146ab25b0dd3d9192e8f81104ef94b Mon Sep 17 00:00:00 2001
From: salimtb
Date: Thu, 20 Jun 2024 14:33:49 +0200
Subject: [PATCH 11/14] fix: add enabled network section
---
app/_locales/en/messages.json | 3 +++
.../network-list-item-menu.js | 27 ++-----------------
.../network-list-menu/network-list-menu.js | 9 +++++++
.../networks-form/networks-form.js | 4 ++-
4 files changed, 17 insertions(+), 26 deletions(-)
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 6307c797e954..ed694b49e987 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -6356,6 +6356,9 @@
"whatsThis": {
"message": "What's this?"
},
+ "wrongNetworkName": {
+ "message": "According to our records, the network name may not correctly match this chain ID."
+ },
"xOfYPending": {
"message": "$1 of $2 pending",
"description": "$1 and $2 are intended to be two numbers, where $2 is a total number of pending confirmations, and $1 is a count towards that total"
diff --git a/ui/components/multichain/network-list-item-menu/network-list-item-menu.js b/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
index a1a0f9ad6632..51a77c851a72 100644
--- a/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
+++ b/ui/components/multichain/network-list-item-menu/network-list-item-menu.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef } from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
import { useI18nContext } from '../../../hooks/useI18nContext';
import {
@@ -21,29 +21,6 @@ export const NetworkListItemMenu = ({
}) => {
const t = useI18nContext();
- // Handle Tab key press for accessibility inside the popover and will close the popover on the last MenuItem
- const lastItemRef = useRef(null);
- const accountDetailsItemRef = useRef(null);
- const removeAccountItemRef = useRef(null);
- const removeJWTItemRef = useRef(null);
-
- // Checks the MenuItems from the bottom to top to set lastItemRef on the last MenuItem that is not disabled
- useEffect(() => {
- if (removeAccountItemRef.current) {
- lastItemRef.current = removeAccountItemRef.current;
- } else {
- lastItemRef.current = accountDetailsItemRef.current;
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [
- removeJWTItemRef.current,
- removeAccountItemRef.current,
- accountDetailsItemRef.current,
- ]);
-
- // Handle click outside of the popover to close it
- const popoverDialogRef = useRef(null);
-
return (
-
+
{onEditClick ? (
{
/>
) : null}
+
+ {t('enabledNetworks')}
+
{searchResults.length === 0 && focusSearch ? (
name.toLowerCase() === formName.toLowerCase(),
+ (name) => name?.toLowerCase() === formName.toLowerCase(),
)
) {
warningKey = 'wrongNetworkName';
@@ -671,12 +671,14 @@ const NetworksForm = ({
const nameWarning = await validateNetworkName(chainId, networkName);
const blockExplorerError = validateBlockExplorerURL(blockExplorerUrl);
const rpcUrlError = validateRPCUrl(rpcUrl);
+
setErrors({
...errors,
blockExplorerUrl: blockExplorerError,
rpcUrl: rpcUrlError,
chainId: chainIdError,
});
+
setWarnings({
...warnings,
chainId: chainIdWarning,
From a2deb402578416aaedd080e4dc5e1f2563e68047 Mon Sep 17 00:00:00 2001
From: Brian Bergeron
Date: Wed, 19 Jun 2024 20:36:11 -0700
Subject: [PATCH 12/14] merge flow for adding / deleting additional RPC URLs
---
app/_locales/en/messages.json | 18 +++++
.../confirm-delete-rpc-url-modal.tsx | 72 +++++++++++++++++
ui/components/app/modals/modal.js | 14 ++++
.../add-rpc-url-modal/add-rpc-url-modal.tsx | 43 ++++++++++
.../network-list-menu/network-list-menu.js | 80 +++++++++++++------
ui/ducks/app/app.ts | 13 ++-
ui/pages/home/home.component.js | 9 ++-
ui/pages/home/home.container.js | 2 +-
.../add-network-modal/index.js | 3 +
ui/pages/routes/routes.component.js | 9 ++-
ui/pages/routes/routes.container.js | 2 +
ui/pages/settings/networks-tab/index.scss | 2 +
.../networks-form/networks-form.js | 25 +++++-
.../networks-form/rpc-url-editor.tsx | 55 +++++++++----
ui/selectors/selectors.js | 4 +
ui/store/actions.test.js | 2 +
ui/store/actions.ts | 20 ++---
17 files changed, 312 insertions(+), 61 deletions(-)
create mode 100644 ui/components/app/modals/confirm-delete-rpc-url-modal/confirm-delete-rpc-url-modal.tsx
create mode 100644 ui/components/multichain/network-list-menu/add-rpc-url-modal/add-rpc-url-modal.tsx
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index ed694b49e987..33bcfdc1e5f0 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -311,6 +311,9 @@
"message": "Can’t find a token? You can manually add any token by pasting its address. Token contract addresses can be found on $1",
"description": "$1 is a blockchain explorer for a specific network, e.g. Etherscan for Ethereum"
},
+ "addUrl": {
+ "message": "Add URL"
+ },
"addingCustomNetwork": {
"message": "Adding Network"
},
@@ -320,6 +323,9 @@
"additionalNetworks": {
"message": "Additional networks"
},
+ "additionalRpcUrl": {
+ "message": "Additional RPC URL"
+ },
"address": {
"message": "Address"
},
@@ -900,12 +906,18 @@
"confirmConnectionTitle": {
"message": "Confirm connection to $1"
},
+ "confirmDeletion": {
+ "message": "Confirm deletion"
+ },
"confirmPassword": {
"message": "Confirm password"
},
"confirmRecoveryPhrase": {
"message": "Confirm Secret Recovery Phrase"
},
+ "confirmRpcUrlDeletionMessage": {
+ "message": "Are you sure you want to delete the RPC URL? Your information will not be saved for this network."
+ },
"confirmTitleDescContractInteractionTransaction": {
"message": "Only confirm this transaction if you fully understand the content and trust the requesting site."
},
@@ -1385,6 +1397,9 @@
"message": "Delete $1 network?",
"description": "$1 represents the name of the network"
},
+ "deleteRpcUrl": {
+ "message": "Delete RPC URL"
+ },
"deposit": {
"message": "Deposit"
},
@@ -1706,6 +1721,9 @@
"enterPasswordContinue": {
"message": "Enter password to continue"
},
+ "enterRpcUrl": {
+ "message": "Enter RPC URL"
+ },
"enterTokenNameOrAddress": {
"message": "Enter token name or paste address"
},
diff --git a/ui/components/app/modals/confirm-delete-rpc-url-modal/confirm-delete-rpc-url-modal.tsx b/ui/components/app/modals/confirm-delete-rpc-url-modal/confirm-delete-rpc-url-modal.tsx
new file mode 100644
index 000000000000..3eb9a9323048
--- /dev/null
+++ b/ui/components/app/modals/confirm-delete-rpc-url-modal/confirm-delete-rpc-url-modal.tsx
@@ -0,0 +1,72 @@
+import React from 'react';
+import { useDispatch } from 'react-redux';
+import {
+ BlockSize,
+ Display,
+} from '../../../../helpers/constants/design-system';
+import {
+ Box,
+ ButtonPrimary,
+ ButtonPrimarySize,
+ ButtonSecondary,
+ ButtonSecondarySize,
+ Modal,
+ ModalBody,
+ ModalContent,
+ ModalHeader,
+ ModalOverlay,
+} from '../../../component-library';
+import { useI18nContext } from '../../../../hooks/useI18nContext';
+import {
+ hideModal,
+ setEditedNetwork,
+ toggleNetworkMenu,
+} from '../../../../store/actions';
+
+const ConfirmDeleteRpcUrlModal = () => {
+ const t = useI18nContext();
+ const dispatch = useDispatch();
+ return (
+ {
+ dispatch(setEditedNetwork());
+ dispatch(hideModal());
+ }}
+ >
+
+
+ {t('confirmDeletion')}
+
+ {t('confirmRpcUrlDeletionMessage')}
+
+ {
+ dispatch(hideModal());
+ dispatch(toggleNetworkMenu());
+ }}
+ >
+ {t('back')}
+
+ {
+ console.log('TODO: Delete RPc URL');
+ }}
+ >
+ {t('deleteRpcUrl')}
+
+
+
+
+
+ );
+};
+
+export default ConfirmDeleteRpcUrlModal;
diff --git a/ui/components/app/modals/modal.js b/ui/components/app/modals/modal.js
index f3eb1a950a40..9d546e5de74d 100644
--- a/ui/components/app/modals/modal.js
+++ b/ui/components/app/modals/modal.js
@@ -38,6 +38,7 @@ import TransactionAlreadyConfirmed from './transaction-already-confirmed';
// Metamask Notifications
import ConfirmTurnOffProfileSyncing from './confirm-turn-off-profile-syncing';
import TurnOnMetamaskNotifications from './turn-on-metamask-notifications/turn-on-metamask-notifications';
+import ConfirmDeleteRpcUrlModal from './confirm-delete-rpc-url-modal/confirm-delete-rpc-url-modal';
const modalContainerBaseStyle = {
transform: 'translate3d(-50%, 0, 0px)',
@@ -230,6 +231,19 @@ const MODALS = {
},
},
+ CONFIRM_DELETE_RPC_URL: {
+ contents: ,
+ mobileModalStyle: {
+ ...modalContainerMobileStyle,
+ },
+ laptopModalStyle: {
+ ...modalContainerLaptopStyle,
+ },
+ contentStyle: {
+ borderRadius: '8px',
+ },
+ },
+
EDIT_APPROVAL_PERMISSION: {
contents: ,
mobileModalStyle: {
diff --git a/ui/components/multichain/network-list-menu/add-rpc-url-modal/add-rpc-url-modal.tsx b/ui/components/multichain/network-list-menu/add-rpc-url-modal/add-rpc-url-modal.tsx
new file mode 100644
index 000000000000..8e4269928bc8
--- /dev/null
+++ b/ui/components/multichain/network-list-menu/add-rpc-url-modal/add-rpc-url-modal.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import {
+ Box,
+ ButtonPrimary,
+ ButtonPrimarySize,
+ FormTextField,
+} from '../../../component-library';
+import {
+ BlockSize,
+ Display,
+ TextVariant,
+} from '../../../../helpers/constants/design-system';
+import { useI18nContext } from '../../../../hooks/useI18nContext';
+
+const AddRpcUrlModal = () => {
+ const t = useI18nContext();
+
+ return (
+
+
+
+
+ {t('addUrl')}
+
+
+ );
+};
+
+export default AddRpcUrlModal;
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 c481b163f8a0..3a1f279103d4 100644
--- a/ui/components/multichain/network-list-menu/network-list-menu.js
+++ b/ui/components/multichain/network-list-menu/network-list-menu.js
@@ -1,4 +1,4 @@
-import React, { useContext, useEffect, useState } from 'react';
+import React, { useContext, useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { useDispatch, useSelector } from 'react-redux';
@@ -15,6 +15,7 @@ import {
toggleNetworkMenu,
updateNetworksList,
setNetworkClientIdForDomain,
+ setEditedNetwork,
} from '../../../store/actions';
import {
FEATURED_RPCS,
@@ -32,6 +33,7 @@ import {
getOriginOfCurrentTab,
getUseRequestQueue,
getNetworkConfigurations,
+ getEditedNetwork,
} from '../../../selectors';
import ToggleButton from '../../ui/toggle-button';
import {
@@ -70,6 +72,7 @@ import { getLocalNetworkMenuRedesignFeatureFlag } from '../../../helpers/utils/f
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';
+import AddRpcUrlModal from './add-rpc-url-modal/add-rpc-url-modal';
const ACTION_MODES = {
// Displays the search box and network list
@@ -78,14 +81,13 @@ const ACTION_MODES = {
ADD: 'add',
// Displays the Edit form
EDIT: 'edit',
+ // Displays the page for adding an additional RPC URL
+ ADD_RPC: 'add_rpc',
};
export const NetworkListMenu = ({ onClose }) => {
const t = useI18nContext();
- const [actionMode, setActionMode] = useState(ACTION_MODES.LIST);
- const [modalTitle, setModalTitle] = useState(t('networkMenuHeading'));
- const [networkToEdit, setNetworkToEdit] = useState(null);
const nonTestNetworks = useSelector(getNonTestNetworks);
const testNetworks = useSelector(getTestNetworks);
const showTestNetworks = useSelector(getShowTestNetworks);
@@ -114,6 +116,19 @@ export const NetworkListMenu = ({ onClose }) => {
const orderedNetworksList = useSelector(getOrderedNetworksList);
+ const editedNetwork = useSelector(getEditedNetwork);
+
+ const [actionMode, setActionMode] = useState(
+ editedNetwork ? ACTION_MODES.EDIT : ACTION_MODES.LIST,
+ );
+
+ const networkToEdit = useMemo(() => {
+ const network = [...nonTestNetworks, ...testNetworks].find(
+ (n) => n.id === editedNetwork?.networkConfigurationId,
+ );
+ return network ? { ...network, label: network.nickname } : undefined;
+ }, [editedNetwork, nonTestNetworks, testNetworks]);
+
const networkConfigurationChainIds = Object.values(networkConfigurations).map(
(net) => net.chainId,
);
@@ -259,12 +274,12 @@ export const NetworkListMenu = ({ onClose }) => {
const getOnEditCallback = (network) => {
return () => {
- const networkToUse = {
- ...network,
- label: network.nickname,
- };
- setModalTitle(network.nickname);
- setNetworkToEdit(networkToUse);
+ dispatch(
+ setEditedNetwork({
+ networkConfigurationId: network.id,
+ nickname: network.nickname,
+ }),
+ );
setActionMode(ACTION_MODES.EDIT);
};
};
@@ -518,7 +533,6 @@ export const NetworkListMenu = ({ onClose }) => {
category: MetaMetricsEventCategory.Network,
});
setActionMode(ACTION_MODES.ADD);
- setModalTitle(t('addCustomNetwork'));
}}
>
{t('addNetwork')}
@@ -528,20 +542,38 @@ export const NetworkListMenu = ({ onClose }) => {
);
} else if (actionMode === ACTION_MODES.ADD) {
return ;
+ } else if (actionMode === ACTION_MODES.EDIT) {
+ return (
+ setActionMode(ACTION_MODES.ADD_RPC)}
+ />
+ );
+ } else if (actionMode === ACTION_MODES.ADD_RPC) {
+ return ;
}
- return (
-
- );
+ return null; // Unreachable, but satisfies linter
};
- const headerAdditionalProps =
- actionMode === ACTION_MODES.LIST
- ? {}
- : { onBack: () => setActionMode(ACTION_MODES.LIST) };
+ // Modal back button
+ let onBack;
+ if (actionMode === ACTION_MODES.EDIT || actionMode === ACTION_MODES.ADD) {
+ onBack = () => setActionMode(ACTION_MODES.LIST);
+ } else if (actionMode === ACTION_MODES.ADD_RPC) {
+ onBack = () => setActionMode(ACTION_MODES.EDIT);
+ }
+
+ // Modal title
+ let title;
+ if (actionMode === ACTION_MODES.LIST) {
+ title = t('networkMenuHeading');
+ } else if (actionMode === ACTION_MODES.ADD) {
+ title = t('addCustomNetwork');
+ } else {
+ title = editedNetwork.nickname;
+ }
return (
@@ -560,9 +592,9 @@ export const NetworkListMenu = ({ onClose }) => {
paddingRight={4}
paddingBottom={6}
onClose={onClose}
- {...headerAdditionalProps}
+ onBack={onBack}
>
- {modalTitle}
+ {title}
{renderListNetworks()}
diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts
index db1e67f662c9..1242ac84cbc8 100644
--- a/ui/ducks/app/app.ts
+++ b/ui/ducks/app/app.ts
@@ -82,7 +82,13 @@ type AppState = {
newNftAddedMessage: string;
removeNftMessage: string;
newNetworkAddedName: string;
- editedNetwork: string;
+ editedNetwork:
+ | {
+ networkConfigurationId: string;
+ nickname: string;
+ editCompleted: boolean;
+ }
+ | undefined;
newNetworkAddedConfigurationId: string;
selectedNetworkConfigurationId: string;
sendInputCurrencySwitched: boolean;
@@ -165,7 +171,7 @@ const initialState: AppState = {
newNftAddedMessage: '',
removeNftMessage: '',
newNetworkAddedName: '',
- editedNetwork: '',
+ editedNetwork: undefined,
newNetworkAddedConfigurationId: '',
selectedNetworkConfigurationId: '',
sendInputCurrencySwitched: false,
@@ -493,10 +499,9 @@ export default function reduceApp(
};
}
case actionConstants.SET_EDIT_NETWORK: {
- const { nickname } = action.payload;
return {
...appState,
- editedNetwork: nickname,
+ editedNetwork: action.payload,
};
}
case actionConstants.SET_NEW_TOKENS_IMPORTED:
diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js
index d43616dc230d..c4abadc9b5b9 100644
--- a/ui/pages/home/home.component.js
+++ b/ui/pages/home/home.component.js
@@ -80,6 +80,7 @@ import {
///: END:ONLY_INCLUDE_IF
} from '../../../shared/lib/ui-utils';
import { AccountOverview } from '../../components/multichain/account-overview';
+import { setEditedNetwork } from '../../store/actions';
///: BEGIN:ONLY_INCLUDE_IF(build-beta)
import BetaHomeFooter from './beta/beta-home-footer.component';
///: END:ONLY_INCLUDE_IF
@@ -181,7 +182,7 @@ export default class Home extends PureComponent {
showOutdatedBrowserWarning: PropTypes.bool.isRequired,
setOutdatedBrowserWarningLastShown: PropTypes.func.isRequired,
newNetworkAddedName: PropTypes.string,
- editedNetwork: PropTypes.string,
+ editedNetwork: PropTypes.object,
// This prop is used in the `shouldCloseNotificationPopup` function
// eslint-disable-next-line react/no-unused-prop-types
isSigningQRHardwareTransaction: PropTypes.bool.isRequired,
@@ -489,7 +490,7 @@ export default class Home extends PureComponent {
setRemoveNftMessage('');
setNewTokensImported(''); // Added this so we dnt see the notif if user does not close it
setNewTokensImportedError('');
- clearEditedNetwork({});
+ setEditedNetwork();
};
const autoHideDelay = 5 * SECOND;
@@ -596,7 +597,7 @@ export default class Home extends PureComponent {
}
/>
) : null}
- {editedNetwork ? (
+ {editedNetwork?.editCompleted ? (
- {t('newNetworkEdited', [editedNetwork])}
+ {t('newNetworkEdited', [editedNetwork.nickname])}
{
dispatch(setNewNetworkAdded({}));
},
clearEditedNetwork: () => {
- dispatch(setEditedNetwork({}));
+ dispatch(setEditedNetwork());
},
setActiveNetwork: (networkConfigurationId) => {
dispatch(setActiveNetwork(networkConfigurationId));
diff --git a/ui/pages/onboarding-flow/add-network-modal/index.js b/ui/pages/onboarding-flow/add-network-modal/index.js
index c031739b68b6..b35785e4f2ac 100644
--- a/ui/pages/onboarding-flow/add-network-modal/index.js
+++ b/ui/pages/onboarding-flow/add-network-modal/index.js
@@ -19,6 +19,7 @@ export default function AddNetworkModal({
isNewNetworkFlow = false,
addNewNetwork = true,
networkToEdit = null,
+ onRpcUrlAdd,
}) {
const dispatch = useDispatch();
const t = useI18nContext();
@@ -50,6 +51,7 @@ export default function AddNetworkModal({
networksToRender={[]}
cancelCallback={closeCallback}
submitCallback={closeCallback}
+ onRpcUrlAdd={onRpcUrlAdd}
isNewNetworkFlow={isNewNetworkFlow}
{...additionalProps}
/>
@@ -62,6 +64,7 @@ AddNetworkModal.propTypes = {
isNewNetworkFlow: PropTypes.bool,
addNewNetwork: PropTypes.bool,
networkToEdit: PropTypes.object,
+ onRpcUrlAdd: PropTypes.func,
};
AddNetworkModal.defaultProps = {
diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js
index 1d23999731f9..c5cd44d9a048 100644
--- a/ui/pages/routes/routes.component.js
+++ b/ui/pages/routes/routes.component.js
@@ -213,6 +213,7 @@ export default class Routes extends Component {
newPrivacyPolicyToastShownDate: PropTypes.number,
setSurveyLinkLastClickedOrClosed: PropTypes.func.isRequired,
setNewPrivacyPolicyToastShownDate: PropTypes.func.isRequired,
+ clearEditedNetwork: PropTypes.func.isRequired,
setNewPrivacyPolicyToastClickedOrClosed: PropTypes.func.isRequired,
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
isShowKeyringSnapRemovalResultModal: PropTypes.bool.isRequired,
@@ -816,6 +817,7 @@ export default class Routes extends Component {
switchedNetworkDetails,
clearSwitchedNetworkDetails,
networkMenuRedesign,
+ clearEditedNetwork,
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
isShowKeyringSnapRemovalResultModal,
hideShowKeyringSnapRemovalResultModal,
@@ -898,7 +900,12 @@ export default class Routes extends Component {
toggleAccountMenu()} />
) : null}
{isNetworkMenuOpen ? (
- toggleNetworkMenu()} />
+ {
+ toggleNetworkMenu();
+ clearEditedNetwork();
+ }}
+ />
) : null}
{networkMenuRedesign ? : null}
{accountDetailsAddress ? (
diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js
index d4334e00b4de..da6d62636d5f 100644
--- a/ui/pages/routes/routes.container.js
+++ b/ui/pages/routes/routes.container.js
@@ -52,6 +52,7 @@ import {
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
hideKeyringRemovalResultModal,
///: END:ONLY_INCLUDE_IF
+ setEditedNetwork,
} from '../../store/actions';
import { pageChanged } from '../../ducks/history/history';
import { prepareToLeaveSwaps } from '../../ducks/swaps/swaps';
@@ -176,6 +177,7 @@ function mapDispatchToProps(dispatch) {
dispatch(setNewPrivacyPolicyToastClickedOrClosed()),
setNewPrivacyPolicyToastShownDate: (date) =>
dispatch(setNewPrivacyPolicyToastShownDate(date)),
+ clearEditedNetwork: () => dispatch(setEditedNetwork()),
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
hideShowKeyringSnapRemovalResultModal: () =>
dispatch(hideKeyringRemovalResultModal()),
diff --git a/ui/pages/settings/networks-tab/index.scss b/ui/pages/settings/networks-tab/index.scss
index 99a4840aead0..86e792d988c4 100644
--- a/ui/pages/settings/networks-tab/index.scss
+++ b/ui/pages/settings/networks-tab/index.scss
@@ -7,10 +7,12 @@
&__rpc-dropdown {
cursor: pointer;
+ word-break: break-all;
}
&__rpc-item {
position: relative;
+ word-break: break-all;
}
&__rpc-item:hover {
diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.js b/ui/pages/settings/networks-tab/networks-form/networks-form.js
index 298a063f98b6..5409a645368d 100644
--- a/ui/pages/settings/networks-tab/networks-form/networks-form.js
+++ b/ui/pages/settings/networks-tab/networks-form/networks-form.js
@@ -120,6 +120,7 @@ const NetworksForm = ({
selectedNetwork,
cancelCallback,
submitCallback,
+ onRpcUrlAdd,
}) => {
const t = useI18nContext();
const dispatch = useDispatch();
@@ -501,6 +502,15 @@ const NetworksForm = ({
return null;
},
[rpcUrl, networksToRender, t],
+ // [
+ // rpcUrl,
+ // networksToRender,
+ // t,
+ // autoSuggestTicker,
+ // orderedNetworksList,
+ // autoSuggestName,
+ // addNewNetwork,
+ // ],
);
/**
@@ -795,7 +805,13 @@ const NetworksForm = ({
},
});
if (networkMenuRedesign) {
- dispatch(setEditedNetwork({ nickname: networkName }));
+ dispatch(
+ setEditedNetwork({
+ networkConfigurationId,
+ nickname: networkName,
+ editCompleted: true,
+ }),
+ );
}
}
@@ -925,8 +941,12 @@ const NetworksForm = ({
))}
) : null}
+
{networkMenuRedesign ? (
-
+
) : (
{
+import { showModal, toggleNetworkMenu } from '../../../../store/actions';
+
+export const RpcUrlEditor = ({
+ currentRpcUrl,
+ onRpcUrlAdd,
+}: {
+ currentRpcUrl: string;
+ onRpcUrlAdd: () => void;
+}) => {
// TODO: real endpoints
const dummyRpcUrls = [
currentRpcUrl,
- 'https://dummy.mainnet.public.blastapi.io',
- 'https://dummy.io/v3/blockchain/node/dummy',
+ 'https://mainnet.public.blastapi.io',
+ 'https://infura.foo.bar.baz/123456789',
];
const t = useI18nContext();
+ const dispatch = useDispatch();
const rpcDropdown = useRef(null);
- const [isOpen, setIsOpen] = useState(false);
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [currentRpcEndpoint, setCurrentRpcEndpoint] = useState(currentRpcUrl);
return (
@@ -48,7 +58,7 @@ export const RpcUrlEditor = ({ currentRpcUrl }: { currentRpcUrl: string }) => {
{t('defaultRpcUrl')}
setIsOpen(!isOpen)}
+ onClick={() => setIsDropdownOpen(!isDropdownOpen)}
className="networks-tab__rpc-dropdown"
display={Display.Flex}
justifyContent={JustifyContent.spaceBetween}
@@ -58,9 +68,13 @@ export const RpcUrlEditor = ({ currentRpcUrl }: { currentRpcUrl: string }) => {
padding={2}
ref={rpcDropdown}
>
- {currentRpcEndpoint}
+
+ {currentRpcEndpoint}
+
@@ -69,19 +83,24 @@ export const RpcUrlEditor = ({ currentRpcUrl }: { currentRpcUrl: string }) => {
paddingTop={2}
paddingBottom={2}
paddingLeft={0}
+ matchWidth={true}
paddingRight={0}
className="networks-tab__rpc-popover"
referenceElement={rpcDropdown.current}
position={PopoverPosition.Bottom}
- isOpen={isOpen}
+ isOpen={isDropdownOpen}
>
{dummyRpcUrls.map((rpcEndpoint) => (
setCurrentRpcEndpoint(rpcEndpoint)}
+ onClick={() => {
+ setCurrentRpcEndpoint(rpcEndpoint);
+ setIsDropdownOpen(false);
+ }}
className={classnames('networks-tab__rpc-item', {
'networks-tab__rpc-item--selected':
rpcEndpoint === currentRpcEndpoint,
@@ -103,23 +122,29 @@ export const RpcUrlEditor = ({ currentRpcUrl }: { currentRpcUrl: string }) => {
{rpcEndpoint}
alert('TODO: delete confirmation modal')}
+ onClick={(e: React.MouseEvent) => {
+ e.stopPropagation();
+ dispatch(toggleNetworkMenu());
+ dispatch(
+ showModal({
+ name: 'CONFIRM_DELETE_RPC_URL',
+ }),
+ );
+ }}
/>
))}
alert('TODO: add RPC modal')}
+ onClick={onRpcUrlAdd}
padding={4}
display={Display.Flex}
alignItems={AlignItems.center}
- className="networks-tab__rpc-item"
+ // className="networks-tab__rpc-item" // todo what?
>
{
const newNetworkAddedDetails = {
nickname: 'test-chain',
+ networkConfigurationId: 'testNetworkConfigurationId',
+ editCompleted: true,
};
store.dispatch(actions.setEditedNetwork(newNetworkAddedDetails));
diff --git a/ui/store/actions.ts b/ui/store/actions.ts
index cc0dd1507bc3..18157c7b55ae 100644
--- a/ui/store/actions.ts
+++ b/ui/store/actions.ts
@@ -4149,16 +4149,16 @@ export function setNewNetworkAdded({
};
}
-export function setEditedNetwork({
- nickname,
-}: {
- networkConfigurationId: string;
- nickname: string;
-}): PayloadAction {
- return {
- type: actionConstants.SET_EDIT_NETWORK,
- payload: { nickname },
- };
+export function setEditedNetwork(
+ payload:
+ | {
+ networkConfigurationId: string;
+ nickname: string;
+ editCompleted: boolean;
+ }
+ | undefined = undefined,
+): PayloadAction {
+ return { type: actionConstants.SET_EDIT_NETWORK, payload };
}
export function setNewNftAddedMessage(
From dbea8b5afa7890c074b395a14b51b5c3fb0a4890 Mon Sep 17 00:00:00 2001
From: Brian Bergeron
Date: Thu, 20 Jun 2024 12:44:54 -0700
Subject: [PATCH 13/14] fixes
---
.../settings/networks-tab/networks-form/networks-form.js | 9 ---------
.../networks-tab/networks-form/rpc-url-editor.tsx | 8 ++------
2 files changed, 2 insertions(+), 15 deletions(-)
diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.js b/ui/pages/settings/networks-tab/networks-form/networks-form.js
index 5409a645368d..c5bdaeeb1fc5 100644
--- a/ui/pages/settings/networks-tab/networks-form/networks-form.js
+++ b/ui/pages/settings/networks-tab/networks-form/networks-form.js
@@ -502,15 +502,6 @@ const NetworksForm = ({
return null;
},
[rpcUrl, networksToRender, t],
- // [
- // rpcUrl,
- // networksToRender,
- // t,
- // autoSuggestTicker,
- // orderedNetworksList,
- // autoSuggestName,
- // addNewNetwork,
- // ],
);
/**
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
index 6845e6777a73..41ece0b7388d 100644
--- a/ui/pages/settings/networks-tab/networks-form/rpc-url-editor.tsx
+++ b/ui/pages/settings/networks-tab/networks-form/rpc-url-editor.tsx
@@ -68,11 +68,7 @@ export const RpcUrlEditor = ({
padding={2}
ref={rpcDropdown}
>
-
- {currentRpcEndpoint}
-
+ {currentRpcEndpoint}
Date: Thu, 20 Jun 2024 13:07:33 -0700
Subject: [PATCH 14/14] remove unused message
---
app/_locales/en/messages.json | 3 ---
1 file changed, 3 deletions(-)
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 33bcfdc1e5f0..80fd403e03e3 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1721,9 +1721,6 @@
"enterPasswordContinue": {
"message": "Enter password to continue"
},
- "enterRpcUrl": {
- "message": "Enter RPC URL"
- },
"enterTokenNameOrAddress": {
"message": "Enter token name or paste address"
},