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 ? ( - - drag-and-drop - - } - 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 ? ( - + drag-and-drop + + } + 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 ? ( + + drag-and-drop + + } + 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 ? ( - - drag-and-drop - - } - 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" },