diff --git a/app/components/UI/NetworkSelectorList/NetworkSelectorList.tsx b/app/components/UI/NetworkSelectorList/NetworkSelectorList.tsx index 59fd85ddfef..152348b8573 100644 --- a/app/components/UI/NetworkSelectorList/NetworkSelectorList.tsx +++ b/app/components/UI/NetworkSelectorList/NetworkSelectorList.tsx @@ -24,7 +24,7 @@ const NetworkSelectorList = ({ onSelectNetwork, networks = [], isLoading = false, - selectedNetworkIds, + selectedChainIds, isMultiSelect = true, renderRightAccessory, isSelectionDisabled, @@ -33,7 +33,6 @@ const NetworkSelectorList = ({ }: NetworkConnectMultiSelectorProps) => { const networksLengthRef = useRef(0); const { styles } = useStyles(styleSheet, {}); - /** * Ref for the FlatList component. * The type of the ref is not explicitly defined. @@ -51,8 +50,8 @@ const NetworkSelectorList = ({ ? CellVariant.MultiSelect : CellVariant.Select; let isSelectedNetwork = isSelected; - if (selectedNetworkIds) { - isSelectedNetwork = selectedNetworkIds.includes(id); + if (selectedChainIds) { + isSelectedNetwork = selectedChainIds.includes(id); } return ( @@ -76,7 +75,7 @@ const NetworkSelectorList = ({ }, [ isLoading, - selectedNetworkIds, + selectedChainIds, renderRightAccessory, isSelectionDisabled, onSelectNetwork, diff --git a/app/components/UI/NetworkSelectorList/NetworkSelectorList.types.ts b/app/components/UI/NetworkSelectorList/NetworkSelectorList.types.ts index ebc0e3b4d6c..48b3bd6ab07 100644 --- a/app/components/UI/NetworkSelectorList/NetworkSelectorList.types.ts +++ b/app/components/UI/NetworkSelectorList/NetworkSelectorList.types.ts @@ -12,7 +12,7 @@ export interface NetworkConnectMultiSelectorProps { onSelectNetwork?: (id: string, isSelected: boolean) => void; networks?: Network[]; isLoading?: boolean; - selectedNetworkIds?: string[]; + selectedChainIds?: string[]; isMultiSelect?: boolean; renderRightAccessory?: (id: string, name: string) => React.ReactNode; isSelectionDisabled?: boolean; diff --git a/app/components/Views/AccountPermissions/AccountPermissions.tsx b/app/components/Views/AccountPermissions/AccountPermissions.tsx index 7db1f844369..c85b0eba071 100755 --- a/app/components/Views/AccountPermissions/AccountPermissions.tsx +++ b/app/components/Views/AccountPermissions/AccountPermissions.tsx @@ -116,6 +116,9 @@ const AccountPermissions = (props: AccountPermissionsProps) => { const activeAddress: string = permittedAccountsByHostname[0]; const [userIntent, setUserIntent] = useState(USER_INTENT.None); + const [networkSelectorUserIntent, setNetworkSelectorUserIntent] = useState( + USER_INTENT.None, + ); const hideSheet = useCallback( (callback?: () => void) => @@ -371,8 +374,13 @@ const AccountPermissions = (props: AccountPermissionsProps) => { ]); useEffect(() => { - if (userIntent === USER_INTENT.None) return; + if (networkSelectorUserIntent === USER_INTENT.Confirm) { + hideSheet(); + } + }, [networkSelectorUserIntent, hideSheet]); + useEffect(() => { + if (userIntent === USER_INTENT.None) return; const handleUserActions = (action: USER_INTENT) => { switch (action) { case USER_INTENT.Confirm: { @@ -586,7 +594,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => { @@ -597,7 +605,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => { ), [ isLoading, - setUserIntent, + setNetworkSelectorUserIntent, urlWithProtocol, hostname, isRenderedAsBottomSheet, diff --git a/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.tsx b/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.tsx index 181b86d44d7..80977152295 100644 --- a/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.tsx +++ b/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.tsx @@ -1,6 +1,9 @@ // Third party dependencies. -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useState, useEffect } from 'react'; import { Platform, SafeAreaView, View } from 'react-native'; +import { useSelector } from 'react-redux'; +import { useNavigation } from '@react-navigation/native'; +import { NetworkConfiguration } from '@metamask/network-controller'; // External dependencies. import { strings } from '../../../../../locales/i18n'; @@ -10,14 +13,12 @@ import Button, { ButtonVariants, } from '../../../../component-library/components/Buttons/Button'; import SheetHeader from '../../../../component-library/components/Sheet/SheetHeader'; -import { useNavigation } from '@react-navigation/native'; import { useStyles } from '../../../../component-library/hooks'; import { USER_INTENT } from '../../../../constants/permissions'; import HelpText, { HelpTextSeverity, } from '../../../../component-library/components/Form/HelpText'; -import { Network } from '../../../../components/UI/NetworkSelectorList/NetworkSelectorList.types'; // Internal dependencies. import ConnectNetworkModalSelectorsIDs from '../../../../../e2e/selectors/Modals/ConnectNetworkModal.selectors'; @@ -26,7 +27,10 @@ import { NetworkConnectMultiSelectorProps } from './NetworkConnectMultiSelector. import Routes from '../../../../constants/navigation/Routes'; import Checkbox from '../../../../component-library/components/Checkbox'; import NetworkSelectorList from '../../../UI/NetworkSelectorList/NetworkSelectorList'; -import { PopularList } from '../../../../util/networks/customNetworks'; +import { selectNetworkConfigurations } from '../../../../selectors/networkController'; +import Engine from '../../../../core/Engine'; +import { PermissionKeys } from '../../../../core/Permissions/specifications'; +import { CaveatTypes } from '../../../../core/Permissions/constants'; const NetworkConnectMultiSelector = ({ isLoading, @@ -38,34 +42,95 @@ const NetworkConnectMultiSelector = ({ }: NetworkConnectMultiSelectorProps) => { const { styles } = useStyles(styleSheet, { isRenderedAsBottomSheet }); const { navigate } = useNavigation(); - const [selectedNetworkIds, setSelectedNetworkIds] = useState([]); + const [selectedChainIds, setSelectedChainIds] = useState([]); + const networkConfigurations = useSelector(selectNetworkConfigurations); + + useEffect(() => { + let currentlyPermittedChains; + try { + currentlyPermittedChains = Engine.context.PermissionController.getCaveat( + hostname, + PermissionKeys.permittedChains, + CaveatTypes.restrictNetworkSwitching, + ); + } catch (e) { + // noop + } - const mockNetworks: Network[] = PopularList.map((network) => ({ - id: network.chainId, - name: network.nickname, - rpcUrl: network.rpcUrl, - isSelected: false, - imageSource: network.rpcPrefs.imageSource, - })); + setSelectedChainIds(currentlyPermittedChains?.value || []); + }, [hostname]); + + const handleUpdateNetworkPermissions = useCallback(async () => { + let hasPermittedChains = false; + try { + hasPermittedChains = Engine.context.PermissionController.hasCaveat( + hostname, + PermissionKeys.permittedChains, + CaveatTypes.restrictNetworkSwitching, + ); + } catch { + // noop + } + if (hasPermittedChains) { + Engine.context.PermissionController.updateCaveat( + hostname, + PermissionKeys.permittedChains, + CaveatTypes.restrictNetworkSwitching, + selectedChainIds, + ); + } else { + Engine.context.PermissionController.grantPermissionsIncremental({ + subject: { + origin: hostname, + }, + approvedPermissions: { + [PermissionKeys.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: selectedChainIds, + }, + ], + }, + }, + }); + } + onUserAction(USER_INTENT.Confirm); + }, [selectedChainIds, hostname, onUserAction]); + const networks = Object.entries(networkConfigurations).map( + ([key, network]: [string, NetworkConfiguration]) => ({ + id: key, + name: network.name, + rpcUrl: network.rpcEndpoints[network.defaultRpcEndpointIndex].url, + isSelected: false, + imageSource: '', // TODO not sure where to get image sources + }), + ); const onSelectNetwork = useCallback( - (clickedNetworkId) => { - const selectedAddressIndex = selectedNetworkIds.indexOf(clickedNetworkId); + (clickedChainId) => { + const selectedAddressIndex = selectedChainIds.indexOf(clickedChainId); // Reconstruct selected network ids. - const newNetworkList = mockNetworks.reduce((acc, { id }) => { - if (clickedNetworkId === id) { + const newNetworkList = networks.reduce((acc, { id }) => { + if (clickedChainId === id) { selectedAddressIndex === -1 && acc.push(id); - } else if (selectedNetworkIds.includes(id)) { + } else if (selectedChainIds.includes(id)) { acc.push(id); } return acc; }, [] as string[]); - setSelectedNetworkIds(newNetworkList); + setSelectedChainIds(newNetworkList); }, - [mockNetworks, selectedNetworkIds], + [networks, selectedChainIds], ); const toggleRevokeAllNetworkPermissionsModal = useCallback(() => { + // not sure if we want to do this here or on the sub modal + // which provides the extra warning that it will fully disconnect you + Engine.context.PermissionController.revokePermissions({ + [hostname]: [PermissionKeys.permittedChains, PermissionKeys.eth_accounts], + }); + navigate(Routes.MODAL.ROOT_MODAL_FLOW, { screen: Routes.SHEET.REVOKE_ALL_ACCOUNT_PERMISSIONS, params: { @@ -76,14 +141,14 @@ const NetworkConnectMultiSelector = ({ }, }, }); - }, [navigate, urlWithProtocol]); + }, [navigate, urlWithProtocol, hostname]); - const areAllNetworksSelected = mockNetworks + const areAllNetworksSelected = networks .map(({ id }) => id) - .every((id) => selectedNetworkIds.includes(id)); + .every((id) => selectedChainIds?.includes(id)); - const areAnyNetworksSelected = selectedNetworkIds?.length !== 0; - const areNoNetworksSelected = selectedNetworkIds?.length === 0; + const areAnyNetworksSelected = selectedChainIds?.length !== 0; + const areNoNetworksSelected = selectedChainIds?.length === 0; const renderSelectAllCheckbox = useCallback((): React.JSX.Element | null => { const areSomeNetworksSelectedButNotAll = @@ -91,13 +156,13 @@ const NetworkConnectMultiSelector = ({ const selectAll = () => { if (isLoading) return; - const allSelectedNetworkIds = mockNetworks.map(({ id }) => id); - setSelectedNetworkIds(allSelectedNetworkIds); + const allSelectedChainIds = networks.map(({ id }) => id); + setSelectedChainIds(allSelectedChainIds); }; const unselectAll = () => { if (isLoading) return; - setSelectedNetworkIds([]); + setSelectedChainIds([]); }; const onPress = () => { @@ -118,14 +183,14 @@ const NetworkConnectMultiSelector = ({ }, [ areAllNetworksSelected, areAnyNetworksSelected, - mockNetworks, + networks, isLoading, - setSelectedNetworkIds, + setSelectedChainIds, styles.selectAllContainer, ]); const renderCtaButtons = useCallback(() => { - const isConnectDisabled = Boolean(!selectedNetworkIds.length) || isLoading; + const isConnectDisabled = Boolean(!selectedChainIds.length) || isLoading; return ( @@ -134,7 +199,7 @@ const NetworkConnectMultiSelector = ({