From 4e2ceac12213e74c997cb9207882b37e60252e64 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 24 Jul 2024 15:19:08 +0100 Subject: [PATCH 001/125] added networks screen modal --- app/_locales/en/messages.json | 3 + .../edit-networks-modal.js | 112 ++++++++++++++++-- .../edit-networks-modal.test.js | 0 3 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index c5f16a427c8c..3dbbbfc2aede 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -2983,6 +2983,9 @@ "nativeTokenScamWarningConversion": { "message": "Edit network details" }, + "editNetworksTitle": { + "message": "Edit networks" + }, "nativeTokenScamWarningDescription": { "message": "This network doesn't match its associated chain ID or name. Many popular tokens use the name $1, making it a target for scams. Scammers may trick you into sending them more valuable currency in return. Verify everything before you continue.", "description": "$1 represents the currency name" diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index 3cce3266ad5e..c502a6264205 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -1,9 +1,14 @@ import PropTypes from 'prop-types'; -import React from 'react'; -import { useSelector } from 'react-redux'; +import React, { useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { TextVariant } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { getNonTestNetworks, getTestNetworks } from '../../../selectors'; +import { + getNonTestNetworks, + getOriginOfCurrentTab, + getPermittedChainsByOrigin, + getTestNetworks, +} from '../../../selectors'; import { Modal, ModalOverlay, @@ -12,13 +17,63 @@ import { Checkbox, Text, Box, + ModalFooter, + ButtonPrimary, + ButtonPrimarySize, } from '../../component-library'; import { NetworkListItem } from '..'; +import { + grantPermittedChains, + removePermittedChain, +} from '../../../store/actions'; -export const EditNetworksModal = ({ onClose }) => { +export const EditNetworksModal = ({ onClose, onClick }) => { const t = useI18nContext(); + const dispatch = useDispatch(); const nonTestNetworks = useSelector(getNonTestNetworks); const testNetworks = useSelector(getTestNetworks); + const chains = useSelector(getPermittedChainsByOrigin); + const permittedChains = Object.values(chains); + const activeTabOrigin = useSelector(getOriginOfCurrentTab); + const flattenedPermittedChains = permittedChains.flat(); + const [selectedChains, setSelectedChains] = useState( + flattenedPermittedChains, + ); + + const handleAccountClick = (chainId) => { + const index = selectedChains.indexOf(chainId); + let newSelectedChains = []; + + if (index === -1) { + // If chainId is not already selected, add it to the selectedChains array + newSelectedChains = [...selectedChains, chainId]; + } else { + // If chainId is already selected, remove it from the selectedChains array + newSelectedChains = selectedChains.filter((_item, idx) => idx !== index); + } + setSelectedChains(newSelectedChains); + }; + const managePermittedChains = ( + selectedChains, + flattenedPermittedChains, + activeTabOrigin, + ) => { + if (!Array.isArray(selectedChains)) { + console.error('selectedChains is not an array:', selectedChains); + selectedChains = []; + } + dispatch(grantPermittedChains(activeTabOrigin, selectedChains)); + + const removedElements = flattenedPermittedChains.filter( + (chain) => !selectedChains.includes(chain), + ); + + // Dispatch removePermittedChains for each removed element + removedElements.forEach((chain) => { + const selectedChain = [chain]; + dispatch(removePermittedChain(activeTabOrigin, selectedChain)); + }); + }; return ( { iconSrc={network?.rpcPrefs?.imageUrl} key={network.id} onClick={() => { - console.log(network.id); + handleAccountClick(network.chainId); }} - startAccessory={} + startAccessory={ + + } /> ))} @@ -65,12 +122,47 @@ export const EditNetworksModal = ({ onClose }) => { iconSrc={network?.rpcPrefs?.imageUrl} key={network.id} onClick={() => { - console.log(network.id); + handleAccountClick(network.chainId); }} - startAccessory={} + startAccessory={ + + } showEndAccessory={false} /> ))} + + {selectedChains.length === 0 ? ( + { + // disconnectAllAccounts(); + onClose(); + }} + size={ButtonPrimarySize.Lg} + block + danger + > + {t('disconnect')} + + ) : ( + { + onClick(); + onClose(); + managePermittedChains( + selectedChains, + flattenedPermittedChains, + activeTabOrigin, + ); // Then call the managePermittedChains function + }} + size={ButtonPrimarySize.Lg} + block + > + {t('confirm')} + + )} + ); @@ -81,4 +173,8 @@ EditNetworksModal.propTypes = { * Executes when the modal closes */ onClose: PropTypes.func.isRequired, + /** + * Executes when the permissions are updated to show the toggle + */ + onClick: PropTypes.func.isRequired, }; diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js new file mode 100644 index 000000000000..e69de29bb2d1 From 5d1adc40b21ce09ff43e4940598526cfef4a7658 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Mon, 29 Jul 2024 13:38:54 +0100 Subject: [PATCH 002/125] added test and checkbo --- app/_locales/en/messages.json | 3 ++ .../edit-networks-modal.test.js.snap | 3 ++ .../edit-networks-modal.test.js | 50 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 3dbbbfc2aede..3893b5b47500 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -2976,6 +2976,9 @@ "message": "Do you want this site to do the following?", "description": "Description below header used on Permission Connect screen for native permissions." }, + "testnets":{ + "message ": "Testnets" + }, "nativeToken": { "message": "The native token on this network is $1. It is the token used for gas fees. ", "description": "$1 represents the name of the native token on the current network" diff --git a/ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap b/ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap new file mode 100644 index 000000000000..641698978939 --- /dev/null +++ b/ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EditNetworksModal renders properly 1`] = `
`; diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js index e69de29bb2d1..c0082eb1c785 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js @@ -0,0 +1,50 @@ +import React from 'react'; +import { renderWithProvider } from '../../../../test/jest'; +import configureStore from '../../../store/store'; +import mockState from '../../../../test/data/mock-state.json'; +import { EditNetworksModal } from '.'; + +const mockSetShowTestNetworks = jest.fn(); + +jest.mock('../../../store/actions.ts', () => ({ + setShowTestNetworks: () => mockSetShowTestNetworks, +})); + +const render = ({ + showTestNetworks = false, + currentChainId = '0x5', + providerConfigId = 'chain5', + isUnlocked = true, +} = {}) => { + const state = { + metamask: { + ...mockState.metamask, + isUnlocked, + providerConfig: { + ...mockState.metamask.providerConfig, + chainId: currentChainId, + id: providerConfigId, + }, + preferences: { + showTestNetworks, + }, + useRequestQueue: true, + }, + activeTab: { + origin, + }, + }; + + const store = configureStore(state); + return renderWithProvider(, store); +}; +describe('EditNetworksModal', () => { + it('renders properly', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + it('should render correctly', () => { + const { getByText } = render(); + expect(getByText('Edit networks')).toBeInTheDocument(); + }); +}); From ec54d0bad0b9ec368281952125f6c50bbddb2113 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Mon, 29 Jul 2024 13:51:07 +0100 Subject: [PATCH 003/125] lint fix --- app/_locales/en/messages.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 3893b5b47500..c5f16a427c8c 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -2976,9 +2976,6 @@ "message": "Do you want this site to do the following?", "description": "Description below header used on Permission Connect screen for native permissions." }, - "testnets":{ - "message ": "Testnets" - }, "nativeToken": { "message": "The native token on this network is $1. It is the token used for gas fees. ", "description": "$1 represents the name of the native token on the current network" @@ -2986,9 +2983,6 @@ "nativeTokenScamWarningConversion": { "message": "Edit network details" }, - "editNetworksTitle": { - "message": "Edit networks" - }, "nativeTokenScamWarningDescription": { "message": "This network doesn't match its associated chain ID or name. Many popular tokens use the name $1, making it a target for scams. Scammers may trick you into sending them more valuable currency in return. Verify everything before you continue.", "description": "$1 represents the currency name" From 62fc967066823c56b87d1ec0d6ccdb2ffa753cb0 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 30 Jul 2024 10:43:02 +0100 Subject: [PATCH 004/125] lint fix --- .../multichain/edit-networks-modal/edit-networks-modal.test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js index c0082eb1c785..f56ff2f526a6 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js @@ -30,9 +30,6 @@ const render = ({ }, useRequestQueue: true, }, - activeTab: { - origin, - }, }; const store = configureStore(state); From cfe64f411cb6231ccf138f7cecb204d2a3822839 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 30 Jul 2024 10:43:57 +0100 Subject: [PATCH 005/125] updated snapshot --- .../edit-networks-modal.test.js | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js deleted file mode 100644 index f56ff2f526a6..000000000000 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import { renderWithProvider } from '../../../../test/jest'; -import configureStore from '../../../store/store'; -import mockState from '../../../../test/data/mock-state.json'; -import { EditNetworksModal } from '.'; - -const mockSetShowTestNetworks = jest.fn(); - -jest.mock('../../../store/actions.ts', () => ({ - setShowTestNetworks: () => mockSetShowTestNetworks, -})); - -const render = ({ - showTestNetworks = false, - currentChainId = '0x5', - providerConfigId = 'chain5', - isUnlocked = true, -} = {}) => { - const state = { - metamask: { - ...mockState.metamask, - isUnlocked, - providerConfig: { - ...mockState.metamask.providerConfig, - chainId: currentChainId, - id: providerConfigId, - }, - preferences: { - showTestNetworks, - }, - useRequestQueue: true, - }, - }; - - const store = configureStore(state); - return renderWithProvider(, store); -}; -describe('EditNetworksModal', () => { - it('renders properly', () => { - const { container } = render(); - expect(container).toMatchSnapshot(); - }); - it('should render correctly', () => { - const { getByText } = render(); - expect(getByText('Edit networks')).toBeInTheDocument(); - }); -}); From 2a038a8d7e7a0271858a1d5439717ca724d367c0 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 30 Jul 2024 11:47:11 +0100 Subject: [PATCH 006/125] updated jest --- .../__snapshots__/edit-networks-modal.test.js.snap | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap diff --git a/ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap b/ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap deleted file mode 100644 index 641698978939..000000000000 --- a/ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`EditNetworksModal renders properly 1`] = `
`; From 46d344127129a3b078143c82d9f607ce7126c090 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 30 Jul 2024 12:52:51 +0100 Subject: [PATCH 007/125] updated tests --- .../multichain/network-list-item/network-list-item.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 5f52fcd19832..003406ef28fe 100644 --- a/ui/components/multichain/network-list-item/network-list-item.js +++ b/ui/components/multichain/network-list-item/network-list-item.js @@ -114,7 +114,11 @@ export const NetworkListItem = ({ width={BlockSize.Full} onClick={onClick} > - {startAccessory ? {startAccessory} : null} + {startAccessory ? ( + + {startAccessory} + + ) : null} {selected && ( Date: Wed, 31 Jul 2024 15:34:27 +0100 Subject: [PATCH 008/125] lint fix --- .../multichain/network-list-item/network-list-item.js | 6 +----- 1 file changed, 1 insertion(+), 5 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 003406ef28fe..5f52fcd19832 100644 --- a/ui/components/multichain/network-list-item/network-list-item.js +++ b/ui/components/multichain/network-list-item/network-list-item.js @@ -114,11 +114,7 @@ export const NetworkListItem = ({ width={BlockSize.Full} onClick={onClick} > - {startAccessory ? ( - - {startAccessory} - - ) : null} + {startAccessory ? {startAccessory} : null} {selected && ( Date: Wed, 14 Aug 2024 15:33:17 +0530 Subject: [PATCH 009/125] added edit accounts modal --- .../edit-accounts-modal.tsx | 166 +++++++++++++++--- 1 file changed, 143 insertions(+), 23 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index 00f460034b5c..3cf1e73fec83 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -1,13 +1,17 @@ -import React, { useMemo } from 'react'; -import { useSelector } from 'react-redux'; +import React, { useMemo, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { EthAccountType, InternalAccount, + isEvmAccountType, KeyringAccountType, } from '@metamask/keyring-api'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getInternalAccounts, + getOrderedConnectedAccountsForConnectedDapp, + getOriginOfCurrentTab, + getPermissionSubjects, getUpdatedAndSortedAccounts, } from '../../../selectors'; import { @@ -17,39 +21,124 @@ import { ModalHeader, Checkbox, Box, + ModalFooter, + ButtonPrimary, + ButtonPrimarySize, } from '../../component-library'; import { AccountListItem } from '..'; import { MergedInternalAccount } from '../../../selectors/selectors.types'; import { mergeAccounts } from '../account-list-menu/account-list-menu'; +import { + addMorePermittedAccounts, + removePermissionsFor, + removePermittedAccount, +} from '../../../store/actions'; +import { NonEmptyArray } from '@metamask/utils'; +import { SubjectsType } from '../pages/connections/components/connections.types'; + +const defaultAllowedAccountTypes = [EthAccountType.Eoa, EthAccountType.Erc4337]; -type EditAccountsModalProps = { +interface EditAccountsModalProps { onClose: () => void; + onClick: () => void; allowedAccountTypes?: KeyringAccountType[]; -}; +} -const defaultAllowedAccountTypes: KeyringAccountType[] = [ - EthAccountType.Eoa, - EthAccountType.Erc4337, -]; - -export const EditAccountsModal = ({ +export const EditAccountsModal: React.FC = ({ onClose, + onClick, allowedAccountTypes = defaultAllowedAccountTypes, -}: EditAccountsModalProps) => { +}) => { const t = useI18nContext(); const accounts = useSelector(getUpdatedAndSortedAccounts); const internalAccounts = useSelector(getInternalAccounts); + const dispatch = useDispatch(); const mergedAccounts: MergedInternalAccount[] = useMemo(() => { return mergeAccounts(accounts, internalAccounts).filter( (account: InternalAccount) => allowedAccountTypes.includes(account.type), ); - }, [accounts, internalAccounts, allowedAccountTypes]); // Add allowedAccountTypes to dependency array + }, [accounts, internalAccounts, allowedAccountTypes]); + + const activeTabOrigin = useSelector(getOriginOfCurrentTab); + const subjects = useSelector(getPermissionSubjects); + const connectedAccounts = useSelector((state: any) => + getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin).filter( + (account: InternalAccount) => isEvmAccountType(account.type), + ), + ); + + const connectedAccountsAddresses = connectedAccounts.map( + (account: InternalAccount) => account.address, + ); + + const [selectedAccounts, setSelectedAccounts] = useState( + connectedAccountsAddresses, + ); + + const handleAccountClick = (address: string) => { + const index = selectedAccounts.indexOf(address); + let newSelectedAccounts: string[]; + + if (index === -1) { + newSelectedAccounts = [...selectedAccounts, address]; + } else { + newSelectedAccounts = selectedAccounts.filter( + (account) => account !== address, + ); + } + setSelectedAccounts(newSelectedAccounts); + }; + + const disconnectAllAccounts = () => { + const subject = (subjects as SubjectsType)[activeTabOrigin]; + if (subject) { + const permissionMethodNames = Object.values(subject.permissions).map( + ({ parentCapability }: { parentCapability: string }) => + parentCapability, + ) as string[]; + if (permissionMethodNames.length > 0) { + const permissionsRecord: Record = { + [activeTabOrigin]: permissionMethodNames, + }; + + dispatch( + removePermissionsFor( + permissionsRecord as Record>, + ), + ); + } + } + }; + + const managePermittedAccounts = ( + selectedAccounts: string[], + connectedAccountsAddresses: string[], + activeTabOrigin: string, + ) => { + const removedElements = connectedAccountsAddresses.filter( + (account) => !selectedAccounts.includes(account), + ); + + if (removedElements.length > 0) { + removedElements.forEach((account) => { + dispatch(removePermittedAccount(activeTabOrigin, account)); + }); + } + + const newElements = selectedAccounts.filter( + (account) => !connectedAccountsAddresses.includes(account), + ); + + if (newElements.length > 0) { + dispatch(addMorePermittedAccounts(activeTabOrigin, newElements)); + } + }; return ( @@ -57,25 +146,56 @@ export const EditAccountsModal = ({ {t('editAccounts')} - (allAreSelected() ? deselectAll() : selectAll())} - // isIndeterminate={isIndeterminate} - /> + {mergedAccounts.map((account) => ( handleAccountClick(account.address)} account={account} key={account.address} isPinned={Boolean(account.pinned)} - startAccessory={} - onClick={() => console.log(null)} + startAccessory={ + + } selected={false} /> ))} + + + {selectedAccounts.length === 0 ? ( + { + disconnectAllAccounts(); + onClose(); + }} + size={ButtonPrimarySize.Lg} + block + danger + > + {t('disconnect')} + + ) : ( + { + onClick(); + managePermittedAccounts( + selectedAccounts, + connectedAccountsAddresses, + activeTabOrigin, + ); + onClose(); + }} + size={ButtonPrimarySize.Lg} + block + > + {t('confirm')} + + )} + ); From 1fde970224a7833e9b868fcc6eada3ac9d46d7dc Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 14 Aug 2024 18:07:28 +0530 Subject: [PATCH 010/125] added toast for permission switch --- app/_locales/en/messages.json | 3 +++ ui/pages/routes/routes.component.js | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index c5f16a427c8c..33093b893fb8 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1782,6 +1782,9 @@ "editPermission": { "message": "Edit permission" }, + "editPermissions": { + "message": "Edit permissions" + }, "editSpeedUpEditGasFeeModalTitle": { "message": "Edit speed up gas fee" }, diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index d28b99a9a5c9..862a618ee04e 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -760,6 +760,23 @@ export default class Routes extends Component { onAutoHideToast={onAutoHideToast} /> ) : null} + + {process.env.CHAIN_PERMISSIONS ? + + } + text="app.uniswap.org has been given access to Optimism." + actionText={this.context.t('editPermissions')} + onActionClick={() => console.log('Yo')} + onClose={() => console.log('lo')} + /> : null} ); } From 0f9b04925863a2b6c06d9fcb6e2dc26d5deb117a Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 21 Aug 2024 19:07:02 +0100 Subject: [PATCH 011/125] added review perm page --- app/_locales/en/messages.json | 3 + .../multichain/global-menu/global-menu.js | 19 ++ ui/components/multichain/pages/index.js | 1 + .../pages/review-permissions-page/index.js | 1 + .../review-permissions-page.stories.tsx | 10 + .../review-permissions-page.test.tsx | 0 .../review-permissions-page.tsx | 45 +++++ .../site-cell/site-cell.stories.tsx | 0 .../site-cell/site-cell.tsx | 184 ++++++++++++++++++ ui/helpers/constants/routes.ts | 2 + ui/pages/routes/routes.component.js | 7 + 11 files changed, 272 insertions(+) create mode 100644 ui/components/multichain/pages/review-permissions-page/index.js create mode 100644 ui/components/multichain/pages/review-permissions-page/review-permissions-page.stories.tsx create mode 100644 ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx create mode 100644 ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx create mode 100644 ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx create mode 100644 ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 33093b893fb8..25535fd5394f 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1817,6 +1817,9 @@ "encryptionPublicKeyRequest": { "message": "Request encryption public key" }, + "accountsPermissionsTitle": { + "message": "See your accounts and suggest transactions" + }, "endpointReturnedDifferentChainId": { "message": "The RPC URL you have entered returned a different chain ID ($1). Please update the Chain ID to match the RPC URL of the network you are trying to add.", "description": "$1 is the return value of eth_chainId from an RPC endpoint" diff --git a/ui/components/multichain/global-menu/global-menu.js b/ui/components/multichain/global-menu/global-menu.js index ab33de326ee7..606b53fd13e1 100644 --- a/ui/components/multichain/global-menu/global-menu.js +++ b/ui/components/multichain/global-menu/global-menu.js @@ -14,6 +14,7 @@ import { NOTIFICATIONS_ROUTE, SNAPS_ROUTE, PERMISSIONS, + REVIEW_PERMISSIONS, } from '../../../helpers/constants/routes'; import { lockMetamask, @@ -251,6 +252,24 @@ export const GlobalMenu = ({ closeMenu, anchorElement, isOpen }) => { > {t('allPermissions')} + { + history.push(REVIEW_PERMISSIONS); + trackEvent({ + event: MetaMetricsEventName.NavPermissionsOpened, + category: MetaMetricsEventCategory.Navigation, + properties: { + location: METRICS_LOCATION, + }, + }); + closeMenu(); + }} + data-testid="global-menu-connected-sites" + disabled={hasUnapprovedTransactions} + > + "nid" + { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) diff --git a/ui/components/multichain/pages/index.js b/ui/components/multichain/pages/index.js index a8e64d1fcf9b..ffb51f39d015 100644 --- a/ui/components/multichain/pages/index.js +++ b/ui/components/multichain/pages/index.js @@ -1,2 +1,3 @@ export { Connections } from './connections'; export { PermissionsPage } from './permissions-page/permissions-page'; +export { ReviewPermissions } from './review-permissions-page/review-permissions-page'; diff --git a/ui/components/multichain/pages/review-permissions-page/index.js b/ui/components/multichain/pages/review-permissions-page/index.js new file mode 100644 index 000000000000..7af352026e76 --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/index.js @@ -0,0 +1 @@ +export { ReviewPermissions } from './review-permissions-page'; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.stories.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.stories.tsx new file mode 100644 index 000000000000..b2da4553ce50 --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.stories.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { ReviewPermissions } from '.'; + +export default { + title: 'Components/Multichain/ReviewPermissions', +}; + +export const DefaultStory = () => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx new file mode 100644 index 000000000000..ab7578f01a9a --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -0,0 +1,45 @@ +import React, { useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import { + BackgroundColor, + IconColor, +} from '../../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../../hooks/useI18nContext'; +import { getConnectedSitesListWithNetworkInfo } from '../../../../selectors/index'; +import { + ButtonIcon, + ButtonIconSize, + IconName, +} from '../../../component-library'; +import { Content, Footer, Header, Page } from '../page'; + +export const ReviewPermissions = () => { + const t = useI18nContext(); + const dispatch = useDispatch(); + const history = useHistory(); + const sitesConnectionsList = useSelector( + getConnectedSitesListWithNetworkInfo, + ); + console.log(sitesConnectionsList, 'sitesConnectionsList'); + return ( + +
+ } + >
+ Nidhi +
+
+ ); +}; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx new file mode 100644 index 000000000000..23134a596049 --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -0,0 +1,184 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { SubjectType } from '@metamask/permission-controller'; +import { + AlignItems, + BackgroundColor, + BlockSize, + Display, + FlexDirection, + IconColor, + JustifyContent, + TextAlign, + TextColor, + TextVariant, +} from '../../../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../../../hooks/useI18nContext'; +import { + AvatarFavicon, + AvatarNetwork, + AvatarNetworkSize, + BadgeWrapper, + Box, + Icon, + IconName, + IconSize, + Text, +} from '../../../../component-library'; +import { getURLHost } from '../../../../../helpers/utils/util'; +import { ConnectionListTooltip } from '../../permissions-page/connection-list-tooltip/connection-list-tooltip'; + +export const ConnectionListItem = ({ connection, onClick }) => { + const t = useI18nContext(); + const isSnap = connection.subjectType === SubjectType.Snap; + + return ( + <> + + + + + {t('accountsPermissionsTitle')} + + + + + {t('connectedWith')} + + + + + + + + + + + + + {t('accountsPermissionsTitle')} + + + + + {t('connectedWith')} + + + + + + + + + + ); +}; + +ConnectionListItem.propTypes = { + /** + * The connection data to display + */ + connection: PropTypes.object.isRequired, + /** + * The function to call when the connection is clicked + */ + onClick: PropTypes.func.isRequired, +}; diff --git a/ui/helpers/constants/routes.ts b/ui/helpers/constants/routes.ts index 40e35c6a0df6..1d35bf44fd58 100644 --- a/ui/helpers/constants/routes.ts +++ b/ui/helpers/constants/routes.ts @@ -40,6 +40,7 @@ const SRP_REMINDER = '/onboarding/remind-srp'; ///: END:ONLY_INCLUDE_IF const SEND_ROUTE = '/send'; const CONNECTIONS = '/connections'; +const REVIEW_PERMISSIONS = './review-permissions'; const PERMISSIONS = '/permissions'; const TOKEN_DETAILS = '/token-details'; const CONNECT_ROUTE = '/connect'; @@ -222,6 +223,7 @@ export { SEND_ROUTE, CONNECTIONS, PERMISSIONS, + REVIEW_PERMISSIONS, TOKEN_DETAILS, CONFIRM_TRANSACTION_ROUTE, CONFIRM_SEND_ETHER_PATH, diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 862a618ee04e..c2e9aff9c77e 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -11,6 +11,7 @@ import Home from '../home'; import { PermissionsPage, Connections, + ReviewPermissions, } from '../../components/multichain/pages'; import Settings from '../settings'; import Authenticated from '../../helpers/higher-order-components/authenticated'; @@ -77,6 +78,7 @@ import { TOKEN_DETAILS, CONNECTIONS, PERMISSIONS, + REVIEW_PERMISSIONS, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) INSTITUTIONAL_FEATURES_DONE_ROUTE, CUSTODY_ACCOUNT_DONE_ROUTE, @@ -438,6 +440,11 @@ export default class Routes extends Component { component={Connections} /> + ); From e0d2bcefd2bd23fa427e0ca9fcc735ccfe415522 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 22 Aug 2024 13:50:58 +0100 Subject: [PATCH 012/125] added review permission page --- ui/components/multichain/pages/index.js | 5 ++- .../pages/review-permissions-page/index.js | 1 + .../review-permissions-page.tsx | 7 ++- .../site-cell/site-cell.stories.tsx | 10 +++++ .../site-cell/site-cell.tsx | 43 +++++++++++-------- ui/helpers/constants/routes.ts | 2 +- 6 files changed, 45 insertions(+), 23 deletions(-) diff --git a/ui/components/multichain/pages/index.js b/ui/components/multichain/pages/index.js index ffb51f39d015..5d24b78b6b2e 100644 --- a/ui/components/multichain/pages/index.js +++ b/ui/components/multichain/pages/index.js @@ -1,3 +1,6 @@ export { Connections } from './connections'; export { PermissionsPage } from './permissions-page/permissions-page'; -export { ReviewPermissions } from './review-permissions-page/review-permissions-page'; +export { + ReviewPermissions, + SiteCell, +} from './review-permissions-page/review-permissions-page'; diff --git a/ui/components/multichain/pages/review-permissions-page/index.js b/ui/components/multichain/pages/review-permissions-page/index.js index 7af352026e76..e2da178368f1 100644 --- a/ui/components/multichain/pages/review-permissions-page/index.js +++ b/ui/components/multichain/pages/review-permissions-page/index.js @@ -1 +1,2 @@ export { ReviewPermissions } from './review-permissions-page'; +export { SiteCell } from './site-cell/site-cell'; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index ab7578f01a9a..ba9e8904a7ea 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -13,6 +13,7 @@ import { IconName, } from '../../../component-library'; import { Content, Footer, Header, Page } from '../page'; +import { SiteCell } from './index'; export const ReviewPermissions = () => { const t = useI18nContext(); @@ -38,8 +39,10 @@ export const ReviewPermissions = () => { /> } > - Nidhi -
+ + + + ); }; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx index e69de29bb2d1..21e44327389d 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { SiteCell } from './site-cell'; + +export default { + title: 'Components/Multichain/SiteCell', +}; + +export const DefaultStory = () => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 23134a596049..5fb08ccb6412 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -16,6 +16,8 @@ import { import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { AvatarFavicon, + AvatarIcon, + AvatarIconSize, AvatarNetwork, AvatarNetworkSize, BadgeWrapper, @@ -28,9 +30,8 @@ import { import { getURLHost } from '../../../../../helpers/utils/util'; import { ConnectionListTooltip } from '../../permissions-page/connection-list-tooltip/connection-list-tooltip'; -export const ConnectionListItem = ({ connection, onClick }) => { +export const SiteCell = ({}) => { const t = useI18nContext(); - const isSnap = connection.subjectType === SubjectType.Snap; return ( <> @@ -42,17 +43,19 @@ export const ConnectionListItem = ({ connection, onClick }) => { alignItems={AlignItems.baseline} width={BlockSize.Full} backgroundColor={BackgroundColor.backgroundDefault} - onClick={onClick} + // onClick={onClick} padding={4} gap={4} className="multichain-connection-list-item" > - { > {t('connectedWith')} - + {/* */}
{ alignItems={AlignItems.baseline} width={BlockSize.Full} backgroundColor={BackgroundColor.backgroundDefault} - onClick={onClick} + // onClick={onClick} padding={4} gap={4} className="multichain-connection-list-item" > - { > {t('connectedWith')} - + {/* */} { ); }; -ConnectionListItem.propTypes = { +SiteCell.propTypes = { /** * The connection data to display */ diff --git a/ui/helpers/constants/routes.ts b/ui/helpers/constants/routes.ts index 1d35bf44fd58..c755c9914f25 100644 --- a/ui/helpers/constants/routes.ts +++ b/ui/helpers/constants/routes.ts @@ -40,7 +40,7 @@ const SRP_REMINDER = '/onboarding/remind-srp'; ///: END:ONLY_INCLUDE_IF const SEND_ROUTE = '/send'; const CONNECTIONS = '/connections'; -const REVIEW_PERMISSIONS = './review-permissions'; +const REVIEW_PERMISSIONS = '/review-permissions'; const PERMISSIONS = '/permissions'; const TOKEN_DETAILS = '/token-details'; const CONNECT_ROUTE = '/connect'; From f60df8f684cf74494a840abddf3b34265724058f Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Fri, 23 Aug 2024 13:07:47 +0100 Subject: [PATCH 013/125] editing flow --- test/data/mock-send-state.json | 2 + test/data/mock-state.json | 2 + .../site-cell/site-cell-menu.tsx | 116 ++++++++++++++++++ .../site-cell/site-cell.tsx | 16 ++- ui/ducks/app/app.ts | 28 +++++ ui/pages/routes/routes.component.js | 24 +++- ui/pages/routes/routes.container.js | 6 + ui/store/actionConstants.ts | 4 + ui/store/actions.ts | 24 ++++ 9 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx diff --git a/test/data/mock-send-state.json b/test/data/mock-send-state.json index 09b9cad15757..5c44605d2cb3 100644 --- a/test/data/mock-send-state.json +++ b/test/data/mock-send-state.json @@ -12,6 +12,8 @@ "appState": { "networkDropdownOpen": false, "importNftsModal": { "open": false }, + "showEditAccountsModalOpen": false, + "showEditNetworksModalOpen": false, "gasIsLoading": false, "isLoading": false, "importTokensModalOpen": false, diff --git a/test/data/mock-state.json b/test/data/mock-state.json index 93af077c77b1..c426723e0894 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -14,6 +14,8 @@ "importNftsModal": { "open": false }, + "showEditAccountsModalOpen": false, + "showEditNetworksModalOpen": false, "gasIsLoading": false, "isLoading": false, "modal": { diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx new file mode 100644 index 000000000000..8f0fa843c691 --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx @@ -0,0 +1,116 @@ +import React, { useRef, useCallback, useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { + PopoverRole, + PopoverPosition, + Popover, + IconName, + Text, + ModalFocus, + Box, +} from '../../../../component-library'; +import { MenuItem } from '../../../../ui/menu'; +import { + IconColor, + TextColor, + TextVariant, +} from '../../../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../../../hooks/useI18nContext'; +import { getPermissionsForActiveTab } from '../../../../../selectors'; +import { PermissionDetailsModal } from '../../../permission-details-modal/permission-details-modal'; + +// TODO: Replace `any` with type +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const TsMenuItem = MenuItem as any; + +export const SiteCellMenu = ({ + isOpen, + account, + anchorElement, + disableAccountSwitcher = false, + onClose, + closeMenu, + onActionClick, + activeTabOrigin, +}: { + isOpen: boolean; + anchorElement: HTMLElement | null; + disableAccountSwitcher: boolean; + onClose: () => void; + closeMenu: () => void; + onActionClick: (message: string) => void; + activeTabOrigin: string; +}) => { + const dispatch = useDispatch(); + const t = useI18nContext(); + const popoverDialogRef = useRef(null); + const [showPermissionModal, setShowPermissionModal] = useState(false); + const permissions = useSelector(getPermissionsForActiveTab); + + 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]); + + const handleKeyDown = useCallback( + (event) => { + if ( + event.key === 'Tab' && + popoverDialogRef?.current?.contains(event.target) && + onClose + ) { + onClose(); + } + }, + [onClose], + ); + + return ( + <> + + + + { + setShowPermissionModal(true); + onClose(); + }} + > + + {t('permissionDetails')} + + + + + + + ); +}; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 5fb08ccb6412..9ad0bb61132f 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -29,9 +29,15 @@ import { } from '../../../../component-library'; import { getURLHost } from '../../../../../helpers/utils/util'; import { ConnectionListTooltip } from '../../permissions-page/connection-list-tooltip/connection-list-tooltip'; +import { + showEditAccountsModal, + showEditNetworksModal, +} from '../../../../../store/actions'; +import { useDispatch } from 'react-redux'; export const SiteCell = ({}) => { const t = useI18nContext(); + const dispatch = useDispatch(); return ( <> @@ -94,6 +100,9 @@ export const SiteCell = ({}) => { alignItems={AlignItems.center} style={{ flex: '1', alignSelf: 'center' }} gap={2} + onClick={() => { + dispatch(showEditAccountsModal()); + }} > { className="multichain-connection-list-item" > { textAlign={TextAlign.Left} ellipsis > - {t('accountsPermissionsTitle')} + {t('permission_walletSwitchEthereumChain')} { alignItems={AlignItems.center} style={{ flex: '1', alignSelf: 'center' }} gap={2} + onClick={() => { + dispatch(showEditNetworksModal()); + }} > ) : null} - {process.env.CHAIN_PERMISSIONS ? + {process.env.CHAIN_PERMISSIONS ? ( console.log('Yo')} + onActionClick={() => this.props.history.push(REVIEW_PERMISSIONS)} onClose={() => console.log('lo')} - /> : null} + /> + ) : null} ); } @@ -848,6 +855,10 @@ export default class Routes extends Component { location, isImportNftsModalOpen, hideImportNftsModal, + isEditAccountsModalOpen, + hideEditAccountsModal, + isEditNetworksModalOpen, + hideEditNetworksModal, isIpfsModalOpen, isBasicConfigurationModalOpen, hideIpfsModal, @@ -953,6 +964,13 @@ export default class Routes extends Component { {isImportNftsModalOpen ? ( hideImportNftsModal()} /> ) : null} + {isEditAccountsModalOpen ? ( + hideEditAccountsModal()} /> + ) : null} + {isEditNetworksModalOpen ? ( + hideEditNetworksModal()} /> + ) : null} + {isIpfsModalOpen ? ( hideIpfsModal()} /> ) : null} diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index dd88ca780596..a62f1d9c7325 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -53,6 +53,8 @@ import { hideKeyringRemovalResultModal, ///: END:ONLY_INCLUDE_IF setEditedNetwork, + hideEditAccountsModal, + hideEditNetworksModal, } from '../../store/actions'; import { pageChanged } from '../../ducks/history/history'; import { prepareToLeaveSwaps } from '../../ducks/swaps/swaps'; @@ -130,6 +132,8 @@ function mapStateToProps(state) { accountDetailsAddress: state.appState.accountDetailsAddress, isImportNftsModalOpen: state.appState.importNftsModal.open, isIpfsModalOpen: state.appState.showIpfsModalOpen, + isEditNetworksModalOpen: state.appState.showEditNetworksModalOpen, + isEditAccountsModalOpen: state.appState.showEditAccountsModalOpen, switchedNetworkDetails, useNftDetection, showNftEnablementToast, @@ -162,6 +166,8 @@ function mapDispatchToProps(dispatch) { toggleNetworkMenu: () => dispatch(toggleNetworkMenu()), hideImportNftsModal: () => dispatch(hideImportNftsModal()), hideIpfsModal: () => dispatch(hideIpfsModal()), + hideEditAccountsModal: () => dispatch(hideEditAccountsModal()), + hideEditNetworksModal: () => dispatch(hideEditNetworksModal()), hideImportTokensModal: () => dispatch(hideImportTokensModal()), hideDeprecatedNetworkModal: () => dispatch(hideDeprecatedNetworkModal()), addPermittedAccount: (activeTabOrigin, address) => diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts index a54a5a220be8..d9dbb2724fec 100644 --- a/ui/store/actionConstants.ts +++ b/ui/store/actionConstants.ts @@ -14,6 +14,10 @@ export const NETWORK_DROPDOWN_CLOSE = 'UI_NETWORK_DROPDOWN_CLOSE'; export const IMPORT_NFTS_MODAL_OPEN = 'UI_IMPORT_NFTS_MODAL_OPEN'; export const IMPORT_NFTS_MODAL_CLOSE = 'UI_IMPORT_NFTS_MODAL_CLOSE'; export const SHOW_IPFS_MODAL_OPEN = 'UI_IPFS_MODAL_OPEN'; +export const SHOW_EDIT_ACCOUNTS_MODAL_OPEN = 'UI_EDIT_ACCOUNTS_MODAL_OPEN'; +export const SHOW_EDIT_ACCOUNTS_MODAL_CLOSE = 'UI_EDIT_ACCOUNTS_MODAL_CLOSE'; +export const SHOW_EDIT_NETWORKS_MODAL_OPEN = 'UI_EDIT_NETWORKS_MODAL_OPEN'; +export const SHOW_EDIT_NETWORKS_MODAL_CLOSE = 'UI_EDIT_NETWORKS_MODAL_CLOSE'; export const SHOW_IPFS_MODAL_CLOSE = 'UI_IPFS_MODAL_CLOSE'; export const IMPORT_TOKENS_POPOVER_OPEN = 'UI_IMPORT_TOKENS_POPOVER_OPEN'; export const IMPORT_TOKENS_POPOVER_CLOSE = 'UI_IMPORT_TOKENS_POPOVER_CLOSE'; diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 6bf5ef82ae9f..3a5bc4b9dc90 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -2599,6 +2599,30 @@ export function hideImportNftsModal(): Action { }; } +export function showEditAccountsModal(): Action { + return { + type: actionConstants.SHOW_EDIT_ACCOUNTS_MODAL_OPEN, + }; +} + +export function hideEditAccountsModal(): Action { + return { + type: actionConstants.SHOW_EDIT_ACCOUNTS_MODAL_CLOSE, + }; +} + +export function hideEditNetworksModal(): Action { + return { + type: actionConstants.SHOW_EDIT_NETWORKS_MODAL_CLOSE, + }; +} + +export function showEditNetworksModal(): Action { + return { + type: actionConstants.SHOW_EDIT_NETWORKS_MODAL_OPEN, + }; +} + // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any export function setConfirmationExchangeRates(value: Record) { From b6cc4d9ca548800185220e8a1fc4f408d3be5f0a Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Fri, 23 Aug 2024 15:27:50 +0100 Subject: [PATCH 014/125] updated selector --- .../controllers/permissions/specifications.js | 5 +++ app/scripts/metamask-controller.js | 5 +++ .../network-list-menu/network-list-menu.js | 7 +++- ui/selectors/permissions.js | 37 +++++++++++++++++++ ui/store/actions.ts | 22 +++++++++++ 5 files changed, 75 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index 2d25ab16b1e4..ea2392bfd7e4 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -212,6 +212,11 @@ export const getPermissionSpecifications = ({ subjectTypes: [SubjectType.Website], factory: (permissionOptions, requestData) => { + if (requestData === undefined) { + return constructPermission({ + ...permissionOptions, + }); + } if (!requestData.approvedChainIds) { throw new Error( `${PermissionNames.permittedChains}: No approved networks specified.`, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 1d5803237bb7..711621284379 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -3149,6 +3149,11 @@ export default class MetamaskController extends EventEmitter { getProviderConfig({ metamask: this.networkController.state, }), + grantPermissionsIncremental: + this.permissionController.grantPermissionsIncremental.bind( + this.permissionController, + ), + setSecurityAlertsEnabled: preferencesController.setSecurityAlertsEnabled.bind( preferencesController, 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 3900ba4a067a..5dab1b89c357 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -15,6 +15,7 @@ import { updateNetworksList, setNetworkClientIdForDomain, setEditedNetwork, + grantPermittedChain, } from '../../../store/actions'; import { FEATURED_RPCS, @@ -34,6 +35,7 @@ import { getNetworkConfigurations, getEditedNetwork, getAllDomains, + getPermittedChainsByOrigin, } from '../../../selectors'; import ToggleButton from '../../ui/toggle-button'; import { @@ -101,7 +103,8 @@ export const NetworkListMenu = ({ onClose }) => { const useRequestQueue = useSelector(getUseRequestQueue); const networkConfigurations = useSelector(getNetworkConfigurations); const domains = useSelector(getAllDomains); - + const chains = useSelector(getPermittedChainsByOrigin); +console.log(chains, "hh") const dispatch = useDispatch(); const history = useHistory(); const trackEvent = useContext(MetaMetricsContext); @@ -171,6 +174,7 @@ export const NetworkListMenu = ({ onClose }) => { return sortedNonTestNetworks; }; + //check if not granted chain is clicked, then show the toast and grant permission const networksList = newOrderNetworks(); const [items, setItems] = useState([...networksList]); @@ -303,6 +307,7 @@ export const NetworkListMenu = ({ onClose }) => { onClick={() => { dispatch(toggleNetworkMenu()); dispatch(setActiveNetwork(network.providerType || network.id)); + grantPermittedChain(selectedTabOrigin, network.chainId); // If presently on and connected to a dapp, communicate a change to // the dapp via silent switchEthereumChain that the network has diff --git a/ui/selectors/permissions.js b/ui/selectors/permissions.js index 65f2acf37c4b..9f08979ee6e0 100644 --- a/ui/selectors/permissions.js +++ b/ui/selectors/permissions.js @@ -83,6 +83,7 @@ export function getPermittedAccountsForSelectedTab(state, activeTab) { */ export function getPermittedAccountsByOrigin(state) { const subjects = getPermissionSubjects(state); + console.log(subjects, "nidhi") return Object.keys(subjects).reduce((acc, subjectKey) => { const accounts = getAccountsFromSubject(subjects[subjectKey]); if (accounts.length > 0) { @@ -92,6 +93,18 @@ export function getPermittedAccountsByOrigin(state) { }, {}); } +export function getPermittedChainsByOrigin(state) { + const subjects = getPermissionSubjects(state); + console.log(subjects, 'nidhi'); + return Object.keys(subjects).reduce((acc, subjectKey) => { + const chains = getChainsFromSubject(subjects[subjectKey]); + if (chains.length > 0) { + acc[subjectKey] = chains; + } + return acc; + }, {}); +} + export function getSubjectMetadata(state) { return state.metamask.subjectMetadata; } @@ -256,6 +269,14 @@ function getAccountsPermissionFromSubject(subject = {}) { return subject.permissions?.eth_accounts || {}; } +function getChainsFromSubject(subject) { + return getChainsFromPermission(getChainsPermissionFromSubject(subject)); +} + +function getChainsPermissionFromSubject(subject = {}) { + return subject.permissions?.['endowment:permitted-chains'] || {}; +} + function getAccountsFromPermission(accountsPermission) { const accountsCaveat = getAccountsCaveatFromPermission(accountsPermission); return accountsCaveat && Array.isArray(accountsCaveat.value) @@ -263,6 +284,22 @@ function getAccountsFromPermission(accountsPermission) { : []; } +function getChainsFromPermission(chainsPermission) { + const chainsCaveat = getChainsCaveatFromPermission(chainsPermission); + return chainsCaveat && Array.isArray(chainsCaveat.value) + ? chainsCaveat.value + : []; +} + +function getChainsCaveatFromPermission(chainsPermission = {}) { + return ( + Array.isArray(chainsPermission.caveats) && + chainsPermission.caveats.find( + (caveat) => caveat.type === CaveatTypes.restrictNetworkSwitching, + ) + ); +} + function getAccountsCaveatFromPermission(accountsPermission = {}) { return ( Array.isArray(accountsPermission.caveats) && diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 3a5bc4b9dc90..32e7c77886ce 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -130,6 +130,8 @@ import { MetaMaskReduxState, TemporaryMessageDataType, } from './store'; +import { CaveatFactories, PermissionNames } from '../../app/scripts/controllers/permissions/specifications'; +import { CaveatTypes } from '../../shared/constants/permissions'; type CustomGasSettings = { gas?: string; @@ -5580,6 +5582,26 @@ export async function getNextAvailableAccountName( ); } +export async function grantPermittedChain( + selectedTabOrigin: string, + chainId?: [], +): Promise { + return await submitRequestToBackground('grantPermissionsIncremental', [ + { + subject: { origin: selectedTabOrigin }, + approvedPermissions: { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]( + [chainId], + ), + ], + }, + }, + } + ]); +} + export async function decodeTransactionData({ transactionData, contractAddress, From a295937b3af3f71a0cdfbd8c6d40798d3213bfee Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Aug 2024 11:22:53 -0500 Subject: [PATCH 015/125] fix --- .../permission-page-container.component.js | 9 +++++---- .../multichain/pages/connections/connections.tsx | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js index 2ef99bacd3f3..a0e545d960c3 100644 --- a/ui/components/app/permission-page-container/permission-page-container.component.js +++ b/ui/components/app/permission-page-container/permission-page-container.component.js @@ -146,10 +146,11 @@ export default class PermissionPageContainer extends Component { (selectedAccount) => selectedAccount.address, ), }), - ...(_request.permissions.permittedChains && { - approvedChainIds: _request.permissions?.permittedChains?.caveats.find( - (caveat) => caveat.type === 'restrictNetworkSwitching', - )?.value, + ...(_request.permissions[PermissionNames.permittedChains] && { + approvedChainIds: _request.permissions?.[ + PermissionNames.permittedChains + ]?.caveats?.find((caveat) => caveat.type === 'restrictNetworkSwitching') + ?.value, }), }; diff --git a/ui/components/multichain/pages/connections/connections.tsx b/ui/components/multichain/pages/connections/connections.tsx index ae1f5b88193e..cbb5a4b7f24f 100644 --- a/ui/components/multichain/pages/connections/connections.tsx +++ b/ui/components/multichain/pages/connections/connections.tsx @@ -396,7 +396,7 @@ export const Connections = () => { size={ButtonPrimarySize.Lg} block data-test-id="no-connections-button" - onClick={() => dispatch(requestAccountsPermission())} + onClick={() => requestAccountsPermission()} > {t('connectAccounts')} From c7f45076829e926c926e5e49b7266ff9132fce63 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Sun, 25 Aug 2024 10:58:42 +0100 Subject: [PATCH 016/125] permitted chains and accounts updated for review screen --- app/_locales/en/messages.json | 3 + test/data/mock-send-state.json | 1 + test/data/mock-state.json | 1 + .../network-list-menu/network-list-menu.js | 17 ++-- .../review-permissions-page.tsx | 86 +++++++++++++++++-- .../site-cell/site-cell.tsx | 24 +++++- ui/ducks/app/app.ts | 16 +++- ui/pages/routes/routes.component.js | 29 +++++-- ui/pages/routes/routes.container.js | 6 ++ ui/selectors/permissions.js | 14 +++ ui/store/actionConstants.ts | 4 + ui/store/actions.ts | 31 +++++-- 12 files changed, 196 insertions(+), 36 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 25535fd5394f..13c657646160 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -641,6 +641,9 @@ "message": "Approved on $1 for $2", "description": "$1 is the approval date for a permission. $2 is the AvatarGroup component displaying account images." }, + "permittedChainToastUpdate": { + "message": "$1 has been given access to $2." + }, "areYouSure": { "message": "Are you sure?" }, diff --git a/test/data/mock-send-state.json b/test/data/mock-send-state.json index 5c44605d2cb3..7dbf0967f4eb 100644 --- a/test/data/mock-send-state.json +++ b/test/data/mock-send-state.json @@ -14,6 +14,7 @@ "importNftsModal": { "open": false }, "showEditAccountsModalOpen": false, "showEditNetworksModalOpen": false, + "showPermittedNetworkToastOpen": false, "gasIsLoading": false, "isLoading": false, "importTokensModalOpen": false, diff --git a/test/data/mock-state.json b/test/data/mock-state.json index c426723e0894..f5e40d0b2495 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -16,6 +16,7 @@ }, "showEditAccountsModalOpen": false, "showEditNetworksModalOpen": false, + "showPermittedNetworkToastOpen": false, "gasIsLoading": false, "isLoading": false, "modal": { 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 5dab1b89c357..cc9e17ffb215 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -16,6 +16,7 @@ import { setNetworkClientIdForDomain, setEditedNetwork, grantPermittedChain, + showPermittedNetworkToast, } from '../../../store/actions'; import { FEATURED_RPCS, @@ -104,7 +105,8 @@ export const NetworkListMenu = ({ onClose }) => { const networkConfigurations = useSelector(getNetworkConfigurations); const domains = useSelector(getAllDomains); const chains = useSelector(getPermittedChainsByOrigin); -console.log(chains, "hh") + const permittedChains = Object.values(chains); + console.log(permittedChains, 'hhh'); const dispatch = useDispatch(); const history = useHistory(); const trackEvent = useContext(MetaMetricsContext); @@ -176,6 +178,7 @@ console.log(chains, "hh") //check if not granted chain is clicked, then show the toast and grant permission const networksList = newOrderNetworks(); + const [items, setItems] = useState([...networksList]); useEffect(() => { @@ -307,16 +310,18 @@ console.log(chains, "hh") onClick={() => { dispatch(toggleNetworkMenu()); dispatch(setActiveNetwork(network.providerType || network.id)); - grantPermittedChain(selectedTabOrigin, network.chainId); - - // If presently on and connected to a dapp, communicate a change to - // the dapp via silent switchEthereumChain that the network has - // changed due to user action + grantPermittedChain(selectedTabOrigin, network.chainId); + if (!permittedChains.includes(network.chainId)) { + dispatch(showPermittedNetworkToast()); + } if ( useRequestQueue && selectedTabOrigin && domains[selectedTabOrigin] ) { + // If presently on and connected to a dapp, communicate a change to + // the dapp via silent switchEthereumChain that the network has + // changed due to user action setNetworkClientIdForDomain(selectedTabOrigin, network.id); } diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index ba9e8904a7ea..432f0e370d11 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -1,16 +1,36 @@ import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { useHistory } from 'react-router-dom'; +import { useHistory, useParams } from 'react-router-dom'; import { + AlignItems, BackgroundColor, + Display, IconColor, + JustifyContent, + TextAlign, + TextVariant, } from '../../../../helpers/constants/design-system'; +import { getURLHost } from '../../../../helpers/utils/util'; import { useI18nContext } from '../../../../hooks/useI18nContext'; -import { getConnectedSitesListWithNetworkInfo } from '../../../../selectors/index'; import { + getConnectedSitesList, + getConnectedSitesListWithNetworkInfo, + getNonTestNetworks, + getOrderedConnectedAccountsForConnectedDapp, + getOrderedNetworksList, + getPermittedChainsByOrigin, + getPermittedChainsForSelectedTab, +} from '../../../../selectors/index'; +import { + AvatarFavicon, + AvatarFaviconSize, + Box, ButtonIcon, ButtonIconSize, + Icon, IconName, + IconSize, + Text, } from '../../../component-library'; import { Content, Footer, Header, Page } from '../page'; import { SiteCell } from './index'; @@ -19,10 +39,30 @@ export const ReviewPermissions = () => { const t = useI18nContext(); const dispatch = useDispatch(); const history = useHistory(); - const sitesConnectionsList = useSelector( - getConnectedSitesListWithNetworkInfo, + const urlParams: { origin: string } = useParams(); + const securedOrigin = decodeURIComponent(urlParams.origin); + + const activeTabOrigin: string = securedOrigin; + const subjectMetadata = useSelector(getConnectedSitesList); + const connectedSubjectsMetadata = subjectMetadata[activeTabOrigin]; + const connectedNetworks = useSelector((state) => + getPermittedChainsForSelectedTab(state, activeTabOrigin), + ); + const networksList = useSelector(getNonTestNetworks); + const connectedAccounts = useSelector((state) => + getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin), + ); + + const grantedNetworks = networksList.filter( + (net: { chainId: any }) => connectedNetworks.indexOf(net.chainId) !== -1, + ); + console.log( + networksList, + connectedNetworks, + grantedNetworks, + connectedAccounts, + 'sitesConnectionsList', ); - console.log(sitesConnectionsList, 'sitesConnectionsList'); return ( { iconName={IconName.ArrowLeft} className="connections-header__start-accessory" size={ButtonIconSize.Sm} + onClick={() => (history as any).goBack()} /> } - > + > + + {connectedSubjectsMetadata?.iconUrl ? ( + + ) : ( + + )} + + {getURLHost(securedOrigin)} + + + - + - ); }; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 9ad0bb61132f..4aeb5a72b5f3 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -34,11 +34,19 @@ import { showEditNetworksModal, } from '../../../../../store/actions'; import { useDispatch } from 'react-redux'; +import { AvatarGroup } from '../../../index'; +import { AvatarType } from '../../../avatar-group/avatar-group.types'; -export const SiteCell = ({}) => { +export const SiteCell = ({ networks, accounts }) => { const t = useI18nContext(); const dispatch = useDispatch(); - + const avatarNetworksData = networks.map((network) => ({ + avatarValue: network.rpcPrefs.imageUrl, + symbol: network.nickname, + })); + const avatarAccountsData = accounts.map((account) => ({ + avatarValue: account.address, + })); return ( <> { > {t('connectedWith')} - {/* */} + { > {t('connectedWith')} - {/* */} + Boolean( @@ -192,7 +193,9 @@ export default class Routes extends Component { isImportNftsModalOpen: PropTypes.bool.isRequired, hideImportNftsModal: PropTypes.func.isRequired, isEditNetworksModalOpen: PropTypes.bool.isRequired, + isPermittedNetworkToastOpen: PropTypes.bool.isRequired, hideEditNetworksModal: PropTypes.func.isRequired, + hidePermittedNetworkToast: PropTypes.func.isRequired, isEditAccountsModalOpen: PropTypes.bool.isRequired, hideEditAccountsModal: PropTypes.func.isRequired, isIpfsModalOpen: PropTypes.bool.isRequired, @@ -205,6 +208,7 @@ export default class Routes extends Component { addPermittedAccount: PropTypes.func.isRequired, switchedNetworkDetails: PropTypes.object, useNftDetection: PropTypes.bool, + currentNetwork: PropTypes.object, showNftEnablementToast: PropTypes.bool, setHideNftEnablementToast: PropTypes.func.isRequired, clearSwitchedNetworkDetails: PropTypes.func.isRequired, @@ -447,7 +451,7 @@ export default class Routes extends Component { /> @@ -647,14 +651,16 @@ export default class Routes extends Component { useNftDetection, showNftEnablementToast, setHideNftEnablementToast, + isPermittedNetworkToastOpen, + currentNetwork, } = this.props; const showAutoNetworkSwitchToast = this.getShowAutoNetworkSwitchTest(); const isPrivacyToastRecent = this.getIsPrivacyToastRecent(); const isPrivacyToastNotShown = !newPrivacyPolicyToastShownDate; const isEvmAccount = isEvmAccountType(account?.type); - const autoHideToastDelay = 5 * SECOND; + const safeEncodedHost = encodeURIComponent(activeTabOrigin); const onAutoHideToast = () => { setHideNftEnablementToast(false); @@ -774,21 +780,28 @@ export default class Routes extends Component { /> ) : null} - {process.env.CHAIN_PERMISSIONS ? ( + {process.env.CHAIN_PERMISSIONS && isPermittedNetworkToastOpen ? ( } - text="app.uniswap.org has been given access to Optimism." + text={this.context.t('permittedChainToastUpdate', [ + getURLHost(activeTabOrigin), + currentNetwork?.nickname, + ])} actionText={this.context.t('editPermissions')} - onActionClick={() => this.props.history.push(REVIEW_PERMISSIONS)} - onClose={() => console.log('lo')} + onActionClick={() => + this.props.history.push( + `${REVIEW_PERMISSIONS}/${safeEncodedHost}`, + ) + } + onClose={hidePermittedNetworkToast} /> ) : null} diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index a62f1d9c7325..abe9b661cb5d 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -28,6 +28,7 @@ import { getUseRequestQueue, getUseNftDetection, getNftDetectionEnablementToast, + getCurrentNetwork, } from '../../selectors'; import { getLocalNetworkMenuRedesignFeatureFlag } from '../../helpers/utils/feature-flags'; import { getSmartTransactionsOptInStatus } from '../../../shared/modules/selectors'; @@ -55,6 +56,7 @@ import { setEditedNetwork, hideEditAccountsModal, hideEditNetworksModal, + hidePermittedNetworkToast, } from '../../store/actions'; import { pageChanged } from '../../ducks/history/history'; import { prepareToLeaveSwaps } from '../../ducks/swaps/swaps'; @@ -80,6 +82,7 @@ function mapStateToProps(state) { const account = getSelectedAccount(state); const activeTabOrigin = activeTab?.origin; const connectedAccounts = getPermittedAccountsForCurrentTab(state); + const currentNetwork = getCurrentNetwork(state); const showConnectAccountToast = Boolean( allowShowAccountSetting && account && @@ -133,11 +136,13 @@ function mapStateToProps(state) { isImportNftsModalOpen: state.appState.importNftsModal.open, isIpfsModalOpen: state.appState.showIpfsModalOpen, isEditNetworksModalOpen: state.appState.showEditNetworksModalOpen, + isPermittedNetworkToastOpen: state.appState.showPermittedNetworkToastOpen, isEditAccountsModalOpen: state.appState.showEditAccountsModalOpen, switchedNetworkDetails, useNftDetection, showNftEnablementToast, networkToAutomaticallySwitchTo, + currentNetwork, totalUnapprovedConfirmationCount: getNumberOfAllUnapprovedTransactionsAndMessages(state), neverShowSwitchedNetworkMessage: getNeverShowSwitchedNetworkMessage(state), @@ -168,6 +173,7 @@ function mapDispatchToProps(dispatch) { hideIpfsModal: () => dispatch(hideIpfsModal()), hideEditAccountsModal: () => dispatch(hideEditAccountsModal()), hideEditNetworksModal: () => dispatch(hideEditNetworksModal()), + hidePermittedNetworkToast: () => dispatch(hidePermittedNetworkToast()), hideImportTokensModal: () => dispatch(hideImportTokensModal()), hideDeprecatedNetworkModal: () => dispatch(hideDeprecatedNetworkModal()), addPermittedAccount: (activeTabOrigin, address) => diff --git a/ui/selectors/permissions.js b/ui/selectors/permissions.js index 9f08979ee6e0..b90abd0698d5 100644 --- a/ui/selectors/permissions.js +++ b/ui/selectors/permissions.js @@ -60,6 +60,12 @@ export function getPermittedAccounts(state, origin) { ); } +export function getPermittedChains(state, origin) { + return getChainsFromPermission( + getChainsPermissionFromSubject(subjectSelector(state, origin)), + ); +} + /** * Selects the permitted accounts from the eth_accounts permission for the * origin of the current tab. @@ -75,6 +81,14 @@ export function getPermittedAccountsForSelectedTab(state, activeTab) { return getPermittedAccounts(state, activeTab); } +export function getPermittedChainsForCurrentTab(state) { + return getPermittedAccounts(state, getOriginOfCurrentTab(state)); +} + +export function getPermittedChainsForSelectedTab(state, activeTab) { + return getPermittedChains(state, activeTab); +} + /** * Returns a map of permitted accounts by origin for all origins. * diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts index d9dbb2724fec..9d6dffc8ae65 100644 --- a/ui/store/actionConstants.ts +++ b/ui/store/actionConstants.ts @@ -18,6 +18,10 @@ export const SHOW_EDIT_ACCOUNTS_MODAL_OPEN = 'UI_EDIT_ACCOUNTS_MODAL_OPEN'; export const SHOW_EDIT_ACCOUNTS_MODAL_CLOSE = 'UI_EDIT_ACCOUNTS_MODAL_CLOSE'; export const SHOW_EDIT_NETWORKS_MODAL_OPEN = 'UI_EDIT_NETWORKS_MODAL_OPEN'; export const SHOW_EDIT_NETWORKS_MODAL_CLOSE = 'UI_EDIT_NETWORKS_MODAL_CLOSE'; +export const SHOW_PERMITTED_NETWORK_TOAST_OPEN = + 'UI_PERMITTED_NETWORK_TOAST_OPEN'; +export const SHOW_PERMITTED_NETWORK_TOAST_CLOSE = + 'UI_PERMITTED_NETWORK_TOAST_CLOSE'; export const SHOW_IPFS_MODAL_CLOSE = 'UI_IPFS_MODAL_CLOSE'; export const IMPORT_TOKENS_POPOVER_OPEN = 'UI_IMPORT_TOKENS_POPOVER_OPEN'; export const IMPORT_TOKENS_POPOVER_CLOSE = 'UI_IMPORT_TOKENS_POPOVER_CLOSE'; diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 32e7c77886ce..faec3fd0d933 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -130,7 +130,10 @@ import { MetaMaskReduxState, TemporaryMessageDataType, } from './store'; -import { CaveatFactories, PermissionNames } from '../../app/scripts/controllers/permissions/specifications'; +import { + CaveatFactories, + PermissionNames, +} from '../../app/scripts/controllers/permissions/specifications'; import { CaveatTypes } from '../../shared/constants/permissions'; type CustomGasSettings = { @@ -2625,6 +2628,18 @@ export function showEditNetworksModal(): Action { }; } +export function hidePermittedNetworkToast(): Action { + return { + type: actionConstants.SHOW_PERMITTED_NETWORK_TOAST_CLOSE, + }; +} + +export function showPermittedNetworkToast(): Action { + return { + type: actionConstants.SHOW_PERMITTED_NETWORK_TOAST_OPEN, + }; +} + // TODO: Replace `any` with type // eslint-disable-next-line @typescript-eslint/no-explicit-any export function setConfirmationExchangeRates(value: Record) { @@ -5590,15 +5605,13 @@ export async function grantPermittedChain( { subject: { origin: selectedTabOrigin }, approvedPermissions: { - [PermissionNames.permittedChains]: { - caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching]( - [chainId], - ), - ], - }, + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]([chainId]), + ], + }, }, - } + }, ]); } From ecdf8a58dd11c1f8178f997b04436e5aed187d3b Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Sun, 25 Aug 2024 10:59:42 +0100 Subject: [PATCH 017/125] removed unused console --- ui/components/multichain/global-menu/global-menu.js | 2 +- ui/selectors/permissions.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/components/multichain/global-menu/global-menu.js b/ui/components/multichain/global-menu/global-menu.js index 606b53fd13e1..46a1347f7cfb 100644 --- a/ui/components/multichain/global-menu/global-menu.js +++ b/ui/components/multichain/global-menu/global-menu.js @@ -268,7 +268,7 @@ export const GlobalMenu = ({ closeMenu, anchorElement, isOpen }) => { data-testid="global-menu-connected-sites" disabled={hasUnapprovedTransactions} > - "nid" + "global menu" { diff --git a/ui/selectors/permissions.js b/ui/selectors/permissions.js index b90abd0698d5..bcda3bbf5397 100644 --- a/ui/selectors/permissions.js +++ b/ui/selectors/permissions.js @@ -97,7 +97,6 @@ export function getPermittedChainsForSelectedTab(state, activeTab) { */ export function getPermittedAccountsByOrigin(state) { const subjects = getPermissionSubjects(state); - console.log(subjects, "nidhi") return Object.keys(subjects).reduce((acc, subjectKey) => { const accounts = getAccountsFromSubject(subjects[subjectKey]); if (accounts.length > 0) { @@ -109,7 +108,6 @@ export function getPermittedAccountsByOrigin(state) { export function getPermittedChainsByOrigin(state) { const subjects = getPermissionSubjects(state); - console.log(subjects, 'nidhi'); return Object.keys(subjects).reduce((acc, subjectKey) => { const chains = getChainsFromSubject(subjects[subjectKey]); if (chains.length > 0) { From 6c81230f0ac158de43f43765dba4f81640e7fc97 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Mon, 26 Aug 2024 12:19:25 +0100 Subject: [PATCH 018/125] added edit accounts functionality --- app/scripts/metamask-controller.js | 1 - .../edit-accounts-modal.tsx | 134 ++++++++++++++++++ .../edit-networks-modal.js | 24 ++++ .../review-permissions-page.tsx | 8 +- .../choose-account/choose-account.js | 2 +- 5 files changed, 160 insertions(+), 9 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 711621284379..6dfc79f19646 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -3153,7 +3153,6 @@ export default class MetamaskController extends EventEmitter { this.permissionController.grantPermissionsIncremental.bind( this.permissionController, ), - setSecurityAlertsEnabled: preferencesController.setSecurityAlertsEnabled.bind( preferencesController, diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index 3cf1e73fec83..62555d0cae5c 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -1,5 +1,6 @@ import React, { useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +<<<<<<< HEAD import { EthAccountType, InternalAccount, @@ -12,6 +13,17 @@ import { getOrderedConnectedAccountsForConnectedDapp, getOriginOfCurrentTab, getPermissionSubjects, +======= +import { TextVariant } from '../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { + getInternalAccounts, + getNonTestNetworks, + getOrderedConnectedAccountsForConnectedDapp, + getOriginOfCurrentTab, + getPermissionSubjects, + getTestNetworks, +>>>>>>> 66a729057d (added edit accounts functionality) getUpdatedAndSortedAccounts, } from '../../../selectors'; import { @@ -26,6 +38,15 @@ import { ButtonPrimarySize, } from '../../component-library'; import { AccountListItem } from '..'; +<<<<<<< HEAD +======= +import { + EthAccountType, + InternalAccount, + isEvmAccountType, + KeyringAccountType, +} from '@metamask/keyring-api'; +>>>>>>> 66a729057d (added edit accounts functionality) import { MergedInternalAccount } from '../../../selectors/selectors.types'; import { mergeAccounts } from '../account-list-menu/account-list-menu'; import { @@ -33,8 +54,15 @@ import { removePermissionsFor, removePermittedAccount, } from '../../../store/actions'; +<<<<<<< HEAD import { NonEmptyArray } from '@metamask/utils'; import { SubjectsType } from '../pages/connections/components/connections.types'; +======= + +type SendPageYourAccountsProps = { + allowedAccountTypes?: KeyringAccountType[]; +}; +>>>>>>> 66a729057d (added edit accounts functionality) const defaultAllowedAccountTypes = [EthAccountType.Eoa, EthAccountType.Erc4337]; @@ -53,20 +81,33 @@ export const EditAccountsModal: React.FC = ({ const accounts = useSelector(getUpdatedAndSortedAccounts); const internalAccounts = useSelector(getInternalAccounts); const dispatch = useDispatch(); +<<<<<<< HEAD +======= +>>>>>>> 66a729057d (added edit accounts functionality) const mergedAccounts: MergedInternalAccount[] = useMemo(() => { return mergeAccounts(accounts, internalAccounts).filter( (account: InternalAccount) => allowedAccountTypes.includes(account.type), ); +<<<<<<< HEAD }, [accounts, internalAccounts, allowedAccountTypes]); const activeTabOrigin = useSelector(getOriginOfCurrentTab); const subjects = useSelector(getPermissionSubjects); const connectedAccounts = useSelector((state: any) => +======= + }, [accounts, internalAccounts]); + const activeTabOrigin = useSelector(getOriginOfCurrentTab); + const subjects = useSelector(getPermissionSubjects); + const connectedAccounts = useSelector((state) => + // We only consider EVM accounts + // Connections with non-EVM accounts (Bitcoin only for now) are used implicitly and handled by the Bitcoin Snap itself. +>>>>>>> 66a729057d (added edit accounts functionality) getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin).filter( (account: InternalAccount) => isEvmAccountType(account.type), ), ); +<<<<<<< HEAD const connectedAccountsAddresses = connectedAccounts.map( (account: InternalAccount) => account.address, @@ -87,11 +128,37 @@ export const EditAccountsModal: React.FC = ({ (account) => account !== address, ); } +======= + const connectedAccountsAddresses = connectedAccounts.map( + (account: { address: any }) => account.address, + ); + const [selectedAccounts, setSelectedAccounts] = useState( + connectedAccountsAddresses, + ); + const handleAccountClick = (address: string) => { + const index = selectedAccounts.indexOf(address); + let newSelectedAccounts: string[] = []; + + if (index === -1) { + // If address is not already selected, add it to the selectedAccounts array + newSelectedAccounts = [...selectedAccounts, address]; + } else { + // If address is already selected, remove it from the selectedAccounts array + newSelectedAccounts = selectedAccounts.filter( + (_item, idx) => idx !== index, + ); + } + console.log(newSelectedAccounts, 'ggg'); +>>>>>>> 66a729057d (added edit accounts functionality) setSelectedAccounts(newSelectedAccounts); }; const disconnectAllAccounts = () => { const subject = (subjects as SubjectsType)[activeTabOrigin]; +<<<<<<< HEAD +======= + +>>>>>>> 66a729057d (added edit accounts functionality) if (subject) { const permissionMethodNames = Object.values(subject.permissions).map( ({ parentCapability }: { parentCapability: string }) => @@ -108,10 +175,16 @@ export const EditAccountsModal: React.FC = ({ ), ); } +<<<<<<< HEAD +======= + + console.log('all disconnected'); +>>>>>>> 66a729057d (added edit accounts functionality) } }; const managePermittedAccounts = ( +<<<<<<< HEAD selectedAccounts: string[], connectedAccountsAddresses: string[], activeTabOrigin: string, @@ -126,15 +199,57 @@ export const EditAccountsModal: React.FC = ({ }); } +======= + selectedAccounts, + connectedAccountsAddresses, + activeTabOrigin, + ) => { + // Check if inputs are arrays; if not, set them to empty arrays + if (!Array.isArray(selectedAccounts)) { + console.error('selectedAccounts is not an array:', selectedAccounts); + selectedAccounts = []; + } + + if (!Array.isArray(connectedAccountsAddresses)) { + console.error( + 'connectedAccountsAddresses is not an array:', + connectedAccountsAddresses, + ); + connectedAccountsAddresses = []; + } + + // Find new elements in selectedAccounts that are not in connectedAccountsAddresses +>>>>>>> 66a729057d (added edit accounts functionality) const newElements = selectedAccounts.filter( (account) => !connectedAccountsAddresses.includes(account), ); +<<<<<<< HEAD if (newElements.length > 0) { dispatch(addMorePermittedAccounts(activeTabOrigin, newElements)); } }; +======= + // Dispatch addMorePermittedAccounts for new elements + dispatch(addMorePermittedAccounts(activeTabOrigin, newElements)); + + // Find elements in connectedAccountsAddresses that are not in selectedAccounts + const removedElements = connectedAccountsAddresses.filter( + (account) => !selectedAccounts.includes(account), + ); + + console.log(removedElements, newElements, 'remo'); + + // Dispatch removePermittedAccounts for each removed element + removedElements.forEach((account) => { + const selectedAccount = [account]; // Assuming selectedAccounts should be an array of one + dispatch(removePermittedAccount(activeTabOrigin, selectedAccount)); + }); + }; + + console.log(selectedAccounts, connectedAccountsAddresses); +>>>>>>> 66a729057d (added edit accounts functionality) return ( = ({ startAccessory={ } selected={false} +======= + // onClick={() => (allAreSelected() ? deselectAll() : selectAll())} + /> + } +>>>>>>> 66a729057d (added edit accounts functionality) /> ))} @@ -167,6 +288,7 @@ export const EditAccountsModal: React.FC = ({ {selectedAccounts.length === 0 ? ( { disconnectAllAccounts(); onClose(); @@ -174,12 +296,18 @@ export const EditAccountsModal: React.FC = ({ size={ButtonPrimarySize.Lg} block danger +======= + onClick={() => { disconnectAllAccounts(); onClose(); }} + size={ButtonPrimarySize.Lg} + block +>>>>>>> 66a729057d (added edit accounts functionality) > {t('disconnect')} ) : ( { onClick(); managePermittedAccounts( @@ -189,6 +317,9 @@ export const EditAccountsModal: React.FC = ({ ); onClose(); }} +======= + onClick={() => { managePermittedAccounts(selectedAccounts, connectedAccountsAddresses, activeTabOrigin); onClose(); }} +>>>>>>> 66a729057d (added edit accounts functionality) size={ButtonPrimarySize.Lg} block > @@ -200,3 +331,6 @@ export const EditAccountsModal: React.FC = ({ ); }; + +// if unchecked and connected then remove the account from connected list +// if checked and new update the connected accounts array diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index c502a6264205..1bfb89262174 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import React, { useState } from 'react'; +<<<<<<< HEAD import { useDispatch, useSelector } from 'react-redux'; import { TextVariant } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; @@ -9,6 +10,12 @@ import { getPermittedChainsByOrigin, getTestNetworks, } from '../../../selectors'; +======= +import { useSelector } from 'react-redux'; +import { TextVariant } from '../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { getNonTestNetworks, getPermittedChainsByOrigin, getTestNetworks } from '../../../selectors'; +>>>>>>> 66a729057d (added edit accounts functionality) import { Modal, ModalOverlay, @@ -34,12 +41,19 @@ export const EditNetworksModal = ({ onClose, onClick }) => { const testNetworks = useSelector(getTestNetworks); const chains = useSelector(getPermittedChainsByOrigin); const permittedChains = Object.values(chains); +<<<<<<< HEAD const activeTabOrigin = useSelector(getOriginOfCurrentTab); +======= +>>>>>>> 66a729057d (added edit accounts functionality) const flattenedPermittedChains = permittedChains.flat(); const [selectedChains, setSelectedChains] = useState( flattenedPermittedChains, ); +<<<<<<< HEAD +======= + console.log(chains, selectedChains); +>>>>>>> 66a729057d (added edit accounts functionality) const handleAccountClick = (chainId) => { const index = selectedChains.indexOf(chainId); let newSelectedChains = []; @@ -49,6 +63,7 @@ export const EditNetworksModal = ({ onClose, onClick }) => { newSelectedChains = [...selectedChains, chainId]; } else { // If chainId is already selected, remove it from the selectedChains array +<<<<<<< HEAD newSelectedChains = selectedChains.filter((_item, idx) => idx !== index); } setSelectedChains(newSelectedChains); @@ -74,6 +89,15 @@ export const EditNetworksModal = ({ onClose, onClick }) => { dispatch(removePermittedChain(activeTabOrigin, selectedChain)); }); }; +======= + newSelectedChains = selectedChains.filter( + (_item, idx) => idx !== index, + ); + } + console.log(newSelectedChains, 'ggg'); + setSelectedChains(newSelectedChains); + }; +>>>>>>> 66a729057d (added edit accounts functionality) return ( { const grantedNetworks = networksList.filter( (net: { chainId: any }) => connectedNetworks.indexOf(net.chainId) !== -1, ); - console.log( - networksList, - connectedNetworks, - grantedNetworks, - connectedAccounts, - 'sitesConnectionsList', - ); + return ( Date: Mon, 26 Aug 2024 13:07:09 +0100 Subject: [PATCH 019/125] updated toast --- .../edit-accounts-modal.tsx | 134 ------------------ .../edit-networks-modal.js | 24 ---- .../network-list-menu/network-list-menu.js | 8 +- .../review-permissions-page.tsx | 41 +++++- ui/pages/routes/routes.component.js | 2 +- 5 files changed, 48 insertions(+), 161 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index 62555d0cae5c..3cf1e73fec83 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -1,6 +1,5 @@ import React, { useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -<<<<<<< HEAD import { EthAccountType, InternalAccount, @@ -13,17 +12,6 @@ import { getOrderedConnectedAccountsForConnectedDapp, getOriginOfCurrentTab, getPermissionSubjects, -======= -import { TextVariant } from '../../../helpers/constants/design-system'; -import { useI18nContext } from '../../../hooks/useI18nContext'; -import { - getInternalAccounts, - getNonTestNetworks, - getOrderedConnectedAccountsForConnectedDapp, - getOriginOfCurrentTab, - getPermissionSubjects, - getTestNetworks, ->>>>>>> 66a729057d (added edit accounts functionality) getUpdatedAndSortedAccounts, } from '../../../selectors'; import { @@ -38,15 +26,6 @@ import { ButtonPrimarySize, } from '../../component-library'; import { AccountListItem } from '..'; -<<<<<<< HEAD -======= -import { - EthAccountType, - InternalAccount, - isEvmAccountType, - KeyringAccountType, -} from '@metamask/keyring-api'; ->>>>>>> 66a729057d (added edit accounts functionality) import { MergedInternalAccount } from '../../../selectors/selectors.types'; import { mergeAccounts } from '../account-list-menu/account-list-menu'; import { @@ -54,15 +33,8 @@ import { removePermissionsFor, removePermittedAccount, } from '../../../store/actions'; -<<<<<<< HEAD import { NonEmptyArray } from '@metamask/utils'; import { SubjectsType } from '../pages/connections/components/connections.types'; -======= - -type SendPageYourAccountsProps = { - allowedAccountTypes?: KeyringAccountType[]; -}; ->>>>>>> 66a729057d (added edit accounts functionality) const defaultAllowedAccountTypes = [EthAccountType.Eoa, EthAccountType.Erc4337]; @@ -81,33 +53,20 @@ export const EditAccountsModal: React.FC = ({ const accounts = useSelector(getUpdatedAndSortedAccounts); const internalAccounts = useSelector(getInternalAccounts); const dispatch = useDispatch(); -<<<<<<< HEAD -======= ->>>>>>> 66a729057d (added edit accounts functionality) const mergedAccounts: MergedInternalAccount[] = useMemo(() => { return mergeAccounts(accounts, internalAccounts).filter( (account: InternalAccount) => allowedAccountTypes.includes(account.type), ); -<<<<<<< HEAD }, [accounts, internalAccounts, allowedAccountTypes]); const activeTabOrigin = useSelector(getOriginOfCurrentTab); const subjects = useSelector(getPermissionSubjects); const connectedAccounts = useSelector((state: any) => -======= - }, [accounts, internalAccounts]); - const activeTabOrigin = useSelector(getOriginOfCurrentTab); - const subjects = useSelector(getPermissionSubjects); - const connectedAccounts = useSelector((state) => - // We only consider EVM accounts - // Connections with non-EVM accounts (Bitcoin only for now) are used implicitly and handled by the Bitcoin Snap itself. ->>>>>>> 66a729057d (added edit accounts functionality) getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin).filter( (account: InternalAccount) => isEvmAccountType(account.type), ), ); -<<<<<<< HEAD const connectedAccountsAddresses = connectedAccounts.map( (account: InternalAccount) => account.address, @@ -128,37 +87,11 @@ export const EditAccountsModal: React.FC = ({ (account) => account !== address, ); } -======= - const connectedAccountsAddresses = connectedAccounts.map( - (account: { address: any }) => account.address, - ); - const [selectedAccounts, setSelectedAccounts] = useState( - connectedAccountsAddresses, - ); - const handleAccountClick = (address: string) => { - const index = selectedAccounts.indexOf(address); - let newSelectedAccounts: string[] = []; - - if (index === -1) { - // If address is not already selected, add it to the selectedAccounts array - newSelectedAccounts = [...selectedAccounts, address]; - } else { - // If address is already selected, remove it from the selectedAccounts array - newSelectedAccounts = selectedAccounts.filter( - (_item, idx) => idx !== index, - ); - } - console.log(newSelectedAccounts, 'ggg'); ->>>>>>> 66a729057d (added edit accounts functionality) setSelectedAccounts(newSelectedAccounts); }; const disconnectAllAccounts = () => { const subject = (subjects as SubjectsType)[activeTabOrigin]; -<<<<<<< HEAD -======= - ->>>>>>> 66a729057d (added edit accounts functionality) if (subject) { const permissionMethodNames = Object.values(subject.permissions).map( ({ parentCapability }: { parentCapability: string }) => @@ -175,16 +108,10 @@ export const EditAccountsModal: React.FC = ({ ), ); } -<<<<<<< HEAD -======= - - console.log('all disconnected'); ->>>>>>> 66a729057d (added edit accounts functionality) } }; const managePermittedAccounts = ( -<<<<<<< HEAD selectedAccounts: string[], connectedAccountsAddresses: string[], activeTabOrigin: string, @@ -199,57 +126,15 @@ export const EditAccountsModal: React.FC = ({ }); } -======= - selectedAccounts, - connectedAccountsAddresses, - activeTabOrigin, - ) => { - // Check if inputs are arrays; if not, set them to empty arrays - if (!Array.isArray(selectedAccounts)) { - console.error('selectedAccounts is not an array:', selectedAccounts); - selectedAccounts = []; - } - - if (!Array.isArray(connectedAccountsAddresses)) { - console.error( - 'connectedAccountsAddresses is not an array:', - connectedAccountsAddresses, - ); - connectedAccountsAddresses = []; - } - - // Find new elements in selectedAccounts that are not in connectedAccountsAddresses ->>>>>>> 66a729057d (added edit accounts functionality) const newElements = selectedAccounts.filter( (account) => !connectedAccountsAddresses.includes(account), ); -<<<<<<< HEAD if (newElements.length > 0) { dispatch(addMorePermittedAccounts(activeTabOrigin, newElements)); } }; -======= - // Dispatch addMorePermittedAccounts for new elements - dispatch(addMorePermittedAccounts(activeTabOrigin, newElements)); - - // Find elements in connectedAccountsAddresses that are not in selectedAccounts - const removedElements = connectedAccountsAddresses.filter( - (account) => !selectedAccounts.includes(account), - ); - - console.log(removedElements, newElements, 'remo'); - - // Dispatch removePermittedAccounts for each removed element - removedElements.forEach((account) => { - const selectedAccount = [account]; // Assuming selectedAccounts should be an array of one - dispatch(removePermittedAccount(activeTabOrigin, selectedAccount)); - }); - }; - - console.log(selectedAccounts, connectedAccountsAddresses); ->>>>>>> 66a729057d (added edit accounts functionality) return ( = ({ startAccessory={ } selected={false} -======= - // onClick={() => (allAreSelected() ? deselectAll() : selectAll())} - /> - } ->>>>>>> 66a729057d (added edit accounts functionality) /> ))} @@ -288,7 +167,6 @@ export const EditAccountsModal: React.FC = ({ {selectedAccounts.length === 0 ? ( { disconnectAllAccounts(); onClose(); @@ -296,18 +174,12 @@ export const EditAccountsModal: React.FC = ({ size={ButtonPrimarySize.Lg} block danger -======= - onClick={() => { disconnectAllAccounts(); onClose(); }} - size={ButtonPrimarySize.Lg} - block ->>>>>>> 66a729057d (added edit accounts functionality) > {t('disconnect')} ) : ( { onClick(); managePermittedAccounts( @@ -317,9 +189,6 @@ export const EditAccountsModal: React.FC = ({ ); onClose(); }} -======= - onClick={() => { managePermittedAccounts(selectedAccounts, connectedAccountsAddresses, activeTabOrigin); onClose(); }} ->>>>>>> 66a729057d (added edit accounts functionality) size={ButtonPrimarySize.Lg} block > @@ -331,6 +200,3 @@ export const EditAccountsModal: React.FC = ({ ); }; - -// if unchecked and connected then remove the account from connected list -// if checked and new update the connected accounts array diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index 1bfb89262174..c502a6264205 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -1,6 +1,5 @@ import PropTypes from 'prop-types'; import React, { useState } from 'react'; -<<<<<<< HEAD import { useDispatch, useSelector } from 'react-redux'; import { TextVariant } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; @@ -10,12 +9,6 @@ import { getPermittedChainsByOrigin, getTestNetworks, } from '../../../selectors'; -======= -import { useSelector } from 'react-redux'; -import { TextVariant } from '../../../helpers/constants/design-system'; -import { useI18nContext } from '../../../hooks/useI18nContext'; -import { getNonTestNetworks, getPermittedChainsByOrigin, getTestNetworks } from '../../../selectors'; ->>>>>>> 66a729057d (added edit accounts functionality) import { Modal, ModalOverlay, @@ -41,19 +34,12 @@ export const EditNetworksModal = ({ onClose, onClick }) => { const testNetworks = useSelector(getTestNetworks); const chains = useSelector(getPermittedChainsByOrigin); const permittedChains = Object.values(chains); -<<<<<<< HEAD const activeTabOrigin = useSelector(getOriginOfCurrentTab); -======= ->>>>>>> 66a729057d (added edit accounts functionality) const flattenedPermittedChains = permittedChains.flat(); const [selectedChains, setSelectedChains] = useState( flattenedPermittedChains, ); -<<<<<<< HEAD -======= - console.log(chains, selectedChains); ->>>>>>> 66a729057d (added edit accounts functionality) const handleAccountClick = (chainId) => { const index = selectedChains.indexOf(chainId); let newSelectedChains = []; @@ -63,7 +49,6 @@ export const EditNetworksModal = ({ onClose, onClick }) => { newSelectedChains = [...selectedChains, chainId]; } else { // If chainId is already selected, remove it from the selectedChains array -<<<<<<< HEAD newSelectedChains = selectedChains.filter((_item, idx) => idx !== index); } setSelectedChains(newSelectedChains); @@ -89,15 +74,6 @@ export const EditNetworksModal = ({ onClose, onClick }) => { dispatch(removePermittedChain(activeTabOrigin, selectedChain)); }); }; -======= - newSelectedChains = selectedChains.filter( - (_item, idx) => idx !== index, - ); - } - console.log(newSelectedChains, 'ggg'); - setSelectedChains(newSelectedChains); - }; ->>>>>>> 66a729057d (added edit accounts functionality) return ( { const domains = useSelector(getAllDomains); const chains = useSelector(getPermittedChainsByOrigin); const permittedChains = Object.values(chains); + const flattenedPermittedChains = permittedChains.flat(); console.log(permittedChains, 'hhh'); const dispatch = useDispatch(); const history = useHistory(); @@ -308,10 +309,15 @@ export const NetworkListMenu = ({ onClose }) => { selected={isCurrentNetwork && !focusSearch} focus={isCurrentNetwork && !focusSearch} onClick={() => { + console.log(permittedChains, network.chainId); dispatch(toggleNetworkMenu()); dispatch(setActiveNetwork(network.providerType || network.id)); grantPermittedChain(selectedTabOrigin, network.chainId); - if (!permittedChains.includes(network.chainId)) { + if (!flattenedPermittedChains.includes(network.chainId)) { + console.log( + 'hello hi bye', + permittedChains.includes(network.chainId), + ); dispatch(showPermittedNetworkToast()); } if ( diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 9d60ef347c08..31a9906c9142 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -18,15 +18,20 @@ import { getNonTestNetworks, getOrderedConnectedAccountsForConnectedDapp, getOrderedNetworksList, + getPermissionSubjects, getPermittedChainsByOrigin, getPermittedChainsForSelectedTab, } from '../../../../selectors/index'; +import { removePermissionsFor } from '../../../../store/actions'; import { AvatarFavicon, AvatarFaviconSize, Box, + Button, ButtonIcon, ButtonIconSize, + ButtonSize, + ButtonVariant, Icon, IconName, IconSize, @@ -52,11 +57,33 @@ export const ReviewPermissions = () => { const connectedAccounts = useSelector((state) => getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin), ); - + const subjects = useSelector(getPermissionSubjects); const grantedNetworks = networksList.filter( (net: { chainId: any }) => connectedNetworks.indexOf(net.chainId) !== -1, ); + const disconnectAllAccounts = () => { + const subject = (subjects as SubjectsType)[activeTabOrigin]; + + if (subject) { + const permissionMethodNames = Object.values(subject.permissions).map( + ({ parentCapability }: { parentCapability: string }) => + parentCapability, + ) as string[]; + if (permissionMethodNames.length > 0) { + const permissionsRecord: Record = { + [activeTabOrigin]: permissionMethodNames, + }; + dispatch( + removePermissionsFor( + permissionsRecord as Record>, + ), + ); + } + + console.log('all disconnected'); + } + }; return ( { +
+ +
); }; diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 73f33bb40945..7120644dd7d9 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -801,7 +801,7 @@ export default class Routes extends Component { `${REVIEW_PERMISSIONS}/${safeEncodedHost}`, ) } - onClose={hidePermittedNetworkToast} + onClose={() => hidePermittedNetworkToast()} /> ) : null} From d2da713610bb4dc30c64373dd27569dcdc45b0f1 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Mon, 26 Aug 2024 14:11:34 +0100 Subject: [PATCH 020/125] added no connection page --- .../review-permissions-page/review-permissions-page.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 31a9906c9142..2dbad37a4c1f 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -37,6 +37,7 @@ import { IconSize, Text, } from '../../../component-library'; +import { NoConnectionContent } from '../connections/components/no-connection'; import { Content, Footer, Header, Page } from '../page'; import { SiteCell } from './index'; @@ -89,6 +90,7 @@ export const ReviewPermissions = () => { data-testid="connections-page" className="main-container connections-page" > + {connectedAccounts.length > 0 ? <>
{ > {t('disconnectAllAccounts')} - + : +} ); }; From 5e0cacf153d43621e190f22ef3d0d4748ac951bb Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 28 Aug 2024 15:29:32 +0100 Subject: [PATCH 021/125] added edit networks functionality --- .../controllers/permissions/background-api.js | 31 +++++++++++++- ui/store/actions.ts | 40 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index d3a29f129379..09ba4e1a4aaf 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -26,7 +26,6 @@ export function getPermissionBackgroundApiMethods(permissionController) { // To add more than one account when already connected to the dapp addMorePermittedAccounts: (origin, accounts) => addMoreAccounts(origin, accounts), - removePermittedAccount: (origin, account) => { const { value: existingAccounts } = permissionController.getCaveat( origin, @@ -57,6 +56,36 @@ export function getPermissionBackgroundApiMethods(permissionController) { } }, + removePermittedChain: (origin, chain) => { + const { value: existingChains } = permissionController.getCaveat( + origin, + RestrictedMethods.permittedChains, + CaveatTypes.restrictNetworkSwitching, + ); + + const remainingChains = existingChains.filter( + (existingChain) => existingChain !== chain, + ); + + if (remainingChains.length === existingChains.length) { + return; + } + + if (remainingChains.length === 0) { + permissionController.revokePermission( + origin, + RestrictedMethods.permittedChains, + ); + } else { + permissionController.updateCaveat( + origin, + RestrictedMethods.permittedChains, + CaveatTypes.restrictNetworkSwitching, + remainingChains, + ); + } + }, + requestAccountsPermissionWithId: async (origin) => { const id = nanoid(); permissionController.requestPermissions( diff --git a/ui/store/actions.ts b/ui/store/actions.ts index faec3fd0d933..8c86008f2d3b 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -1844,6 +1844,28 @@ export function removePermittedAccount( }; } +export function removePermittedChain( + origin: string, + chain: string, +): ThunkAction { + return async (dispatch: MetaMaskReduxDispatch) => { + await new Promise((resolve, reject) => { + callBackgroundMethod( + 'removePermittedChain', + [origin, chain], + (error) => { + if (error) { + reject(error); + return; + } + resolve(); + }, + ); + }); + await forceUpdateMetamaskState(dispatch); + }; +} + export function showAccountsPage() { return { type: actionConstants.SHOW_ACCOUNTS_PAGE, @@ -5615,6 +5637,24 @@ export async function grantPermittedChain( ]); } +export async function grantPermittedChains( + selectedTabOrigin: string, + chainId?: [], +): Promise { + return await submitRequestToBackground('grantPermissions', [ + { + subject: { origin: selectedTabOrigin }, + approvedPermissions: { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]([chainId]), + ], + }, + }, + }, + ]); +} + export async function decodeTransactionData({ transactionData, contractAddress, From aa4c3688cc605cffe01c7d9f9825cd6b4b21427f Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 28 Aug 2024 10:19:46 -0500 Subject: [PATCH 022/125] fix --- ui/store/actions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 8c86008f2d3b..aa90b14ea210 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -5639,7 +5639,7 @@ export async function grantPermittedChain( export async function grantPermittedChains( selectedTabOrigin: string, - chainId?: [], + chainIds: [], ): Promise { return await submitRequestToBackground('grantPermissions', [ { @@ -5647,7 +5647,7 @@ export async function grantPermittedChains( approvedPermissions: { [PermissionNames.permittedChains]: { caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching]([chainId]), + CaveatFactories[CaveatTypes.restrictNetworkSwitching](chainIds), ], }, }, From 5ce582ab96ef99cbedc6f977385e7c7347a672cb Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 3 Sep 2024 12:38:14 +0100 Subject: [PATCH 023/125] removed console statements --- .../multichain/network-list-menu/network-list-menu.js | 6 ------ ui/store/actions.ts | 1 + 2 files changed, 1 insertion(+), 6 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 96d46b70d3b4..7e8b10e99ce8 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -107,7 +107,6 @@ export const NetworkListMenu = ({ onClose }) => { const chains = useSelector(getPermittedChainsByOrigin); const permittedChains = Object.values(chains); const flattenedPermittedChains = permittedChains.flat(); - console.log(permittedChains, 'hhh'); const dispatch = useDispatch(); const history = useHistory(); const trackEvent = useContext(MetaMetricsContext); @@ -309,15 +308,10 @@ export const NetworkListMenu = ({ onClose }) => { selected={isCurrentNetwork && !focusSearch} focus={isCurrentNetwork && !focusSearch} onClick={() => { - console.log(permittedChains, network.chainId); dispatch(toggleNetworkMenu()); dispatch(setActiveNetwork(network.providerType || network.id)); grantPermittedChain(selectedTabOrigin, network.chainId); if (!flattenedPermittedChains.includes(network.chainId)) { - console.log( - 'hello hi bye', - permittedChains.includes(network.chainId), - ); dispatch(showPermittedNetworkToast()); } if ( diff --git a/ui/store/actions.ts b/ui/store/actions.ts index aa90b14ea210..f64a8244962b 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -1826,6 +1826,7 @@ export function removePermittedAccount( origin: string, address: string, ): ThunkAction { + console.log("removed is done") return async (dispatch: MetaMaskReduxDispatch) => { await new Promise((resolve, reject) => { callBackgroundMethod( From acaddf8c150f12c370c6bdd944fdd212e1dfa51f Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 4 Sep 2024 12:22:37 +0100 Subject: [PATCH 024/125] updated final screen for editing flow --- app/_locales/en/messages.json | 20 +- test/data/mock-send-state.json | 2 - test/data/mock-state.json | 2 - .../app-header-unlocked-content.tsx | 5 +- .../multichain/global-menu/global-menu.js | 19 -- .../network-list-menu/network-list-menu.js | 10 +- .../permissions-page/permissions-page.js | 4 + .../review-permissions-page.tsx | 175 +++++++++++------- .../site-cell/site-cell-menu.tsx | 22 +-- .../site-cell/site-cell.tsx | 36 +++- ui/ducks/app/app.ts | 28 --- .../choose-account/choose-account.js | 2 +- ui/pages/routes/routes.component.js | 18 +- ui/pages/routes/routes.container.js | 6 - ui/selectors/permissions.js | 2 +- ui/store/actionConstants.ts | 4 - ui/store/actions.ts | 54 ++---- 17 files changed, 192 insertions(+), 217 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 13c657646160..d84bbfcfe7ed 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -162,6 +162,10 @@ "accountOptions": { "message": "Account options" }, + "accountPermissionToast": { + "message": "Account permissions updated for $1", + "description": "$1 represents connected dapp" + }, "accountSelectionRequired": { "message": "You need to select an account!" }, @@ -174,6 +178,9 @@ "accountsConnected": { "message": "Accounts connected" }, + "accountsPermissionsTitle": { + "message": "See your accounts and suggest transactions" + }, "active": { "message": "Active" }, @@ -641,9 +648,6 @@ "message": "Approved on $1 for $2", "description": "$1 is the approval date for a permission. $2 is the AvatarGroup component displaying account images." }, - "permittedChainToastUpdate": { - "message": "$1 has been given access to $2." - }, "areYouSure": { "message": "Are you sure?" }, @@ -1820,9 +1824,6 @@ "encryptionPublicKeyRequest": { "message": "Request encryption public key" }, - "accountsPermissionsTitle": { - "message": "See your accounts and suggest transactions" - }, "endpointReturnedDifferentChainId": { "message": "The RPC URL you have entered returned a different chain ID ($1). Please update the Chain ID to match the RPC URL of the network you are trying to add.", "description": "$1 is the return value of eth_chainId from an RPC endpoint" @@ -3088,6 +3089,10 @@ "networkOptions": { "message": "Network options" }, + "networkPermissionToast": { + "message": "Network permissions updated for $1", + "description": "$1 represents connected dapp" + }, "networkProvider": { "message": "Network provider" }, @@ -4039,6 +4044,9 @@ "permitSimulationDetailInfo": { "message": "You're giving the spender permission to spend this many tokens from your account." }, + "permittedChainToastUpdate": { + "message": "$1 has been given access to $2." + }, "personalAddressDetected": { "message": "Personal address detected. Input the token contract address." }, diff --git a/test/data/mock-send-state.json b/test/data/mock-send-state.json index 7dbf0967f4eb..1f8e6988e293 100644 --- a/test/data/mock-send-state.json +++ b/test/data/mock-send-state.json @@ -12,8 +12,6 @@ "appState": { "networkDropdownOpen": false, "importNftsModal": { "open": false }, - "showEditAccountsModalOpen": false, - "showEditNetworksModalOpen": false, "showPermittedNetworkToastOpen": false, "gasIsLoading": false, "isLoading": false, diff --git a/test/data/mock-state.json b/test/data/mock-state.json index f5e40d0b2495..b755b9bed3d5 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -14,8 +14,6 @@ "importNftsModal": { "open": false }, - "showEditAccountsModalOpen": false, - "showEditNetworksModalOpen": false, "showPermittedNetworkToastOpen": false, "gasIsLoading": false, "isLoading": false, diff --git a/ui/components/multichain/app-header/app-header-unlocked-content.tsx b/ui/components/multichain/app-header/app-header-unlocked-content.tsx index 03e73b6e757d..b62c52e06696 100644 --- a/ui/components/multichain/app-header/app-header-unlocked-content.tsx +++ b/ui/components/multichain/app-header/app-header-unlocked-content.tsx @@ -51,7 +51,7 @@ import { MetaMetricsContext } from '../../../contexts/metametrics'; import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'; import { MINUTE } from '../../../../shared/constants/time'; import { NotificationsTagCounter } from '../notifications-tag-counter'; -import { CONNECTIONS } from '../../../helpers/constants/routes'; +import { CONNECTIONS, REVIEW_PERMISSIONS } from '../../../helpers/constants/routes'; import { MultichainNetwork } from '../../../selectors/multichain'; type AppHeaderUnlockedContentProps = { @@ -115,6 +115,9 @@ export const AppHeaderUnlockedContent = ({ }; const handleConnectionsRoute = () => { + if (process.env.CHAIN_PERMISSIONS) { + history.push(`${REVIEW_PERMISSIONS}/${encodeURIComponent(origin)}`); + } history.push(`${CONNECTIONS}/${encodeURIComponent(origin)}`); }; diff --git a/ui/components/multichain/global-menu/global-menu.js b/ui/components/multichain/global-menu/global-menu.js index 46a1347f7cfb..5c51f057940f 100644 --- a/ui/components/multichain/global-menu/global-menu.js +++ b/ui/components/multichain/global-menu/global-menu.js @@ -252,25 +252,6 @@ export const GlobalMenu = ({ closeMenu, anchorElement, isOpen }) => { > {t('allPermissions')} - { - history.push(REVIEW_PERMISSIONS); - trackEvent({ - event: MetaMetricsEventName.NavPermissionsOpened, - category: MetaMetricsEventCategory.Navigation, - properties: { - location: METRICS_LOCATION, - }, - }); - closeMenu(); - }} - data-testid="global-menu-connected-sites" - disabled={hasUnapprovedTransactions} - > - "global menu" - - { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) mmiPortfolioEnabled && ( 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 7e8b10e99ce8..f89655a5a713 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -176,7 +176,7 @@ export const NetworkListMenu = ({ onClose }) => { return sortedNonTestNetworks; }; - //check if not granted chain is clicked, then show the toast and grant permission + // check if not granted chain is clicked, then show the toast and grant permission const networksList = newOrderNetworks(); const [items, setItems] = useState([...networksList]); @@ -310,9 +310,11 @@ export const NetworkListMenu = ({ onClose }) => { onClick={() => { dispatch(toggleNetworkMenu()); dispatch(setActiveNetwork(network.providerType || network.id)); - grantPermittedChain(selectedTabOrigin, network.chainId); - if (!flattenedPermittedChains.includes(network.chainId)) { - dispatch(showPermittedNetworkToast()); + if (process.env.CHAIN_PERMISSIONS) { + grantPermittedChain(selectedTabOrigin, network.chainId); + if (!flattenedPermittedChains.includes(network.chainId)) { + dispatch(showPermittedNetworkToast()); + } } if ( useRequestQueue && diff --git a/ui/components/multichain/pages/permissions-page/permissions-page.js b/ui/components/multichain/pages/permissions-page/permissions-page.js index a9b6e8467969..1089f3181a31 100644 --- a/ui/components/multichain/pages/permissions-page/permissions-page.js +++ b/ui/components/multichain/pages/permissions-page/permissions-page.js @@ -25,6 +25,7 @@ import { import { CONNECTIONS, DEFAULT_ROUTE, + REVIEW_PERMISSIONS, } from '../../../../helpers/constants/routes'; import { getOnboardedInThisUISession, @@ -53,6 +54,9 @@ export const PermissionsPage = () => { const handleConnectionClick = (connection) => { const hostName = connection.origin; const safeEncodedHost = encodeURIComponent(hostName); + if (process.env.CHAIN_PERMISSIONS) { + history.push(`${REVIEW_PERMISSIONS}/${safeEncodedHost}`); + } history.push(`${CONNECTIONS}/${safeEncodedHost}`); }; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 2dbad37a4c1f..966fe4cd6fd3 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -4,7 +4,9 @@ import { useHistory, useParams } from 'react-router-dom'; import { AlignItems, BackgroundColor, + BlockSize, Display, + FlexDirection, IconColor, JustifyContent, TextAlign, @@ -21,7 +23,7 @@ import { getPermissionSubjects, getPermittedChainsByOrigin, getPermittedChainsForSelectedTab, -} from '../../../../selectors/index'; +} from '../../../../selectors'; import { removePermissionsFor } from '../../../../store/actions'; import { AvatarFavicon, @@ -37,9 +39,10 @@ import { IconSize, Text, } from '../../../component-library'; +import { ToastContainer, Toast } from '../..'; import { NoConnectionContent } from '../connections/components/no-connection'; import { Content, Footer, Header, Page } from '../page'; -import { SiteCell } from './index'; +import { SiteCell } from '.'; export const ReviewPermissions = () => { const t = useI18nContext(); @@ -47,7 +50,8 @@ export const ReviewPermissions = () => { const history = useHistory(); const urlParams: { origin: string } = useParams(); const securedOrigin = decodeURIComponent(urlParams.origin); - + const [showAccountToast, setShowAccountToast] = useState(false); + const [showNetworkToast, setShowNetworkToast] = useState(false); const activeTabOrigin: string = securedOrigin; const subjectMetadata = useSelector(getConnectedSitesList); const connectedSubjectsMetadata = subjectMetadata[activeTabOrigin]; @@ -62,6 +66,7 @@ export const ReviewPermissions = () => { const grantedNetworks = networksList.filter( (net: { chainId: any }) => connectedNetworks.indexOf(net.chainId) !== -1, ); + const hostName = getURLHost(securedOrigin); const disconnectAllAccounts = () => { const subject = (subjects as SubjectsType)[activeTabOrigin]; @@ -81,8 +86,6 @@ export const ReviewPermissions = () => { ), ); } - - console.log('all disconnected'); } }; return ( @@ -90,65 +93,111 @@ export const ReviewPermissions = () => { data-testid="connections-page" className="main-container connections-page" > - {connectedAccounts.length > 0 ? <> -
(history as any).goBack()} - /> - } - > - - {connectedSubjectsMetadata?.iconUrl ? ( - - ) : ( - - )} - 0 ? ( + <> +
(history as any).goBack()} + /> + } > - {getURLHost(securedOrigin)} - - -
- - - -
- -
: -} + + {connectedSubjectsMetadata?.iconUrl ? ( + + ) : ( + + )} + + {hostName} + + +
+ + setShowAccountToast(true)} + onNetworksClick={() => setShowNetworkToast(true)} + /> + +
+ + {showAccountToast ? ( + + setShowAccountToast(false)} + startAdornment={ + + } + /> + + ) : null} + {showNetworkToast ? ( + + setShowNetworkToast(false)} + startAdornment={ + + } + /> + + ) : null} + + +
{' '} + + ) : ( + + )} ); }; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx index 8f0fa843c691..a8a94af75951 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx @@ -96,18 +96,16 @@ export const SiteCellMenu = ({ > - { - setShowPermissionModal(true); - onClose(); - }} - > - - {t('permissionDetails')} - - + { + setShowPermissionModal(true); + onClose(); + }} + > + {t('permissionDetails')} + diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 4aeb5a72b5f3..676b87fa52b2 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -1,6 +1,7 @@ -import React from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { SubjectType } from '@metamask/permission-controller'; +import { useDispatch } from 'react-redux'; import { AlignItems, BackgroundColor, @@ -29,15 +30,15 @@ import { } from '../../../../component-library'; import { getURLHost } from '../../../../../helpers/utils/util'; import { ConnectionListTooltip } from '../../permissions-page/connection-list-tooltip/connection-list-tooltip'; -import { - showEditAccountsModal, - showEditNetworksModal, -} from '../../../../../store/actions'; -import { useDispatch } from 'react-redux'; -import { AvatarGroup } from '../../../index'; +import { AvatarGroup, EditAccountsModal, EditNetworksModal } from '../../..'; import { AvatarType } from '../../../avatar-group/avatar-group.types'; -export const SiteCell = ({ networks, accounts }) => { +export const SiteCell = ({ + networks, + accounts, + onAccountsClick, + onNetworksClick, +}) => { const t = useI18nContext(); const dispatch = useDispatch(); const avatarNetworksData = networks.map((network) => ({ @@ -47,6 +48,9 @@ export const SiteCell = ({ networks, accounts }) => { const avatarAccountsData = accounts.map((account) => ({ avatarValue: account.address, })); + const [showEditAccountsModal, setShowEditAccountsModal] = useState(false); + const [showEditNetworksModal, setShowEditNetworksModal] = useState(false); + return ( <> { style={{ flex: '1', alignSelf: 'center' }} gap={2} onClick={() => { - dispatch(showEditAccountsModal()); + setShowEditAccountsModal(true); }} > { style={{ flex: '1', alignSelf: 'center' }} gap={2} onClick={() => { - dispatch(showEditNetworksModal()); + setShowEditNetworksModal(true); }} > { /> + {showEditNetworksModal ? ( + setShowEditNetworksModal(false)} + onClick={onNetworksClick} + /> + ) : null} + {showEditAccountsModal ? ( + setShowEditAccountsModal(false)} + onClick={onAccountsClick} + /> + ) : null} ); }; diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts index e08ce5ef1c38..39dc257c0d54 100644 --- a/ui/ducks/app/app.ts +++ b/ui/ducks/app/app.ts @@ -34,8 +34,6 @@ type AppState = { tokenId?: string; ignoreErc20Token?: boolean; }; - showEditAccountsModalOpen: boolean; - showEditNetworksModalOpen: boolean; showPermittedNetworkToastOpen: boolean; showIpfsModalOpen: boolean; keyringRemovalSnapModal: { @@ -127,8 +125,6 @@ const initialState: AppState = { qrCodeData: null, networkDropdownOpen: false, importNftsModal: { open: false }, - showEditAccountsModalOpen: false, - showEditNetworksModalOpen: false, showPermittedNetworkToastOpen: false, showIpfsModalOpen: false, showBasicFunctionalityModal: false, @@ -264,12 +260,6 @@ export default function reduceApp( showIpfsModalOpen: false, }; - case actionConstants.SHOW_EDIT_NETWORKS_MODAL_OPEN: - return { - ...appState, - showEditNetworksModalOpen: true, - }; - case actionConstants.SHOW_PERMITTED_NETWORK_TOAST_OPEN: return { ...appState, @@ -282,24 +272,6 @@ export default function reduceApp( showPermittedNetworkToastOpen: false, }; - case actionConstants.SHOW_EDIT_NETWORKS_MODAL_CLOSE: - return { - ...appState, - showEditNetworksModalOpen: false, - }; - - case actionConstants.SHOW_EDIT_ACCOUNTS_MODAL_OPEN: - return { - ...appState, - showEditAccountsModalOpen: true, - }; - - case actionConstants.SHOW_EDIT_ACCOUNTS_MODAL_CLOSE: - return { - ...appState, - showEditAccountsModalOpen: false, - }; - case actionConstants.IMPORT_TOKENS_POPOVER_OPEN: return { ...appState, diff --git a/ui/pages/permissions-connect/choose-account/choose-account.js b/ui/pages/permissions-connect/choose-account/choose-account.js index 8cc07a2743b2..03916bc2b901 100644 --- a/ui/pages/permissions-connect/choose-account/choose-account.js +++ b/ui/pages/permissions-connect/choose-account/choose-account.js @@ -189,4 +189,4 @@ ChooseAccount.propTypes = { }), }; -export default ChooseAccount; \ No newline at end of file +export default ChooseAccount; diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 7120644dd7d9..01df40cb0aa8 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -34,8 +34,6 @@ import { NetworkListMenu, AccountDetails, ImportNftsModal, - EditAccountsModal, - EditNetworksModal, ImportTokensModal, ToastContainer, Toast, @@ -192,12 +190,8 @@ export default class Routes extends Component { accountDetailsAddress: PropTypes.string, isImportNftsModalOpen: PropTypes.bool.isRequired, hideImportNftsModal: PropTypes.func.isRequired, - isEditNetworksModalOpen: PropTypes.bool.isRequired, isPermittedNetworkToastOpen: PropTypes.bool.isRequired, - hideEditNetworksModal: PropTypes.func.isRequired, hidePermittedNetworkToast: PropTypes.func.isRequired, - isEditAccountsModalOpen: PropTypes.bool.isRequired, - hideEditAccountsModal: PropTypes.func.isRequired, isIpfsModalOpen: PropTypes.bool.isRequired, isBasicConfigurationModalOpen: PropTypes.bool.isRequired, hideIpfsModal: PropTypes.func.isRequired, @@ -801,7 +795,7 @@ export default class Routes extends Component { `${REVIEW_PERMISSIONS}/${safeEncodedHost}`, ) } - onClose={() => hidePermittedNetworkToast()} + onClose={() => this.props.hidePermittedNetworkToast()} /> ) : null} @@ -868,10 +862,6 @@ export default class Routes extends Component { location, isImportNftsModalOpen, hideImportNftsModal, - isEditAccountsModalOpen, - hideEditAccountsModal, - isEditNetworksModalOpen, - hideEditNetworksModal, isIpfsModalOpen, isBasicConfigurationModalOpen, hideIpfsModal, @@ -977,12 +967,6 @@ export default class Routes extends Component { {isImportNftsModalOpen ? ( hideImportNftsModal()} /> ) : null} - {isEditAccountsModalOpen ? ( - hideEditAccountsModal()} /> - ) : null} - {isEditNetworksModalOpen ? ( - hideEditNetworksModal()} /> - ) : null} {isIpfsModalOpen ? ( hideIpfsModal()} /> diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index abe9b661cb5d..dba24a8fb2ab 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -54,8 +54,6 @@ import { hideKeyringRemovalResultModal, ///: END:ONLY_INCLUDE_IF setEditedNetwork, - hideEditAccountsModal, - hideEditNetworksModal, hidePermittedNetworkToast, } from '../../store/actions'; import { pageChanged } from '../../ducks/history/history'; @@ -135,9 +133,7 @@ function mapStateToProps(state) { accountDetailsAddress: state.appState.accountDetailsAddress, isImportNftsModalOpen: state.appState.importNftsModal.open, isIpfsModalOpen: state.appState.showIpfsModalOpen, - isEditNetworksModalOpen: state.appState.showEditNetworksModalOpen, isPermittedNetworkToastOpen: state.appState.showPermittedNetworkToastOpen, - isEditAccountsModalOpen: state.appState.showEditAccountsModalOpen, switchedNetworkDetails, useNftDetection, showNftEnablementToast, @@ -171,8 +167,6 @@ function mapDispatchToProps(dispatch) { toggleNetworkMenu: () => dispatch(toggleNetworkMenu()), hideImportNftsModal: () => dispatch(hideImportNftsModal()), hideIpfsModal: () => dispatch(hideIpfsModal()), - hideEditAccountsModal: () => dispatch(hideEditAccountsModal()), - hideEditNetworksModal: () => dispatch(hideEditNetworksModal()), hidePermittedNetworkToast: () => dispatch(hidePermittedNetworkToast()), hideImportTokensModal: () => dispatch(hideImportTokensModal()), hideDeprecatedNetworkModal: () => dispatch(hideDeprecatedNetworkModal()), diff --git a/ui/selectors/permissions.js b/ui/selectors/permissions.js index bcda3bbf5397..52f9ed91152f 100644 --- a/ui/selectors/permissions.js +++ b/ui/selectors/permissions.js @@ -61,7 +61,7 @@ export function getPermittedAccounts(state, origin) { } export function getPermittedChains(state, origin) { - return getChainsFromPermission( + return getChainsFromPermission( getChainsPermissionFromSubject(subjectSelector(state, origin)), ); } diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts index 9d6dffc8ae65..8ed418346b9a 100644 --- a/ui/store/actionConstants.ts +++ b/ui/store/actionConstants.ts @@ -14,10 +14,6 @@ export const NETWORK_DROPDOWN_CLOSE = 'UI_NETWORK_DROPDOWN_CLOSE'; export const IMPORT_NFTS_MODAL_OPEN = 'UI_IMPORT_NFTS_MODAL_OPEN'; export const IMPORT_NFTS_MODAL_CLOSE = 'UI_IMPORT_NFTS_MODAL_CLOSE'; export const SHOW_IPFS_MODAL_OPEN = 'UI_IPFS_MODAL_OPEN'; -export const SHOW_EDIT_ACCOUNTS_MODAL_OPEN = 'UI_EDIT_ACCOUNTS_MODAL_OPEN'; -export const SHOW_EDIT_ACCOUNTS_MODAL_CLOSE = 'UI_EDIT_ACCOUNTS_MODAL_CLOSE'; -export const SHOW_EDIT_NETWORKS_MODAL_OPEN = 'UI_EDIT_NETWORKS_MODAL_OPEN'; -export const SHOW_EDIT_NETWORKS_MODAL_CLOSE = 'UI_EDIT_NETWORKS_MODAL_CLOSE'; export const SHOW_PERMITTED_NETWORK_TOAST_OPEN = 'UI_PERMITTED_NETWORK_TOAST_OPEN'; export const SHOW_PERMITTED_NETWORK_TOAST_CLOSE = diff --git a/ui/store/actions.ts b/ui/store/actions.ts index f64a8244962b..65ca15a2a2f7 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -116,6 +116,11 @@ import { getMethodDataAsync } from '../../shared/lib/four-byte'; import { DecodedTransactionDataResponse } from '../../shared/types/transaction-decode'; import { LastInteractedConfirmationInfo } from '../pages/confirmations/types/confirm'; import { EndTraceRequest } from '../../shared/lib/trace'; +import { + CaveatFactories, + PermissionNames, +} from '../../app/scripts/controllers/permissions/specifications'; +import { CaveatTypes } from '../../shared/constants/permissions'; import * as actionConstants from './actionConstants'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) import { updateCustodyState } from './institutional/institution-actions'; @@ -130,11 +135,6 @@ import { MetaMaskReduxState, TemporaryMessageDataType, } from './store'; -import { - CaveatFactories, - PermissionNames, -} from '../../app/scripts/controllers/permissions/specifications'; -import { CaveatTypes } from '../../shared/constants/permissions'; type CustomGasSettings = { gas?: string; @@ -1826,7 +1826,7 @@ export function removePermittedAccount( origin: string, address: string, ): ThunkAction { - console.log("removed is done") + console.log('removed is done'); return async (dispatch: MetaMaskReduxDispatch) => { await new Promise((resolve, reject) => { callBackgroundMethod( @@ -1851,17 +1851,13 @@ export function removePermittedChain( ): ThunkAction { return async (dispatch: MetaMaskReduxDispatch) => { await new Promise((resolve, reject) => { - callBackgroundMethod( - 'removePermittedChain', - [origin, chain], - (error) => { - if (error) { - reject(error); - return; - } - resolve(); - }, - ); + callBackgroundMethod('removePermittedChain', [origin, chain], (error) => { + if (error) { + reject(error); + return; + } + resolve(); + }); }); await forceUpdateMetamaskState(dispatch); }; @@ -2627,30 +2623,6 @@ export function hideImportNftsModal(): Action { }; } -export function showEditAccountsModal(): Action { - return { - type: actionConstants.SHOW_EDIT_ACCOUNTS_MODAL_OPEN, - }; -} - -export function hideEditAccountsModal(): Action { - return { - type: actionConstants.SHOW_EDIT_ACCOUNTS_MODAL_CLOSE, - }; -} - -export function hideEditNetworksModal(): Action { - return { - type: actionConstants.SHOW_EDIT_NETWORKS_MODAL_CLOSE, - }; -} - -export function showEditNetworksModal(): Action { - return { - type: actionConstants.SHOW_EDIT_NETWORKS_MODAL_OPEN, - }; -} - export function hidePermittedNetworkToast(): Action { return { type: actionConstants.SHOW_PERMITTED_NETWORK_TOAST_CLOSE, From 1b44b2157c0b0bfb6025d73e513d81ec6fb0b4f9 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 4 Sep 2024 12:44:14 +0100 Subject: [PATCH 025/125] updated controllers --- app/scripts/metamask-controller.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 6dfc79f19646..659ae114e8c6 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -3153,6 +3153,9 @@ export default class MetamaskController extends EventEmitter { this.permissionController.grantPermissionsIncremental.bind( this.permissionController, ), + grantPermissions: this.permissionController.grantPermissions.bind( + this.permissionController, + ), setSecurityAlertsEnabled: preferencesController.setSecurityAlertsEnabled.bind( preferencesController, From 27bf622c62f172e27761e89e99f2fabc40fd64b7 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 4 Sep 2024 13:03:31 +0100 Subject: [PATCH 026/125] updated edit networks --- .../edit-networks-modal/edit-networks-modal.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index c502a6264205..bd4cf13cf2bf 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -53,6 +53,18 @@ export const EditNetworksModal = ({ onClose, onClick }) => { } setSelectedChains(newSelectedChains); }; + const allAreSelected = () => { + return nonTestNetworks.length === selectedChains.length; + }; + let checked = false; + let isIndeterminate = false; + if (allAreSelected()) { + checked = true; + isIndeterminate = false; + } else if (selectedChains.length > 0 && !allAreSelected()) { + checked = false; + isIndeterminate = true; + } const managePermittedChains = ( selectedChains, flattenedPermittedChains, @@ -94,10 +106,10 @@ export const EditNetworksModal = ({ onClose, onClick }) => { (allAreSelected() ? deselectAll() : selectAll())} - // isIndeterminate={isIndeterminate} + onClick={() => (allAreSelected() ? deselectAll() : selectAll())} + isIndeterminate={isIndeterminate} /> {nonTestNetworks.map((network) => ( From 676c90c0a3df3dd84470e5b86db5f6c6814e0253 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 4 Sep 2024 13:26:40 +0100 Subject: [PATCH 027/125] lint fix --- .../edit-networks-modal.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index bd4cf13cf2bf..0728a4525e69 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -53,18 +53,18 @@ export const EditNetworksModal = ({ onClose, onClick }) => { } setSelectedChains(newSelectedChains); }; - const allAreSelected = () => { - return nonTestNetworks.length === selectedChains.length; - }; - let checked = false; - let isIndeterminate = false; - if (allAreSelected()) { - checked = true; - isIndeterminate = false; - } else if (selectedChains.length > 0 && !allAreSelected()) { - checked = false; - isIndeterminate = true; - } + const allAreSelected = () => { + return nonTestNetworks.length === selectedChains.length; + }; + let checked = false; + let isIndeterminate = false; + if (allAreSelected()) { + checked = true; + isIndeterminate = false; + } else if (selectedChains.length > 0 && !allAreSelected()) { + checked = false; + isIndeterminate = true; + } const managePermittedChains = ( selectedChains, flattenedPermittedChains, From a590b916e255aa2d14691eebead760031618e75e Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 4 Sep 2024 17:08:21 +0100 Subject: [PATCH 028/125] lint fix --- .../pages/review-permissions-page/review-permissions-page.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 966fe4cd6fd3..448f51ad4379 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -43,6 +43,8 @@ import { ToastContainer, Toast } from '../..'; import { NoConnectionContent } from '../connections/components/no-connection'; import { Content, Footer, Header, Page } from '../page'; import { SiteCell } from '.'; +import { NonEmptyArray } from '@metamask/utils'; +import { SubjectsType } from '../connections/components/connections.types'; export const ReviewPermissions = () => { const t = useI18nContext(); From 388ae6a183436f5d225a9b58fa4f7fbbf6879317 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 4 Sep 2024 17:20:34 +0100 Subject: [PATCH 029/125] prep build fix --- ui/pages/routes/routes.component.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 01df40cb0aa8..a2633404d395 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -137,7 +137,6 @@ import { MultichainMetaFoxLogo } from '../../components/multichain/app-header/mu import NetworkConfirmationPopover from '../../components/multichain/network-list-menu/network-confirmation-popover/network-confirmation-popover'; import NftFullImage from '../../components/app/assets/nfts/nft-details/nft-full-image'; import CrossChainSwap from '../bridge'; -import { hidePermittedNetworkToast } from '../../store/actions'; const isConfirmTransactionRoute = (pathname) => Boolean( From 855bee269b8a4fcef4ed427e8617386f810d00b1 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 4 Sep 2024 17:38:55 +0100 Subject: [PATCH 030/125] lint fix --- .../app-header-unlocked-content.tsx | 8 +- .../edit-accounts-modal.tsx | 6 +- .../multichain/global-menu/global-menu.js | 1 - .../permissions-page/permissions-page.js | 3 +- .../review-permissions-page.tsx | 11 +- .../site-cell/site-cell-menu.tsx | 114 ------------------ .../site-cell/site-cell.tsx | 13 +- 7 files changed, 18 insertions(+), 138 deletions(-) delete mode 100644 ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx diff --git a/ui/components/multichain/app-header/app-header-unlocked-content.tsx b/ui/components/multichain/app-header/app-header-unlocked-content.tsx index b62c52e06696..8a37fe8db01c 100644 --- a/ui/components/multichain/app-header/app-header-unlocked-content.tsx +++ b/ui/components/multichain/app-header/app-header-unlocked-content.tsx @@ -51,7 +51,10 @@ import { MetaMetricsContext } from '../../../contexts/metametrics'; import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'; import { MINUTE } from '../../../../shared/constants/time'; import { NotificationsTagCounter } from '../notifications-tag-counter'; -import { CONNECTIONS, REVIEW_PERMISSIONS } from '../../../helpers/constants/routes'; +import { + CONNECTIONS, + REVIEW_PERMISSIONS, +} from '../../../helpers/constants/routes'; import { MultichainNetwork } from '../../../selectors/multichain'; type AppHeaderUnlockedContentProps = { @@ -117,8 +120,9 @@ export const AppHeaderUnlockedContent = ({ const handleConnectionsRoute = () => { if (process.env.CHAIN_PERMISSIONS) { history.push(`${REVIEW_PERMISSIONS}/${encodeURIComponent(origin)}`); + } else { + history.push(`${CONNECTIONS}/${encodeURIComponent(origin)}`); } - history.push(`${CONNECTIONS}/${encodeURIComponent(origin)}`); }; return ( diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index 3cf1e73fec83..acebc35b0e98 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -6,6 +6,7 @@ import { isEvmAccountType, KeyringAccountType, } from '@metamask/keyring-api'; +import { NonEmptyArray } from '@metamask/utils'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getInternalAccounts, @@ -33,16 +34,15 @@ import { removePermissionsFor, removePermittedAccount, } from '../../../store/actions'; -import { NonEmptyArray } from '@metamask/utils'; import { SubjectsType } from '../pages/connections/components/connections.types'; const defaultAllowedAccountTypes = [EthAccountType.Eoa, EthAccountType.Erc4337]; -interface EditAccountsModalProps { +type EditAccountsModalProps = { onClose: () => void; onClick: () => void; allowedAccountTypes?: KeyringAccountType[]; -} +}; export const EditAccountsModal: React.FC = ({ onClose, diff --git a/ui/components/multichain/global-menu/global-menu.js b/ui/components/multichain/global-menu/global-menu.js index 5c51f057940f..ade0fb20020c 100644 --- a/ui/components/multichain/global-menu/global-menu.js +++ b/ui/components/multichain/global-menu/global-menu.js @@ -14,7 +14,6 @@ import { NOTIFICATIONS_ROUTE, SNAPS_ROUTE, PERMISSIONS, - REVIEW_PERMISSIONS, } from '../../../helpers/constants/routes'; import { lockMetamask, diff --git a/ui/components/multichain/pages/permissions-page/permissions-page.js b/ui/components/multichain/pages/permissions-page/permissions-page.js index 1089f3181a31..2b5a99fc55f5 100644 --- a/ui/components/multichain/pages/permissions-page/permissions-page.js +++ b/ui/components/multichain/pages/permissions-page/permissions-page.js @@ -56,8 +56,9 @@ export const PermissionsPage = () => { const safeEncodedHost = encodeURIComponent(hostName); if (process.env.CHAIN_PERMISSIONS) { history.push(`${REVIEW_PERMISSIONS}/${safeEncodedHost}`); + } else { + history.push(`${CONNECTIONS}/${safeEncodedHost}`); } - history.push(`${CONNECTIONS}/${safeEncodedHost}`); }; const renderConnectionsList = (connectionList) => diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 448f51ad4379..650718af3551 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory, useParams } from 'react-router-dom'; +import { NonEmptyArray } from '@metamask/utils'; import { AlignItems, BackgroundColor, @@ -16,12 +17,9 @@ import { getURLHost } from '../../../../helpers/utils/util'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import { getConnectedSitesList, - getConnectedSitesListWithNetworkInfo, getNonTestNetworks, getOrderedConnectedAccountsForConnectedDapp, - getOrderedNetworksList, getPermissionSubjects, - getPermittedChainsByOrigin, getPermittedChainsForSelectedTab, } from '../../../../selectors'; import { removePermissionsFor } from '../../../../store/actions'; @@ -42,9 +40,8 @@ import { import { ToastContainer, Toast } from '../..'; import { NoConnectionContent } from '../connections/components/no-connection'; import { Content, Footer, Header, Page } from '../page'; -import { SiteCell } from '.'; -import { NonEmptyArray } from '@metamask/utils'; import { SubjectsType } from '../connections/components/connections.types'; +import { SiteCell } from '.'; export const ReviewPermissions = () => { const t = useI18nContext(); @@ -55,7 +52,9 @@ export const ReviewPermissions = () => { const [showAccountToast, setShowAccountToast] = useState(false); const [showNetworkToast, setShowNetworkToast] = useState(false); const activeTabOrigin: string = securedOrigin; - const subjectMetadata = useSelector(getConnectedSitesList); + const subjectMetadata: { [key: string]: any } = useSelector( + getConnectedSitesList, + ); const connectedSubjectsMetadata = subjectMetadata[activeTabOrigin]; const connectedNetworks = useSelector((state) => getPermittedChainsForSelectedTab(state, activeTabOrigin), diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx deleted file mode 100644 index a8a94af75951..000000000000 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-menu.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React, { useRef, useCallback, useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { - PopoverRole, - PopoverPosition, - Popover, - IconName, - Text, - ModalFocus, - Box, -} from '../../../../component-library'; -import { MenuItem } from '../../../../ui/menu'; -import { - IconColor, - TextColor, - TextVariant, -} from '../../../../../helpers/constants/design-system'; -import { useI18nContext } from '../../../../../hooks/useI18nContext'; -import { getPermissionsForActiveTab } from '../../../../../selectors'; -import { PermissionDetailsModal } from '../../../permission-details-modal/permission-details-modal'; - -// TODO: Replace `any` with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const TsMenuItem = MenuItem as any; - -export const SiteCellMenu = ({ - isOpen, - account, - anchorElement, - disableAccountSwitcher = false, - onClose, - closeMenu, - onActionClick, - activeTabOrigin, -}: { - isOpen: boolean; - anchorElement: HTMLElement | null; - disableAccountSwitcher: boolean; - onClose: () => void; - closeMenu: () => void; - onActionClick: (message: string) => void; - activeTabOrigin: string; -}) => { - const dispatch = useDispatch(); - const t = useI18nContext(); - const popoverDialogRef = useRef(null); - const [showPermissionModal, setShowPermissionModal] = useState(false); - const permissions = useSelector(getPermissionsForActiveTab); - - 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]); - - const handleKeyDown = useCallback( - (event) => { - if ( - event.key === 'Tab' && - popoverDialogRef?.current?.contains(event.target) && - onClose - ) { - onClose(); - } - }, - [onClose], - ); - - return ( - <> - - - - { - setShowPermissionModal(true); - onClose(); - }} - > - {t('permissionDetails')} - - - - - - ); -}; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 676b87fa52b2..0f1100bfed7e 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -1,7 +1,5 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import { SubjectType } from '@metamask/permission-controller'; -import { useDispatch } from 'react-redux'; import { AlignItems, BackgroundColor, @@ -16,20 +14,14 @@ import { } from '../../../../../helpers/constants/design-system'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { - AvatarFavicon, AvatarIcon, AvatarIconSize, - AvatarNetwork, - AvatarNetworkSize, - BadgeWrapper, Box, Icon, IconName, IconSize, Text, } from '../../../../component-library'; -import { getURLHost } from '../../../../../helpers/utils/util'; -import { ConnectionListTooltip } from '../../permissions-page/connection-list-tooltip/connection-list-tooltip'; import { AvatarGroup, EditAccountsModal, EditNetworksModal } from '../../..'; import { AvatarType } from '../../../avatar-group/avatar-group.types'; @@ -40,12 +32,11 @@ export const SiteCell = ({ onNetworksClick, }) => { const t = useI18nContext(); - const dispatch = useDispatch(); - const avatarNetworksData = networks.map((network) => ({ + const avatarNetworksData = networks.map((network: { rpcPrefs: { imageUrl: string; }; nickname: string; }) => ({ avatarValue: network.rpcPrefs.imageUrl, symbol: network.nickname, })); - const avatarAccountsData = accounts.map((account) => ({ + const avatarAccountsData = accounts.map((account: { address: string; }) => ({ avatarValue: account.address, })); const [showEditAccountsModal, setShowEditAccountsModal] = useState(false); From f4c7ef95e8893d7249be967ecec156ed1a700420 Mon Sep 17 00:00:00 2001 From: Nidhi Kumari Date: Wed, 11 Sep 2024 16:28:31 +0100 Subject: [PATCH 031/125] feat: Connections flow (#26922) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR is to add connections Flow ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: Alex --- app/_locales/en/messages.json | 26 ++ .../controllers/permissions/background-api.js | 15 +- .../controllers/permissions/specifications.js | 1 - .../handlers/add-ethereum-chain.js | 4 + .../handlers/ethereum-chain-utils.js | 8 +- app/scripts/metamask-controller.js | 21 +- test/data/mock-send-state.json | 2 + .../edit-accounts-modal.tsx | 266 ++++++++++++------ .../new-accounts-modal.tsx | 32 +++ .../edit-networks-modal.js | 241 ++++++++++------ .../pages/connections/connections.tsx | 8 +- .../connection-list-tooltip.js | 4 +- .../review-permissions-page.tsx | 265 ++++++++++------- .../site-cell/site-cell-tooltip.js | 161 +++++++++++ .../site-cell/site-cell.tsx | 217 ++++++++------ ui/ducks/app/app.ts | 16 ++ .../connect-page/connect-page.tsx | 149 ++++++++++ .../permissions-connect.component.js | 83 ++++-- ui/pages/routes/routes.component.js | 2 +- ui/selectors/permissions.js | 2 + ui/selectors/selectors.js | 8 + ui/store/actionConstants.ts | 4 + ui/store/actions.ts | 27 ++ 23 files changed, 1164 insertions(+), 398 deletions(-) create mode 100644 ui/components/multichain/edit-accounts-modal/new-accounts-modal.tsx create mode 100644 ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js create mode 100644 ui/pages/permissions-connect/connect-page/connect-page.tsx diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index d84bbfcfe7ed..c3414a0dd0b9 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1186,6 +1186,10 @@ "connectedWith": { "message": "Connected with" }, + "connectedWithAccount": { + "message": "Connected with $1", + "description": "$1 represents account name" + }, "connecting": { "message": "Connecting" }, @@ -1213,6 +1217,9 @@ "connectingToSepolia": { "message": "Connecting to Sepolia test network" }, + "connectionDescription": { + "message": "This site wants to" + }, "connectionFailed": { "message": "Connection failed" }, @@ -1622,6 +1629,10 @@ "message": "Disconnect all $1", "description": "$1 will map to `disconnectAllAccountsText` or `disconnectAllSnapsText`" }, + "disconnectMessage": { + "message": "This will disconnect you from $1", + "description": "$1 is the name of the dapp" + }, "disconnectPrompt": { "message": "Disconnect $1" }, @@ -2909,6 +2920,14 @@ "more": { "message": "more" }, + "moreAccounts": { + "message": "+ $1 more accounts", + "description": "$1 is the number of accounts" + }, + "moreNetworks": { + "message": "+ $1 more networks", + "description": "$1 is the number of networks" + }, "multichainAddEthereumChainConfirmationDescription": { "message": "You're adding this network to MetaMask and giving this site permission to use it." }, @@ -4419,6 +4438,13 @@ "requestNotVerifiedError": { "message": "Because of an error, this request was not verified by the security provider. Proceed with caution." }, + "requestingFor": { + "message": "Requesting for" + }, + "requestingForAccount": { + "message": "Requesting for $1", + "description": "Name of Account" + }, "requestsAwaitingAcknowledgement": { "message": "requests waiting to be acknowledged" }, diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index 09ba4e1a4aaf..72d206b76e9d 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -3,7 +3,7 @@ import { CaveatTypes, RestrictedMethods, } from '../../../../shared/constants/permissions'; -import { CaveatFactories } from './specifications'; +import { CaveatFactories, PermissionNames } from './specifications'; export function getPermissionBackgroundApiMethods(permissionController) { const addMoreAccounts = (origin, accountOrAccounts) => { @@ -86,6 +86,19 @@ export function getPermissionBackgroundApiMethods(permissionController) { } }, + requestAccountsAndChainPermissionsWithId: async (origin) => { + const id = nanoid(); + permissionController.requestPermissions( + { origin }, + { + [PermissionNames.eth_accounts]: {}, + [PermissionNames.permittedChains]: {}, + }, + { id }, + ); + return id; + }, + requestAccountsPermissionWithId: async (origin) => { const id = nanoid(); permissionController.requestPermissions( diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index ea2392bfd7e4..60e0e95a1ff6 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -209,7 +209,6 @@ export const getPermissionSpecifications = ({ permissionType: PermissionType.Endowment, targetName: PermissionNames.permittedChains, allowedCaveats: [CaveatTypes.restrictNetworkSwitching], - subjectTypes: [SubjectType.Website], factory: (permissionOptions, requestData) => { if (requestData === undefined) { diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js index 571688688611..43c51fb0bc24 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js @@ -23,6 +23,7 @@ const addEthereumChain = { getCaveat: true, requestPermittedChainsPermission: true, getChainPermissionsFeatureFlag: true, + grantPermittedChainsPermissionIncremental: true, }, }; @@ -45,6 +46,7 @@ async function addEthereumChainHandler( getCaveat, requestPermittedChainsPermission, getChainPermissionsFeatureFlag, + grantPermittedChainsPermissionIncremental, }, ) { let validParams; @@ -157,12 +159,14 @@ async function addEthereumChainHandler( networkClientId, approvalFlowId, { + isAddFlow: true, getChainPermissionsFeatureFlag, setActiveNetwork, requestUserApproval, getCaveat, requestPermittedChainsPermission, endApprovalFlow, + grantPermittedChainsPermissionIncremental, }, ); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index 27526ed6cc4e..508554f81c82 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -194,12 +194,14 @@ export async function switchChain( networkClientId, approvalFlowId, { + isAddFlow, getChainPermissionsFeatureFlag, setActiveNetwork, endApprovalFlow, requestUserApproval, getCaveat, requestPermittedChainsPermission, + grantPermittedChainsPermissionIncremental }, ) { try { @@ -214,7 +216,11 @@ export async function switchChain( permissionedChainIds === undefined || !permissionedChainIds.includes(chainId) ) { - await requestPermittedChainsPermission([chainId]); + if (isAddFlow) { + await grantPermittedChainsPermissionIncremental([chainId]); + } else { + await requestPermittedChainsPermission([chainId]); + } } } else { await requestUserApproval({ diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 659ae114e8c6..2cf44b4828d1 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -5561,7 +5561,7 @@ export default class MetamaskController extends EventEmitter { this.permissionController.requestPermissions.bind( this.permissionController, { origin }, - { eth_accounts: {} }, + { eth_accounts: {}, [PermissionNames.permittedChains]: {} }, ), requestPermittedChainsPermission: (chainIds) => this.permissionController.requestPermissionsIncremental( @@ -5576,11 +5576,26 @@ export default class MetamaskController extends EventEmitter { }, }, ), - requestPermissionsForOrigin: + grantPermittedChainsPermissionIncremental: (chainIds) => + this.permissionController.grantPermissionsIncremental({ + subject: { origin }, + approvedPermissions: { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]( + chainIds, + ), + ], + }, + }, + }), + requestPermissionsForOrigin: (requestedPermissions) => { this.permissionController.requestPermissions.bind( this.permissionController, { origin }, - ), + { [PermissionNames.permittedChains]: {}, ...requestedPermissions }, + ); + }, revokePermissionsForOrigin: (permissionKeys) => { try { this.permissionController.revokePermissions({ diff --git a/test/data/mock-send-state.json b/test/data/mock-send-state.json index 1f8e6988e293..e95ff54505a4 100644 --- a/test/data/mock-send-state.json +++ b/test/data/mock-send-state.json @@ -27,6 +27,8 @@ } }, "showIpfsModalOpen": false, + "selectedAccountsForDappConnection": {}, + "selectedNetworksForDappConnection": {}, "showKeyringRemovalSnapModal": false, "showWhatsNewPopup": false, "warning": null, diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index acebc35b0e98..3d5f9a2dc6c1 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -11,7 +11,6 @@ import { useI18nContext } from '../../../hooks/useI18nContext'; import { getInternalAccounts, getOrderedConnectedAccountsForConnectedDapp, - getOriginOfCurrentTab, getPermissionSubjects, getUpdatedAndSortedAccounts, } from '../../../selectors'; @@ -25,6 +24,12 @@ import { ModalFooter, ButtonPrimary, ButtonPrimarySize, + ButtonLink, + ModalBody, + Text, + IconSize, + IconName, + Icon, } from '../../component-library'; import { AccountListItem } from '..'; import { MergedInternalAccount } from '../../../selectors/selectors.types'; @@ -33,26 +38,48 @@ import { addMorePermittedAccounts, removePermissionsFor, removePermittedAccount, + setSelectedAccountsForDappConnection, } from '../../../store/actions'; import { SubjectsType } from '../pages/connections/components/connections.types'; +import { + JustifyContent, + Display, + TextVariant, + TextColor, + IconColor, + FlexDirection, + AlignItems, +} from '../../../helpers/constants/design-system'; +import { NewAccountModal } from './new-accounts-modal'; +import { getURLHost } from '../../../helpers/utils/util'; const defaultAllowedAccountTypes = [EthAccountType.Eoa, EthAccountType.Erc4337]; type EditAccountsModalProps = { onClose: () => void; onClick: () => void; + onDisconnectClick: () => void; allowedAccountTypes?: KeyringAccountType[]; + approvedAccounts: string[]; + activeTabOrigin: string; + currentTabHasNoAccounts: boolean; }; export const EditAccountsModal: React.FC = ({ onClose, onClick, + onDisconnectClick, allowedAccountTypes = defaultAllowedAccountTypes, + approvedAccounts, + activeTabOrigin, + currentTabHasNoAccounts, }) => { const t = useI18nContext(); const accounts = useSelector(getUpdatedAndSortedAccounts); const internalAccounts = useSelector(getInternalAccounts); const dispatch = useDispatch(); + const hostName = getURLHost(activeTabOrigin); + const [showAddNewAccountsModal, setShowAddNewAccountsModal] = useState(false); const mergedAccounts: MergedInternalAccount[] = useMemo(() => { return mergeAccounts(accounts, internalAccounts).filter( @@ -60,7 +87,6 @@ export const EditAccountsModal: React.FC = ({ ); }, [accounts, internalAccounts, allowedAccountTypes]); - const activeTabOrigin = useSelector(getOriginOfCurrentTab); const subjects = useSelector(getPermissionSubjects); const connectedAccounts = useSelector((state: any) => getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin).filter( @@ -72,22 +98,21 @@ export const EditAccountsModal: React.FC = ({ (account: InternalAccount) => account.address, ); + const defaultAccountsAddresses = + connectedAccountsAddresses.length > 0 + ? connectedAccountsAddresses + : approvedAccounts; + const [selectedAccounts, setSelectedAccounts] = useState( - connectedAccountsAddresses, + defaultAccountsAddresses, ); const handleAccountClick = (address: string) => { - const index = selectedAccounts.indexOf(address); - let newSelectedAccounts: string[]; - - if (index === -1) { - newSelectedAccounts = [...selectedAccounts, address]; - } else { - newSelectedAccounts = selectedAccounts.filter( - (account) => account !== address, - ); - } - setSelectedAccounts(newSelectedAccounts); + setSelectedAccounts((prevSelectedAccounts) => + prevSelectedAccounts.includes(address) + ? prevSelectedAccounts.filter((acc) => acc !== address) + : [...prevSelectedAccounts, address], + ); }; const disconnectAllAccounts = () => { @@ -114,89 +139,156 @@ export const EditAccountsModal: React.FC = ({ const managePermittedAccounts = ( selectedAccounts: string[], connectedAccountsAddresses: string[], - activeTabOrigin: string, ) => { - const removedElements = connectedAccountsAddresses.filter( - (account) => !selectedAccounts.includes(account), + const removedAccounts = connectedAccountsAddresses.filter( + (acc) => !selectedAccounts.includes(acc), ); + removedAccounts.forEach((account) => { + dispatch(removePermittedAccount(activeTabOrigin, account)); + }); - if (removedElements.length > 0) { - removedElements.forEach((account) => { - dispatch(removePermittedAccount(activeTabOrigin, account)); - }); + const newAccounts = selectedAccounts.filter( + (acc) => !connectedAccountsAddresses.includes(acc), + ); + if (newAccounts.length > 0) { + dispatch(addMorePermittedAccounts(activeTabOrigin, newAccounts)); } - - const newElements = selectedAccounts.filter( - (account) => !connectedAccountsAddresses.includes(account), + }; + const selectAll = () => { + const newSelectedAccounts = accounts.map( + (account: { address: string }) => account.address, ); + setSelectedAccounts(newSelectedAccounts); + }; - if (newElements.length > 0) { - dispatch(addMorePermittedAccounts(activeTabOrigin, newElements)); - } + const deselectAll = () => { + setSelectedAccounts([]); + }; + + const allAreSelected = () => { + return accounts.length === selectedAccounts.length; }; + let checked = false; + let isIndeterminate = false; + if (allAreSelected()) { + checked = true; + isIndeterminate = false; + } else if (selectedAccounts.length > 0 && !allAreSelected()) { + checked = false; + isIndeterminate = true; + } return ( - - - - {t('editAccounts')} - - - - {mergedAccounts.map((account) => ( - handleAccountClick(account.address)} - account={account} - key={account.address} - isPinned={Boolean(account.pinned)} - startAccessory={ + <> + console.log('bb')} + data-testid="edit-accounts-modal" + className="edit-accounts-modal" + > + + + {t('editAccounts')} + + (allAreSelected() ? deselectAll() : selectAll())} + isIndeterminate={isIndeterminate} /> - } - selected={false} - /> - ))} - - - {selectedAccounts.length === 0 ? ( - { - disconnectAllAccounts(); - onClose(); - }} - size={ButtonPrimarySize.Lg} - block - danger - > - {t('disconnect')} - - ) : ( - { - onClick(); - managePermittedAccounts( - selectedAccounts, - connectedAccountsAddresses, - activeTabOrigin, - ); - onClose(); - }} - size={ButtonPrimarySize.Lg} - block - > - {t('confirm')} - - )} - - - + setShowAddNewAccountsModal(true)}> + {t('newAccount')} + + + {mergedAccounts.map((account) => ( + handleAccountClick(account.address)} + account={account} + key={account.address} + isPinned={Boolean(account.pinned)} + startAccessory={ + + } + selected={false} + /> + ))} + + + {selectedAccounts.length === 0 ? ( + + + + + {t('disconnectMessage', [hostName])} + + + { + onDisconnectClick(); + onClose(); + }} + size={ButtonPrimarySize.Lg} + block + danger + > + {t('disconnect')} + + + ) : ( + { + onClick(); + if (currentTabHasNoAccounts) { + dispatch( + setSelectedAccountsForDappConnection(selectedAccounts), + ); + } else { + managePermittedAccounts( + selectedAccounts, + connectedAccountsAddresses, + ); + } + onClose(); + }} + size={ButtonPrimarySize.Lg} + block + > + {t('update')} + + )} + + + + + + {showAddNewAccountsModal && ( + setShowAddNewAccountsModal(false)} /> + )} + ); }; diff --git a/ui/components/multichain/edit-accounts-modal/new-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/new-accounts-modal.tsx new file mode 100644 index 000000000000..5b7ac5390d4b --- /dev/null +++ b/ui/components/multichain/edit-accounts-modal/new-accounts-modal.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { + Modal, + ModalOverlay, + ModalContent, + Box, +} from '../../component-library'; +import { CreateEthAccount } from '..'; + +type NewAccountModalProps = { + onClose: () => void; +}; + +export const NewAccountModal: React.FC = ({ + onClose, +}) => { + return ( + + + + + + + + + ); +}; diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index 0728a4525e69..617a1a275cf0 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -1,12 +1,13 @@ import PropTypes from 'prop-types'; import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { TextVariant } from '../../../helpers/constants/design-system'; +import { AlignItems, Display, FlexDirection, IconColor, TextColor, TextVariant } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getNonTestNetworks, getOriginOfCurrentTab, getPermittedChainsByOrigin, + getPermittedChainsForSelectedTab, getTestNetworks, } from '../../../selectors'; import { @@ -20,41 +21,61 @@ import { ModalFooter, ButtonPrimary, ButtonPrimarySize, + ModalBody, + Icon, + IconName, + IconSize, } from '../../component-library'; import { NetworkListItem } from '..'; import { grantPermittedChains, removePermittedChain, + setSelectedNetworksForDappConnection, } from '../../../store/actions'; +import { getURLHost } from '../../../helpers/utils/util'; -export const EditNetworksModal = ({ onClose, onClick }) => { +export const EditNetworksModal = ({ + onClose, + onClick, + currentTabHasNoAccounts, + combinedNetworks, + onDisconnectClick, + defaultNetworks, +}) => { const t = useI18nContext(); const dispatch = useDispatch(); - const nonTestNetworks = useSelector(getNonTestNetworks); const testNetworks = useSelector(getTestNetworks); - const chains = useSelector(getPermittedChainsByOrigin); - const permittedChains = Object.values(chains); const activeTabOrigin = useSelector(getOriginOfCurrentTab); - const flattenedPermittedChains = permittedChains.flat(); - const [selectedChains, setSelectedChains] = useState( - flattenedPermittedChains, - ); - - const handleAccountClick = (chainId) => { - const index = selectedChains.indexOf(chainId); - let newSelectedChains = []; + const connectedNetworks = useSelector((state) => + getPermittedChainsForSelectedTab(state, activeTabOrigin), + ); + const combinedNetworksIds = combinedNetworks.map((network) => network.chainId); + const selectedPermittedChains = + connectedNetworks.length > 0 ? connectedNetworks : combinedNetworksIds; + const [selectedChains, setSelectedChains] = useState(selectedPermittedChains); + const selectAll = () => { + const newSelectedAccounts = combinedNetworks.map( + (network) => network.chainId, + ); + setSelectedChains(newSelectedAccounts); + }; - if (index === -1) { - // If chainId is not already selected, add it to the selectedChains array - newSelectedChains = [...selectedChains, chainId]; - } else { - // If chainId is already selected, remove it from the selectedChains array - newSelectedChains = selectedChains.filter((_item, idx) => idx !== index); - } - setSelectedChains(newSelectedChains); + const deselectAll = () => { + setSelectedChains([]); }; + +const handleAccountClick = (chainId) => { + if (selectedChains.includes(chainId)) { + // Remove the chainId from the selectedChains + setSelectedChains(selectedChains.filter((id) => id !== chainId)); + } else { + // Add the chainId to selectedChains + setSelectedChains([...selectedChains, chainId]); + } +}; + const allAreSelected = () => { - return nonTestNetworks.length === selectedChains.length; + return combinedNetworksIds.length === selectedChains.length; }; let checked = false; let isIndeterminate = false; @@ -67,7 +88,7 @@ export const EditNetworksModal = ({ onClose, onClick }) => { } const managePermittedChains = ( selectedChains, - flattenedPermittedChains, + selectedPermittedChains, activeTabOrigin, ) => { if (!Array.isArray(selectedChains)) { @@ -76,7 +97,7 @@ export const EditNetworksModal = ({ onClose, onClick }) => { } dispatch(grantPermittedChains(activeTabOrigin, selectedChains)); - const removedElements = flattenedPermittedChains.filter( + const removedElements = selectedPermittedChains.filter( (chain) => !selectedChains.includes(chain), ); @@ -86,6 +107,7 @@ export const EditNetworksModal = ({ onClose, onClick }) => { dispatch(removePermittedChain(activeTabOrigin, selectedChain)); }); }; + const hostName = getURLHost(activeTabOrigin); return ( { > {t('editNetworksTitle')} - - (allAreSelected() ? deselectAll() : selectAll())} - isIndeterminate={isIndeterminate} - /> - - {nonTestNetworks.map((network) => ( - { - handleAccountClick(network.chainId); - }} - startAccessory={ - - } - /> - ))} - - {t('testnets')} - - {testNetworks.map((network) => ( - { - handleAccountClick(network.chainId); - }} - startAccessory={ - - } - showEndAccessory={false} - /> - ))} - - {selectedChains.length === 0 ? ( - + + (allAreSelected() ? deselectAll() : selectAll())} + isIndeterminate={isIndeterminate} + /> + + {combinedNetworks.map((network) => ( + { - // disconnectAllAccounts(); - onClose(); + handleAccountClick(network.chainId); }} - size={ButtonPrimarySize.Lg} - block - danger - > - {t('disconnect')} - - ) : ( - + } + /> + ))} + + {t('testnets')} + + {testNetworks.map((network) => ( + { - onClick(); - onClose(); - managePermittedChains( - selectedChains, - flattenedPermittedChains, - activeTabOrigin, - ); // Then call the managePermittedChains function + handleAccountClick(network.chainId); }} - size={ButtonPrimarySize.Lg} - block - > - {t('confirm')} - - )} - + startAccessory={ + + } + showEndAccessory={false} + /> + ))} + + {selectedChains.length === 0 ? ( + + + + + {t('disconnectMessage', [hostName])} + + + { + onDisconnectClick(); + onClose(); + }} + size={ButtonPrimarySize.Lg} + block + danger + > + {t('disconnect')} + + + ) : ( + { + onClick(); + onClose(); + if (currentTabHasNoAccounts) { + dispatch( + setSelectedNetworksForDappConnection(selectedChains), + ); + } else { + managePermittedChains( + selectedChains, + selectedPermittedChains, + activeTabOrigin, + ); // Then call the managePermittedChains function + } + }} + size={ButtonPrimarySize.Lg} + block + > + {t('update')} + + )} + + ); diff --git a/ui/components/multichain/pages/connections/connections.tsx b/ui/components/multichain/pages/connections/connections.tsx index cbb5a4b7f24f..696326408daa 100644 --- a/ui/components/multichain/pages/connections/connections.tsx +++ b/ui/components/multichain/pages/connections/connections.tsx @@ -53,7 +53,7 @@ import { import { Content, Footer, Header, Page } from '../page'; import { ConnectAccountsModal } from '../../connect-accounts-modal/connect-accounts-modal'; import { - requestAccountsPermissionWithId, + requestAccountsAndChainPermissionsWithId, removePermissionsFor, } from '../../../../store/actions'; import { @@ -131,9 +131,9 @@ export const Connections = () => { origin: activeTabOrigin, }; } - const requestAccountsPermission = async () => { + const requestAccountsAndChainPermissionsWithId = async () => { const requestId = await dispatch( - requestAccountsPermissionWithId(tabToConnect.origin), + requestAccountsAndChainPermissionsWithId(tabToConnect.origin), ); history.push(`${CONNECT_ROUTE}/${requestId}`); }; @@ -396,7 +396,7 @@ export const Connections = () => { size={ButtonPrimarySize.Lg} block data-test-id="no-connections-button" - onClick={() => requestAccountsPermission()} + onClick={requestAccountsAndChainPermissionsWithId} > {t('connectAccounts')} diff --git a/ui/components/multichain/pages/permissions-page/connection-list-tooltip/connection-list-tooltip.js b/ui/components/multichain/pages/permissions-page/connection-list-tooltip/connection-list-tooltip.js index fbed1fa6f37a..51bb4c0df15a 100644 --- a/ui/components/multichain/pages/permissions-page/connection-list-tooltip/connection-list-tooltip.js +++ b/ui/components/multichain/pages/permissions-page/connection-list-tooltip/connection-list-tooltip.js @@ -26,8 +26,8 @@ import { useI18nContext } from '../../../../../hooks/useI18nContext'; export const ConnectionListTooltip = ({ connection }) => { const t = useI18nContext(); - const AVATAR_GROUP_LIMIT = 5; - const TOOLTIP_LIMIT = 7; + const AVATAR_GROUP_LIMIT = 4; + const TOOLTIP_LIMIT = 4; const addressIconList = connection.addresses ?.slice(0, TOOLTIP_LIMIT) .map((address) => ({ diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 650718af3551..d2b1a19d978b 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -20,9 +20,15 @@ import { getNonTestNetworks, getOrderedConnectedAccountsForConnectedDapp, getPermissionSubjects, + getPermittedAccountsByOrigin, getPermittedChainsForSelectedTab, + getTestNetworks, } from '../../../../selectors'; -import { removePermissionsFor } from '../../../../store/actions'; +import { + removePermissionsFor, + requestAccountsAndChainPermissionsWithId, + requestAccountsPermissionWithId, +} from '../../../../store/actions'; import { AvatarFavicon, AvatarFaviconSize, @@ -30,6 +36,8 @@ import { Button, ButtonIcon, ButtonIconSize, + ButtonPrimary, + ButtonPrimarySize, ButtonSize, ButtonVariant, Icon, @@ -42,6 +50,11 @@ import { NoConnectionContent } from '../connections/components/no-connection'; import { Content, Footer, Header, Page } from '../page'; import { SubjectsType } from '../connections/components/connections.types'; import { SiteCell } from '.'; +import { CONNECT_ROUTE } from '../../../../helpers/constants/routes'; +import { + DisconnectAllModal, + DisconnectType, +} from '../../disconnect-all-modal/disconnect-all-modal'; export const ReviewPermissions = () => { const t = useI18nContext(); @@ -51,7 +64,14 @@ export const ReviewPermissions = () => { const securedOrigin = decodeURIComponent(urlParams.origin); const [showAccountToast, setShowAccountToast] = useState(false); const [showNetworkToast, setShowNetworkToast] = useState(false); + const [showDisconnectAllModal, setShowDisconnectAllModal] = useState(false); const activeTabOrigin: string = securedOrigin; + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { openMetaMaskTabs } = useSelector((state: any) => state.appState); + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { id } = useSelector((state: any) => state.activeTab); const subjectMetadata: { [key: string]: any } = useSelector( getConnectedSitesList, ); @@ -59,15 +79,39 @@ export const ReviewPermissions = () => { const connectedNetworks = useSelector((state) => getPermittedChainsForSelectedTab(state, activeTabOrigin), ); + console.log(connectedNetworks, 'connectedNetworks'); + const permittedAccountsByOrigin = useSelector( + getPermittedAccountsByOrigin, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as { [key: string]: any[] }; const networksList = useSelector(getNonTestNetworks); + const testNetworks = useSelector(getTestNetworks); + const combinedNetworks = [...networksList, ...testNetworks]; const connectedAccounts = useSelector((state) => getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin), ); const subjects = useSelector(getPermissionSubjects); - const grantedNetworks = networksList.filter( + const grantedNetworks = combinedNetworks.filter( (net: { chainId: any }) => connectedNetworks.indexOf(net.chainId) !== -1, ); const hostName = getURLHost(securedOrigin); + console.log(connectedNetworks, grantedNetworks); + const currentTabHasNoAccounts = + !permittedAccountsByOrigin[activeTabOrigin]?.length; + + let tabToConnect: { origin: any } = { origin: null }; + if (activeTabOrigin && currentTabHasNoAccounts && !openMetaMaskTabs[id]) { + tabToConnect = { + origin: activeTabOrigin, + }; + } + const requestAccountsAndChainPermissions = async () => { + const requestId = await dispatch( + requestAccountsAndChainPermissionsWithId(tabToConnect.origin), + ); + history.push(`${CONNECT_ROUTE}/${requestId}`); + }; + const disconnectAllAccounts = () => { const subject = (subjects as SubjectsType)[activeTabOrigin]; @@ -94,111 +138,138 @@ export const ReviewPermissions = () => { data-testid="connections-page" className="main-container connections-page" > - {connectedAccounts.length > 0 ? ( - <> -
(history as any).goBack()} - /> - } + <> +
(history as any).goBack()} + /> + } + > + - + ) : ( + + )} + - {connectedSubjectsMetadata?.iconUrl ? ( - - ) : ( - - )} - - {hostName} - - -
- + {hostName} + + +
+ + {connectedAccounts.length > 0 ? ( setShowAccountToast(true)} onNetworksClick={() => setShowNetworkToast(true)} + onDisconnectClick={() => setShowDisconnectAllModal(true)} + activeTabOrigin={activeTabOrigin} + combinedNetworks={networksList} /> - -
- - {showAccountToast ? ( - - setShowAccountToast(false)} - startAdornment={ - - } - /> - - ) : null} - {showNetworkToast ? ( - - setShowNetworkToast(false)} - startAdornment={ - - } - /> - - ) : null} - + + ) : ( + disconnectAllAccounts()} + data-test-id="no-connections-button" + onClick={requestAccountsAndChainPermissions} > - {t('disconnectAllAccounts')} - - -
{' '} - - ) : ( - - )} + {t('connectAccounts')} + + )} + + {' '} + ); }; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js new file mode 100644 index 000000000000..9be67f39e5cb --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js @@ -0,0 +1,161 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Tooltip } from 'react-tippy'; +import { useSelector } from 'react-redux'; +import { + AlignItems, + BackgroundColor, + BorderStyle, + Display, + FlexDirection, + TextAlign, + TextColor, + TextVariant, +} from '../../../../../helpers/constants/design-system'; +import { AvatarType } from '../../../avatar-group/avatar-group.types'; +import { AvatarGroup } from '../../..'; +import { + AvatarAccount, + AvatarAccountSize, + AvatarAccountVariant, + AvatarNetwork, + AvatarNetworkSize, + Box, + Text, +} from '../../../../component-library'; +import { getUseBlockie } from '../../../../../selectors'; +import { useI18nContext } from '../../../../../hooks/useI18nContext'; + +export const SiteCellTooltip = ({ + accounts, + avatarAccountsData, + networks, + avatarNetworksData, +}) => { + const t = useI18nContext(); + const AVATAR_GROUP_LIMIT = 4; + const TOOLTIP_LIMIT = 4; + const useBlockie = useSelector(getUseBlockie); + const avatarAccountVariant = useBlockie + ? AvatarAccountVariant.Blockies + : AvatarAccountVariant.Jazzicon; + + return ( + + + {accounts?.slice(0, TOOLTIP_LIMIT).map((acc) => { + return ( + + + + {acc.label || acc.metadata.name} + + + ); + })} + {networks?.slice(0, TOOLTIP_LIMIT).map((network) => { + return ( + + + + {network.nickname} + + + ); + })} + {accounts?.length > TOOLTIP_LIMIT || + networks?.length > TOOLTIP_LIMIT ? ( + + + {accounts?.length > 0 + ? t('moreAccounts', [accounts?.length - TOOLTIP_LIMIT]) + : t('moreNetworks', [networks.length - TOOLTIP_LIMIT])} + + + ) : null} + + + } + arrow + offset={0} + delay={50} + duration={0} + size="small" + title={t('alertDisableTooltip')} + trigger="mouseenter focus" + theme="dark" + tag="div" + > + {accounts?.length > 0 ? ( + + ) : ( + + )} + + ); +}; +SiteCellTooltip.propTypes = { + /** + * The accounts data to display + */ + accounts: PropTypes.object.isRequired, +}; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 0f1100bfed7e..d7a0db9427f7 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -1,9 +1,10 @@ import React, { useState } from 'react'; -import PropTypes from 'prop-types'; +import { useSelector } from 'react-redux'; import { AlignItems, BackgroundColor, BlockSize, + BorderColor, Display, FlexDirection, IconColor, @@ -14,33 +15,79 @@ import { } from '../../../../../helpers/constants/design-system'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { + AvatarAccount, + AvatarAccountSize, AvatarIcon, AvatarIconSize, Box, + ButtonLink, Icon, IconName, IconSize, Text, } from '../../../../component-library'; -import { AvatarGroup, EditAccountsModal, EditNetworksModal } from '../../..'; -import { AvatarType } from '../../../avatar-group/avatar-group.types'; +import { EditAccountsModal, EditNetworksModal } from '../../..'; +import { getPermittedAccountsByOrigin } from '../../../../../selectors/permissions'; +import { SiteCellTooltip } from './site-cell-tooltip'; -export const SiteCell = ({ +type SiteCellProps = { + networks: { + rpcPrefs?: { imageUrl?: string }; + nickname: string; + chainId?: string; + }[]; + accounts: { + address: string; + label: string; + metadata: { + name: string; + }; + }[]; + onAccountsClick: () => void; + onNetworksClick: () => void; + onDisconnectClick: () => void; + approvedAccounts: { address: string }[]; + activeTabOrigin: string; + combinedNetworks; +}; + +export const SiteCell: React.FC = ({ networks, accounts, onAccountsClick, onNetworksClick, + approvedAccounts, + activeTabOrigin, + combinedNetworks, + onDisconnectClick, }) => { const t = useI18nContext(); - const avatarNetworksData = networks.map((network: { rpcPrefs: { imageUrl: string; }; nickname: string; }) => ({ - avatarValue: network.rpcPrefs.imageUrl, + const avatarNetworksData = networks.map((network) => ({ + avatarValue: network?.rpcPrefs?.imageUrl || '', symbol: network.nickname, })); - const avatarAccountsData = accounts.map((account: { address: string; }) => ({ + const avatarAccountsData = accounts.map((account) => ({ avatarValue: account.address, })); + const [showEditAccountsModal, setShowEditAccountsModal] = useState(false); const [showEditNetworksModal, setShowEditNetworksModal] = useState(false); + const accountMessageConnectedState = + accounts.length > 1 + ? t('connectedWith') + : t('connectedWithAccount', [ + accounts[0].label || accounts[0].metadata.name, + ]); + const accountMessageNotConnectedState = + accounts.length > 1 + ? t('requestingFor') + : t('requestingForAccount', [ + accounts[0].label || accounts[0].metadata.name, + ]); + + const permittedAccountsByOrigin = useSelector(getPermittedAccountsByOrigin); + const currentTabHasNoAccounts = + !permittedAccountsByOrigin[activeTabOrigin]?.length; return ( <> @@ -52,7 +99,6 @@ export const SiteCell = ({ alignItems={AlignItems.baseline} width={BlockSize.Full} backgroundColor={BackgroundColor.backgroundDefault} - // onClick={onClick} padding={4} gap={4} className="multichain-connection-list-item" @@ -60,9 +106,7 @@ export const SiteCell = ({ @@ -70,7 +114,7 @@ export const SiteCell = ({ display={Display.Flex} flexDirection={FlexDirection.Column} width={BlockSize.FiveTwelfths} - style={{ alignSelf: 'center', flexGrow: '1' }} + style={{ alignSelf: 'center', flexGrow: 1 }} > {t('accountsPermissionsTitle')} - - {t('connectedWith')} + {currentTabHasNoAccounts + ? accountMessageNotConnectedState + : accountMessageConnectedState} - + {accounts.length > 1 ? ( + + ) : ( + + )} - { - setShowEditAccountsModal(true); - }} - > - setShowEditAccountsModal(true)}> + {t('edit')} + + ) : ( + - + justifyContent={JustifyContent.flexEnd} + alignItems={AlignItems.center} + style={{ flex: 1, alignSelf: 'center' }} + gap={2} + onClick={() => setShowEditAccountsModal(true)} + > + + + )} + @@ -146,7 +201,7 @@ export const SiteCell = ({ display={Display.Flex} flexDirection={FlexDirection.Column} width={BlockSize.FiveTwelfths} - style={{ alignSelf: 'center', flexGrow: '1' }} + style={{ alignSelf: 'center', flexGrow: 1 }} > {t('permission_walletSwitchEthereumChain')} - - {t('connectedWith')} + {currentTabHasNoAccounts + ? t('requestingFor') + : t('connectedWith')} - - { - setShowEditNetworksModal(true); - }} - > - setShowEditNetworksModal(true)}> + {t('edit')} + + ) : ( + - + justifyContent={JustifyContent.flexEnd} + alignItems={AlignItems.center} + style={{ flex: 1, alignSelf: 'center' }} + gap={2} + onClick={() => setShowEditNetworksModal(true)} + > + + + )} - {showEditNetworksModal ? ( + + {showEditNetworksModal && ( setShowEditNetworksModal(false)} onClick={onNetworksClick} + currentTabHasNoAccounts={currentTabHasNoAccounts} + combinedNetworks={combinedNetworks} + onDisconnectClick={onDisconnectClick} /> - ) : null} - {showEditAccountsModal ? ( + )} + + {showEditAccountsModal && ( setShowEditAccountsModal(false)} onClick={onAccountsClick} + selAccounts={accounts} + approvedAccounts={approvedAccounts} + activeTabOrigin={activeTabOrigin} + currentTabHasNoAccounts={currentTabHasNoAccounts} + onDisconnectClick={onDisconnectClick} /> - ) : null} + )} ); }; - -SiteCell.propTypes = { - /** - * The connection data to display - */ - connection: PropTypes.object.isRequired, - /** - * The function to call when the connection is clicked - */ - onClick: PropTypes.func.isRequired, -}; diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts index 39dc257c0d54..6a381edde5f0 100644 --- a/ui/ducks/app/app.ts +++ b/ui/ducks/app/app.ts @@ -36,6 +36,8 @@ type AppState = { }; showPermittedNetworkToastOpen: boolean; showIpfsModalOpen: boolean; + selectedAccountsForDappConnection: object; + selectedNetworksForDappConnection: object; keyringRemovalSnapModal: { snapName: string; result: 'success' | 'failure' | 'none'; @@ -127,6 +129,8 @@ const initialState: AppState = { importNftsModal: { open: false }, showPermittedNetworkToastOpen: false, showIpfsModalOpen: false, + selectedAccountsForDappConnection: {}, + selectedNetworksForDappConnection: {}, showBasicFunctionalityModal: false, externalServicesOnboardingToggleState: true, keyringRemovalSnapModal: { @@ -544,6 +548,18 @@ export default function reduceApp( requestAccountTabs: action.value, }; + case actionConstants.SET_SELECTED_ACCOUNTS_FOR_DAPP_CONNECTIONS: + return { + ...appState, + selectedAccountsForDappConnection: action.payload, + }; + + case actionConstants.SET_SELECTED_NETWORKS_FOR_DAPP_CONNECTIONS: + return { + ...appState, + selectedNetworksForDappConnection: action.payload, + }; + case actionConstants.SET_OPEN_METAMASK_TAB_IDS: return { ...appState, diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx new file mode 100644 index 000000000000..ee0cddf5ba8c --- /dev/null +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -0,0 +1,149 @@ +import React, { useState } from 'react'; +import { useSelector } from 'react-redux'; +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { + getNonTestNetworks, + getSelectedAccountsForDappConnection, + getSelectedInternalAccount, + getSelectedNetworksForDappConnection, + getTestNetworks, +} from '../../../selectors'; +import { + Box, + Button, + ButtonSize, + ButtonVariant, + Text, +} from '../../../components/component-library'; +import { + Content, + Footer, + Header, + Page, +} from '../../../components/multichain/pages/page'; +import { SiteCell } from '../../../components/multichain/pages/review-permissions-page'; +import { + BlockSize, + Display, + TextVariant, +} from '../../../helpers/constants/design-system'; +import { PermissionNames } from '../../../../app/scripts/controllers/permissions/specifications'; + +export const ConnectPage = ({ + request, + rejectPermissionsRequest, + approveConnection, + accounts, + selectAccounts, + selectedAccountAddresses, + activeTabOrigin, + permissionsRequestId, +}: { + request: any; + permissionsRequestId: string; + rejectPermissionsRequest: () => void; + approveConnection: (request: any) => void; + accounts: any[]; + selectAccounts: (addresses: any[]) => void; + selectedAccountAddresses: Set; + activeTabOrigin: string; +}) => { + const t = useI18nContext(); + const networksList = useSelector(getNonTestNetworks); + const selectednetworksList = useSelector( + getSelectedNetworksForDappConnection, + ); + const testNetworks = useSelector(getTestNetworks); + const combinedNetworks = [...networksList, ...testNetworks]; + + const currentAccount = useSelector(getSelectedInternalAccount); + const [selectedAccounts, setSelectedAccounts] = useState( + selectedAccountAddresses, + ); + + const selectedAccountsForDappConnection = useSelector( + getSelectedAccountsForDappConnection, + ); + + // Handle account selection/deselection + const handleAccountClick = (address: string) => { + const newSelectedAccounts = new Set(selectedAccounts); + if (newSelectedAccounts.has(address)) { + newSelectedAccounts.delete(address); + } else { + newSelectedAccounts.add(address); + } + setSelectedAccounts(newSelectedAccounts); + }; + // Filter networks based on chainId + const filteredNetworks = Array.isArray(selectednetworksList) + ? combinedNetworks.filter((network) => + selectednetworksList.includes(network.chainId), + ) + : networksList; + + // Select approved accounts and networks + const approvedAccounts = + selectedAccountsForDappConnection.length > 0 + ? selectedAccountsForDappConnection + : [currentAccount.address]; + + // Handle confirmation + const onConfirm = () => { + const _request = { + ...request, + approvedAccounts, + approvedChainIds: filteredNetworks.map((network) => network.chainId), + }; + approveConnection(_request); + }; + + const filterAccountsByAddress = accounts.filter((account) => + approvedAccounts.includes(account.address), + ); + + return ( + +
+ {t('connectWithMetaMask')} + This site wants to: +
+ + selectAccounts(Array.from(selectedAccounts))} + onNetworksClick={() => console.log('testing')} + approvedAccounts={approvedAccounts} + activeTabOrigin={activeTabOrigin} + combinedNetworks={networksList} + /> + +
+ + + + +
+
+ ); +}; diff --git a/ui/pages/permissions-connect/permissions-connect.component.js b/ui/pages/permissions-connect/permissions-connect.component.js index af84867202a1..12bb0e182be8 100644 --- a/ui/pages/permissions-connect/permissions-connect.component.js +++ b/ui/pages/permissions-connect/permissions-connect.component.js @@ -21,6 +21,7 @@ import SnapsConnect from './snaps/snaps-connect'; import SnapInstall from './snaps/snap-install'; import SnapUpdate from './snaps/snap-update'; import SnapResult from './snaps/snap-result'; +import { ConnectPage } from './connect-page/connect-page'; const APPROVE_TIMEOUT = MILLISECOND * 1200; @@ -143,6 +144,9 @@ export default class PermissionConnect extends Component { history.replace(DEFAULT_ROUTE); return; } + if (process.env.CHAIN_PERMISSIONS) { + history.replace(confirmPermissionPath); + } // if this is an incremental permission request for permitted chains, skip the account selection if ( permissionsRequest?.diff?.permissionDiffMap?.[ @@ -151,7 +155,6 @@ export default class PermissionConnect extends Component { ) { history.replace(confirmPermissionPath); } - if (history.location.pathname === connectPath && !isRequestingAccounts) { switch (requestType) { case 'wallet_installSnap': @@ -288,9 +291,15 @@ export default class PermissionConnect extends Component { ); } + approveConnection = (...args) => { + const { approvePermissionsRequest } = this.props; + console.log('testing', approvePermissionsRequest, ...args); + approvePermissionsRequest(...args); + this.redirect(true); + }; + render() { const { - approvePermissionsRequest, accounts, showNewAccountModal, newAccountNumber, @@ -353,30 +362,47 @@ export default class PermissionConnect extends Component { ( - { - approvePermissionsRequest(...args); - this.redirect(true); - }} - rejectPermissionsRequest={(requestId) => - this.cancelPermissionsRequest(requestId) - } - selectedAccounts={accounts.filter((account) => - selectedAccountAddresses.has(account.address), - )} - targetSubjectMetadata={targetSubjectMetadata} - history={this.props.history} - connectPath={connectPath} - snapsInstallPrivacyWarningShown={ - snapsInstallPrivacyWarningShown - } - setSnapsInstallPrivacyWarningShownStatus={ - setSnapsInstallPrivacyWarningShownStatus - } - /> - )} + render={() => + process.env.CHAIN_PERMISSIONS ? ( + + this.cancelPermissionsRequest(requestId) + } + activeTabOrigin={this.state.origin} + request={permissionsRequest} + permissionsRequestId={permissionsRequestId} + approveConnection={this.approveConnection} + selectAccounts={(addresses) => + this.selectAccounts(addresses) + } + selectedAccountAddresses={selectedAccountAddresses} + /> + ) : ( + { + approvePermissionsRequest(...args); + this.redirect(true); + }} + rejectPermissionsRequest={(requestId) => + this.cancelPermissionsRequest(requestId) + } + selectedAccounts={accounts.filter((account) => + selectedAccountAddresses.has(account.address), + )} + targetSubjectMetadata={targetSubjectMetadata} + history={this.props.history} + connectPath={connectPath} + snapsInstallPrivacyWarningShown={ + snapsInstallPrivacyWarningShown + } + setSnapsInstallPrivacyWarningShownStatus={ + setSnapsInstallPrivacyWarningShownStatus + } + /> + ) + } /> ( { - approvePermissionsRequest(...args); - this.redirect(true); - }} + approveConnection={this.approveConnection} rejectConnection={(requestId) => this.cancelPermissionsRequest(requestId) } diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index a2633404d395..da808911054a 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -780,7 +780,7 @@ export default class Routes extends Component { } diff --git a/ui/selectors/permissions.js b/ui/selectors/permissions.js index 52f9ed91152f..3dcfb06cf682 100644 --- a/ui/selectors/permissions.js +++ b/ui/selectors/permissions.js @@ -286,6 +286,7 @@ function getChainsFromSubject(subject) { } function getChainsPermissionFromSubject(subject = {}) { + console.log(subject.permissions?.['endowment:permitted-chains'], subject, "subject"); return subject.permissions?.['endowment:permitted-chains'] || {}; } @@ -298,6 +299,7 @@ function getAccountsFromPermission(accountsPermission) { function getChainsFromPermission(chainsPermission) { const chainsCaveat = getChainsCaveatFromPermission(chainsPermission); + console.log(chainsCaveat, 'chainsCaveat'); return chainsCaveat && Array.isArray(chainsCaveat.value) ? chainsCaveat.value : []; diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 785d54d9dc70..ce373f9643a9 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -1261,6 +1261,14 @@ export function getIpfsGateway(state) { return state.metamask.ipfsGateway; } +export function getSelectedAccountsForDappConnection(state) { + return state.appState.selectedAccountsForDappConnection; +} + +export function getSelectedNetworksForDappConnection(state) { + return state.appState.selectedNetworksForDappConnection; +} + export function getUseExternalServices(state) { return state.metamask.useExternalServices; } diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts index 8ed418346b9a..074568cfbf1d 100644 --- a/ui/store/actionConstants.ts +++ b/ui/store/actionConstants.ts @@ -82,6 +82,10 @@ export const SHOW_NFT_DETECTION_ENABLEMENT_TOAST = export const TOGGLE_ACCOUNT_MENU = 'TOGGLE_ACCOUNT_MENU'; export const TOGGLE_NETWORK_MENU = 'TOGGLE_NETWORK_MENU'; +export const SET_SELECTED_ACCOUNTS_FOR_DAPP_CONNECTIONS = + 'SET_SELECTED_ACCOUNTS_FOR_DAPP_CONNECTIONS'; +export const SET_SELECTED_NETWORKS_FOR_DAPP_CONNECTIONS = + 'SET_SELECTED_NETWORKS_FOR_DAPP_CONNECTIONS'; // deprecated network modal export const DEPRECATED_NETWORK_POPOVER_OPEN = diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 65ca15a2a2f7..9cc85316b51b 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -3227,6 +3227,20 @@ export function setAccountDetailsAddress(address: string) { }; } +export function setSelectedAccountsForDappConnection(addresses: []) { + return { + type: actionConstants.SET_SELECTED_ACCOUNTS_FOR_DAPP_CONNECTIONS, + payload: addresses, + }; +} + +export function setSelectedNetworksForDappConnection(addresses: []) { + return { + type: actionConstants.SET_SELECTED_NETWORKS_FOR_DAPP_CONNECTIONS, + payload: addresses, + }; +} + export function setParticipateInMetaMetrics( participationPreference: boolean, ): ThunkAction< @@ -3877,6 +3891,19 @@ export function requestAccountsPermissionWithId( }; } +export function requestAccountsAndChainPermissionsWithId( + origin: string, +): ThunkAction { + return async (dispatch: MetaMaskReduxDispatch) => { + const id = await submitRequestToBackground( + 'requestAccountsAndChainPermissionsWithId', + [origin], + ); + await forceUpdateMetamaskState(dispatch); + return id; + }; +} + /** * Approves the permissions request. * From 35f675eb119168bf96a13e1aa8c8e7dc70672901 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 11 Sep 2024 16:58:39 +0100 Subject: [PATCH 032/125] lint fix --- .../handlers/ethereum-chain-utils.js | 2 +- .../edit-accounts-modal.tsx | 2 +- .../edit-networks-modal.js | 37 ++++++++++++------- .../review-permissions-page.tsx | 2 +- ui/selectors/permissions.js | 6 ++- yarn.lock | 6 +-- 6 files changed, 34 insertions(+), 21 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index 0afffefa225b..216cd205a090 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -194,7 +194,7 @@ export async function switchChain( requestUserApproval, getCaveat, requestPermittedChainsPermission, - grantPermittedChainsPermissionIncremental + grantPermittedChainsPermissionIncremental, }, ) { try { diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index 3d5f9a2dc6c1..960a99f0040c 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -50,8 +50,8 @@ import { FlexDirection, AlignItems, } from '../../../helpers/constants/design-system'; -import { NewAccountModal } from './new-accounts-modal'; import { getURLHost } from '../../../helpers/utils/util'; +import { NewAccountModal } from './new-accounts-modal'; const defaultAllowedAccountTypes = [EthAccountType.Eoa, EthAccountType.Erc4337]; diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index 617a1a275cf0..c8f751c841b5 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -1,7 +1,14 @@ import PropTypes from 'prop-types'; import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { AlignItems, Display, FlexDirection, IconColor, TextColor, TextVariant } from '../../../helpers/constants/design-system'; +import { + AlignItems, + Display, + FlexDirection, + IconColor, + TextColor, + TextVariant, +} from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getNonTestNetworks, @@ -46,10 +53,12 @@ export const EditNetworksModal = ({ const dispatch = useDispatch(); const testNetworks = useSelector(getTestNetworks); const activeTabOrigin = useSelector(getOriginOfCurrentTab); - const connectedNetworks = useSelector((state) => - getPermittedChainsForSelectedTab(state, activeTabOrigin), - ); - const combinedNetworksIds = combinedNetworks.map((network) => network.chainId); + const connectedNetworks = useSelector((state) => + getPermittedChainsForSelectedTab(state, activeTabOrigin), + ); + const combinedNetworksIds = combinedNetworks.map( + (network) => network.chainId, + ); const selectedPermittedChains = connectedNetworks.length > 0 ? connectedNetworks : combinedNetworksIds; const [selectedChains, setSelectedChains] = useState(selectedPermittedChains); @@ -64,15 +73,15 @@ export const EditNetworksModal = ({ setSelectedChains([]); }; -const handleAccountClick = (chainId) => { - if (selectedChains.includes(chainId)) { - // Remove the chainId from the selectedChains - setSelectedChains(selectedChains.filter((id) => id !== chainId)); - } else { - // Add the chainId to selectedChains - setSelectedChains([...selectedChains, chainId]); - } -}; + const handleAccountClick = (chainId) => { + if (selectedChains.includes(chainId)) { + // Remove the chainId from the selectedChains + setSelectedChains(selectedChains.filter((id) => id !== chainId)); + } else { + // Add the chainId to selectedChains + setSelectedChains([...selectedChains, chainId]); + } + }; const allAreSelected = () => { return combinedNetworksIds.length === selectedChains.length; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index d2b1a19d978b..c76f64a32e54 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -49,12 +49,12 @@ import { ToastContainer, Toast } from '../..'; import { NoConnectionContent } from '../connections/components/no-connection'; import { Content, Footer, Header, Page } from '../page'; import { SubjectsType } from '../connections/components/connections.types'; -import { SiteCell } from '.'; import { CONNECT_ROUTE } from '../../../../helpers/constants/routes'; import { DisconnectAllModal, DisconnectType, } from '../../disconnect-all-modal/disconnect-all-modal'; +import { SiteCell } from '.'; export const ReviewPermissions = () => { const t = useI18nContext(); diff --git a/ui/selectors/permissions.js b/ui/selectors/permissions.js index 3dcfb06cf682..d87ad1f9bea6 100644 --- a/ui/selectors/permissions.js +++ b/ui/selectors/permissions.js @@ -286,7 +286,11 @@ function getChainsFromSubject(subject) { } function getChainsPermissionFromSubject(subject = {}) { - console.log(subject.permissions?.['endowment:permitted-chains'], subject, "subject"); + console.log( + subject.permissions?.['endowment:permitted-chains'], + subject, + 'subject', + ); return subject.permissions?.['endowment:permitted-chains'] || {}; } diff --git a/yarn.lock b/yarn.lock index 5d333062da8a..1062fca38317 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14403,9 +14403,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001587, caniuse-lite@npm:^1.0.30001599": - version: 1.0.30001600 - resolution: "caniuse-lite@npm:1.0.30001600" - checksum: 10/4c52f83ed71bc5f6e443bd17923460f1c77915adc2c2aa79ddaedceccc690b5917054b0c41b79e9138cbbd9abcdc0db9e224e79e3e734e581dfec06505f3a2b4 + version: 1.0.30001660 + resolution: "caniuse-lite@npm:1.0.30001660" + checksum: 10/5d83f0b7e2075b7e31f114f739155dc6c21b0afe8cb61180f625a4903b0ccd3d7591a5f81c930f14efddfa57040203ba0890850b8a3738f6c7f17c7dd83b9de8 languageName: node linkType: hard From 5ef36cbf62d1fc389cfec86e53eb0f0ccc3370b4 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 11 Sep 2024 17:00:36 +0100 Subject: [PATCH 033/125] lint fix --- app/scripts/controllers/permissions/specifications.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index 60e0e95a1ff6..a3c108a4b73c 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -1,7 +1,6 @@ import { constructPermission, PermissionType, - SubjectType, } from '@metamask/permission-controller'; import { caveatSpecifications as snapsCaveatsSpecifications, From bc50ba1937b160832e97b2f5d61a4b23a2dd1034 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 12 Sep 2024 12:29:10 +0100 Subject: [PATCH 034/125] error fix --- .../edit-accounts-modal.tsx | 33 ++----------- .../edit-networks-modal.js | 49 ++++++++++++++++--- ui/store/actions.ts | 6 ++- 3 files changed, 52 insertions(+), 36 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index 960a99f0040c..c9a170cb06f6 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -6,12 +6,10 @@ import { isEvmAccountType, KeyringAccountType, } from '@metamask/keyring-api'; -import { NonEmptyArray } from '@metamask/utils'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getInternalAccounts, getOrderedConnectedAccountsForConnectedDapp, - getPermissionSubjects, getUpdatedAndSortedAccounts, } from '../../../selectors'; import { @@ -34,13 +32,12 @@ import { import { AccountListItem } from '..'; import { MergedInternalAccount } from '../../../selectors/selectors.types'; import { mergeAccounts } from '../account-list-menu/account-list-menu'; + import { addMorePermittedAccounts, - removePermissionsFor, removePermittedAccount, setSelectedAccountsForDappConnection, } from '../../../store/actions'; -import { SubjectsType } from '../pages/connections/components/connections.types'; import { JustifyContent, Display, @@ -83,11 +80,11 @@ export const EditAccountsModal: React.FC = ({ const mergedAccounts: MergedInternalAccount[] = useMemo(() => { return mergeAccounts(accounts, internalAccounts).filter( - (account: InternalAccount) => allowedAccountTypes.includes(account.type), + (account: InternalAccount) => + allowedAccountTypes.includes(account.type as KeyringAccountType), ); }, [accounts, internalAccounts, allowedAccountTypes]); - const subjects = useSelector(getPermissionSubjects); const connectedAccounts = useSelector((state: any) => getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin).filter( (account: InternalAccount) => isEvmAccountType(account.type), @@ -115,27 +112,6 @@ export const EditAccountsModal: React.FC = ({ ); }; - const disconnectAllAccounts = () => { - const subject = (subjects as SubjectsType)[activeTabOrigin]; - if (subject) { - const permissionMethodNames = Object.values(subject.permissions).map( - ({ parentCapability }: { parentCapability: string }) => - parentCapability, - ) as string[]; - if (permissionMethodNames.length > 0) { - const permissionsRecord: Record = { - [activeTabOrigin]: permissionMethodNames, - }; - - dispatch( - removePermissionsFor( - permissionsRecord as Record>, - ), - ); - } - } - }; - const managePermittedAccounts = ( selectedAccounts: string[], connectedAccountsAddresses: string[], @@ -168,6 +144,7 @@ export const EditAccountsModal: React.FC = ({ const allAreSelected = () => { return accounts.length === selectedAccounts.length; }; + let checked = false; let isIndeterminate = false; if (allAreSelected()) { @@ -182,7 +159,7 @@ export const EditAccountsModal: React.FC = ({ <> console.log('bb')} + onClose={onClose} data-testid="edit-accounts-modal" className="edit-accounts-modal" > diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index c8f751c841b5..95b5eed5d3b1 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -1,5 +1,5 @@ -import PropTypes from 'prop-types'; import React, { useState } from 'react'; +import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { AlignItems, @@ -11,9 +11,7 @@ import { } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { - getNonTestNetworks, getOriginOfCurrentTab, - getPermittedChainsByOrigin, getPermittedChainsForSelectedTab, getTestNetworks, } from '../../../selectors'; @@ -62,6 +60,7 @@ export const EditNetworksModal = ({ const selectedPermittedChains = connectedNetworks.length > 0 ? connectedNetworks : combinedNetworksIds; const [selectedChains, setSelectedChains] = useState(selectedPermittedChains); + const selectAll = () => { const newSelectedAccounts = combinedNetworks.map( (network) => network.chainId, @@ -86,6 +85,7 @@ export const EditNetworksModal = ({ const allAreSelected = () => { return combinedNetworksIds.length === selectedChains.length; }; + let checked = false; let isIndeterminate = false; if (allAreSelected()) { @@ -95,6 +95,7 @@ export const EditNetworksModal = ({ checked = false; isIndeterminate = true; } + const managePermittedChains = ( selectedChains, selectedPermittedChains, @@ -116,7 +117,9 @@ export const EditNetworksModal = ({ dispatch(removePermittedChain(activeTabOrigin, selectedChain)); }); }; + const hostName = getURLHost(activeTabOrigin); + return ( Date: Thu, 12 Sep 2024 13:13:55 +0100 Subject: [PATCH 035/125] updated permissions --- ui/components/multichain/pages/index.js | 2 +- .../review-permission.types.tsx | 35 ++++++++++ .../review-permissions-page.tsx | 65 +++++++++++++------ .../site-cell/site-cell-tooltip.js | 50 +++++++++++++- .../connect-page/connect-page.tsx | 1 - .../permissions-connect.component.js | 1 - 6 files changed, 128 insertions(+), 26 deletions(-) create mode 100644 ui/components/multichain/pages/review-permissions-page/review-permission.types.tsx diff --git a/ui/components/multichain/pages/index.js b/ui/components/multichain/pages/index.js index 5d24b78b6b2e..f0a21405e66b 100644 --- a/ui/components/multichain/pages/index.js +++ b/ui/components/multichain/pages/index.js @@ -3,4 +3,4 @@ export { PermissionsPage } from './permissions-page/permissions-page'; export { ReviewPermissions, SiteCell, -} from './review-permissions-page/review-permissions-page'; +} from './review-permissions-page'; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permission.types.tsx b/ui/components/multichain/pages/review-permissions-page/review-permission.types.tsx new file mode 100644 index 000000000000..9bdbf86d9d95 --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/review-permission.types.tsx @@ -0,0 +1,35 @@ +import { type InternalAccount } from '@metamask/keyring-api'; + +// Define ConnectedSite interface +export type ConnectedSite = { + iconUrl: string; + name: string; + origin: string; + subjectType: string; + extensionId: string | null; + // Add other properties as needed +}; + +// Define ConnectedSites interface +export type ConnectedSites = { + [address: string]: ConnectedSite[]; // Index signature +}; + +// Define KeyringType interface +export type KeyringType = { + type: string; +}; + +// Define AccountType interface +export type AccountType = InternalAccount & { + balance: string; + keyring: KeyringType; + label: string; +}; + +export type Subject = { + permissions: { parentCapability: string }[]; +}; +export type SubjectsType = { + [key: string]: Subject; +}; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index c76f64a32e54..8438a1520f0a 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -27,7 +27,6 @@ import { import { removePermissionsFor, requestAccountsAndChainPermissionsWithId, - requestAccountsPermissionWithId, } from '../../../../store/actions'; import { AvatarFavicon, @@ -48,7 +47,7 @@ import { import { ToastContainer, Toast } from '../..'; import { NoConnectionContent } from '../connections/components/no-connection'; import { Content, Footer, Header, Page } from '../page'; -import { SubjectsType } from '../connections/components/connections.types'; +import { AccountType, SubjectsType } from '../connections/components/connections.types'; import { CONNECT_ROUTE } from '../../../../helpers/constants/routes'; import { DisconnectAllModal, @@ -56,60 +55,82 @@ import { } from '../../disconnect-all-modal/disconnect-all-modal'; import { SiteCell } from '.'; +interface UrlParams { + origin: string; +} + +interface PermittedAccountsByOrigin { + [key: string]: Array<{ address: string }>; +} + export const ReviewPermissions = () => { const t = useI18nContext(); const dispatch = useDispatch(); const history = useHistory(); - const urlParams: { origin: string } = useParams(); + const urlParams = useParams(); const securedOrigin = decodeURIComponent(urlParams.origin); const [showAccountToast, setShowAccountToast] = useState(false); const [showNetworkToast, setShowNetworkToast] = useState(false); const [showDisconnectAllModal, setShowDisconnectAllModal] = useState(false); const activeTabOrigin: string = securedOrigin; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { openMetaMaskTabs } = useSelector((state: any) => state.appState); - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { id } = useSelector((state: any) => state.activeTab); + + // Define types for state + const { openMetaMaskTabs }: { openMetaMaskTabs: Record } = + useSelector( + (state: { appState: { openMetaMaskTabs: Record } }) => + state.appState, + ); + + const { id }: { id: string } = useSelector( + (state: { activeTab: { id: string } }) => state.activeTab, + ); + const subjectMetadata: { [key: string]: any } = useSelector( getConnectedSitesList, ); const connectedSubjectsMetadata = subjectMetadata[activeTabOrigin]; + const connectedNetworks = useSelector((state) => getPermittedChainsForSelectedTab(state, activeTabOrigin), - ); - console.log(connectedNetworks, 'connectedNetworks'); + ) as string[]; + const permittedAccountsByOrigin = useSelector( getPermittedAccountsByOrigin, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) as { [key: string]: any[] }; + ) as PermittedAccountsByOrigin; const networksList = useSelector(getNonTestNetworks); const testNetworks = useSelector(getTestNetworks); const combinedNetworks = [...networksList, ...testNetworks]; + const connectedAccounts = useSelector((state) => getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin), - ); + ) as AccountType[]; + const subjects = useSelector(getPermissionSubjects); const grantedNetworks = combinedNetworks.filter( (net: { chainId: any }) => connectedNetworks.indexOf(net.chainId) !== -1, ); + const hostName = getURLHost(securedOrigin); - console.log(connectedNetworks, grantedNetworks); const currentTabHasNoAccounts = !permittedAccountsByOrigin[activeTabOrigin]?.length; - let tabToConnect: { origin: any } = { origin: null }; + let tabToConnect: { origin: string | null } = { origin: null }; if (activeTabOrigin && currentTabHasNoAccounts && !openMetaMaskTabs[id]) { tabToConnect = { origin: activeTabOrigin, }; } + const requestAccountsAndChainPermissions = async () => { - const requestId = await dispatch( - requestAccountsAndChainPermissionsWithId(tabToConnect.origin), - ); - history.push(`${CONNECT_ROUTE}/${requestId}`); + if (tabToConnect.origin) { + // Ensure origin is not null + const requestId = await dispatch( + requestAccountsAndChainPermissionsWithId(tabToConnect.origin), + ); + history.push(`${CONNECT_ROUTE}/${requestId}`); + } else { + console.error('Tab origin is null.'); + } }; const disconnectAllAccounts = () => { @@ -133,6 +154,7 @@ export const ReviewPermissions = () => { } } }; + return ( { iconName={IconName.ArrowLeft} className="connections-header__start-accessory" size={ButtonIconSize.Sm} + // eslint-disable-next-line @typescript-eslint/no-explicit-any onClick={() => (history as any).goBack()} /> } @@ -191,7 +214,7 @@ export const ReviewPermissions = () => { onDisconnectClick={() => setShowDisconnectAllModal(true)} activeTabOrigin={activeTabOrigin} combinedNetworks={networksList} - /> + approvedAccounts={[]} /> ) : ( )} diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js index 9be67f39e5cb..d54db51729be 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js @@ -155,7 +155,53 @@ export const SiteCellTooltip = ({ }; SiteCellTooltip.propTypes = { /** - * The accounts data to display + * An array of account objects to be displayed in the tooltip. + * Each object should contain `address`, `label`, and `metadata.name`. */ - accounts: PropTypes.object.isRequired, + accounts: PropTypes.arrayOf( + PropTypes.shape({ + address: PropTypes.string.isRequired, // The unique address of the account. + label: PropTypes.string, // Optional label for the account. + metadata: PropTypes.shape({ + name: PropTypes.string.isRequired, // Account's name from metadata. + }).isRequired, + }), + ).isRequired, + + /** + * Data for the avatar group component related to accounts. + * This array contains account avatar data to be rendered in the group. + */ + avatarAccountsData: PropTypes.arrayOf( + PropTypes.shape({ + address: PropTypes.string.isRequired, // The account address to display. + name: PropTypes.string, // The account's name. + avatarUrl: PropTypes.string, // Optional URL for the avatar image. + }), + ).isRequired, + + /** + * An array of network objects to display in the tooltip. + */ + networks: PropTypes.arrayOf( + PropTypes.shape({ + chainId: PropTypes.string.isRequired, // The unique chain ID of the network. + nickname: PropTypes.string.isRequired, // The network's name. + rpcPrefs: PropTypes.shape({ + imageUrl: PropTypes.string, // Optional URL for the network's image. + }), + }), + ).isRequired, + + /** + * Data for the avatar group component related to networks. + */ + avatarNetworksData: PropTypes.arrayOf( + PropTypes.shape({ + chainId: PropTypes.string.isRequired, // The unique chain ID of the network. + name: PropTypes.string, // The network's name. + avatarUrl: PropTypes.string, // Optional URL for the network's avatar image. + }), + ).isRequired, }; + diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index ee0cddf5ba8c..4b98467026f7 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -115,7 +115,6 @@ export const ConnectPage = ({ selectAccounts(Array.from(selectedAccounts))} onNetworksClick={() => console.log('testing')} approvedAccounts={approvedAccounts} diff --git a/ui/pages/permissions-connect/permissions-connect.component.js b/ui/pages/permissions-connect/permissions-connect.component.js index 12bb0e182be8..5bfef06422d5 100644 --- a/ui/pages/permissions-connect/permissions-connect.component.js +++ b/ui/pages/permissions-connect/permissions-connect.component.js @@ -293,7 +293,6 @@ export default class PermissionConnect extends Component { approveConnection = (...args) => { const { approvePermissionsRequest } = this.props; - console.log('testing', approvePermissionsRequest, ...args); approvePermissionsRequest(...args); this.redirect(true); }; From 7bee52ada240df4bbeb59d1ffee8d632136ce684 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 12 Sep 2024 13:32:05 +0100 Subject: [PATCH 036/125] jest update --- .../edit-accounts-modal/edit-accounts-modal.test.tsx | 8 +++++++- .../review-permissions-page.test.tsx | 0 2 files changed, 7 insertions(+), 1 deletion(-) delete mode 100644 ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx index a13ccf1ef14d..63a9184479a3 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx @@ -9,9 +9,11 @@ const render = ( props: { onClose: () => void; allowedAccountTypes: KeyringAccountType[]; + activeTabOrigin: string; } = { onClose: () => jest.fn(), allowedAccountTypes: [EthAccountType.Eoa, EthAccountType.Erc4337], + activeTabOrigin: "https://test.dapp", }, state = {}, ) => { @@ -34,7 +36,11 @@ const render = ( origin: 'https://test.dapp', }, }); - return renderWithProvider(, store); + return renderWithProvider(, store); }; describe('EditAccountsModal', () => { it('should render correctly', () => { diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx deleted file mode 100644 index e69de29bb2d1..000000000000 From 58c11e4641a7363869c7e99cbb8e12fb80c06af8 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 12 Sep 2024 15:59:43 +0100 Subject: [PATCH 037/125] lint fix --- .../edit-accounts-modal.test.tsx | 21 +++++++++++++------ .../edit-accounts-modal.tsx | 4 ++-- ui/components/multichain/pages/index.js | 5 +---- .../review-permissions-page.tsx | 21 ++++++++++++------- .../site-cell/site-cell-tooltip.js | 1 - .../permissions-connect.component.js | 1 + 6 files changed, 32 insertions(+), 21 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx index 63a9184479a3..397712c0a823 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx @@ -13,7 +13,7 @@ const render = ( } = { onClose: () => jest.fn(), allowedAccountTypes: [EthAccountType.Eoa, EthAccountType.Erc4337], - activeTabOrigin: "https://test.dapp", + activeTabOrigin: 'https://test.dapp', }, state = {}, ) => { @@ -36,11 +36,20 @@ const render = ( origin: 'https://test.dapp', }, }); - return renderWithProvider(, store); + return renderWithProvider( + , + store, + ); }; describe('EditAccountsModal', () => { it('should render correctly', () => { diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index c9a170cb06f6..bd4a1723a5e0 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -113,8 +113,8 @@ export const EditAccountsModal: React.FC = ({ }; const managePermittedAccounts = ( - selectedAccounts: string[], - connectedAccountsAddresses: string[], + selectedAccounts, + connectedAccountsAddresses, ) => { const removedAccounts = connectedAccountsAddresses.filter( (acc) => !selectedAccounts.includes(acc), diff --git a/ui/components/multichain/pages/index.js b/ui/components/multichain/pages/index.js index f0a21405e66b..a19f23039138 100644 --- a/ui/components/multichain/pages/index.js +++ b/ui/components/multichain/pages/index.js @@ -1,6 +1,3 @@ export { Connections } from './connections'; export { PermissionsPage } from './permissions-page/permissions-page'; -export { - ReviewPermissions, - SiteCell, -} from './review-permissions-page'; +export { ReviewPermissions, SiteCell } from './review-permissions-page'; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 8438a1520f0a..5463f9e0f341 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -47,7 +47,10 @@ import { import { ToastContainer, Toast } from '../..'; import { NoConnectionContent } from '../connections/components/no-connection'; import { Content, Footer, Header, Page } from '../page'; -import { AccountType, SubjectsType } from '../connections/components/connections.types'; +import { + AccountType, + SubjectsType, +} from '../connections/components/connections.types'; import { CONNECT_ROUTE } from '../../../../helpers/constants/routes'; import { DisconnectAllModal, @@ -55,13 +58,13 @@ import { } from '../../disconnect-all-modal/disconnect-all-modal'; import { SiteCell } from '.'; -interface UrlParams { +type UrlParams = { origin: string; -} +}; -interface PermittedAccountsByOrigin { - [key: string]: Array<{ address: string }>; -} +type PermittedAccountsByOrigin = { + [key: string]: { address: string }[]; +}; export const ReviewPermissions = () => { const t = useI18nContext(); @@ -85,6 +88,7 @@ export const ReviewPermissions = () => { (state: { activeTab: { id: string } }) => state.activeTab, ); + // eslint-disable-next-line @typescript-eslint/no-explicit-any const subjectMetadata: { [key: string]: any } = useSelector( getConnectedSitesList, ); @@ -107,7 +111,7 @@ export const ReviewPermissions = () => { const subjects = useSelector(getPermissionSubjects); const grantedNetworks = combinedNetworks.filter( - (net: { chainId: any }) => connectedNetworks.indexOf(net.chainId) !== -1, + (net: { chainId: string }) => connectedNetworks.indexOf(net.chainId) !== -1, ); const hostName = getURLHost(securedOrigin); @@ -214,7 +218,8 @@ export const ReviewPermissions = () => { onDisconnectClick={() => setShowDisconnectAllModal(true)} activeTabOrigin={activeTabOrigin} combinedNetworks={networksList} - approvedAccounts={[]} /> + approvedAccounts={[]} + /> ) : ( )} diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js index d54db51729be..8d514764a9f2 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js @@ -204,4 +204,3 @@ SiteCellTooltip.propTypes = { }), ).isRequired, }; - diff --git a/ui/pages/permissions-connect/permissions-connect.component.js b/ui/pages/permissions-connect/permissions-connect.component.js index 5bfef06422d5..fb915fd9c304 100644 --- a/ui/pages/permissions-connect/permissions-connect.component.js +++ b/ui/pages/permissions-connect/permissions-connect.component.js @@ -318,6 +318,7 @@ export default class PermissionConnect extends Component { approvePendingApproval, rejectPendingApproval, setSnapsInstallPrivacyWarningShownStatus, + approvePermissionsRequest, } = this.props; const { selectedAccountAddresses, From 065454169e2350909f622dbd4d12c4da37264823 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 12 Sep 2024 16:20:05 +0100 Subject: [PATCH 038/125] updated lint errors --- .../edit-accounts-modal.tsx | 16 +++--- .../edit-networks-modal.js | 31 ++++------- .../connect-page/connect-page.tsx | 55 ++++++++++--------- 3 files changed, 47 insertions(+), 55 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index bd4a1723a5e0..9906b9181cd2 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -84,8 +84,7 @@ export const EditAccountsModal: React.FC = ({ allowedAccountTypes.includes(account.type as KeyringAccountType), ); }, [accounts, internalAccounts, allowedAccountTypes]); - - const connectedAccounts = useSelector((state: any) => + const connectedAccounts = useSelector((state) => getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin).filter( (account: InternalAccount) => isEvmAccountType(account.type), ), @@ -113,23 +112,24 @@ export const EditAccountsModal: React.FC = ({ }; const managePermittedAccounts = ( - selectedAccounts, - connectedAccountsAddresses, + accounts: string[], + accountsAddresses: string[], ) => { - const removedAccounts = connectedAccountsAddresses.filter( - (acc) => !selectedAccounts.includes(acc), + const removedAccounts = accountsAddresses.filter( + (acc) => !accounts.includes(acc), ); removedAccounts.forEach((account) => { dispatch(removePermittedAccount(activeTabOrigin, account)); }); - const newAccounts = selectedAccounts.filter( - (acc) => !connectedAccountsAddresses.includes(acc), + const newAccounts = accounts.filter( + (acc) => !accountsAddresses.includes(acc), ); if (newAccounts.length > 0) { dispatch(addMorePermittedAccounts(activeTabOrigin, newAccounts)); } }; + const selectAll = () => { const newSelectedAccounts = accounts.map( (account: { address: string }) => account.address, diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index 95b5eed5d3b1..84f4c19788d3 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -45,7 +45,6 @@ export const EditNetworksModal = ({ currentTabHasNoAccounts, combinedNetworks, onDisconnectClick, - defaultNetworks, }) => { const t = useI18nContext(); const dispatch = useDispatch(); @@ -97,24 +96,24 @@ export const EditNetworksModal = ({ } const managePermittedChains = ( - selectedChains, - selectedPermittedChains, - activeTabOrigin, + chains, + permittedChains, + tabOrigin, ) => { - if (!Array.isArray(selectedChains)) { - console.error('selectedChains is not an array:', selectedChains); - selectedChains = []; + if (!Array.isArray(chains)) { + console.error('selectedChains is not an array:', chains); + chains = []; } - dispatch(grantPermittedChains(activeTabOrigin, selectedChains)); + dispatch(grantPermittedChains(activeTabOrigin, chains)); - const removedElements = selectedPermittedChains.filter( - (chain) => !selectedChains.includes(chain), + const removedElements = permittedChains.filter( + (chain) => !chains.includes(chain), ); // Dispatch removePermittedChains for each removed element removedElements.forEach((chain) => { const selectedChain = [chain]; - dispatch(removePermittedChain(activeTabOrigin, selectedChain)); + dispatch(removePermittedChain(tabOrigin, selectedChain)); }); }; @@ -282,14 +281,4 @@ EditNetworksModal.propTypes = { * Function to execute when the disconnect button is clicked. */ onDisconnectClick: PropTypes.func.isRequired, - - /** - * Array of network objects representing default networks. - */ - defaultNetworks: PropTypes.arrayOf( - PropTypes.shape({ - chainId: PropTypes.string.isRequired, // The chain ID of the default network - nickname: PropTypes.string.isRequired, // Display name of the default network - }), - ), }; diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index 4b98467026f7..1c7931f189ee 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -27,30 +27,40 @@ import { Display, TextVariant, } from '../../../helpers/constants/design-system'; -import { PermissionNames } from '../../../../app/scripts/controllers/permissions/specifications'; +import { AccountType } from '../../../components/multichain/pages/connections/components/connections.types'; -export const ConnectPage = ({ +interface Request { + id: string; + origin: string; + [key: string]: any; // Adjust this if you have a more specific shape for the request +} + +interface ConnectPageProps { + request: Request; + permissionsRequestId: string; + rejectPermissionsRequest: (id: string) => void; + approveConnection: (request: Request) => void; + accounts: AccountType[]; + selectAccounts: (addresses: string[]) => void; + selectedAccountAddresses: Set; + activeTabOrigin: string; +} + +export const ConnectPage: React.FC = ({ request, + permissionsRequestId, rejectPermissionsRequest, approveConnection, accounts, selectAccounts, selectedAccountAddresses, activeTabOrigin, - permissionsRequestId, -}: { - request: any; - permissionsRequestId: string; - rejectPermissionsRequest: () => void; - approveConnection: (request: any) => void; - accounts: any[]; - selectAccounts: (addresses: any[]) => void; - selectedAccountAddresses: Set; - activeTabOrigin: string; }) => { const t = useI18nContext(); + + // Get networks and accounts from Redux const networksList = useSelector(getNonTestNetworks); - const selectednetworksList = useSelector( + const selectedNetworksList = useSelector( getSelectedNetworksForDappConnection, ); const testNetworks = useSelector(getTestNetworks); @@ -65,20 +75,11 @@ export const ConnectPage = ({ getSelectedAccountsForDappConnection, ); - // Handle account selection/deselection - const handleAccountClick = (address: string) => { - const newSelectedAccounts = new Set(selectedAccounts); - if (newSelectedAccounts.has(address)) { - newSelectedAccounts.delete(address); - } else { - newSelectedAccounts.add(address); - } - setSelectedAccounts(newSelectedAccounts); - }; + // Filter networks based on chainId - const filteredNetworks = Array.isArray(selectednetworksList) + const filteredNetworks = Array.isArray(selectedNetworksList) ? combinedNetworks.filter((network) => - selectednetworksList.includes(network.chainId), + selectedNetworksList.includes(network.chainId), ) : networksList; @@ -86,7 +87,7 @@ export const ConnectPage = ({ const approvedAccounts = selectedAccountsForDappConnection.length > 0 ? selectedAccountsForDappConnection - : [currentAccount.address]; + : [currentAccount?.address]; // Handle confirmation const onConfirm = () => { @@ -98,6 +99,7 @@ export const ConnectPage = ({ approveConnection(_request); }; + // Filter accounts by address const filterAccountsByAddress = accounts.filter((account) => approvedAccounts.includes(account.address), ); @@ -120,6 +122,7 @@ export const ConnectPage = ({ approvedAccounts={approvedAccounts} activeTabOrigin={activeTabOrigin} combinedNetworks={networksList} + onDisconnectClick={() => null} />
From 0dab6181e35f42ab9aad86adfd132493900e2abe Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 12 Sep 2024 16:25:44 +0100 Subject: [PATCH 039/125] lint fix --- .../pages/review-permissions-page/review-permissions-page.tsx | 2 +- ui/pages/permissions-connect/connect-page/connect-page.tsx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 5463f9e0f341..48274b2c24c8 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -296,7 +296,7 @@ export const ReviewPermissions = () => { )} -
{' '} +
); diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index 1c7931f189ee..21802528429f 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -75,7 +75,6 @@ export const ConnectPage: React.FC = ({ getSelectedAccountsForDappConnection, ); - // Filter networks based on chainId const filteredNetworks = Array.isArray(selectedNetworksList) ? combinedNetworks.filter((network) => @@ -111,7 +110,7 @@ export const ConnectPage: React.FC = ({ >
{t('connectWithMetaMask')} - This site wants to: + {t('connectionDescription')}:
Date: Thu, 12 Sep 2024 21:56:01 +0100 Subject: [PATCH 040/125] lint fix --- .../edit-accounts-modal/edit-accounts-modal.tsx | 6 +++--- .../edit-networks-modal/edit-networks-modal.js | 10 +--------- .../multichain/pages/connections/connections.tsx | 4 ++-- .../connect-page/connect-page.tsx | 16 ++++++++-------- 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index 9906b9181cd2..b4e84dd26fa2 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -112,17 +112,17 @@ export const EditAccountsModal: React.FC = ({ }; const managePermittedAccounts = ( - accounts: string[], + selectedAcc: string[], accountsAddresses: string[], ) => { const removedAccounts = accountsAddresses.filter( - (acc) => !accounts.includes(acc), + (acc) => !selectedAcc.includes(acc), ); removedAccounts.forEach((account) => { dispatch(removePermittedAccount(activeTabOrigin, account)); }); - const newAccounts = accounts.filter( + const newAccounts = selectedAcc.filter( (acc) => !accountsAddresses.includes(acc), ); if (newAccounts.length > 0) { diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index 84f4c19788d3..df87b2270f3a 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -95,15 +95,7 @@ export const EditNetworksModal = ({ isIndeterminate = true; } - const managePermittedChains = ( - chains, - permittedChains, - tabOrigin, - ) => { - if (!Array.isArray(chains)) { - console.error('selectedChains is not an array:', chains); - chains = []; - } + const managePermittedChains = (chains, permittedChains, tabOrigin) => { dispatch(grantPermittedChains(activeTabOrigin, chains)); const removedElements = permittedChains.filter( diff --git a/ui/components/multichain/pages/connections/connections.tsx b/ui/components/multichain/pages/connections/connections.tsx index 696326408daa..a80e1611d2db 100644 --- a/ui/components/multichain/pages/connections/connections.tsx +++ b/ui/components/multichain/pages/connections/connections.tsx @@ -131,7 +131,7 @@ export const Connections = () => { origin: activeTabOrigin, }; } - const requestAccountsAndChainPermissionsWithId = async () => { + const requestAccountsAndChainPermissions = async () => { const requestId = await dispatch( requestAccountsAndChainPermissionsWithId(tabToConnect.origin), ); @@ -396,7 +396,7 @@ export const Connections = () => { size={ButtonPrimarySize.Lg} block data-test-id="no-connections-button" - onClick={requestAccountsAndChainPermissionsWithId} + onClick={requestAccountsAndChainPermissions} > {t('connectAccounts')} diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index 21802528429f..7610ae1c93ba 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -29,13 +29,14 @@ import { } from '../../../helpers/constants/design-system'; import { AccountType } from '../../../components/multichain/pages/connections/components/connections.types'; -interface Request { +type Request = { id: string; origin: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any; // Adjust this if you have a more specific shape for the request -} +}; -interface ConnectPageProps { +type ConnectPageProps = { request: Request; permissionsRequestId: string; rejectPermissionsRequest: (id: string) => void; @@ -44,7 +45,7 @@ interface ConnectPageProps { selectAccounts: (addresses: string[]) => void; selectedAccountAddresses: Set; activeTabOrigin: string; -} +}; export const ConnectPage: React.FC = ({ request, @@ -67,9 +68,6 @@ export const ConnectPage: React.FC = ({ const combinedNetworks = [...networksList, ...testNetworks]; const currentAccount = useSelector(getSelectedInternalAccount); - const [selectedAccounts, setSelectedAccounts] = useState( - selectedAccountAddresses, - ); const selectedAccountsForDappConnection = useSelector( getSelectedAccountsForDappConnection, @@ -116,7 +114,9 @@ export const ConnectPage: React.FC = ({ selectAccounts(Array.from(selectedAccounts))} + onAccountsClick={() => + selectAccounts(Array.from(selectedAccountAddresses)) + } onNetworksClick={() => console.log('testing')} approvedAccounts={approvedAccounts} activeTabOrigin={activeTabOrigin} From 36c5c316fd76e9ccf9d4c56c9a344234247386ca Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 12 Sep 2024 21:59:21 +0100 Subject: [PATCH 041/125] updated controller --- app/scripts/metamask-controller.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index c36b838a1dd7..5001b167fd90 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -5588,8 +5588,7 @@ export default class MetamaskController extends EventEmitter { }, }), requestPermissionsForOrigin: (requestedPermissions) => { - this.permissionController.requestPermissions.bind( - this.permissionController, + return this.permissionController.requestPermissions( { origin }, { [PermissionNames.permittedChains]: {}, ...requestedPermissions }, ); From be9e6a5142cc132b57211b209d3929513499c60c Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 12 Sep 2024 22:16:40 +0100 Subject: [PATCH 042/125] updated syntax --- .../review-permissions-page.tsx | 17 ++++++----------- .../connect-page/connect-page.tsx | 2 +- ui/store/actions.ts | 1 - 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 48274b2c24c8..ad231bdadb49 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -117,8 +117,8 @@ export const ReviewPermissions = () => { const hostName = getURLHost(securedOrigin); const currentTabHasNoAccounts = !permittedAccountsByOrigin[activeTabOrigin]?.length; - - let tabToConnect: { origin: string | null } = { origin: null }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let tabToConnect: { origin: any} = { origin: null }; // origin could be null or a string based on the connection status or screen view if (activeTabOrigin && currentTabHasNoAccounts && !openMetaMaskTabs[id]) { tabToConnect = { origin: activeTabOrigin, @@ -126,15 +126,10 @@ export const ReviewPermissions = () => { } const requestAccountsAndChainPermissions = async () => { - if (tabToConnect.origin) { - // Ensure origin is not null - const requestId = await dispatch( - requestAccountsAndChainPermissionsWithId(tabToConnect.origin), - ); - history.push(`${CONNECT_ROUTE}/${requestId}`); - } else { - console.error('Tab origin is null.'); - } + const requestId = await dispatch( + requestAccountsAndChainPermissionsWithId(tabToConnect.origin), + ); + history.push(`${CONNECT_ROUTE}/${requestId}`); }; const disconnectAllAccounts = () => { diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index 7610ae1c93ba..96bef39e4c16 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -117,7 +117,7 @@ export const ConnectPage: React.FC = ({ onAccountsClick={() => selectAccounts(Array.from(selectedAccountAddresses)) } - onNetworksClick={() => console.log('testing')} + onNetworksClick={() => null} approvedAccounts={approvedAccounts} activeTabOrigin={activeTabOrigin} combinedNetworks={networksList} diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 2b63c736a670..c4ca64ed11f4 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -1826,7 +1826,6 @@ export function removePermittedAccount( origin: string, address: string, ): ThunkAction { - console.log('removed is done'); return async (dispatch: MetaMaskReduxDispatch) => { await new Promise((resolve, reject) => { callBackgroundMethod( From 2a3ab14c75d4d4f6585686af34cf5950a60cc2a3 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 12 Sep 2024 22:47:57 +0100 Subject: [PATCH 043/125] updated stories --- .../edit-accounts-modal.stories.tsx | 2 + .../edit-networks-modal.stories.js | 67 ++++++++++++++++++- .../site-cell/site-cell.stories.tsx | 50 +++++++++++++- .../connect-page/connect-page.tsx | 2 +- ui/selectors/permissions.js | 6 -- 5 files changed, 118 insertions(+), 9 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx index d1eb6c373f43..cb45f7027dc1 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx @@ -5,9 +5,11 @@ export default { title: 'Components/Multichain/EditAccountsModal', argTypes: { onClose: { action: 'onClose' }, + activeTabOrigin: { control: 'text' }, }, args: { onClose: () => undefined, + activeTabOrigin: 'https://test.dapp', }, }; diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js index 10cf14383bba..7a902f660f47 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js @@ -3,8 +3,73 @@ import { EditNetworksModal } from '.'; export default { title: 'Components/Multichain/EditNetworksModal', + component: EditNetworksModal, + argTypes: { + combinedNetworks: { + control: 'array', + }, + }, + args: { + combinedNetworks: + [ + { + "chainId": "0x1", + "nickname": "Ethereum Mainnet", + "rpcUrl": "https://mainnet.infura.io/v3/cb3fa73a8bdf4342b8ed8b07e0740be9", + "rpcPrefs": { + "imageUrl": "./images/eth_logo.svg" + }, + "providerType": "mainnet", + "ticker": "ETH", + "id": "mainnet", + "removable": false, + "blockExplorerUrl": "https://etherscan.io" + }, + { + "chainId": "0xe708", + "nickname": "Linea Mainnet", + "rpcUrl": "https://linea-mainnet.infura.io/v3/cb3fa73a8bdf4342b8ed8b07e0740be9", + "rpcPrefs": { + "imageUrl": "./images/linea-logo-mainnet.svg" + }, + "providerType": "linea-mainnet", + "ticker": "ETH", + "id": "linea-mainnet", + "removable": false, + "blockExplorerUrl": "https://lineascan.build" + }, + { + "rpcUrl": "https://mainnet.base.org", + "chainId": "0x2105", + "ticker": "ETH", + "nickname": "Base", + "rpcPrefs": { + "blockExplorerUrl": "https://basescan.org", + "imageUrl": "./images/base.svg" + }, + "id": "61bb70f8-6583-42aa-8547-ef6c044614ad", + "blockExplorerUrl": "https://basescan.org", + "removable": true + }, + { + "rpcUrl": "https://arb1.arbitrum.io/rpc", + "chainId": "0xa4b1", + "ticker": "ETH", + "nickname": "Arbitrum One", + "rpcPrefs": { + "blockExplorerUrl": "https://arbiscan.io", + "imageUrl": "./images/arbitrum.svg" + }, + "id": "f8f98123-f3ae-418c-b1e7-d08f057f395c", + "blockExplorerUrl": "https://arbiscan.io", + "removable": true + } +] + + + }, }; -export const DefaultStory = () => ; +export const DefaultStory = (args) => ; DefaultStory.storyName = 'Default'; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx index 21e44327389d..ae15a8dc1099 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx @@ -1,10 +1,58 @@ import React from 'react'; +import { ARBITRUM_NOVA_IMAGE_URL } from '../../../../../../shared/constants/network'; import { SiteCell } from './site-cell'; export default { title: 'Components/Multichain/SiteCell', + components: SiteCell, + argTypes: { + accounts: { control: 'array' }, + networks: { control: 'array' }, + }, + args: { + accounts: [ + { + id: '689821df-0e8f-4093-bbbb-b95cf0fa79cb', + address: '0x860092756917d3e069926ba130099375eeeb9440', + options: {}, + methods: [ + 'personal_sign', + 'eth_sign', + 'eth_signTransaction', + 'eth_signTypedData_v1', + 'eth_signTypedData_v3', + 'eth_signTypedData_v4', + ], + type: 'eip155:eoa', + metadata: { + name: 'Account 1', + importTime: 1726046726882, + keyring: { + type: 'HD Key Tree', + }, + lastSelected: 1726046726882, + }, + balance: '0x00', + }, + ], + networks: [ + { + rpcUrl: 'https://arb1.arbitrum.io/rpc', + chainId: '0xa4b1', + ticker: 'ETH', + nickname: 'Arbitrum One', + rpcPrefs: { + blockExplorerUrl: 'https://arbiscan.io', + imageUrl: ARBITRUM_NOVA_IMAGE_URL, + }, + id: 'f8f98123-f3ae-418c-b1e7-d08f057f395c', + blockExplorerUrl: 'https://arbiscan.io', + removable: true, + }, + ], + }, }; -export const DefaultStory = () => ; +export const DefaultStory = (args) => ; DefaultStory.storyName = 'Default'; diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index 96bef39e4c16..2abd4b8d9ff9 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { useSelector } from 'react-redux'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { diff --git a/ui/selectors/permissions.js b/ui/selectors/permissions.js index d87ad1f9bea6..52f9ed91152f 100644 --- a/ui/selectors/permissions.js +++ b/ui/selectors/permissions.js @@ -286,11 +286,6 @@ function getChainsFromSubject(subject) { } function getChainsPermissionFromSubject(subject = {}) { - console.log( - subject.permissions?.['endowment:permitted-chains'], - subject, - 'subject', - ); return subject.permissions?.['endowment:permitted-chains'] || {}; } @@ -303,7 +298,6 @@ function getAccountsFromPermission(accountsPermission) { function getChainsFromPermission(chainsPermission) { const chainsCaveat = getChainsCaveatFromPermission(chainsPermission); - console.log(chainsCaveat, 'chainsCaveat'); return chainsCaveat && Array.isArray(chainsCaveat.value) ? chainsCaveat.value : []; From 8e8fc4f5571ce1135923bfd8740c5605b56fff6a Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 12 Sep 2024 23:16:25 +0100 Subject: [PATCH 044/125] lint fix --- .../edit-networks-modal.stories.js | 104 +++++++++--------- .../review-permissions-page.tsx | 2 +- 2 files changed, 52 insertions(+), 54 deletions(-) diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js index 7a902f660f47..e961eb13f4f2 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js @@ -10,63 +10,61 @@ export default { }, }, args: { - combinedNetworks: - [ - { - "chainId": "0x1", - "nickname": "Ethereum Mainnet", - "rpcUrl": "https://mainnet.infura.io/v3/cb3fa73a8bdf4342b8ed8b07e0740be9", - "rpcPrefs": { - "imageUrl": "./images/eth_logo.svg" + combinedNetworks: [ + { + chainId: '0x1', + nickname: 'Ethereum Mainnet', + rpcUrl: 'https://mainnet.infura.io/v3/cb3fa73a8bdf4342b8ed8b07e0740be9', + rpcPrefs: { + imageUrl: './images/eth_logo.svg', }, - "providerType": "mainnet", - "ticker": "ETH", - "id": "mainnet", - "removable": false, - "blockExplorerUrl": "https://etherscan.io" - }, - { - "chainId": "0xe708", - "nickname": "Linea Mainnet", - "rpcUrl": "https://linea-mainnet.infura.io/v3/cb3fa73a8bdf4342b8ed8b07e0740be9", - "rpcPrefs": { - "imageUrl": "./images/linea-logo-mainnet.svg" + providerType: 'mainnet', + ticker: 'ETH', + id: 'mainnet', + removable: false, + blockExplorerUrl: 'https://etherscan.io', + }, + { + chainId: '0xe708', + nickname: 'Linea Mainnet', + rpcUrl: + 'https://linea-mainnet.infura.io/v3/cb3fa73a8bdf4342b8ed8b07e0740be9', + rpcPrefs: { + imageUrl: './images/linea-logo-mainnet.svg', }, - "providerType": "linea-mainnet", - "ticker": "ETH", - "id": "linea-mainnet", - "removable": false, - "blockExplorerUrl": "https://lineascan.build" - }, - { - "rpcUrl": "https://mainnet.base.org", - "chainId": "0x2105", - "ticker": "ETH", - "nickname": "Base", - "rpcPrefs": { - "blockExplorerUrl": "https://basescan.org", - "imageUrl": "./images/base.svg" + providerType: 'linea-mainnet', + ticker: 'ETH', + id: 'linea-mainnet', + removable: false, + blockExplorerUrl: 'https://lineascan.build', + }, + { + rpcUrl: 'https://mainnet.base.org', + chainId: '0x2105', + ticker: 'ETH', + nickname: 'Base', + rpcPrefs: { + blockExplorerUrl: 'https://basescan.org', + imageUrl: './images/base.svg', }, - "id": "61bb70f8-6583-42aa-8547-ef6c044614ad", - "blockExplorerUrl": "https://basescan.org", - "removable": true - }, - { - "rpcUrl": "https://arb1.arbitrum.io/rpc", - "chainId": "0xa4b1", - "ticker": "ETH", - "nickname": "Arbitrum One", - "rpcPrefs": { - "blockExplorerUrl": "https://arbiscan.io", - "imageUrl": "./images/arbitrum.svg" + id: '61bb70f8-6583-42aa-8547-ef6c044614ad', + blockExplorerUrl: 'https://basescan.org', + removable: true, + }, + { + rpcUrl: 'https://arb1.arbitrum.io/rpc', + chainId: '0xa4b1', + ticker: 'ETH', + nickname: 'Arbitrum One', + rpcPrefs: { + blockExplorerUrl: 'https://arbiscan.io', + imageUrl: './images/arbitrum.svg', }, - "id": "f8f98123-f3ae-418c-b1e7-d08f057f395c", - "blockExplorerUrl": "https://arbiscan.io", - "removable": true - } -] - - + id: 'f8f98123-f3ae-418c-b1e7-d08f057f395c', + blockExplorerUrl: 'https://arbiscan.io', + removable: true, + }, + ], }, }; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index ad231bdadb49..76a1c11ce07d 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -118,7 +118,7 @@ export const ReviewPermissions = () => { const currentTabHasNoAccounts = !permittedAccountsByOrigin[activeTabOrigin]?.length; // eslint-disable-next-line @typescript-eslint/no-explicit-any - let tabToConnect: { origin: any} = { origin: null }; // origin could be null or a string based on the connection status or screen view + let tabToConnect: { origin: any } = { origin: null }; // origin could be null or a string based on the connection status or screen view if (activeTabOrigin && currentTabHasNoAccounts && !openMetaMaskTabs[id]) { tabToConnect = { origin: activeTabOrigin, From 9017c119967c2b4952463974ad116db9bd306b5a Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 12 Sep 2024 15:26:13 -0700 Subject: [PATCH 045/125] Fix requestPermissionsForOrigin --- app/scripts/metamask-controller.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index c36b838a1dd7..73f3d7e1e58a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -5587,13 +5587,11 @@ export default class MetamaskController extends EventEmitter { }, }, }), - requestPermissionsForOrigin: (requestedPermissions) => { - this.permissionController.requestPermissions.bind( - this.permissionController, + requestPermissionsForOrigin: (requestedPermissions) => + this.permissionController.requestPermissions( { origin }, { [PermissionNames.permittedChains]: {}, ...requestedPermissions }, - ); - }, + ), revokePermissionsForOrigin: (permissionKeys) => { try { this.permissionController.revokePermissions({ From 067bd1c5fead63e9c738424889b3427cc3c2d493 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 13 Sep 2024 10:31:53 -0700 Subject: [PATCH 046/125] only request permittedChains when feature flag and eth_accounts is requested too --- app/scripts/metamask-controller.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 0d198bfb024b..87529905aaee 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -5557,7 +5557,12 @@ export default class MetamaskController extends EventEmitter { this.permissionController.requestPermissions.bind( this.permissionController, { origin }, - { eth_accounts: {}, [PermissionNames.permittedChains]: {} }, + { + eth_accounts: {}, + ...(process.env.CHAIN_PERMISSIONS && { + [PermissionNames.permittedChains]: {}, + }), + }, ), requestPermittedChainsPermission: (chainIds) => this.permissionController.requestPermissionsIncremental( @@ -5588,7 +5593,13 @@ export default class MetamaskController extends EventEmitter { requestPermissionsForOrigin: (requestedPermissions) => this.permissionController.requestPermissions( { origin }, - { [PermissionNames.permittedChains]: {}, ...requestedPermissions }, + { + ...(process.env.CHAIN_PERMISSIONS && + requestedPermissions[RestrictedMethods.eth_accounts] && { + [PermissionNames.permittedChains]: {}, + }), + ...requestedPermissions, + }, ), revokePermissionsForOrigin: (permissionKeys) => { try { From 9fc03e92b988156cc6aa5e929ccb269906f3ce25 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 13 Sep 2024 11:51:55 -0700 Subject: [PATCH 047/125] remove approvedChainIds from legacy permission page submit --- .../permission-page-container.component.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js index a0e545d960c3..7d47a71b2798 100644 --- a/ui/components/app/permission-page-container/permission-page-container.component.js +++ b/ui/components/app/permission-page-container/permission-page-container.component.js @@ -146,12 +146,6 @@ export default class PermissionPageContainer extends Component { (selectedAccount) => selectedAccount.address, ), }), - ...(_request.permissions[PermissionNames.permittedChains] && { - approvedChainIds: _request.permissions?.[ - PermissionNames.permittedChains - ]?.caveats?.find((caveat) => caveat.type === 'restrictNetworkSwitching') - ?.value, - }), }; if (Object.keys(request.permissions).length > 0) { From 3b224f103dea6581ed248d1f3e5eb883888e82a2 Mon Sep 17 00:00:00 2001 From: jiexi Date: Mon, 16 Sep 2024 14:47:10 -0700 Subject: [PATCH 048/125] Jl/editing flow fix incremental (#27181) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27181?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../permission-page-container.component.js | 7 ++++++- .../permissions-connect/permissions-connect.component.js | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js index 7d47a71b2798..0bb306adf29a 100644 --- a/ui/components/app/permission-page-container/permission-page-container.component.js +++ b/ui/components/app/permission-page-container/permission-page-container.component.js @@ -8,7 +8,7 @@ import { SubjectType } from '@metamask/permission-controller'; import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics'; import { PageContainerFooter } from '../../ui/page-container'; import PermissionsConnectFooter from '../permissions-connect-footer'; -import { RestrictedMethods } from '../../../../shared/constants/permissions'; +import { CaveatTypes, RestrictedMethods } from '../../../../shared/constants/permissions'; import { PermissionNames } from '../../../../app/scripts/controllers/permissions'; import SnapPrivacyWarning from '../snaps/snap-privacy-warning'; @@ -146,6 +146,11 @@ export default class PermissionPageContainer extends Component { (selectedAccount) => selectedAccount.address, ), }), + ...(_request.permissions[PermissionNames.permittedChains] && { + approvedChainIds: _request.permissions[PermissionNames.permittedChains]?.caveats.find( + (caveat) => caveat.type === CaveatTypes.restrictNetworkSwitching, + )?.value, + }), }; if (Object.keys(request.permissions).length > 0) { diff --git a/ui/pages/permissions-connect/permissions-connect.component.js b/ui/pages/permissions-connect/permissions-connect.component.js index fb915fd9c304..74e1b4cda276 100644 --- a/ui/pages/permissions-connect/permissions-connect.component.js +++ b/ui/pages/permissions-connect/permissions-connect.component.js @@ -363,7 +363,7 @@ export default class PermissionConnect extends Component { path={confirmPermissionPath} exact render={() => - process.env.CHAIN_PERMISSIONS ? ( + process.env.CHAIN_PERMISSIONS && !permissionsRequest?.diff ? ( From 5ef45ccf264ea15b4bcd473899b0db423cccefe1 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 17 Sep 2024 15:34:29 +0100 Subject: [PATCH 049/125] updated edit accounts modal story --- .../permission-page-container.component.js | 9 +++++++-- .../edit-accounts-modal.stories.tsx | 10 ++++++++++ .../edit-accounts-modal/edit-accounts-modal.tsx | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js index 0bb306adf29a..27dbc9f71c32 100644 --- a/ui/components/app/permission-page-container/permission-page-container.component.js +++ b/ui/components/app/permission-page-container/permission-page-container.component.js @@ -8,7 +8,10 @@ import { SubjectType } from '@metamask/permission-controller'; import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics'; import { PageContainerFooter } from '../../ui/page-container'; import PermissionsConnectFooter from '../permissions-connect-footer'; -import { CaveatTypes, RestrictedMethods } from '../../../../shared/constants/permissions'; +import { + CaveatTypes, + RestrictedMethods, +} from '../../../../shared/constants/permissions'; import { PermissionNames } from '../../../../app/scripts/controllers/permissions'; import SnapPrivacyWarning from '../snaps/snap-privacy-warning'; @@ -147,7 +150,9 @@ export default class PermissionPageContainer extends Component { ), }), ...(_request.permissions[PermissionNames.permittedChains] && { - approvedChainIds: _request.permissions[PermissionNames.permittedChains]?.caveats.find( + approvedChainIds: _request.permissions[ + PermissionNames.permittedChains + ]?.caveats.find( (caveat) => caveat.type === CaveatTypes.restrictNetworkSwitching, )?.value, }), diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx index cb45f7027dc1..b25b4423d86d 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx @@ -1,15 +1,25 @@ import React from 'react'; +import { Provider } from 'react-redux'; import { EditAccountsModal } from '.'; +import testData from '../../../../.storybook/test-data'; +import configureStore from '../../../store/store'; + +const store = configureStore(testData); export default { title: 'Components/Multichain/EditAccountsModal', + decorators: [(story) => {story()}], argTypes: { onClose: { action: 'onClose' }, activeTabOrigin: { control: 'text' }, }, args: { onClose: () => undefined, + onClick: () => undefined, + onDisconnectClick: () => undefined, + approvedAccounts: [], activeTabOrigin: 'https://test.dapp', + currentTabHasNoAccounts: false, }, }; diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index b4e84dd26fa2..d0120d84113a 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -71,6 +71,7 @@ export const EditAccountsModal: React.FC = ({ activeTabOrigin, currentTabHasNoAccounts, }) => { + console.log(approvedAccounts, 'approvedAccounts'); const t = useI18nContext(); const accounts = useSelector(getUpdatedAndSortedAccounts); const internalAccounts = useSelector(getInternalAccounts); From f120d3440fc0e569b650ffb069e6dd9dd0e9af27 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 17 Sep 2024 15:34:59 +0100 Subject: [PATCH 050/125] lint fix --- .../multichain/edit-accounts-modal/edit-accounts-modal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index d0120d84113a..b4e84dd26fa2 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -71,7 +71,6 @@ export const EditAccountsModal: React.FC = ({ activeTabOrigin, currentTabHasNoAccounts, }) => { - console.log(approvedAccounts, 'approvedAccounts'); const t = useI18nContext(); const accounts = useSelector(getUpdatedAndSortedAccounts); const internalAccounts = useSelector(getInternalAccounts); From 753c28dc95e78c6bb95381bf6fd9f8365f326dcc Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 17 Sep 2024 08:22:08 -0700 Subject: [PATCH 051/125] Fix wallet_addEthereumChain handler test --- .../handlers/add-ethereum-chain.test.js | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index bd7434e61481..480ce8b28ca7 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -61,6 +61,7 @@ describe('addEthereumChainHandler', () => { .mockReturnValue(createMockMainnetConfiguration().rpcUrl), requestUserApproval: jest.fn().mockResolvedValue(123), requestPermittedChainsPermission: jest.fn(), + grantPermittedChainsPermissionIncremental: jest.fn(), getCaveat: jest.fn().mockReturnValue({ value: permissionedChainIds }), upsertNetworkConfiguration: jest.fn().mockResolvedValue(123), startApprovalFlow: () => ({ id: 'approvalFlowId' }), @@ -316,10 +317,12 @@ describe('addEthereumChainHandler', () => { createMockNonInfuraConfiguration(), { referrer: 'example.com', source: 'dapp' }, ); - expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledTimes(1); - expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledWith([ - createMockNonInfuraConfiguration().chainId, - ]); + expect( + mocks.grantPermittedChainsPermissionIncremental, + ).toHaveBeenCalledTimes(1); + expect( + mocks.grantPermittedChainsPermissionIncremental, + ).toHaveBeenCalledWith([createMockNonInfuraConfiguration().chainId]); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); expect(mocks.setActiveNetwork).toHaveBeenCalledWith(123); }); @@ -397,12 +400,12 @@ describe('addEthereumChainHandler', () => { ); expect(mocks.upsertNetworkConfiguration).toHaveBeenCalledTimes(1); - expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledTimes( - 1, - ); - expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledWith([ - NON_INFURA_CHAIN_ID, - ]); + expect( + mocks.grantPermittedChainsPermissionIncremental, + ).toHaveBeenCalledTimes(1); + expect( + mocks.grantPermittedChainsPermissionIncremental, + ).toHaveBeenCalledWith([NON_INFURA_CHAIN_ID]); expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); }); }); @@ -501,7 +504,7 @@ describe('addEthereumChainHandler', () => { permissionsFeatureFlagIsActive: true, permissionedChainIds: [], overrides: { - requestPermittedChainsPermission: jest + grantPermittedChainsPermissionIncremental: jest .fn() .mockRejectedValue(mockError), }, @@ -530,7 +533,9 @@ describe('addEthereumChainHandler', () => { mocks, ); - expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledTimes(1); + expect( + mocks.grantPermittedChainsPermissionIncremental, + ).toHaveBeenCalledTimes(1); expect(mockEnd).toHaveBeenCalledWith(mockError); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); }); From f945e28aa5af5df6eb464e9f9980643a41647b2b Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 17 Sep 2024 19:50:23 +0100 Subject: [PATCH 052/125] updated changes --- .../site-cell/site-cell-tooltip.js | 23 ++++----- .../site-cell/site-cell.tsx | 49 ++++++++++--------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js index 8d514764a9f2..019f780e8c5e 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js @@ -160,13 +160,13 @@ SiteCellTooltip.propTypes = { */ accounts: PropTypes.arrayOf( PropTypes.shape({ - address: PropTypes.string.isRequired, // The unique address of the account. + address: PropTypes.string, // The unique address of the account. label: PropTypes.string, // Optional label for the account. metadata: PropTypes.shape({ - name: PropTypes.string.isRequired, // Account's name from metadata. - }).isRequired, + name: PropTypes.string, // Account's name from metadata. + }), }), - ).isRequired, + ), /** * Data for the avatar group component related to accounts. @@ -174,33 +174,30 @@ SiteCellTooltip.propTypes = { */ avatarAccountsData: PropTypes.arrayOf( PropTypes.shape({ - address: PropTypes.string.isRequired, // The account address to display. - name: PropTypes.string, // The account's name. - avatarUrl: PropTypes.string, // Optional URL for the avatar image. + address: PropTypes.string // The account address to display. }), - ).isRequired, + ), /** * An array of network objects to display in the tooltip. */ networks: PropTypes.arrayOf( PropTypes.shape({ - chainId: PropTypes.string.isRequired, // The unique chain ID of the network. - nickname: PropTypes.string.isRequired, // The network's name. + chainId: PropTypes.string, // The unique chain ID of the network. + nickname: PropTypes.string, // The network's name. rpcPrefs: PropTypes.shape({ imageUrl: PropTypes.string, // Optional URL for the network's image. }), }), - ).isRequired, + ), /** * Data for the avatar group component related to networks. */ avatarNetworksData: PropTypes.arrayOf( PropTypes.shape({ - chainId: PropTypes.string.isRequired, // The unique chain ID of the network. name: PropTypes.string, // The network's name. avatarUrl: PropTypes.string, // Optional URL for the network's avatar image. }), - ).isRequired, + ), }; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index d7a0db9427f7..153d2023ed6a 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -29,27 +29,26 @@ import { import { EditAccountsModal, EditNetworksModal } from '../../..'; import { getPermittedAccountsByOrigin } from '../../../../../selectors/permissions'; import { SiteCellTooltip } from './site-cell-tooltip'; +import { AccountType } from '../../../connect-accounts-modal/connect-account-modal.types'; -type SiteCellProps = { - networks: { - rpcPrefs?: { imageUrl?: string }; - nickname: string; - chainId?: string; - }[]; - accounts: { - address: string; - label: string; - metadata: { - name: string; - }; - }[]; +// Define types for networks, accounts, and other props +interface Network { + rpcPrefs?: { imageUrl?: string }; + nickname: string; + chainId?: string; +} + +interface SiteCellProps { + networks: Network[]; + accounts: AccountType[]; onAccountsClick: () => void; onNetworksClick: () => void; onDisconnectClick: () => void; approvedAccounts: { address: string }[]; activeTabOrigin: string; - combinedNetworks; -}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + combinedNetworks: any; +} export const SiteCell: React.FC = ({ networks, @@ -62,16 +61,21 @@ export const SiteCell: React.FC = ({ onDisconnectClick, }) => { const t = useI18nContext(); + console.log(networks, combinedNetworks); + // Map networks and accounts to avatar data const avatarNetworksData = networks.map((network) => ({ - avatarValue: network?.rpcPrefs?.imageUrl || '', + avatarValue: network.rpcPrefs?.imageUrl || '', symbol: network.nickname, })); + const avatarAccountsData = accounts.map((account) => ({ avatarValue: account.address, })); const [showEditAccountsModal, setShowEditAccountsModal] = useState(false); const [showEditNetworksModal, setShowEditNetworksModal] = useState(false); + + // Determine the messages for connected and not connected states const accountMessageConnectedState = accounts.length > 1 ? t('connectedWith') @@ -85,7 +89,12 @@ export const SiteCell: React.FC = ({ accounts[0].label || accounts[0].metadata.name, ]); - const permittedAccountsByOrigin = useSelector(getPermittedAccountsByOrigin); + // Use selector to get permitted accounts by origin + const permittedAccountsByOrigin = useSelector( + getPermittedAccountsByOrigin, + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as { [key: string]: any[] }; const currentTabHasNoAccounts = !permittedAccountsByOrigin[activeTabOrigin]?.length; @@ -106,7 +115,6 @@ export const SiteCell: React.FC = ({ @@ -193,7 +201,6 @@ export const SiteCell: React.FC = ({ @@ -258,7 +265,6 @@ export const SiteCell: React.FC = ({ {showEditNetworksModal && ( setShowEditNetworksModal(false)} onClick={onNetworksClick} currentTabHasNoAccounts={currentTabHasNoAccounts} @@ -271,8 +277,7 @@ export const SiteCell: React.FC = ({ setShowEditAccountsModal(false)} onClick={onAccountsClick} - selAccounts={accounts} - approvedAccounts={approvedAccounts} + approvedAccounts={approvedAccounts.map((account) => account.address)} // Extracting addresses as strings activeTabOrigin={activeTabOrigin} currentTabHasNoAccounts={currentTabHasNoAccounts} onDisconnectClick={onDisconnectClick} From 83789f426d1d523a50da44311594c4b104b25bc2 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 17 Sep 2024 20:52:54 +0100 Subject: [PATCH 053/125] lint fix --- .../site-cell/site-cell-tooltip.js | 2 +- .../review-permissions-page/site-cell/site-cell.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js index 019f780e8c5e..ce05c7cbb300 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js @@ -174,7 +174,7 @@ SiteCellTooltip.propTypes = { */ avatarAccountsData: PropTypes.arrayOf( PropTypes.shape({ - address: PropTypes.string // The account address to display. + address: PropTypes.string, // The account address to display. }), ), diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 153d2023ed6a..4966644e3313 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -28,17 +28,17 @@ import { } from '../../../../component-library'; import { EditAccountsModal, EditNetworksModal } from '../../..'; import { getPermittedAccountsByOrigin } from '../../../../../selectors/permissions'; -import { SiteCellTooltip } from './site-cell-tooltip'; import { AccountType } from '../../../connect-accounts-modal/connect-account-modal.types'; +import { SiteCellTooltip } from './site-cell-tooltip'; // Define types for networks, accounts, and other props -interface Network { +type Network = { rpcPrefs?: { imageUrl?: string }; nickname: string; chainId?: string; -} +}; -interface SiteCellProps { +type SiteCellProps = { networks: Network[]; accounts: AccountType[]; onAccountsClick: () => void; @@ -48,7 +48,7 @@ interface SiteCellProps { activeTabOrigin: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any combinedNetworks: any; -} +}; export const SiteCell: React.FC = ({ networks, From 8c6615fa8fd070dbc5ba8b6dcea945c986b0a192 Mon Sep 17 00:00:00 2001 From: jiexi Date: Tue, 17 Sep 2024 12:56:38 -0700 Subject: [PATCH 054/125] Jl/editing flow network client switch (#27225) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Switch the selected network for the domain and the globally selected network when the permittedChains permission changes and the currently selected network for the domain is no longer in the list of permitted chains [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27225?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../controllers/permissions/selectors.js | 68 +++++++++++++------ app/scripts/metamask-controller.js | 39 ++++++++++- .../edit-networks-modal.js | 21 +----- 3 files changed, 88 insertions(+), 40 deletions(-) diff --git a/app/scripts/controllers/permissions/selectors.js b/app/scripts/controllers/permissions/selectors.js index 1a7fa115dd48..62bd3231fe77 100644 --- a/app/scripts/controllers/permissions/selectors.js +++ b/app/scripts/controllers/permissions/selectors.js @@ -1,5 +1,6 @@ import { createSelector } from 'reselect'; import { CaveatTypes } from '../../../../shared/constants/permissions'; +import { PermissionNames } from './specifications'; /** * This file contains selectors for PermissionController selector event @@ -40,47 +41,74 @@ export const getPermittedAccountsByOrigin = createSelector( ); /** - * Given the current and previous exposed accounts for each PermissionController - * subject, returns a new map containing all accounts that have changed. + * Get the permitted chains for each subject, keyed by origin. + * The values of the returned map are immutable values from the + * PermissionController state. + * + * @returns {Map} The current origin:chainIds[] map. + */ +export const getPermittedChainsByOrigin = createSelector( + getSubjects, + (subjects) => { + return Object.values(subjects).reduce((originToChainsMap, subject) => { + const caveats = + subject.permissions?.[PermissionNames.permittedChains]?.caveats || []; + + const caveat = caveats.find( + ({ type }) => type === CaveatTypes.restrictNetworkSwitching, + ); + + if (caveat) { + originToChainsMap.set(subject.origin, caveat.value); + } + return originToChainsMap; + }, new Map()); + }, +); + +/** + * Given the current and previous exposed origins for each PermissionController + * subject, returns a new map containing all origins that have changed. * The values of each map must be immutable values directly from the * PermissionController state, or an empty array instantiated in this * function. * - * @param {Map} newAccountsMap - The new origin:accounts[] map. - * @param {Map} [previousAccountsMap] - The previous origin:accounts[] map. - * @returns {Map} The origin:accounts[] map of changed accounts. + * @param {Map} newOriginsMap - The new origin:string[] map. + * @param {Map} [previousOriginsMap] - The previous origin:string[] map. + * @returns {Map} The origin:string[] map of changed origins. */ -export const getChangedAccounts = (newAccountsMap, previousAccountsMap) => { - if (previousAccountsMap === undefined) { - return newAccountsMap; +export const getChangedOrigins = (newOriginsMap, previousOriginsMap) => { + if (previousOriginsMap === undefined) { + return newOriginsMap; } - const changedAccounts = new Map(); - if (newAccountsMap === previousAccountsMap) { - return changedAccounts; + const changedOriginsMap = new Map(); + if (newOriginsMap === previousOriginsMap) { + return changedOriginsMap; } - const newOrigins = new Set([...newAccountsMap.keys()]); + const newOrigins = new Set([...newOriginsMap.keys()]); - for (const origin of previousAccountsMap.keys()) { - const newAccounts = newAccountsMap.get(origin) ?? []; + for (const origin of previousOriginsMap.keys()) { + const newValue = newOriginsMap.get(origin) ?? []; + const previousValue = previousOriginsMap.get(origin); // The values of these maps are references to immutable values, which is why // a strict equality check is enough for diffing. The values are either from // PermissionController state, or an empty array initialized in the previous - // call to this function. `newAccountsMap` will never contain any empty + // call to this function. `newOriginsMap` will never contain any empty // arrays. - if (previousAccountsMap.get(origin) !== newAccounts) { - changedAccounts.set(origin, newAccounts); + if (newValue !== previousValue) { + changedOriginsMap.set(origin, newValue); } newOrigins.delete(origin); } // By now, newOrigins is either empty or contains some number of previously - // unencountered origins, and all of their accounts have "changed". + // unencountered origins, and all of their origins have "changed". for (const origin of newOrigins.keys()) { - changedAccounts.set(origin, newAccountsMap.get(origin)); + changedOriginsMap.set(origin, newOriginsMap.get(origin)); } - return changedAccounts; + return changedOriginsMap; }; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index aa1e45fcb83a..b8547e29e54a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -306,10 +306,11 @@ import { CaveatFactories, CaveatMutatorFactories, getCaveatSpecifications, - getChangedAccounts, + getChangedOrigins, getPermissionBackgroundApiMethods, getPermissionSpecifications, getPermittedAccountsByOrigin, + getPermittedChainsByOrigin, NOTIFICATION_NAMES, PermissionNames, unrestrictedMethods, @@ -2796,7 +2797,7 @@ export default class MetamaskController extends EventEmitter { this.controllerMessenger.subscribe( `${this.permissionController.name}:stateChange`, async (currentValue, previousValue) => { - const changedAccounts = getChangedAccounts(currentValue, previousValue); + const changedAccounts = getChangedOrigins(currentValue, previousValue); for (const [origin, accounts] of changedAccounts.entries()) { this._notifyAccountsChange(origin, accounts); @@ -2805,6 +2806,40 @@ export default class MetamaskController extends EventEmitter { getPermittedAccountsByOrigin, ); + this.controllerMessenger.subscribe( + `${this.permissionController.name}:stateChange`, + async (currentValue, previousValue) => { + const changedChains = getChangedOrigins(currentValue, previousValue); + + // This operates under the assumption that there will be at maximum + // one origin permittedChains value change per event handler call + for (const [origin, chains] of changedChains.entries()) { + const currentNetworkClientIdForOrigin = + this.selectedNetworkController.getNetworkClientIdForDomain(origin); + const { chainId: currentChainIdForOrigin } = + this.networkController.getNetworkConfigurationByNetworkClientId( + currentNetworkClientIdForOrigin, + ); + // if(chains.length === 0) { + // TODO: This particular case should also occur at the same time + // that eth_accounts is revoked. When eth_accounts is revoked, + // the networkClientId for that origin should be reset to track + // the globally selected network. + // } + if (chains.length > 0 && !chains.includes(currentChainIdForOrigin)) { + const networkClientId = + this.networkController.findNetworkClientIdByChainId(chains[0]); + this.selectedNetworkController.setNetworkClientIdForDomain( + origin, + networkClientId, + ); + this.networkController.setActiveNetwork(networkClientId); + } + } + }, + getPermittedChainsByOrigin, + ); + this.controllerMessenger.subscribe( 'NetworkController:networkDidChange', async () => { diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index df87b2270f3a..be3ca64ebbc6 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -34,7 +34,6 @@ import { import { NetworkListItem } from '..'; import { grantPermittedChains, - removePermittedChain, setSelectedNetworksForDappConnection, } from '../../../store/actions'; import { getURLHost } from '../../../helpers/utils/util'; @@ -95,18 +94,8 @@ export const EditNetworksModal = ({ isIndeterminate = true; } - const managePermittedChains = (chains, permittedChains, tabOrigin) => { - dispatch(grantPermittedChains(activeTabOrigin, chains)); - - const removedElements = permittedChains.filter( - (chain) => !chains.includes(chain), - ); - - // Dispatch removePermittedChains for each removed element - removedElements.forEach((chain) => { - const selectedChain = [chain]; - dispatch(removePermittedChain(tabOrigin, selectedChain)); - }); + const managePermittedChains = (chains) => { + grantPermittedChains(activeTabOrigin, chains); }; const hostName = getURLHost(activeTabOrigin); @@ -220,11 +209,7 @@ export const EditNetworksModal = ({ setSelectedNetworksForDappConnection(selectedChains), ); } else { - managePermittedChains( - selectedChains, - selectedPermittedChains, - activeTabOrigin, - ); + managePermittedChains(selectedChains); } }} size={ButtonPrimarySize.Lg} From dab66ec29e9d8c24160205890b36a047e28a12e8 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 17 Sep 2024 23:40:30 +0100 Subject: [PATCH 055/125] fixed ts errors --- .../pages/connections/components/connections.types.tsx | 1 + .../pages/review-permissions-page/review-permissions-page.tsx | 2 +- .../pages/review-permissions-page/site-cell/site-cell.tsx | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/components/multichain/pages/connections/components/connections.types.tsx b/ui/components/multichain/pages/connections/components/connections.types.tsx index 9bdbf86d9d95..71d06c6dab30 100644 --- a/ui/components/multichain/pages/connections/components/connections.types.tsx +++ b/ui/components/multichain/pages/connections/components/connections.types.tsx @@ -25,6 +25,7 @@ export type AccountType = InternalAccount & { balance: string; keyring: KeyringType; label: string; + name: string; }; export type Subject = { diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 76a1c11ce07d..2747abb92ee1 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -108,7 +108,7 @@ export const ReviewPermissions = () => { const connectedAccounts = useSelector((state) => getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin), ) as AccountType[]; - + console.log(connectedAccounts, 'connectedAccounts'); const subjects = useSelector(getPermissionSubjects); const grantedNetworks = combinedNetworks.filter( (net: { chainId: string }) => connectedNetworks.indexOf(net.chainId) !== -1, diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 4966644e3313..36337b06f45d 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -61,7 +61,6 @@ export const SiteCell: React.FC = ({ onDisconnectClick, }) => { const t = useI18nContext(); - console.log(networks, combinedNetworks); // Map networks and accounts to avatar data const avatarNetworksData = networks.map((network) => ({ avatarValue: network.rpcPrefs?.imageUrl || '', From ee2fe20c6511f1d5101e8983b364875756ed6fdc Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 17 Sep 2024 15:46:34 -0700 Subject: [PATCH 056/125] Fix permissions/selectors --- .../controllers/permissions/selectors.test.js | 101 +++++++++++++++++- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/app/scripts/controllers/permissions/selectors.test.js b/app/scripts/controllers/permissions/selectors.test.js index a32eabf7738e..c2c328c37c54 100644 --- a/app/scripts/controllers/permissions/selectors.test.js +++ b/app/scripts/controllers/permissions/selectors.test.js @@ -1,16 +1,22 @@ import { cloneDeep } from 'lodash'; -import { getChangedAccounts, getPermittedAccountsByOrigin } from './selectors'; +import { CaveatTypes } from '../../../../shared/constants/permissions'; +import { + getChangedOrigins, + getPermittedAccountsByOrigin, + getPermittedChainsByOrigin, +} from './selectors'; +import { PermissionNames } from './specifications'; describe('PermissionController selectors', () => { - describe('getChangedAccounts', () => { + describe('getChangedOrigins', () => { it('returns the new value if the previous value is undefined', () => { const newAccounts = new Map([['foo.bar', ['0x1']]]); - expect(getChangedAccounts(newAccounts)).toBe(newAccounts); + expect(getChangedOrigins(newAccounts)).toBe(newAccounts); }); it('returns an empty map if the new and previous values are the same', () => { const newAccounts = new Map([['foo.bar', ['0x1']]]); - expect(getChangedAccounts(newAccounts, newAccounts)).toStrictEqual( + expect(getChangedOrigins(newAccounts, newAccounts)).toStrictEqual( new Map(), ); }); @@ -32,7 +38,7 @@ describe('PermissionController selectors', () => { ]); newAccounts.set('foo.bar', identicalValue); - expect(getChangedAccounts(newAccounts, previousAccounts)).toStrictEqual( + expect(getChangedOrigins(newAccounts, previousAccounts)).toStrictEqual( new Map([ ['bar.baz', ['0x1', '0x2']], ['fizz.buzz', []], @@ -113,4 +119,89 @@ describe('PermissionController selectors', () => { expect(selected2).toBe(getPermittedAccountsByOrigin(state2)); }); }); + + describe('getPermittedChainsByOrigin', () => { + it('memoizes and gets permitted chains by origin', () => { + const state1 = { + subjects: { + 'foo.bar': { + origin: 'foo.bar', + permissions: { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1'], + }, + ], + }, + }, + }, + 'bar.baz': { + origin: 'bar.baz', + permissions: { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x2'], + }, + ], + }, + }, + }, + 'baz.bizz': { + origin: 'baz.fizz', + permissions: { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1', '0x2'], + }, + ], + }, + }, + }, + 'no.accounts': { + // we shouldn't see this in the result + permissions: { + foobar: {}, + }, + }, + }, + }; + + const expected1 = new Map([ + ['foo.bar', ['0x1']], + ['bar.baz', ['0x2']], + ['baz.fizz', ['0x1', '0x2']], + ]); + + const selected1 = getPermittedChainsByOrigin(state1); + + expect(selected1).toStrictEqual(expected1); + // The selector should return the memoized value if state.subjects is + // the same object + expect(selected1).toBe(getPermittedChainsByOrigin(state1)); + + // If we mutate the state, the selector return value should be different + // from the first. + const state2 = cloneDeep(state1); + delete state2.subjects['foo.bar']; + + const expected2 = new Map([ + ['bar.baz', ['0x2']], + ['baz.fizz', ['0x1', '0x2']], + ]); + + const selected2 = getPermittedChainsByOrigin(state2); + + expect(selected2).toStrictEqual(expected2); + expect(selected2).not.toBe(selected1); + // Since we didn't mutate the state at this point, the value should once + // again be the memoized. + expect(selected2).toBe(getPermittedChainsByOrigin(state2)); + }); + }); }); From 31edcdff2259a2985b361f78d39e67f846131344 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 18 Sep 2024 01:28:09 +0100 Subject: [PATCH 057/125] fixed ts errors --- .../pages/connections/components/connections.types.tsx | 1 - .../pages/review-permissions-page/review-permission.types.tsx | 1 + .../pages/review-permissions-page/review-permissions-page.tsx | 2 +- ui/pages/permissions-connect/connect-page/connect-page.tsx | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/components/multichain/pages/connections/components/connections.types.tsx b/ui/components/multichain/pages/connections/components/connections.types.tsx index 71d06c6dab30..9bdbf86d9d95 100644 --- a/ui/components/multichain/pages/connections/components/connections.types.tsx +++ b/ui/components/multichain/pages/connections/components/connections.types.tsx @@ -25,7 +25,6 @@ export type AccountType = InternalAccount & { balance: string; keyring: KeyringType; label: string; - name: string; }; export type Subject = { diff --git a/ui/components/multichain/pages/review-permissions-page/review-permission.types.tsx b/ui/components/multichain/pages/review-permissions-page/review-permission.types.tsx index 9bdbf86d9d95..6111dd8d946f 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permission.types.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permission.types.tsx @@ -22,6 +22,7 @@ export type KeyringType = { // Define AccountType interface export type AccountType = InternalAccount & { + name: string; balance: string; keyring: KeyringType; label: string; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 2747abb92ee1..2cba41313ed4 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -48,7 +48,6 @@ import { ToastContainer, Toast } from '../..'; import { NoConnectionContent } from '../connections/components/no-connection'; import { Content, Footer, Header, Page } from '../page'; import { - AccountType, SubjectsType, } from '../connections/components/connections.types'; import { CONNECT_ROUTE } from '../../../../helpers/constants/routes'; @@ -57,6 +56,7 @@ import { DisconnectType, } from '../../disconnect-all-modal/disconnect-all-modal'; import { SiteCell } from '.'; +import { AccountType } from './review-permission.types'; type UrlParams = { origin: string; diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index 2abd4b8d9ff9..1895d87d0290 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -27,7 +27,7 @@ import { Display, TextVariant, } from '../../../helpers/constants/design-system'; -import { AccountType } from '../../../components/multichain/pages/connections/components/connections.types'; +import { AccountType } from '../../../components/multichain/pages/review-permissions-page/review-permission.types'; type Request = { id: string; From ee59fc876dd520d10a0091f472bb3d1e623d3f69 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 18 Sep 2024 12:32:28 +0100 Subject: [PATCH 058/125] lint fix --- .../review-permissions-page/review-permissions-page.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 2cba41313ed4..14bb98f08174 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -47,16 +47,14 @@ import { import { ToastContainer, Toast } from '../..'; import { NoConnectionContent } from '../connections/components/no-connection'; import { Content, Footer, Header, Page } from '../page'; -import { - SubjectsType, -} from '../connections/components/connections.types'; +import { SubjectsType } from '../connections/components/connections.types'; import { CONNECT_ROUTE } from '../../../../helpers/constants/routes'; import { DisconnectAllModal, DisconnectType, } from '../../disconnect-all-modal/disconnect-all-modal'; -import { SiteCell } from '.'; import { AccountType } from './review-permission.types'; +import { SiteCell } from '.'; type UrlParams = { origin: string; From 4e08e8d705169c2c0c7c5cb085058b03e37d7448 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 18 Sep 2024 09:07:18 -0700 Subject: [PATCH 059/125] Fix sonar cloud default assignment warning --- .../connect-accounts-modal/connect-accounts-modal.tsx | 2 -- .../multichain/edit-accounts-modal/edit-accounts-modal.tsx | 2 -- .../multichain/edit-networks-modal/edit-networks-modal.js | 2 -- ui/components/ui/account-list/account-list.js | 4 +--- 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx b/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx index a511e792e77b..8b0157eee4eb 100644 --- a/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx +++ b/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx @@ -57,9 +57,7 @@ export const ConnectAccountsModal = ({ let isIndeterminate = false; if (allAreSelected()) { checked = true; - isIndeterminate = false; } else if (selectedAccounts.length > 0 && !allAreSelected()) { - checked = false; isIndeterminate = true; } return ( diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index b4e84dd26fa2..30396aeca885 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -149,9 +149,7 @@ export const EditAccountsModal: React.FC = ({ let isIndeterminate = false; if (allAreSelected()) { checked = true; - isIndeterminate = false; } else if (selectedAccounts.length > 0 && !allAreSelected()) { - checked = false; isIndeterminate = true; } diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index be3ca64ebbc6..c8a3c7d4952d 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -88,9 +88,7 @@ export const EditNetworksModal = ({ let isIndeterminate = false; if (allAreSelected()) { checked = true; - isIndeterminate = false; } else if (selectedChains.length > 0 && !allAreSelected()) { - checked = false; isIndeterminate = true; } diff --git a/ui/components/ui/account-list/account-list.js b/ui/components/ui/account-list/account-list.js index 18fc35b2c6ce..bef1ca405a5e 100644 --- a/ui/components/ui/account-list/account-list.js +++ b/ui/components/ui/account-list/account-list.js @@ -60,9 +60,7 @@ const AccountList = ({ let isIndeterminate = false; if (allAreSelected()) { checked = true; - } else if (selectedAccounts.size === 0) { - checked = false; - } else { + } else if (selectedAccounts.size !== 0) { isIndeterminate = true; } From bf7eba4d7e6509c3b7daaa76c3433bbf939abfdc Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 18 Sep 2024 09:07:57 -0700 Subject: [PATCH 060/125] remove infura key --- .../edit-networks-modal/edit-networks-modal.stories.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js index e961eb13f4f2..0bbb28fe251e 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js @@ -14,7 +14,7 @@ export default { { chainId: '0x1', nickname: 'Ethereum Mainnet', - rpcUrl: 'https://mainnet.infura.io/v3/cb3fa73a8bdf4342b8ed8b07e0740be9', + rpcUrl: 'https://mainnet.infura.io/v3/', rpcPrefs: { imageUrl: './images/eth_logo.svg', }, @@ -28,7 +28,7 @@ export default { chainId: '0xe708', nickname: 'Linea Mainnet', rpcUrl: - 'https://linea-mainnet.infura.io/v3/cb3fa73a8bdf4342b8ed8b07e0740be9', + 'https://linea-mainnet.infura.io/v3/', rpcPrefs: { imageUrl: './images/linea-logo-mainnet.svg', }, From db2d3d19eec5599fe4c9fa14dbb6b33cc72fb411 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 18 Sep 2024 09:27:21 -0700 Subject: [PATCH 061/125] update data-testid tags --- .../multichain/edit-accounts-modal/edit-accounts-modal.tsx | 4 ++-- .../multichain/edit-networks-modal/edit-networks-modal.js | 2 +- .../edit-networks-modal/edit-networks-modal.stories.js | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index 30396aeca885..921e905b6744 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -221,7 +221,7 @@ export const EditAccountsModal: React.FC = ({ { onDisconnectClick(); onClose(); @@ -235,7 +235,7 @@ export const EditAccountsModal: React.FC = ({ ) : ( { onClick(); if (currentTabHasNoAccounts) { diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index c8a3c7d4952d..e5b6878f46d2 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -198,7 +198,7 @@ export const EditNetworksModal = ({ ) : ( { onClick(); onClose(); diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js index 0bbb28fe251e..d8b624e78b3e 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js @@ -27,8 +27,7 @@ export default { { chainId: '0xe708', nickname: 'Linea Mainnet', - rpcUrl: - 'https://linea-mainnet.infura.io/v3/', + rpcUrl: 'https://linea-mainnet.infura.io/v3/', rpcPrefs: { imageUrl: './images/linea-logo-mainnet.svg', }, From f2495d6c70e97d5c6744ca415dfdfafa72b47445 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 18 Sep 2024 09:38:24 -0700 Subject: [PATCH 062/125] Deal with code dupe --- .../connect-accounts-modal/connect-accounts-modal.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx b/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx index 8b0157eee4eb..1263af76a762 100644 --- a/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx +++ b/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx @@ -39,6 +39,10 @@ export const ConnectAccountsModal = ({ setSelectedAccounts(newSelectedAccounts); }; + const deselectAll = () => { + setSelectedAccounts([]); + }; + const selectAll = () => { const newSelectedAccounts = accounts.map( (account: { address: string }) => account.address, @@ -46,10 +50,6 @@ export const ConnectAccountsModal = ({ setSelectedAccounts(newSelectedAccounts); }; - const deselectAll = () => { - setSelectedAccounts([]); - }; - const allAreSelected = () => { return accounts.length === selectedAccounts.length; }; @@ -60,6 +60,7 @@ export const ConnectAccountsModal = ({ } else if (selectedAccounts.length > 0 && !allAreSelected()) { isIndeterminate = true; } + return ( Date: Wed, 18 Sep 2024 19:08:23 +0100 Subject: [PATCH 063/125] removed yarn change --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 115a7beab2a0..5fd8f17c33a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14420,9 +14420,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001587, caniuse-lite@npm:^1.0.30001599": - version: 1.0.30001660 - resolution: "caniuse-lite@npm:1.0.30001660" - checksum: 10/5d83f0b7e2075b7e31f114f739155dc6c21b0afe8cb61180f625a4903b0ccd3d7591a5f81c930f14efddfa57040203ba0890850b8a3738f6c7f17c7dd83b9de8 + version: 1.0.30001600 + resolution: "caniuse-lite@npm:1.0.30001600" + checksum: 10/4c52f83ed71bc5f6e443bd17923460f1c77915adc2c2aa79ddaedceccc690b5917054b0c41b79e9138cbbd9abcdc0db9e224e79e3e734e581dfec06505f3a2b4 languageName: node linkType: hard From 27b6904cd617903f2f874d221f194b6b5ed9e67b Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 18 Sep 2024 19:19:23 +0100 Subject: [PATCH 064/125] updated url Params --- .../pages/review-permissions-page/review-permissions-page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 14bb98f08174..3e969ec7e00e 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -68,7 +68,7 @@ export const ReviewPermissions = () => { const t = useI18nContext(); const dispatch = useDispatch(); const history = useHistory(); - const urlParams = useParams(); + const urlParams: { origin: string } = useParams(); const securedOrigin = decodeURIComponent(urlParams.origin); const [showAccountToast, setShowAccountToast] = useState(false); const [showNetworkToast, setShowNetworkToast] = useState(false); From f02ce6b9aa7ab1a14647ca1af80d71185d9b8228 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 18 Sep 2024 19:37:55 +0100 Subject: [PATCH 065/125] updated url Params --- .../pages/review-permissions-page/review-permissions-page.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 3e969ec7e00e..87ce06434d27 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -56,10 +56,6 @@ import { import { AccountType } from './review-permission.types'; import { SiteCell } from '.'; -type UrlParams = { - origin: string; -}; - type PermittedAccountsByOrigin = { [key: string]: { address: string }[]; }; From 3868d34057e993b1cbcdf570bd5765b62e4bf5e7 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 18 Sep 2024 20:48:28 +0100 Subject: [PATCH 066/125] nit fix --- .../multichain/network-list-menu/network-list-menu.js | 1 - .../pages/review-permissions-page/review-permissions-page.tsx | 2 +- ui/selectors/permissions.js | 3 ++- 3 files changed, 3 insertions(+), 3 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 f89655a5a713..e5a96aa8044e 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -176,7 +176,6 @@ export const NetworkListMenu = ({ onClose }) => { return sortedNonTestNetworks; }; - // check if not granted chain is clicked, then show the toast and grant permission const networksList = newOrderNetworks(); const [items, setItems] = useState([...networksList]); diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 87ce06434d27..7487ee25914b 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -271,7 +271,7 @@ export const ReviewPermissions = () => { danger onClick={disconnectAllAccounts} > - {t('disconnectAllAccounts')} + {t('disconnect')} ) : ( diff --git a/ui/selectors/permissions.js b/ui/selectors/permissions.js index 52f9ed91152f..7691635e9638 100644 --- a/ui/selectors/permissions.js +++ b/ui/selectors/permissions.js @@ -2,6 +2,7 @@ import { ApprovalType } from '@metamask/controller-utils'; import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/snaps-rpc-methods'; import { isEvmAccountType } from '@metamask/keyring-api'; import { CaveatTypes } from '../../shared/constants/permissions'; +import { PermissionNames } from '../../app/scripts/controllers/permissions'; import { getApprovalRequestsByType } from './approvals'; import { createDeepEqualSelector } from './util'; import { @@ -286,7 +287,7 @@ function getChainsFromSubject(subject) { } function getChainsPermissionFromSubject(subject = {}) { - return subject.permissions?.['endowment:permitted-chains'] || {}; + return subject.permissions?.[PermissionNames.permittedChains] || {}; } function getAccountsFromPermission(accountsPermission) { From be170f8b70c03aec15dcdacb8a7b4b9d8f25969b Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Wed, 18 Sep 2024 21:12:06 +0100 Subject: [PATCH 067/125] updated for test nets --- ui/pages/routes/routes.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index da808911054a..24a8fe87f09e 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -746,7 +746,7 @@ export default class Routes extends Component { } From 024597d47d18f6857ec23998e6b5124a41c130bb Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 18 Sep 2024 13:16:37 -0700 Subject: [PATCH 068/125] rename getChangedOrigins to diffMap --- .../controllers/permissions/selectors.js | 49 +++++++++---------- .../controllers/permissions/selectors.test.js | 14 +++--- app/scripts/metamask-controller.js | 6 +-- 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/app/scripts/controllers/permissions/selectors.js b/app/scripts/controllers/permissions/selectors.js index 62bd3231fe77..76e638d25b54 100644 --- a/app/scripts/controllers/permissions/selectors.js +++ b/app/scripts/controllers/permissions/selectors.js @@ -67,48 +67,45 @@ export const getPermittedChainsByOrigin = createSelector( ); /** - * Given the current and previous exposed origins for each PermissionController - * subject, returns a new map containing all origins that have changed. - * The values of each map must be immutable values directly from the - * PermissionController state, or an empty array instantiated in this - * function. + * Returns a map containing key/value pairs for those that have been + * added, changed, or removed between two string:string[] maps * - * @param {Map} newOriginsMap - The new origin:string[] map. - * @param {Map} [previousOriginsMap] - The previous origin:string[] map. - * @returns {Map} The origin:string[] map of changed origins. + * @param {Map} currentMap - The new string:string[] map. + * @param {Map} previousMap - The previous string:string[] map. + * @returns {Map} The string:string[] map of changed key/values. */ -export const getChangedOrigins = (newOriginsMap, previousOriginsMap) => { - if (previousOriginsMap === undefined) { - return newOriginsMap; +export const diffMap = (currentMap, previousMap) => { + if (previousMap === undefined) { + return currentMap; } - const changedOriginsMap = new Map(); - if (newOriginsMap === previousOriginsMap) { - return changedOriginsMap; + const changedMap = new Map(); + if (currentMap === previousMap) { + return changedMap; } - const newOrigins = new Set([...newOriginsMap.keys()]); + const newKeys = new Set([...currentMap.keys()]); - for (const origin of previousOriginsMap.keys()) { - const newValue = newOriginsMap.get(origin) ?? []; - const previousValue = previousOriginsMap.get(origin); + for (const key of previousMap.keys()) { + const currentValue = currentMap.get(key) ?? []; + const previousValue = previousMap.get(key); // The values of these maps are references to immutable values, which is why // a strict equality check is enough for diffing. The values are either from // PermissionController state, or an empty array initialized in the previous - // call to this function. `newOriginsMap` will never contain any empty + // call to this function. `currentMap` will never contain any empty // arrays. - if (newValue !== previousValue) { - changedOriginsMap.set(origin, newValue); + if (currentValue !== previousValue) { + changedMap.set(key, currentValue); } - newOrigins.delete(origin); + newKeys.delete(key); } - // By now, newOrigins is either empty or contains some number of previously + // By now, newKeys is either empty or contains some number of previously // unencountered origins, and all of their origins have "changed". - for (const origin of newOrigins.keys()) { - changedOriginsMap.set(origin, newOriginsMap.get(origin)); + for (const origin of newKeys.keys()) { + changedMap.set(origin, currentMap.get(origin)); } - return changedOriginsMap; + return changedMap; }; diff --git a/app/scripts/controllers/permissions/selectors.test.js b/app/scripts/controllers/permissions/selectors.test.js index c2c328c37c54..41264d405ab2 100644 --- a/app/scripts/controllers/permissions/selectors.test.js +++ b/app/scripts/controllers/permissions/selectors.test.js @@ -1,27 +1,25 @@ import { cloneDeep } from 'lodash'; import { CaveatTypes } from '../../../../shared/constants/permissions'; import { - getChangedOrigins, + diffMap, getPermittedAccountsByOrigin, getPermittedChainsByOrigin, } from './selectors'; import { PermissionNames } from './specifications'; describe('PermissionController selectors', () => { - describe('getChangedOrigins', () => { + describe('diffMap', () => { it('returns the new value if the previous value is undefined', () => { const newAccounts = new Map([['foo.bar', ['0x1']]]); - expect(getChangedOrigins(newAccounts)).toBe(newAccounts); + expect(diffMap(newAccounts)).toBe(newAccounts); }); it('returns an empty map if the new and previous values are the same', () => { const newAccounts = new Map([['foo.bar', ['0x1']]]); - expect(getChangedOrigins(newAccounts, newAccounts)).toStrictEqual( - new Map(), - ); + expect(diffMap(newAccounts, newAccounts)).toStrictEqual(new Map()); }); - it('returns a new map of the changed accounts if the new and previous values differ', () => { + it('returns a new map of the changed key/value pairs if the new and previous maps differ', () => { // We set this on the new and previous value under the key 'foo.bar' to // check that identical values are excluded. const identicalValue = ['0x1']; @@ -38,7 +36,7 @@ describe('PermissionController selectors', () => { ]); newAccounts.set('foo.bar', identicalValue); - expect(getChangedOrigins(newAccounts, previousAccounts)).toStrictEqual( + expect(diffMap(newAccounts, previousAccounts)).toStrictEqual( new Map([ ['bar.baz', ['0x1', '0x2']], ['fizz.buzz', []], diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index ff428051aac1..2eca356c0946 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -306,7 +306,7 @@ import { CaveatFactories, CaveatMutatorFactories, getCaveatSpecifications, - getChangedOrigins, + diffMap, getPermissionBackgroundApiMethods, getPermissionSpecifications, getPermittedAccountsByOrigin, @@ -2779,7 +2779,7 @@ export default class MetamaskController extends EventEmitter { this.controllerMessenger.subscribe( `${this.permissionController.name}:stateChange`, async (currentValue, previousValue) => { - const changedAccounts = getChangedOrigins(currentValue, previousValue); + const changedAccounts = diffMap(currentValue, previousValue); for (const [origin, accounts] of changedAccounts.entries()) { this._notifyAccountsChange(origin, accounts); @@ -2791,7 +2791,7 @@ export default class MetamaskController extends EventEmitter { this.controllerMessenger.subscribe( `${this.permissionController.name}:stateChange`, async (currentValue, previousValue) => { - const changedChains = getChangedOrigins(currentValue, previousValue); + const changedChains = diffMap(currentValue, previousValue); // This operates under the assumption that there will be at maximum // one origin permittedChains value change per event handler call From 0dc84aafa710174fa4ad9569a18bf965ce3e45e7 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 18 Sep 2024 14:59:43 -0700 Subject: [PATCH 069/125] permission-page-container named vars --- .../permission-page-container.component.js | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js index 27dbc9f71c32..b9f97c8a8ff7 100644 --- a/ui/components/app/permission-page-container/permission-page-container.component.js +++ b/ui/components/app/permission-page-container/permission-page-container.component.js @@ -141,21 +141,22 @@ export default class PermissionPageContainer extends Component { selectedAccounts, } = this.props; + const approvedAccounts = selectedAccounts.map( + (selectedAccount) => selectedAccount.address, + ) + + const permittedChainsPermission = _request.permissions[ + PermissionNames.permittedChains + ] + const approvedChainIds = permittedChainsPermission?.caveats.find( + (caveat) => caveat.type === CaveatTypes.restrictNetworkSwitching, + )?.value + const request = { ..._request, permissions: { ..._request.permissions }, - ...(_request.permissions.eth_accounts && { - approvedAccounts: selectedAccounts.map( - (selectedAccount) => selectedAccount.address, - ), - }), - ...(_request.permissions[PermissionNames.permittedChains] && { - approvedChainIds: _request.permissions[ - PermissionNames.permittedChains - ]?.caveats.find( - (caveat) => caveat.type === CaveatTypes.restrictNetworkSwitching, - )?.value, - }), + ...(_request.permissions.eth_accounts && { approvedAccounts }), + ...(_request.permissions[PermissionNames.permittedChains] && { approvedChainIds }), }; if (Object.keys(request.permissions).length > 0) { From 7a1e4260bdddb6ece6364e7fbf6b9f318d2173fc Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 18 Sep 2024 15:07:11 -0700 Subject: [PATCH 070/125] lint --- .../permission-page-container.component.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js index b9f97c8a8ff7..8ca1a73d6df1 100644 --- a/ui/components/app/permission-page-container/permission-page-container.component.js +++ b/ui/components/app/permission-page-container/permission-page-container.component.js @@ -143,20 +143,21 @@ export default class PermissionPageContainer extends Component { const approvedAccounts = selectedAccounts.map( (selectedAccount) => selectedAccount.address, - ) + ); - const permittedChainsPermission = _request.permissions[ - PermissionNames.permittedChains - ] + const permittedChainsPermission = + _request.permissions[PermissionNames.permittedChains]; const approvedChainIds = permittedChainsPermission?.caveats.find( (caveat) => caveat.type === CaveatTypes.restrictNetworkSwitching, - )?.value + )?.value; const request = { ..._request, permissions: { ..._request.permissions }, ...(_request.permissions.eth_accounts && { approvedAccounts }), - ...(_request.permissions[PermissionNames.permittedChains] && { approvedChainIds }), + ...(_request.permissions[PermissionNames.permittedChains] && { + approvedChainIds, + }), }; if (Object.keys(request.permissions).length > 0) { From 9238525f620635738d2161a39e85fba0eaa7aae3 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 18 Sep 2024 15:07:24 -0700 Subject: [PATCH 071/125] reduce type casts --- .../multichain/pages/connections/connections.tsx | 10 +++------- .../review-permissions-page.tsx | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/ui/components/multichain/pages/connections/connections.tsx b/ui/components/multichain/pages/connections/connections.tsx index a80e1611d2db..2a8f35bb69cd 100644 --- a/ui/components/multichain/pages/connections/connections.tsx +++ b/ui/components/multichain/pages/connections/connections.tsx @@ -152,15 +152,11 @@ export const Connections = () => { parentCapability, ) as string[]; if (permissionMethodNames.length > 0) { - const permissionsRecord: Record = { - [activeTabOrigin]: permissionMethodNames, + const permissionsRecord = { + [activeTabOrigin]: permissionMethodNames as NonEmptyArray, }; - dispatch( - removePermissionsFor( - permissionsRecord as Record>, - ), - ); + dispatch(removePermissionsFor(permissionsRecord)); } setShowDisconnectAllModal(false); diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 7487ee25914b..ff4f89b893eb 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -135,15 +135,11 @@ export const ReviewPermissions = () => { parentCapability, ) as string[]; if (permissionMethodNames.length > 0) { - const permissionsRecord: Record = { - [activeTabOrigin]: permissionMethodNames, + const permissionsRecord = { + [activeTabOrigin]: permissionMethodNames as NonEmptyArray, }; - dispatch( - removePermissionsFor( - permissionsRecord as Record>, - ), - ); + dispatch(removePermissionsFor(permissionsRecord)); } } }; From d065a0cb67631ccf420c3d27c1896ff9552cbc3e Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 18 Sep 2024 15:14:22 -0700 Subject: [PATCH 072/125] cleanup checked isIndeterminate assignment --- .../connect-accounts-modal/connect-accounts-modal.tsx | 10 +++------- .../edit-accounts-modal/edit-accounts-modal.tsx | 9 ++------- .../edit-networks-modal/edit-networks-modal.js | 9 ++------- ui/components/ui/account-list/account-list.js | 9 ++------- 4 files changed, 9 insertions(+), 28 deletions(-) diff --git a/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx b/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx index 1263af76a762..210a53b27bcc 100644 --- a/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx +++ b/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx @@ -53,13 +53,9 @@ export const ConnectAccountsModal = ({ const allAreSelected = () => { return accounts.length === selectedAccounts.length; }; - let checked = false; - let isIndeterminate = false; - if (allAreSelected()) { - checked = true; - } else if (selectedAccounts.length > 0 && !allAreSelected()) { - isIndeterminate = true; - } + + const checked = allAreSelected(); + const isIndeterminate = selectedAccounts.length > 0 && !checked; return ( = ({ return accounts.length === selectedAccounts.length; }; - let checked = false; - let isIndeterminate = false; - if (allAreSelected()) { - checked = true; - } else if (selectedAccounts.length > 0 && !allAreSelected()) { - isIndeterminate = true; - } + const checked = allAreSelected(); + const isIndeterminate = selectedAccounts.length > 0 && !checked; return ( <> diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index e5b6878f46d2..1d3a6d5533a1 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -84,13 +84,8 @@ export const EditNetworksModal = ({ return combinedNetworksIds.length === selectedChains.length; }; - let checked = false; - let isIndeterminate = false; - if (allAreSelected()) { - checked = true; - } else if (selectedChains.length > 0 && !allAreSelected()) { - isIndeterminate = true; - } + const checked = allAreSelected(); + const isIndeterminate = selectedChains.length > 0 && !checked; const managePermittedChains = (chains) => { grantPermittedChains(activeTabOrigin, chains); diff --git a/ui/components/ui/account-list/account-list.js b/ui/components/ui/account-list/account-list.js index bef1ca405a5e..6d81585c97e2 100644 --- a/ui/components/ui/account-list/account-list.js +++ b/ui/components/ui/account-list/account-list.js @@ -56,13 +56,8 @@ const AccountList = ({ }; const Header = () => { - let checked = false; - let isIndeterminate = false; - if (allAreSelected()) { - checked = true; - } else if (selectedAccounts.size !== 0) { - isIndeterminate = true; - } + const checked = allAreSelected(); + const isIndeterminate = selectedAccounts.size !== 0 && !checked; return (
Date: Thu, 19 Sep 2024 15:33:09 +0100 Subject: [PATCH 073/125] updated permissions page --- app/_locales/en/messages.json | 6 ++ .../permissions-page/connection-list-item.js | 85 +++++++++++++------ 2 files changed, 64 insertions(+), 27 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 714e4e8575b2..66992ee221b8 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -3177,6 +3177,12 @@ "networks": { "message": "Networks" }, + "networksSmallCase": { + "message": "networks" + }, + "accountsSmallCase": { + "message": "accounts" + }, "nevermind": { "message": "Nevermind" }, diff --git a/ui/components/multichain/pages/permissions-page/connection-list-item.js b/ui/components/multichain/pages/permissions-page/connection-list-item.js index bf720da5b3bb..6f9a72a6ea0d 100644 --- a/ui/components/multichain/pages/permissions-page/connection-list-item.js +++ b/ui/components/multichain/pages/permissions-page/connection-list-item.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { SubjectType } from '@metamask/permission-controller'; +import { useSelector } from 'react-redux'; import { AlignItems, BackgroundColor, @@ -28,11 +29,41 @@ import { import { getURLHost } from '../../../../helpers/utils/util'; import { getAvatarNetworkColor } from '../../../../helpers/utils/accounts'; import { SnapIcon } from '../../../app/snaps/snap-icon'; +import { getPermittedChainsForSelectedTab } from '../../../../selectors'; import { ConnectionListTooltip } from './connection-list-tooltip/connection-list-tooltip'; export const ConnectionListItem = ({ connection, onClick }) => { const t = useI18nContext(); const isSnap = connection.subjectType === SubjectType.Snap; + const connectedNetworks = useSelector((state) => + getPermittedChainsForSelectedTab(state, connection.origin), + ); + + const renderListItem = process.env.CHAIN_PERMISSIONS ? ( + + ) : ( + + } + > + + + ); return ( { avatarSize={IconSize.Md} /> ) : ( - - } - > - - + <>{renderListItem} )} { alignItems={AlignItems.center} gap={1} > - - {t('connectedWith')} - - + {process.env.CHAIN_PERMISSIONS ? ( + + {connection.addresses.length} {t('accountsSmallCase')}  + •  + {connectedNetworks.length} {t('networksSmallCase')} + + ) : ( + <> + + {t('connectedWith')} + + + + + )} )} + Date: Thu, 19 Sep 2024 08:29:56 -0700 Subject: [PATCH 074/125] swap !checked condition order --- .../connect-accounts-modal/connect-accounts-modal.tsx | 2 +- .../multichain/edit-accounts-modal/edit-accounts-modal.tsx | 2 +- .../multichain/edit-networks-modal/edit-networks-modal.js | 2 +- ui/components/ui/account-list/account-list.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx b/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx index 210a53b27bcc..457e15b0141d 100644 --- a/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx +++ b/ui/components/multichain/connect-accounts-modal/connect-accounts-modal.tsx @@ -55,7 +55,7 @@ export const ConnectAccountsModal = ({ }; const checked = allAreSelected(); - const isIndeterminate = selectedAccounts.length > 0 && !checked; + const isIndeterminate = !checked && selectedAccounts.length > 0; return ( = ({ }; const checked = allAreSelected(); - const isIndeterminate = selectedAccounts.length > 0 && !checked; + const isIndeterminate = !checked && selectedAccounts.length > 0; return ( <> diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index 1d3a6d5533a1..ec1a5928e193 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -85,7 +85,7 @@ export const EditNetworksModal = ({ }; const checked = allAreSelected(); - const isIndeterminate = selectedChains.length > 0 && !checked; + const isIndeterminate = !checked && selectedChains.length > 0; const managePermittedChains = (chains) => { grantPermittedChains(activeTabOrigin, chains); diff --git a/ui/components/ui/account-list/account-list.js b/ui/components/ui/account-list/account-list.js index 6d81585c97e2..13afac6c08f2 100644 --- a/ui/components/ui/account-list/account-list.js +++ b/ui/components/ui/account-list/account-list.js @@ -57,7 +57,7 @@ const AccountList = ({ const Header = () => { const checked = allAreSelected(); - const isIndeterminate = selectedAccounts.size !== 0 && !checked; + const isIndeterminate = !checked && selectedAccounts.size !== 0; return (
Date: Thu, 19 Sep 2024 16:34:30 +0100 Subject: [PATCH 075/125] lint fix --- app/_locales/en/messages.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 66992ee221b8..84c2cefee153 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -181,6 +181,9 @@ "accountsPermissionsTitle": { "message": "See your accounts and suggest transactions" }, + "accountsSmallCase": { + "message": "accounts" + }, "active": { "message": "Active" }, @@ -3180,9 +3183,6 @@ "networksSmallCase": { "message": "networks" }, - "accountsSmallCase": { - "message": "accounts" - }, "nevermind": { "message": "Nevermind" }, From 178ba337d7fde9b284cbdd319d4c82184f3b81c3 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 19 Sep 2024 09:03:10 -0700 Subject: [PATCH 076/125] Fix approvedAccounts selection in connection flow --- .../pages/review-permissions-page/review-permissions-page.tsx | 1 - .../pages/review-permissions-page/site-cell/site-cell.tsx | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index ff4f89b893eb..39e8fa3cc62a 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -102,7 +102,6 @@ export const ReviewPermissions = () => { const connectedAccounts = useSelector((state) => getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin), ) as AccountType[]; - console.log(connectedAccounts, 'connectedAccounts'); const subjects = useSelector(getPermissionSubjects); const grantedNetworks = combinedNetworks.filter( (net: { chainId: string }) => connectedNetworks.indexOf(net.chainId) !== -1, diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 36337b06f45d..74c1404539ff 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -44,7 +44,7 @@ type SiteCellProps = { onAccountsClick: () => void; onNetworksClick: () => void; onDisconnectClick: () => void; - approvedAccounts: { address: string }[]; + approvedAccounts: string[]; activeTabOrigin: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any combinedNetworks: any; @@ -276,7 +276,7 @@ export const SiteCell: React.FC = ({ setShowEditAccountsModal(false)} onClick={onAccountsClick} - approvedAccounts={approvedAccounts.map((account) => account.address)} // Extracting addresses as strings + approvedAccounts={approvedAccounts} activeTabOrigin={activeTabOrigin} currentTabHasNoAccounts={currentTabHasNoAccounts} onDisconnectClick={onDisconnectClick} From 607a63ec7aaf648145993b2358064844a8a3b8f3 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 19 Sep 2024 12:47:34 -0700 Subject: [PATCH 077/125] add SiteCellConnectionListItem --- .../site-cell-connection-list-item.js | 142 ++++++++++++ .../site-cell/site-cell.tsx | 215 +++--------------- 2 files changed, 178 insertions(+), 179 deletions(-) create mode 100644 ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js new file mode 100644 index 000000000000..b3fff0836ae5 --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js @@ -0,0 +1,142 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + AlignItems, + BackgroundColor, + BlockSize, + Display, + FlexDirection, + IconColor, + JustifyContent, + TextAlign, + TextColor, + TextVariant, +} from '../../../../../helpers/constants/design-system'; +import { + AvatarIcon, + AvatarIconSize, + Box, + ButtonLink, + Icon, + IconName, + IconSize, + Text, +} from '../../../../component-library'; +import { useI18nContext } from '../../../../../hooks/useI18nContext'; + +export const SiteCellConnectionListItem = ({ + title, + iconName, + connectedMessage, + unconnectedMessage, + currentTabHasNoAccounts, + onClick, + content, +}) => { + const t = useI18nContext(); + + return ( + + + + + {title} + + + + {currentTabHasNoAccounts ? unconnectedMessage : connectedMessage} + + {content} + + + {currentTabHasNoAccounts ? ( + onClick()}>{t('edit')} + ) : ( + onClick()} + > + + + )} + + ); +}; +SiteCellConnectionListItem.propTypes = { + /** + * Title that should be displayed in the connection list item + */ + title: PropTypes.string, + + /** + * The name of the icon that should be passed to the AvatarIcon component + */ + iconName: PropTypes.string, + + /** + * The message that should be displayed when there are connected accounts + */ + connectedMessage: PropTypes.string, + + /** + * The message that should be displayed when there are no connected accounts + */ + unconnectedMessage: PropTypes.string, + + /** + * If the focused origin has connected accounts + */ + currentTabHasNoAccounts: PropTypes.bool, + + /** + * Handler called when the edit button is clicked + */ + onClick: PropTypes.func, + + /** + * Components to display in the connection list item + */ + content: PropTypes.node, +}; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 74c1404539ff..3747b359e47c 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -1,35 +1,17 @@ import React, { useState } from 'react'; import { useSelector } from 'react-redux'; -import { - AlignItems, - BackgroundColor, - BlockSize, - BorderColor, - Display, - FlexDirection, - IconColor, - JustifyContent, - TextAlign, - TextColor, - TextVariant, -} from '../../../../../helpers/constants/design-system'; +import { BorderColor } from '../../../../../helpers/constants/design-system'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { AvatarAccount, AvatarAccountSize, - AvatarIcon, - AvatarIconSize, - Box, - ButtonLink, - Icon, IconName, - IconSize, - Text, } from '../../../../component-library'; import { EditAccountsModal, EditNetworksModal } from '../../..'; import { getPermittedAccountsByOrigin } from '../../../../../selectors/permissions'; import { AccountType } from '../../../connect-accounts-modal/connect-account-modal.types'; import { SiteCellTooltip } from './site-cell-tooltip'; +import { SiteCellConnectionListItem } from './site-cell-connection-list-item'; // Define types for networks, accounts, and other props type Network = { @@ -99,168 +81,43 @@ export const SiteCell: React.FC = ({ return ( <> - - - - - {t('accountsPermissionsTitle')} - - - - {currentTabHasNoAccounts - ? accountMessageNotConnectedState - : accountMessageConnectedState} - - {accounts.length > 1 ? ( - - ) : ( - - )} - - - {currentTabHasNoAccounts ? ( - setShowEditAccountsModal(true)}> - {t('edit')} - - ) : ( - setShowEditAccountsModal(true)} - > - - - )} - - - - - - - {t('permission_walletSwitchEthereumChain')} - - - - {currentTabHasNoAccounts - ? t('requestingFor') - : t('connectedWith')} - + setShowEditAccountsModal(true)} + content={ + accounts.length > 1 ? ( - - - {currentTabHasNoAccounts ? ( - setShowEditNetworksModal(true)}> - {t('edit')} - - ) : ( - setShowEditNetworksModal(true)} - > - - - )} - + ) + } + /> + + setShowEditNetworksModal(true)} + content={ + + } + /> {showEditNetworksModal && ( Date: Thu, 19 Sep 2024 13:50:39 -0700 Subject: [PATCH 078/125] Add PermissionsHeader --- .../pages/connections/connections.tsx | 62 ++------------ .../review-permissions-page.tsx | 61 ++------------ .../permissions-header/permissions-header.tsx | 80 +++++++++++++++++++ 3 files changed, 92 insertions(+), 111 deletions(-) create mode 100644 ui/components/multichain/permissions-header/permissions-header.tsx diff --git a/ui/components/multichain/pages/connections/connections.tsx b/ui/components/multichain/pages/connections/connections.tsx index 2a8f35bb69cd..05e8108bfae0 100644 --- a/ui/components/multichain/pages/connections/connections.tsx +++ b/ui/components/multichain/pages/connections/connections.tsx @@ -3,15 +3,9 @@ import { useDispatch, useSelector } from 'react-redux'; import { useHistory, useParams } from 'react-router-dom'; import { NonEmptyArray } from '@metamask/utils'; import { - AlignItems, - BackgroundColor, BlockSize, Display, FlexDirection, - IconColor, - JustifyContent, - TextAlign, - TextVariant, } from '../../../../helpers/constants/design-system'; import { CONNECT_ROUTE } from '../../../../helpers/constants/routes'; import { getURLHost } from '../../../../helpers/utils/util'; @@ -32,16 +26,11 @@ import { AvatarFaviconSize, Box, Button, - ButtonIcon, - ButtonIconSize, ButtonPrimary, ButtonPrimarySize, ButtonSize, ButtonVariant, - Icon, IconName, - IconSize, - Text, } from '../../../component-library'; import { mergeAccounts } from '../../account-list-menu/account-list-menu'; import { @@ -50,7 +39,7 @@ import { Toast, ToastContainer, } from '../..'; -import { Content, Footer, Header, Page } from '../page'; +import { Content, Footer, Page } from '../page'; import { ConnectAccountsModal } from '../../connect-accounts-modal/connect-accounts-modal'; import { requestAccountsAndChainPermissionsWithId, @@ -60,6 +49,7 @@ import { DisconnectAllModal, DisconnectType, } from '../../disconnect-all-modal/disconnect-all-modal'; +import { PermissionsHeader } from '../../permissions-header/permissions-header'; import { AccountType, ConnectedSites, @@ -202,50 +192,10 @@ export const Connections = () => { data-testid="connections-page" className="main-container connections-page" > -
(history as any).goBack()} - size={ButtonIconSize.Sm} - /> - } - > - - {connectedSubjectsMetadata?.iconUrl ? ( - - ) : ( - - )} - - {getURLHost(securedOrigin)} - - -
+ {permittedAccounts.length > 0 && mergeAccounts.length > 0 ? ( diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index 39e8fa3cc62a..a6266016a082 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -3,15 +3,9 @@ import { useDispatch, useSelector } from 'react-redux'; import { useHistory, useParams } from 'react-router-dom'; import { NonEmptyArray } from '@metamask/utils'; import { - AlignItems, - BackgroundColor, BlockSize, Display, FlexDirection, - IconColor, - JustifyContent, - TextAlign, - TextVariant, } from '../../../../helpers/constants/design-system'; import { getURLHost } from '../../../../helpers/utils/util'; import { useI18nContext } from '../../../../hooks/useI18nContext'; @@ -33,26 +27,22 @@ import { AvatarFaviconSize, Box, Button, - ButtonIcon, - ButtonIconSize, ButtonPrimary, ButtonPrimarySize, ButtonSize, ButtonVariant, - Icon, IconName, - IconSize, - Text, } from '../../../component-library'; import { ToastContainer, Toast } from '../..'; import { NoConnectionContent } from '../connections/components/no-connection'; -import { Content, Footer, Header, Page } from '../page'; +import { Content, Footer, Page } from '../page'; import { SubjectsType } from '../connections/components/connections.types'; import { CONNECT_ROUTE } from '../../../../helpers/constants/routes'; import { DisconnectAllModal, DisconnectType, } from '../../disconnect-all-modal/disconnect-all-modal'; +import { PermissionsHeader } from '../../permissions-header/permissions-header'; import { AccountType } from './review-permission.types'; import { SiteCell } from '.'; @@ -149,49 +139,10 @@ export const ReviewPermissions = () => { className="main-container connections-page" > <> -
(history as any).goBack()} - /> - } - > - - {connectedSubjectsMetadata?.iconUrl ? ( - - ) : ( - - )} - - {hostName} - - -
+ {connectedAccounts.length > 0 ? ( { + const history = useHistory(); + + return ( +
(history as any).goBack()} + size={ButtonIconSize.Sm} + /> + } + > + + {connectedSubjectsMetadata?.iconUrl ? ( + + ) : ( + + )} + + {getURLHost(securedOrigin)} + + +
+ ); +}; From 413d85ae28f771d77f440300a7f470bb21dc422c Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 19 Sep 2024 14:02:26 -0700 Subject: [PATCH 079/125] Fix missingi mports --- .../multichain/permissions-header/permissions-header.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/components/multichain/permissions-header/permissions-header.tsx b/ui/components/multichain/permissions-header/permissions-header.tsx index 4b83781ac853..9ee7bec7a52c 100644 --- a/ui/components/multichain/permissions-header/permissions-header.tsx +++ b/ui/components/multichain/permissions-header/permissions-header.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { useHistory } from 'react-router-dom'; import { AlignItems, @@ -21,6 +22,7 @@ import { } from '../../component-library'; import { Header } from '../pages/page'; import { getURLHost } from '../../../helpers/utils/util'; +import { useI18nContext } from '../../../hooks/useI18nContext'; export const PermissionsHeader = ({ securedOrigin, @@ -29,6 +31,7 @@ export const PermissionsHeader = ({ securedOrigin: string; connectedSubjectsMetadata?: { name: string; iconUrl: string }; }) => { + const t = useI18nContext(); const history = useHistory(); return ( From fe9fee4d82fdfbd4f0c59fb2f3f83d4d890fcbec Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Thu, 19 Sep 2024 23:10:49 +0100 Subject: [PATCH 080/125] updated connect page --- .../connect-page/connect-page.tsx | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index 1895d87d0290..a14a005a6c69 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -1,8 +1,11 @@ import React from 'react'; import { useSelector } from 'react-redux'; +import { InternalAccount } from '@metamask/keyring-api'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getNonTestNetworks, + getOrderedConnectedAccountsForConnectedDapp, + getPermittedChainsForSelectedTab, getSelectedAccountsForDappConnection, getSelectedInternalAccount, getSelectedNetworksForDappConnection, @@ -68,23 +71,42 @@ export const ConnectPage: React.FC = ({ const combinedNetworks = [...networksList, ...testNetworks]; const currentAccount = useSelector(getSelectedInternalAccount); - + const connectedAccounts = useSelector((state) => + getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin), + ) as AccountType[]; + const connectedAccountsAddresses = connectedAccounts?.map( + (account: InternalAccount) => account.address, + ); + const connectedNetworks = useSelector((state) => + getPermittedChainsForSelectedTab(state, activeTabOrigin), + ) as string[]; const selectedAccountsForDappConnection = useSelector( getSelectedAccountsForDappConnection, ); + const grantedNetworks = combinedNetworks.filter( + (net: { chainId: string }) => + connectedNetworks?.indexOf(net.chainId) !== -1, + ); + const defaultAccountsAddresses = + connectedAccounts?.length > 0 + ? connectedAccountsAddresses + : [currentAccount?.address]; + + const defaultNetworksList = + grantedNetworks?.length > 0 ? grantedNetworks : networksList; // Filter networks based on chainId const filteredNetworks = Array.isArray(selectedNetworksList) ? combinedNetworks.filter((network) => selectedNetworksList.includes(network.chainId), ) - : networksList; + : defaultNetworksList; // Select approved accounts and networks const approvedAccounts = selectedAccountsForDappConnection.length > 0 ? selectedAccountsForDappConnection - : [currentAccount?.address]; + : defaultAccountsAddresses; // Handle confirmation const onConfirm = () => { @@ -120,7 +142,7 @@ export const ConnectPage: React.FC = ({ onNetworksClick={() => null} approvedAccounts={approvedAccounts} activeTabOrigin={activeTabOrigin} - combinedNetworks={networksList} + combinedNetworks={defaultNetworksList} onDisconnectClick={() => null} />
From e4b1a2513655d9009e5dc7e731be97c97fa10322 Mon Sep 17 00:00:00 2001 From: jiexi Date: Mon, 23 Sep 2024 03:47:34 -0700 Subject: [PATCH 081/125] Jl/editing flow refactor (#27309) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27309?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- test/data/mock-send-state.json | 2 - .../edit-accounts-modal.tsx | 313 +++++++----------- .../new-accounts-modal.tsx | 32 -- .../edit-networks-modal.js | 148 ++++----- .../network-list-menu/network-list-menu.tsx | 1 - .../review-permissions-page.tsx | 165 +++++---- ...ite-cell-connection-list-item.test.js.snap | 46 +++ .../site-cell-connection-list-item.js | 10 +- .../site-cell-connection-list-item.test.js | 21 ++ .../site-cell/site-cell-tooltip.js | 53 +-- .../site-cell/site-cell.tsx | 135 ++++---- ui/ducks/app/app.ts | 16 - .../connect-page/connect-page.tsx | 123 +++---- .../permissions-connect.component.js | 5 - ui/selectors/selectors.js | 8 - ui/store/actions.ts | 18 +- 16 files changed, 480 insertions(+), 616 deletions(-) delete mode 100644 ui/components/multichain/edit-accounts-modal/new-accounts-modal.tsx create mode 100644 ui/components/multichain/pages/review-permissions-page/site-cell/__snapshots__/site-cell-connection-list-item.test.js.snap create mode 100644 ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.test.js diff --git a/test/data/mock-send-state.json b/test/data/mock-send-state.json index 2498bb712ba0..6629d8c6ac67 100644 --- a/test/data/mock-send-state.json +++ b/test/data/mock-send-state.json @@ -27,8 +27,6 @@ } }, "showIpfsModalOpen": false, - "selectedAccountsForDappConnection": {}, - "selectedNetworksForDappConnection": {}, "showKeyringRemovalSnapModal": false, "showWhatsNewPopup": false, "warning": null, diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index bc6b96291389..d9303951af2d 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -1,17 +1,5 @@ -import React, { useMemo, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { - EthAccountType, - InternalAccount, - isEvmAccountType, - KeyringAccountType, -} from '@metamask/keyring-api'; +import React, { useEffect, useState } from 'react'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { - getInternalAccounts, - getOrderedConnectedAccountsForConnectedDapp, - getUpdatedAndSortedAccounts, -} from '../../../selectors'; import { Modal, ModalOverlay, @@ -29,15 +17,8 @@ import { IconName, Icon, } from '../../component-library'; -import { AccountListItem } from '..'; -import { MergedInternalAccount } from '../../../selectors/selectors.types'; -import { mergeAccounts } from '../account-list-menu/account-list-menu'; +import { AccountListItem, CreateEthAccount } from '..'; -import { - addMorePermittedAccounts, - removePermittedAccount, - setSelectedAccountsForDappConnection, -} from '../../../store/actions'; import { JustifyContent, Display, @@ -48,105 +29,61 @@ import { AlignItems, } from '../../../helpers/constants/design-system'; import { getURLHost } from '../../../helpers/utils/util'; -import { NewAccountModal } from './new-accounts-modal'; - -const defaultAllowedAccountTypes = [EthAccountType.Eoa, EthAccountType.Erc4337]; +import { MergedInternalAccount } from '../../../selectors/selectors.types'; type EditAccountsModalProps = { - onClose: () => void; - onClick: () => void; - onDisconnectClick: () => void; - allowedAccountTypes?: KeyringAccountType[]; - approvedAccounts: string[]; activeTabOrigin: string; - currentTabHasNoAccounts: boolean; + accounts: MergedInternalAccount[]; + defaultSelectedAccountAddresses: string[]; + onClose: () => void; + onSubmit: (addresses: string[]) => void; }; export const EditAccountsModal: React.FC = ({ - onClose, - onClick, - onDisconnectClick, - allowedAccountTypes = defaultAllowedAccountTypes, - approvedAccounts, activeTabOrigin, - currentTabHasNoAccounts, + accounts, + defaultSelectedAccountAddresses, + onClose, + onSubmit, }) => { const t = useI18nContext(); - const accounts = useSelector(getUpdatedAndSortedAccounts); - const internalAccounts = useSelector(getInternalAccounts); - const dispatch = useDispatch(); - const hostName = getURLHost(activeTabOrigin); - const [showAddNewAccountsModal, setShowAddNewAccountsModal] = useState(false); - - const mergedAccounts: MergedInternalAccount[] = useMemo(() => { - return mergeAccounts(accounts, internalAccounts).filter( - (account: InternalAccount) => - allowedAccountTypes.includes(account.type as KeyringAccountType), - ); - }, [accounts, internalAccounts, allowedAccountTypes]); - const connectedAccounts = useSelector((state) => - getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin).filter( - (account: InternalAccount) => isEvmAccountType(account.type), - ), - ); - - const connectedAccountsAddresses = connectedAccounts.map( - (account: InternalAccount) => account.address, - ); + const [showAddNewAccounts, setShowAddNewAccounts] = useState(false); - const defaultAccountsAddresses = - connectedAccountsAddresses.length > 0 - ? connectedAccountsAddresses - : approvedAccounts; - - const [selectedAccounts, setSelectedAccounts] = useState( - defaultAccountsAddresses, + const [selectedAccountAddresses, setSelectedAccountAddresses] = useState( + defaultSelectedAccountAddresses, ); - const handleAccountClick = (address: string) => { - setSelectedAccounts((prevSelectedAccounts) => - prevSelectedAccounts.includes(address) - ? prevSelectedAccounts.filter((acc) => acc !== address) - : [...prevSelectedAccounts, address], - ); - }; - - const managePermittedAccounts = ( - selectedAcc: string[], - accountsAddresses: string[], - ) => { - const removedAccounts = accountsAddresses.filter( - (acc) => !selectedAcc.includes(acc), - ); - removedAccounts.forEach((account) => { - dispatch(removePermittedAccount(activeTabOrigin, account)); - }); - - const newAccounts = selectedAcc.filter( - (acc) => !accountsAddresses.includes(acc), - ); - if (newAccounts.length > 0) { - dispatch(addMorePermittedAccounts(activeTabOrigin, newAccounts)); - } - }; + useEffect(() => { + setSelectedAccountAddresses(defaultSelectedAccountAddresses); + }, [defaultSelectedAccountAddresses]); const selectAll = () => { - const newSelectedAccounts = accounts.map( - (account: { address: string }) => account.address, - ); - setSelectedAccounts(newSelectedAccounts); + const allNetworksAccountAddresses = accounts.map(({ address }) => address); + setSelectedAccountAddresses(allNetworksAccountAddresses); }; const deselectAll = () => { - setSelectedAccounts([]); + setSelectedAccountAddresses([]); + }; + + const handleAccountClick = (address: string) => { + if (selectedAccountAddresses.includes(address)) { + setSelectedAccountAddresses( + selectedAccountAddresses.filter((_address) => _address !== address), + ); + } else { + setSelectedAccountAddresses([...selectedAccountAddresses, address]); + } }; const allAreSelected = () => { - return accounts.length === selectedAccounts.length; + return accounts.length === selectedAccountAddresses.length; }; const checked = allAreSelected(); - const isIndeterminate = !checked && selectedAccounts.length > 0; + const isIndeterminate = !checked && selectedAccountAddresses.length > 0; + + const hostName = getURLHost(activeTabOrigin); return ( <> @@ -160,105 +97,105 @@ export const EditAccountsModal: React.FC = ({ {t('editAccounts')} - - (allAreSelected() ? deselectAll() : selectAll())} - isIndeterminate={isIndeterminate} - /> - setShowAddNewAccountsModal(true)}> - {t('newAccount')} - - - {mergedAccounts.map((account) => ( - handleAccountClick(account.address)} - account={account} - key={account.address} - isPinned={Boolean(account.pinned)} - startAccessory={ - - } - selected={false} - /> - ))} - - - {selectedAccounts.length === 0 ? ( + {showAddNewAccounts ? ( + + setShowAddNewAccounts(false)} + /> + + ) : ( + <> - - - - {t('disconnectMessage', [hostName])} - - - { - onDisconnectClick(); - onClose(); - }} - size={ButtonPrimarySize.Lg} - block - danger - > - {t('disconnect')} - + + allAreSelected() ? deselectAll() : selectAll() + } + isIndeterminate={isIndeterminate} + /> + setShowAddNewAccounts(true)}> + {t('newAccount')} + - ) : ( - { - onClick(); - if (currentTabHasNoAccounts) { - dispatch( - setSelectedAccountsForDappConnection(selectedAccounts), - ); - } else { - managePermittedAccounts( - selectedAccounts, - connectedAccountsAddresses, - ); + {accounts.map((account) => ( + handleAccountClick(account.address)} + account={account} + key={account.address} + isPinned={Boolean(account.pinned)} + startAccessory={ + } - onClose(); - }} - size={ButtonPrimarySize.Lg} - block - > - {t('update')} - - )} - + selected={false} + /> + ))} + + + {selectedAccountAddresses.length === 0 ? ( + + + + + {t('disconnectMessage', [hostName])} + + + { + onSubmit([]); + onClose(); + }} + size={ButtonPrimarySize.Lg} + block + danger + > + {t('disconnect')} + + + ) : ( + { + onSubmit(selectedAccountAddresses); + onClose(); + }} + size={ButtonPrimarySize.Lg} + block + > + {t('update')} + + )} + + + )} - - {showAddNewAccountsModal && ( - setShowAddNewAccountsModal(false)} /> - )} ); }; diff --git a/ui/components/multichain/edit-accounts-modal/new-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/new-accounts-modal.tsx deleted file mode 100644 index 5b7ac5390d4b..000000000000 --- a/ui/components/multichain/edit-accounts-modal/new-accounts-modal.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import { - Modal, - ModalOverlay, - ModalContent, - Box, -} from '../../component-library'; -import { CreateEthAccount } from '..'; - -type NewAccountModalProps = { - onClose: () => void; -}; - -export const NewAccountModal: React.FC = ({ - onClose, -}) => { - return ( - - - - - - - - - ); -}; diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index cb822f48d412..8f599d149689 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -1,6 +1,5 @@ -import React, { useState, useMemo } from 'react'; +import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -import { useDispatch, useSelector } from 'react-redux'; import { AlignItems, Display, @@ -10,11 +9,6 @@ import { TextVariant, } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { - getOriginOfCurrentTab, - getPermittedChainsForSelectedTab, - getNetworkConfigurationsByChainId, -} from '../../../selectors'; import { Modal, ModalOverlay, @@ -32,80 +26,54 @@ import { IconSize, } from '../../component-library'; import { NetworkListItem } from '..'; -import { - grantPermittedChains, - setSelectedNetworksForDappConnection, -} from '../../../store/actions'; import { getURLHost } from '../../../helpers/utils/util'; -import { - CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, - TEST_CHAINS, -} from '../../../../shared/constants/network'; +import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../shared/constants/network'; export const EditNetworksModal = ({ + activeTabOrigin, + nonTestNetworks, + testNetworks, + defaultSelectedChainIds, onClose, - onClick, - currentTabHasNoAccounts, - combinedNetworks, - onDisconnectClick, + onSubmit, }) => { const t = useI18nContext(); - const dispatch = useDispatch(); - const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); - const [nonTestNetworks, testNetworks] = useMemo( - () => - Object.entries(networkConfigurations).reduce( - ([nonTestNetworksList, testNetworksList], [chainId, network]) => { - const isTest = TEST_CHAINS.includes(chainId); - (isTest ? testNetworksList : nonTestNetworksList).push(network); - return [nonTestNetworksList, testNetworksList]; - }, - [[], []], - ), - [networkConfigurations], - ); - const activeTabOrigin = useSelector(getOriginOfCurrentTab); - const connectedNetworks = useSelector((state) => - getPermittedChainsForSelectedTab(state, activeTabOrigin), - ); - const combinedNetworksIds = combinedNetworks.map( - (network) => network.chainId, + + const allNetworks = [...nonTestNetworks, ...testNetworks]; + + const [selectedChainIds, setSelectedChainIds] = useState( + defaultSelectedChainIds, ); - const selectedPermittedChains = - connectedNetworks.length > 0 ? connectedNetworks : combinedNetworksIds; - const [selectedChains, setSelectedChains] = useState(selectedPermittedChains); + + useEffect(() => { + setSelectedChainIds(defaultSelectedChainIds); + }, [defaultSelectedChainIds]); const selectAll = () => { - const newSelectedAccounts = combinedNetworks.map( - (network) => network.chainId, - ); - setSelectedChains(newSelectedAccounts); + const allNetworksChainIds = allNetworks.map(({ chainId }) => chainId); + setSelectedChainIds(allNetworksChainIds); }; const deselectAll = () => { - setSelectedChains([]); + setSelectedChainIds([]); }; - const handleAccountClick = (chainId) => { - if (selectedChains.includes(chainId)) { - // Remove the chainId from the selectedChains - setSelectedChains(selectedChains.filter((id) => id !== chainId)); + const handleNetworkClick = (chainId) => { + if (selectedChainIds.includes(chainId)) { + setSelectedChainIds( + selectedChainIds.filter((_chainId) => _chainId !== chainId), + ); } else { - // Add the chainId to selectedChains - setSelectedChains([...selectedChains, chainId]); + setSelectedChainIds([...selectedChainIds, chainId]); } }; const allAreSelected = () => { - return combinedNetworksIds.length === selectedChains.length; + return allNetworks.length === selectedChainIds.length; }; const checked = allAreSelected(); - const isIndeterminate = !checked && selectedChains.length > 0; - - const managePermittedChains = (chains) => { - grantPermittedChains(activeTabOrigin, chains); - }; + const isIndeterminate = !checked && selectedChainIds.length > 0; const hostName = getURLHost(activeTabOrigin); @@ -142,11 +110,11 @@ export const EditNetworksModal = ({ iconSrc={CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[network.chainId]} key={network.chainId} onClick={() => { - handleAccountClick(network.chainId); + handleNetworkClick(network.chainId); }} startAccessory={ } /> @@ -160,18 +128,18 @@ export const EditNetworksModal = ({ iconSrc={CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[network.chainId]} key={network.chainId} onClick={() => { - handleAccountClick(network.chainId); + handleNetworkClick(network.chainId); }} startAccessory={ } showEndAccessory={false} /> ))} - {selectedChains.length === 0 ? ( + {selectedChainIds.length === 0 ? ( { - onDisconnectClick(); + onSubmit([]); onClose(); }} size={ButtonPrimarySize.Lg} @@ -211,15 +179,8 @@ export const EditNetworksModal = ({ { - onClick(); + onSubmit(selectedChainIds); onClose(); - if (currentTabHasNoAccounts) { - dispatch( - setSelectedNetworksForDappConnection(selectedChains), - ); - } else { - managePermittedChains(selectedChains); - } }} size={ButtonPrimarySize.Lg} block @@ -236,35 +197,42 @@ export const EditNetworksModal = ({ EditNetworksModal.propTypes = { /** - * Function to execute when the modal is closed. + * Origin for the active tab. */ - onClose: PropTypes.func.isRequired, + activeTabOrigin: PropTypes.string, /** - * Function to execute when an update action is triggered. + * Array of network objects representing available non-test networks to choose from. */ - onClick: PropTypes.func.isRequired, - - /** - * Boolean indicating if the current tab has no associated accounts. - */ - currentTabHasNoAccounts: PropTypes.bool.isRequired, + nonTestNetworks: PropTypes.arrayOf( + PropTypes.shape({ + chainId: PropTypes.string.isRequired, // The chain ID of the network + name: PropTypes.string.isRequired, // Display name of the network + }), + ).isRequired, /** - * Array of network objects representing available networks to choose from. + * Array of network objects representing available test networks to choose from. */ - combinedNetworks: PropTypes.arrayOf( + testNetworks: PropTypes.arrayOf( PropTypes.shape({ chainId: PropTypes.string.isRequired, // The chain ID of the network - nickname: PropTypes.string.isRequired, // Display name of the network - rpcPrefs: PropTypes.shape({ - imageUrl: PropTypes.string, // Optional image URL for the network icon - }), + name: PropTypes.string.isRequired, // Display name of the network }), ).isRequired, /** - * Function to execute when the disconnect button is clicked. + * Array of chain IDs to have selected by default. + */ + defaultSelectedChainIds: PropTypes.arrayOf(PropTypes.string), + + /** + * Function to execute when the modal is closed. + */ + onClose: PropTypes.func.isRequired, + + /** + * Function to execute when an update or disconnect action is triggered. */ - onDisconnectClick: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, }; diff --git a/ui/components/multichain/network-list-menu/network-list-menu.tsx b/ui/components/multichain/network-list-menu/network-list-menu.tsx index 929eb85da656..7916af03a206 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.tsx +++ b/ui/components/multichain/network-list-menu/network-list-menu.tsx @@ -130,7 +130,6 @@ export const NetworkListMenu = ({ onClose }: { onClose: () => void }) => { const currentlyOnTestNetwork = (TEST_CHAINS as Hex[]).includes( currentChainId, ); - const [nonTestNetworks, testNetworks] = useMemo( () => Object.entries(networkConfigurations).reduce( diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx index a6266016a082..7e47769b211d 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.tsx @@ -1,7 +1,9 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory, useParams } from 'react-router-dom'; import { NonEmptyArray } from '@metamask/utils'; +import { InternalAccount, isEvmAccountType } from '@metamask/keyring-api'; +import { NetworkConfiguration } from '@metamask/network-controller'; import { BlockSize, Display, @@ -11,15 +13,19 @@ import { getURLHost } from '../../../../helpers/utils/util'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import { getConnectedSitesList, - getNonTestNetworks, - getOrderedConnectedAccountsForConnectedDapp, + getInternalAccounts, + getNetworkConfigurationsByChainId, getPermissionSubjects, - getPermittedAccountsByOrigin, + getPermittedAccountsForSelectedTab, getPermittedChainsForSelectedTab, - getTestNetworks, + getUpdatedAndSortedAccounts, } from '../../../../selectors'; import { + addMorePermittedAccounts, + grantPermittedChains, removePermissionsFor, + removePermittedAccount, + removePermittedChain, requestAccountsAndChainPermissionsWithId, } from '../../../../store/actions'; import { @@ -43,13 +49,11 @@ import { DisconnectType, } from '../../disconnect-all-modal/disconnect-all-modal'; import { PermissionsHeader } from '../../permissions-header/permissions-header'; -import { AccountType } from './review-permission.types'; +import { mergeAccounts } from '../../account-list-menu/account-list-menu'; +import { MergedInternalAccount } from '../../../../selectors/selectors.types'; +import { TEST_CHAINS } from '../../../../../shared/constants/network'; import { SiteCell } from '.'; -type PermittedAccountsByOrigin = { - [key: string]: { address: string }[]; -}; - export const ReviewPermissions = () => { const t = useI18nContext(); const dispatch = useDispatch(); @@ -61,61 +65,21 @@ export const ReviewPermissions = () => { const [showDisconnectAllModal, setShowDisconnectAllModal] = useState(false); const activeTabOrigin: string = securedOrigin; - // Define types for state - const { openMetaMaskTabs }: { openMetaMaskTabs: Record } = - useSelector( - (state: { appState: { openMetaMaskTabs: Record } }) => - state.appState, + const requestAccountsAndChainPermissions = async () => { + const requestId = await dispatch( + requestAccountsAndChainPermissionsWithId(activeTabOrigin), ); - - const { id }: { id: string } = useSelector( - (state: { activeTab: { id: string } }) => state.activeTab, - ); + history.push(`${CONNECT_ROUTE}/${requestId}`); + }; // eslint-disable-next-line @typescript-eslint/no-explicit-any const subjectMetadata: { [key: string]: any } = useSelector( getConnectedSitesList, ); const connectedSubjectsMetadata = subjectMetadata[activeTabOrigin]; - - const connectedNetworks = useSelector((state) => - getPermittedChainsForSelectedTab(state, activeTabOrigin), - ) as string[]; - - const permittedAccountsByOrigin = useSelector( - getPermittedAccountsByOrigin, - ) as PermittedAccountsByOrigin; - const networksList = useSelector(getNonTestNetworks); - const testNetworks = useSelector(getTestNetworks); - const combinedNetworks = [...networksList, ...testNetworks]; - - const connectedAccounts = useSelector((state) => - getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin), - ) as AccountType[]; const subjects = useSelector(getPermissionSubjects); - const grantedNetworks = combinedNetworks.filter( - (net: { chainId: string }) => connectedNetworks.indexOf(net.chainId) !== -1, - ); - - const hostName = getURLHost(securedOrigin); - const currentTabHasNoAccounts = - !permittedAccountsByOrigin[activeTabOrigin]?.length; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let tabToConnect: { origin: any } = { origin: null }; // origin could be null or a string based on the connection status or screen view - if (activeTabOrigin && currentTabHasNoAccounts && !openMetaMaskTabs[id]) { - tabToConnect = { - origin: activeTabOrigin, - }; - } - - const requestAccountsAndChainPermissions = async () => { - const requestId = await dispatch( - requestAccountsAndChainPermissionsWithId(tabToConnect.origin), - ); - history.push(`${CONNECT_ROUTE}/${requestId}`); - }; - const disconnectAllAccounts = () => { + const disconnectAllPermissions = () => { const subject = (subjects as SubjectsType)[activeTabOrigin]; if (subject) { @@ -131,8 +95,75 @@ export const ReviewPermissions = () => { dispatch(removePermissionsFor(permissionsRecord)); } } + + setShowDisconnectAllModal(true); + }; + + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); + const [nonTestNetworks, testNetworks] = useMemo( + () => + Object.entries(networkConfigurations).reduce( + ([nonTestNetworksList, testNetworksList], [chainId, network]) => { + const isTest = (TEST_CHAINS as string[]).includes(chainId); + (isTest ? testNetworksList : nonTestNetworksList).push(network); + return [nonTestNetworksList, testNetworksList]; + }, + [[] as NetworkConfiguration[], [] as NetworkConfiguration[]], + ), + [networkConfigurations], + ); + const connectedChainIds = useSelector((state) => + getPermittedChainsForSelectedTab(state, activeTabOrigin), + ) as string[]; + + const handleSelectChainIds = async (chainIds: string[]) => { + if (chainIds.length === 0) { + setShowDisconnectAllModal(true); + return; + } + + await grantPermittedChains(activeTabOrigin, chainIds); + + connectedChainIds.forEach((chainId: string) => { + if (!chainIds.includes(chainId)) { + dispatch(removePermittedChain(activeTabOrigin, chainId)); + } + }); + + setShowNetworkToast(true); + }; + + const accounts = useSelector(getUpdatedAndSortedAccounts); + const internalAccounts = useSelector(getInternalAccounts); + const mergedAccounts: MergedInternalAccount[] = useMemo(() => { + return mergeAccounts(accounts, internalAccounts).filter( + (account: InternalAccount) => isEvmAccountType(account.type), + ); + }, [accounts, internalAccounts]); + + const connectedAccountAddresses = useSelector((state) => + getPermittedAccountsForSelectedTab(state, activeTabOrigin), + ) as string[]; + + const handleSelectAccountAddresses = (addresses: string[]) => { + if (addresses.length === 0) { + setShowDisconnectAllModal(true); + return; + } + + dispatch(addMorePermittedAccounts(activeTabOrigin, addresses)); + + connectedAccountAddresses.forEach((address: string) => { + if (!addresses.includes(address)) { + dispatch(removePermittedAccount(activeTabOrigin, address)); + } + }); + + setShowAccountToast(true); }; + const hostName = getURLHost(securedOrigin); + return ( { connectedSubjectsMetadata={connectedSubjectsMetadata} /> - {connectedAccounts.length > 0 ? ( + {connectedAccountAddresses.length > 0 ? ( setShowAccountToast(true)} - onNetworksClick={() => setShowNetworkToast(true)} - onDisconnectClick={() => setShowDisconnectAllModal(true)} + nonTestNetworks={nonTestNetworks} + testNetworks={testNetworks} + accounts={mergedAccounts} + onSelectAccountAddresses={handleSelectAccountAddresses} + onSelectChainIds={handleSelectChainIds} + selectedAccountAddresses={connectedAccountAddresses} + selectedChainIds={connectedChainIds} activeTabOrigin={activeTabOrigin} - combinedNetworks={networksList} - approvedAccounts={[]} /> ) : ( @@ -164,7 +195,7 @@ export const ReviewPermissions = () => { hostname={activeTabOrigin} onClose={() => setShowDisconnectAllModal(false)} onClick={() => { - disconnectAllAccounts(); + disconnectAllPermissions(); setShowDisconnectAllModal(false); }} /> @@ -172,7 +203,7 @@ export const ReviewPermissions = () => {
<> - {connectedAccounts.length > 0 ? ( + {connectedAccountAddresses.length > 0 ? ( { variant={ButtonVariant.Secondary} startIconName={IconName.Logout} danger - onClick={disconnectAllAccounts} + onClick={() => setShowDisconnectAllModal(true)} > {t('disconnect')} diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/__snapshots__/site-cell-connection-list-item.test.js.snap b/ui/components/multichain/pages/review-permissions-page/site-cell/__snapshots__/site-cell-connection-list-item.test.js.snap new file mode 100644 index 000000000000..623b5dd9ccf5 --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/__snapshots__/site-cell-connection-list-item.test.js.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SiteCellConnectionListItem renders correctly with required props 1`] = ` +
+ + +
+`; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js index b3fff0836ae5..7cc8f9279791 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js @@ -29,7 +29,7 @@ export const SiteCellConnectionListItem = ({ iconName, connectedMessage, unconnectedMessage, - currentTabHasNoAccounts, + isConnectFlow, onClick, content, }) => { @@ -76,12 +76,12 @@ export const SiteCellConnectionListItem = ({ variant={TextVariant.bodyMd} ellipsis > - {currentTabHasNoAccounts ? unconnectedMessage : connectedMessage} + {isConnectFlow ? unconnectedMessage : connectedMessage} {content}
- {currentTabHasNoAccounts ? ( + {isConnectFlow ? ( onClick()}>{t('edit')} ) : ( { + it('renders correctly with required props', () => { + const { container } = render( + null} + content={
Content
} + /> + ); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js index ce05c7cbb300..f0d9d0783311 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-tooltip.js @@ -25,13 +25,9 @@ import { } from '../../../../component-library'; import { getUseBlockie } from '../../../../../selectors'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; +import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../../../shared/constants/network'; -export const SiteCellTooltip = ({ - accounts, - avatarAccountsData, - networks, - avatarNetworksData, -}) => { +export const SiteCellTooltip = ({ accounts, networks }) => { const t = useI18nContext(); const AVATAR_GROUP_LIMIT = 4; const TOOLTIP_LIMIT = 4; @@ -40,6 +36,15 @@ export const SiteCellTooltip = ({ ? AvatarAccountVariant.Blockies : AvatarAccountVariant.Jazzicon; + const avatarAccountsData = accounts?.map((account) => ({ + avatarValue: account.address, + })); + + const avatarNetworksData = networks?.map((network) => ({ + avatarValue: CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[network.chainId], + symbol: network.name, + })); + return ( - {network.nickname} + {network.name}
); @@ -136,18 +141,19 @@ export const SiteCellTooltip = ({ theme="dark" tag="div" > - {accounts?.length > 0 ? ( + {accounts?.length > 0 && ( - ) : ( + )} + {networks?.length > 0 && ( )} @@ -168,36 +174,13 @@ SiteCellTooltip.propTypes = { }), ), - /** - * Data for the avatar group component related to accounts. - * This array contains account avatar data to be rendered in the group. - */ - avatarAccountsData: PropTypes.arrayOf( - PropTypes.shape({ - address: PropTypes.string, // The account address to display. - }), - ), - /** * An array of network objects to display in the tooltip. */ networks: PropTypes.arrayOf( PropTypes.shape({ chainId: PropTypes.string, // The unique chain ID of the network. - nickname: PropTypes.string, // The network's name. - rpcPrefs: PropTypes.shape({ - imageUrl: PropTypes.string, // Optional URL for the network's image. - }), - }), - ), - - /** - * Data for the avatar group component related to networks. - */ - avatarNetworksData: PropTypes.arrayOf( - PropTypes.shape({ name: PropTypes.string, // The network's name. - avatarUrl: PropTypes.string, // Optional URL for the network's avatar image. }), ), }; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index 3747b359e47c..a5b393a87ad6 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; import { BorderColor } from '../../../../../helpers/constants/design-system'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { @@ -8,76 +7,66 @@ import { IconName, } from '../../../../component-library'; import { EditAccountsModal, EditNetworksModal } from '../../..'; -import { getPermittedAccountsByOrigin } from '../../../../../selectors/permissions'; -import { AccountType } from '../../../connect-accounts-modal/connect-account-modal.types'; +import { MergedInternalAccount } from '../../../../../selectors/selectors.types'; import { SiteCellTooltip } from './site-cell-tooltip'; import { SiteCellConnectionListItem } from './site-cell-connection-list-item'; // Define types for networks, accounts, and other props type Network = { - rpcPrefs?: { imageUrl?: string }; - nickname: string; - chainId?: string; + name: string; + chainId: string; }; type SiteCellProps = { - networks: Network[]; - accounts: AccountType[]; - onAccountsClick: () => void; - onNetworksClick: () => void; - onDisconnectClick: () => void; - approvedAccounts: string[]; + nonTestNetworks: Network[]; + testNetworks: Network[]; + accounts: MergedInternalAccount[]; + onSelectAccountAddresses: (addresses: string[]) => void; + onSelectChainIds: (chainIds: string[]) => void; + selectedAccountAddresses: string[]; + selectedChainIds: string[]; activeTabOrigin: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - combinedNetworks: any; + isConnectFlow?: boolean; }; export const SiteCell: React.FC = ({ - networks, + nonTestNetworks, + testNetworks, accounts, - onAccountsClick, - onNetworksClick, - approvedAccounts, + onSelectAccountAddresses, + onSelectChainIds, + selectedAccountAddresses, + selectedChainIds, activeTabOrigin, - combinedNetworks, - onDisconnectClick, + isConnectFlow, }) => { const t = useI18nContext(); - // Map networks and accounts to avatar data - const avatarNetworksData = networks.map((network) => ({ - avatarValue: network.rpcPrefs?.imageUrl || '', - symbol: network.nickname, - })); - const avatarAccountsData = accounts.map((account) => ({ - avatarValue: account.address, - })); + const allNetworks = [...nonTestNetworks, ...testNetworks]; const [showEditAccountsModal, setShowEditAccountsModal] = useState(false); const [showEditNetworksModal, setShowEditNetworksModal] = useState(false); + const selectedAccounts = accounts.filter(({ address }) => + selectedAccountAddresses.includes(address), + ); + const selectedNetworks = allNetworks.filter(({ chainId }) => + selectedChainIds.includes(chainId), + ); + // Determine the messages for connected and not connected states const accountMessageConnectedState = - accounts.length > 1 - ? t('connectedWith') - : t('connectedWithAccount', [ - accounts[0].label || accounts[0].metadata.name, - ]); + selectedAccounts.length === 1 + ? t('connectedWithAccount', [ + selectedAccounts[0].label || selectedAccounts[0].metadata.name, + ]) + : t('connectedWith'); const accountMessageNotConnectedState = - accounts.length > 1 - ? t('requestingFor') - : t('requestingForAccount', [ - accounts[0].label || accounts[0].metadata.name, - ]); - - // Use selector to get permitted accounts by origin - const permittedAccountsByOrigin = useSelector( - getPermittedAccountsByOrigin, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ) as { [key: string]: any[] }; - const currentTabHasNoAccounts = - !permittedAccountsByOrigin[activeTabOrigin]?.length; + selectedAccounts.length === 1 + ? t('requestingForAccount', [ + selectedAccounts[0].label || selectedAccounts[0].metadata.name, + ]) + : t('requestingFor'); return ( <> @@ -86,57 +75,49 @@ export const SiteCell: React.FC = ({ iconName={IconName.Wallet} connectedMessage={accountMessageConnectedState} unconnectedMessage={accountMessageNotConnectedState} - currentTabHasNoAccounts={currentTabHasNoAccounts} + isConnectFlow={isConnectFlow} onClick={() => setShowEditAccountsModal(true)} content={ - accounts.length > 1 ? ( - - ) : ( + // Why this difference? + selectedAccounts.length === 1 ? ( + ) : ( + ) } /> - setShowEditNetworksModal(true)} - content={ - - } + content={} /> - {showEditNetworksModal && ( - setShowEditNetworksModal(false)} - onClick={onNetworksClick} - currentTabHasNoAccounts={currentTabHasNoAccounts} - combinedNetworks={combinedNetworks} - onDisconnectClick={onDisconnectClick} - /> - )} - {showEditAccountsModal && ( setShowEditAccountsModal(false)} - onClick={onAccountsClick} - approvedAccounts={approvedAccounts} + onSubmit={onSelectAccountAddresses} + /> + )} + + {showEditNetworksModal && ( + setShowEditNetworksModal(false)} + onSubmit={onSelectChainIds} /> )} diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts index 1404275f69cf..a2e553f34012 100644 --- a/ui/ducks/app/app.ts +++ b/ui/ducks/app/app.ts @@ -36,8 +36,6 @@ type AppState = { }; showPermittedNetworkToastOpen: boolean; showIpfsModalOpen: boolean; - selectedAccountsForDappConnection: object; - selectedNetworksForDappConnection: object; keyringRemovalSnapModal: { snapName: string; result: 'success' | 'failure' | 'none'; @@ -132,8 +130,6 @@ const initialState: AppState = { importNftsModal: { open: false }, showPermittedNetworkToastOpen: false, showIpfsModalOpen: false, - selectedAccountsForDappConnection: {}, - selectedNetworksForDappConnection: {}, showBasicFunctionalityModal: false, externalServicesOnboardingToggleState: true, keyringRemovalSnapModal: { @@ -553,18 +549,6 @@ export default function reduceApp( requestAccountTabs: action.value, }; - case actionConstants.SET_SELECTED_ACCOUNTS_FOR_DAPP_CONNECTIONS: - return { - ...appState, - selectedAccountsForDappConnection: action.payload, - }; - - case actionConstants.SET_SELECTED_NETWORKS_FOR_DAPP_CONNECTIONS: - return { - ...appState, - selectedNetworksForDappConnection: action.payload, - }; - case actionConstants.SET_OPEN_METAMASK_TAB_IDS: return { ...appState, diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index a14a005a6c69..80b2375343fe 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -1,15 +1,13 @@ -import React from 'react'; +import React, { useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; -import { InternalAccount } from '@metamask/keyring-api'; +import { InternalAccount, isEvmAccountType } from '@metamask/keyring-api'; +import { NetworkConfiguration } from '@metamask/network-controller'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { - getNonTestNetworks, - getOrderedConnectedAccountsForConnectedDapp, - getPermittedChainsForSelectedTab, - getSelectedAccountsForDappConnection, + getInternalAccounts, + getNetworkConfigurationsByChainId, getSelectedInternalAccount, - getSelectedNetworksForDappConnection, - getTestNetworks, + getUpdatedAndSortedAccounts, } from '../../../selectors'; import { Box, @@ -30,7 +28,9 @@ import { Display, TextVariant, } from '../../../helpers/constants/design-system'; -import { AccountType } from '../../../components/multichain/pages/review-permissions-page/review-permission.types'; +import { MergedInternalAccount } from '../../../selectors/selectors.types'; +import { mergeAccounts } from '../../../components/multichain/account-list-menu/account-list-menu'; +import { TEST_CHAINS } from '../../../../shared/constants/network'; type Request = { id: string; @@ -44,9 +44,6 @@ type ConnectPageProps = { permissionsRequestId: string; rejectPermissionsRequest: (id: string) => void; approveConnection: (request: Request) => void; - accounts: AccountType[]; - selectAccounts: (addresses: string[]) => void; - selectedAccountAddresses: Set; activeTabOrigin: string; }; @@ -55,74 +52,51 @@ export const ConnectPage: React.FC = ({ permissionsRequestId, rejectPermissionsRequest, approveConnection, - accounts, - selectAccounts, - selectedAccountAddresses, activeTabOrigin, }) => { const t = useI18nContext(); - // Get networks and accounts from Redux - const networksList = useSelector(getNonTestNetworks); - const selectedNetworksList = useSelector( - getSelectedNetworksForDappConnection, + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); + const [nonTestNetworks, testNetworks] = useMemo( + () => + Object.entries(networkConfigurations).reduce( + ([nonTestNetworksList, testNetworksList], [chainId, network]) => { + const isTest = (TEST_CHAINS as string[]).includes(chainId); + (isTest ? testNetworksList : nonTestNetworksList).push(network); + return [nonTestNetworksList, testNetworksList]; + }, + [[] as NetworkConfiguration[], [] as NetworkConfiguration[]], + ), + [networkConfigurations], ); - const testNetworks = useSelector(getTestNetworks); - const combinedNetworks = [...networksList, ...testNetworks]; - - const currentAccount = useSelector(getSelectedInternalAccount); - const connectedAccounts = useSelector((state) => - getOrderedConnectedAccountsForConnectedDapp(state, activeTabOrigin), - ) as AccountType[]; - const connectedAccountsAddresses = connectedAccounts?.map( - (account: InternalAccount) => account.address, - ); - const connectedNetworks = useSelector((state) => - getPermittedChainsForSelectedTab(state, activeTabOrigin), - ) as string[]; - const selectedAccountsForDappConnection = useSelector( - getSelectedAccountsForDappConnection, + const defaultSelectedChainIds = nonTestNetworks.map(({ chainId }) => chainId); + const [selectedChainIds, setSelectedChainIds] = useState( + defaultSelectedChainIds, ); - const grantedNetworks = combinedNetworks.filter( - (net: { chainId: string }) => - connectedNetworks?.indexOf(net.chainId) !== -1, - ); - const defaultAccountsAddresses = - connectedAccounts?.length > 0 - ? connectedAccountsAddresses - : [currentAccount?.address]; - - const defaultNetworksList = - grantedNetworks?.length > 0 ? grantedNetworks : networksList; - // Filter networks based on chainId - const filteredNetworks = Array.isArray(selectedNetworksList) - ? combinedNetworks.filter((network) => - selectedNetworksList.includes(network.chainId), - ) - : defaultNetworksList; + const accounts = useSelector(getUpdatedAndSortedAccounts); + const internalAccounts = useSelector(getInternalAccounts); + const mergedAccounts: MergedInternalAccount[] = useMemo(() => { + return mergeAccounts(accounts, internalAccounts).filter( + (account: InternalAccount) => isEvmAccountType(account.type), + ); + }, [accounts, internalAccounts]); - // Select approved accounts and networks - const approvedAccounts = - selectedAccountsForDappConnection.length > 0 - ? selectedAccountsForDappConnection - : defaultAccountsAddresses; + const currentAccount = useSelector(getSelectedInternalAccount); + const defaultAccountsAddresses = [currentAccount?.address]; + const [selectedAccountAddresses, setSelectedAccountAddresses] = useState( + defaultAccountsAddresses, + ); - // Handle confirmation const onConfirm = () => { const _request = { ...request, - approvedAccounts, - approvedChainIds: filteredNetworks.map((network) => network.chainId), + approvedAccounts: selectedAccountAddresses, + approvedChainIds: selectedChainIds, }; approveConnection(_request); }; - // Filter accounts by address - const filterAccountsByAddress = accounts.filter((account) => - approvedAccounts.includes(account.address), - ); - return ( = ({
- selectAccounts(Array.from(selectedAccountAddresses)) - } - onNetworksClick={() => null} - approvedAccounts={approvedAccounts} + nonTestNetworks={nonTestNetworks} + testNetworks={testNetworks} + accounts={mergedAccounts} + onSelectAccountAddresses={setSelectedAccountAddresses} + onSelectChainIds={setSelectedChainIds} + selectedAccountAddresses={selectedAccountAddresses} + selectedChainIds={selectedChainIds} activeTabOrigin={activeTabOrigin} - combinedNetworks={defaultNetworksList} - onDisconnectClick={() => null} + isConnectFlow />
@@ -162,6 +135,10 @@ export const ConnectPage: React.FC = ({ data-testid="confirm-btn" size={ButtonSize.Lg} onClick={onConfirm} + disabled={ + selectedAccountAddresses.length === 0 || + selectedChainIds.length === 0 + } > {t('confirm')} diff --git a/ui/pages/permissions-connect/permissions-connect.component.js b/ui/pages/permissions-connect/permissions-connect.component.js index 74e1b4cda276..5a3b85d4db7d 100644 --- a/ui/pages/permissions-connect/permissions-connect.component.js +++ b/ui/pages/permissions-connect/permissions-connect.component.js @@ -365,7 +365,6 @@ export default class PermissionConnect extends Component { render={() => process.env.CHAIN_PERMISSIONS && !permissionsRequest?.diff ? ( this.cancelPermissionsRequest(requestId) } @@ -373,10 +372,6 @@ export default class PermissionConnect extends Component { request={permissionsRequest} permissionsRequestId={permissionsRequestId} approveConnection={this.approveConnection} - selectAccounts={(addresses) => - this.selectAccounts(addresses) - } - selectedAccountAddresses={selectedAccountAddresses} /> ) : ( { return await submitRequestToBackground('grantPermissions', [ { From 3ccc81a62e54fd9fd817724a8f037009cf54e5fa Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Mon, 23 Sep 2024 13:51:56 +0100 Subject: [PATCH 082/125] story fix site cell --- .../site-cell/site-cell.stories.tsx | 69 ++++++++++++++----- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx index ae15a8dc1099..23618fe54985 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.stories.tsx @@ -1,13 +1,13 @@ import React from 'react'; -import { ARBITRUM_NOVA_IMAGE_URL } from '../../../../../../shared/constants/network'; import { SiteCell } from './site-cell'; export default { title: 'Components/Multichain/SiteCell', - components: SiteCell, + component: SiteCell, argTypes: { accounts: { control: 'array' }, - networks: { control: 'array' }, + nonTestNetworks: { control: 'array' }, + testNetworks: { control: 'array' }, }, args: { accounts: [ @@ -35,19 +35,56 @@ export default { balance: '0x00', }, ], - networks: [ + selectedAccountAddresses: ['0x860092756917d3e069926ba130099375eeeb9440'], + selectedChainIds: ['0x1', '0xe708', '0x144', '0x89', '0x38'], + activeTabOrigin: 'https://app.uniswap.org', + nonTestNetworks: [ { - rpcUrl: 'https://arb1.arbitrum.io/rpc', - chainId: '0xa4b1', - ticker: 'ETH', - nickname: 'Arbitrum One', - rpcPrefs: { - blockExplorerUrl: 'https://arbiscan.io', - imageUrl: ARBITRUM_NOVA_IMAGE_URL, - }, - id: 'f8f98123-f3ae-418c-b1e7-d08f057f395c', - blockExplorerUrl: 'https://arbiscan.io', - removable: true, + chainId: '0x1', + rpcEndpoints: [ + { + networkClientId: 'mainnet', + url: 'https://mainnet.infura.io/v3/{infuraProjectId}', + type: 'infura', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://etherscan.io'], + defaultBlockExplorerUrlIndex: 0, + name: 'Ethereum Mainnet', + nativeCurrency: 'ETH', + }, + ], + testNetworks: [ + { + chainId: '0xaa36a7', + rpcEndpoints: [ + { + networkClientId: 'sepolia', + url: 'https://sepolia.infura.io/v3/{infuraProjectId}', + type: 'infura', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://sepolia.etherscan.io'], + defaultBlockExplorerUrlIndex: 0, + name: 'Sepolia', + nativeCurrency: 'SepoliaETH', + }, + { + chainId: '0xe705', + rpcEndpoints: [ + { + networkClientId: 'linea-sepolia', + url: 'https://linea-sepolia.infura.io/v3/{infuraProjectId}', + type: 'infura', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://sepolia.lineascan.build'], + defaultBlockExplorerUrlIndex: 0, + name: 'Linea Sepolia', + nativeCurrency: 'LineaETH', }, ], }, @@ -55,4 +92,4 @@ export default { export const DefaultStory = (args) => ; -DefaultStory.storyName = 'Default'; +DefaultStory.storyName = 'Default'; \ No newline at end of file From b9b9a8f8fcb861c9ce23c19b64bfec0fb6efa5ab Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Mon, 23 Sep 2024 14:02:05 +0100 Subject: [PATCH 083/125] fixed story for accounts --- .../edit-accounts-modal.stories.tsx | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx index b25b4423d86d..32c1a6b08595 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.stories.tsx @@ -12,6 +12,7 @@ export default { argTypes: { onClose: { action: 'onClose' }, activeTabOrigin: { control: 'text' }, + accounts: { control: 'array' }, }, args: { onClose: () => undefined, @@ -20,6 +21,34 @@ export default { approvedAccounts: [], activeTabOrigin: 'https://test.dapp', currentTabHasNoAccounts: false, + defaultSelectedAccountAddresses: [ + '0x860092756917d3e069926ba130099375eeeb9440', + ], + accounts: [ + { + id: '689821df-0e8f-4093-bbbb-b95cf0fa79cb', + address: '0x860092756917d3e069926ba130099375eeeb9440', + options: {}, + methods: [ + 'personal_sign', + 'eth_sign', + 'eth_signTransaction', + 'eth_signTypedData_v1', + 'eth_signTypedData_v3', + 'eth_signTypedData_v4', + ], + type: 'eip155:eoa', + metadata: { + name: 'Account 1', + importTime: 1726046726882, + keyring: { + type: 'HD Key Tree', + }, + lastSelected: 1726046726882, + }, + balance: '0x00', + }, + ], }, }; From a9f44fe1e25f746bc2716a2e5094e90d3dc3a6d5 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Mon, 23 Sep 2024 14:11:43 +0100 Subject: [PATCH 084/125] fixed test for edit networks --- .../edit-networks-modal.stories.js | 89 +++++++++---------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js index d8b624e78b3e..160ab4323e7b 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.stories.js @@ -5,65 +5,56 @@ export default { title: 'Components/Multichain/EditNetworksModal', component: EditNetworksModal, argTypes: { - combinedNetworks: { + nonTestNetworks: { + control: 'array', + }, + testNetworks: { + control: 'array', + }, + activeTabOrigin: { + control: 'text', + }, + defaultSelectedChainIds: { control: 'array', }, }, args: { - combinedNetworks: [ + nonTestNetworks: [ { chainId: '0x1', - nickname: 'Ethereum Mainnet', - rpcUrl: 'https://mainnet.infura.io/v3/', - rpcPrefs: { - imageUrl: './images/eth_logo.svg', - }, - providerType: 'mainnet', - ticker: 'ETH', - id: 'mainnet', - removable: false, - blockExplorerUrl: 'https://etherscan.io', - }, - { - chainId: '0xe708', - nickname: 'Linea Mainnet', - rpcUrl: 'https://linea-mainnet.infura.io/v3/', - rpcPrefs: { - imageUrl: './images/linea-logo-mainnet.svg', - }, - providerType: 'linea-mainnet', - ticker: 'ETH', - id: 'linea-mainnet', - removable: false, - blockExplorerUrl: 'https://lineascan.build', - }, - { - rpcUrl: 'https://mainnet.base.org', - chainId: '0x2105', - ticker: 'ETH', - nickname: 'Base', - rpcPrefs: { - blockExplorerUrl: 'https://basescan.org', - imageUrl: './images/base.svg', - }, - id: '61bb70f8-6583-42aa-8547-ef6c044614ad', - blockExplorerUrl: 'https://basescan.org', - removable: true, + rpcEndpoints: [ + { + networkClientId: 'mainnet', + url: 'https://mainnet.infura.io/v3/{infuraProjectId}', + type: 'infura', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://etherscan.io'], + defaultBlockExplorerUrlIndex: 0, + name: 'Ethereum Mainnet', + nativeCurrency: 'ETH', }, + ], + testNetworks: [ { - rpcUrl: 'https://arb1.arbitrum.io/rpc', - chainId: '0xa4b1', - ticker: 'ETH', - nickname: 'Arbitrum One', - rpcPrefs: { - blockExplorerUrl: 'https://arbiscan.io', - imageUrl: './images/arbitrum.svg', - }, - id: 'f8f98123-f3ae-418c-b1e7-d08f057f395c', - blockExplorerUrl: 'https://arbiscan.io', - removable: true, + chainId: '0xaa36a7', + rpcEndpoints: [ + { + networkClientId: 'sepolia', + url: 'https://sepolia.infura.io/v3/{infuraProjectId}', + type: 'infura', + }, + ], + defaultRpcEndpointIndex: 0, + blockExplorerUrls: ['https://sepolia.etherscan.io'], + defaultBlockExplorerUrlIndex: 0, + name: 'Sepolia', + nativeCurrency: 'SepoliaETH', }, ], + activeTabOrigin: 'https://app.uniswap.org', + defaultSelectedChainIds: ['0x1'], }, }; From b08fb0e64cffc7372e18dd66d08c8e13fed209c4 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Mon, 23 Sep 2024 14:13:25 +0100 Subject: [PATCH 085/125] updated lint fix --- .../site-cell/site-cell-connection-list-item.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.test.js b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.test.js index 720caa01a276..ceb9288d8dc1 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.test.js +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.test.js @@ -1,7 +1,7 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { SiteCellConnectionListItem } from './site-cell-connection-list-item'; import { IconName } from '../../../../component-library'; +import { SiteCellConnectionListItem } from './site-cell-connection-list-item'; describe('SiteCellConnectionListItem', () => { it('renders correctly with required props', () => { @@ -14,7 +14,7 @@ describe('SiteCellConnectionListItem', () => { isConnectFlow onClick={() => null} content={
Content
} - /> + />, ); expect(container).toMatchSnapshot(); }); From 28cb6815b961e9838e4a44238ebb7a1d214a80d6 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Mon, 23 Sep 2024 14:33:00 +0100 Subject: [PATCH 086/125] updated copy for disconnect modal --- app/_locales/en/messages.json | 4 ++++ .../disconnect-all-modal/disconnect-all-modal.tsx | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 388dfa070ea1..97deefd38a32 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1605,6 +1605,10 @@ "disconnectAllAccountsText": { "message": "accounts" }, + "disconnectAllDescription": { + "message": "If you disconnect from $1, you’ll need to reconnect your accounts and networks to use this site again.", + "description": "$1 represents the website hostname" + }, "disconnectAllSnapsText": { "message": "Snaps" }, diff --git a/ui/components/multichain/disconnect-all-modal/disconnect-all-modal.tsx b/ui/components/multichain/disconnect-all-modal/disconnect-all-modal.tsx index 34762eb4a128..68f80b873992 100644 --- a/ui/components/multichain/disconnect-all-modal/disconnect-all-modal.tsx +++ b/ui/components/multichain/disconnect-all-modal/disconnect-all-modal.tsx @@ -36,10 +36,16 @@ export const DisconnectAllModal = ({ - {t('disconnectAllTitle', [t(type)])} + {process.env.CHAIN_PERMISSION + ? t('disconnect') + : t('disconnectAllTitle', [t(type)])} - {t('disconnectAllText', [t(type), hostname])} + {process.env.CHAIN_PERMISSION ? ( + {t('disconnectAllDescription', [hostname])} + ) : ( + {t('disconnectAllText', [t(type), hostname])} + )} +
+
+

+

+ + +
+

+
+
+
+
+

+ MetaMask isn’t connected to this site +

+

+ Select an account you want to use on this site to continue. +

+
+
+ + + + +`; diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx new file mode 100644 index 000000000000..9249c9ea1ebc --- /dev/null +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { renderWithProvider } from '../../../../../test/jest/rendering'; +import mockState from '../../../../../test/data/mock-state.json'; +import configureStore from '../../../../store/store'; +import { ReviewPermissions } from '.'; + +const render = ( + state = {}, +) => { + const store = configureStore({ + ...mockState, + metamask: { + ...mockState.metamask, + ...state, + permissionHistory: { + 'https://test.dapp': { + eth_accounts: { + accounts: { + '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': 1709225290848, + }, + }, + }, + }, + }, + activeTab: { + origin: 'https://test.dapp', + }, + }); + return renderWithProvider( + , + store, + ); +}; +describe('ReviewPermissions', () => { + it('should render correctly', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/ui/pages/permissions-connect/connect-page/__snapshots__/connect-page.test.tsx.snap b/ui/pages/permissions-connect/connect-page/__snapshots__/connect-page.test.tsx.snap new file mode 100644 index 000000000000..8a16e689d725 --- /dev/null +++ b/ui/pages/permissions-connect/connect-page/__snapshots__/connect-page.test.tsx.snap @@ -0,0 +1,221 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ConnectPage should render correctly 1`] = ` +
+
+
+
+
+

+

+ Connect with MetaMask +

+

+ This site wants to + : +

+

+
+
+
+ + + + +
+ +
+
+
+`; diff --git a/ui/pages/permissions-connect/connect-page/connect-page.test.tsx b/ui/pages/permissions-connect/connect-page/connect-page.test.tsx new file mode 100644 index 000000000000..380ae7221343 --- /dev/null +++ b/ui/pages/permissions-connect/connect-page/connect-page.test.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { renderWithProvider } from '../../../../test/jest/rendering'; +import mockState from '../../../../test/data/mock-state.json'; +import configureStore from '../../../store/store'; +import { ConnectPage, Request } from './connect-page'; + +const render = ( + props: { + request: Request + permissionsRequestId: string, + rejectPermissionsRequest: (id: string) => void; + approveConnection: (request: Request) => void; + activeTabOrigin: string; + } = { + request: { + id: "1", + origin: "https://test.dapp" + }, + permissionsRequestId: "1", + rejectPermissionsRequest: jest.fn(), + approveConnection: jest.fn(), + activeTabOrigin: 'https://test.dapp', + }, + state = {}, +) => { + const store = configureStore({ + ...mockState, + metamask: { + ...mockState.metamask, + ...state, + permissionHistory: { + 'https://test.dapp': { + eth_accounts: { + accounts: { + '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': 1709225290848, + }, + }, + }, + }, + }, + activeTab: { + origin: 'https://test.dapp', + }, + }); + return renderWithProvider( + , + store, + ); +}; +describe('ConnectPage', () => { + it('should render correctly', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index 80b2375343fe..4f25cd019388 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -32,7 +32,7 @@ import { MergedInternalAccount } from '../../../selectors/selectors.types'; import { mergeAccounts } from '../../../components/multichain/account-list-menu/account-list-menu'; import { TEST_CHAINS } from '../../../../shared/constants/network'; -type Request = { +export type Request = { id: string; origin: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any From 3833009a946c02b736b85069b6cbc0538989f14f Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 23 Sep 2024 11:35:50 -0700 Subject: [PATCH 090/125] Rename Request type. lint --- .../review-permissions-page.test.tsx | 10 ++------- .../connect-page/connect-page.test.tsx | 21 +++++++------------ .../connect-page/connect-page.tsx | 6 +++--- 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx index 9249c9ea1ebc..b644c16b6440 100644 --- a/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx +++ b/ui/components/multichain/pages/review-permissions-page/review-permissions-page.test.tsx @@ -4,9 +4,7 @@ import mockState from '../../../../../test/data/mock-state.json'; import configureStore from '../../../../store/store'; import { ReviewPermissions } from '.'; -const render = ( - state = {}, -) => { +const render = (state = {}) => { const store = configureStore({ ...mockState, metamask: { @@ -26,11 +24,7 @@ const render = ( origin: 'https://test.dapp', }, }); - return renderWithProvider( - , - store, - ); + return renderWithProvider(, store); }; describe('ReviewPermissions', () => { it('should render correctly', () => { diff --git a/ui/pages/permissions-connect/connect-page/connect-page.test.tsx b/ui/pages/permissions-connect/connect-page/connect-page.test.tsx index 380ae7221343..425dba1e1696 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.test.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.test.tsx @@ -2,21 +2,21 @@ import React from 'react'; import { renderWithProvider } from '../../../../test/jest/rendering'; import mockState from '../../../../test/data/mock-state.json'; import configureStore from '../../../store/store'; -import { ConnectPage, Request } from './connect-page'; +import { ConnectPage, ConnectPageRequest } from './connect-page'; const render = ( props: { - request: Request - permissionsRequestId: string, + request: ConnectPageRequest; + permissionsRequestId: string; rejectPermissionsRequest: (id: string) => void; - approveConnection: (request: Request) => void; + approveConnection: (request: ConnectPageRequest) => void; activeTabOrigin: string; } = { request: { - id: "1", - origin: "https://test.dapp" + id: '1', + origin: 'https://test.dapp', }, - permissionsRequestId: "1", + permissionsRequestId: '1', rejectPermissionsRequest: jest.fn(), approveConnection: jest.fn(), activeTabOrigin: 'https://test.dapp', @@ -42,12 +42,7 @@ const render = ( origin: 'https://test.dapp', }, }); - return renderWithProvider( - , - store, - ); + return renderWithProvider(, store); }; describe('ConnectPage', () => { it('should render correctly', () => { diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index 4f25cd019388..784abafd7eb6 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -32,7 +32,7 @@ import { MergedInternalAccount } from '../../../selectors/selectors.types'; import { mergeAccounts } from '../../../components/multichain/account-list-menu/account-list-menu'; import { TEST_CHAINS } from '../../../../shared/constants/network'; -export type Request = { +export type ConnectPageRequest = { id: string; origin: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -40,10 +40,10 @@ export type Request = { }; type ConnectPageProps = { - request: Request; + request: ConnectPageRequest; permissionsRequestId: string; rejectPermissionsRequest: (id: string) => void; - approveConnection: (request: Request) => void; + approveConnection: (request: ConnectPageRequest) => void; activeTabOrigin: string; }; From 9bf45ee4da0541bb23a05f2671da500b4a7b7180 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 23 Sep 2024 11:44:25 -0700 Subject: [PATCH 091/125] Shallow EditNetworksModal test --- .../edit-networks-modal.test.js.snap | 3 + .../edit-networks-modal.test.js | 56 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap create mode 100644 ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js diff --git a/ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap b/ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap new file mode 100644 index 000000000000..caf7146043a0 --- /dev/null +++ b/ui/components/multichain/edit-networks-modal/__snapshots__/edit-networks-modal.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EditNetworksModal should render correctly 1`] = `
`; diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js new file mode 100644 index 000000000000..c97a2d380feb --- /dev/null +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js @@ -0,0 +1,56 @@ +import React from 'react'; +import { renderWithProvider } from '../../../../test/jest/rendering'; +import mockState from '../../../../test/data/mock-state.json'; +import configureStore from '../../../store/store'; +import { EditNetworksModal } from '.'; + +const render = ( + props = { + onClose: () => jest.fn(), + activeTabOrigin: 'https://test.dapp', + }, + state = {}, +) => { + const store = configureStore({ + ...mockState, + metamask: { + ...mockState.metamask, + ...state, + permissionHistory: { + 'https://test.dapp': { + eth_accounts: { + accounts: { + '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': 1709225290848, + }, + }, + }, + }, + }, + activeTab: { + origin: 'https://test.dapp', + }, + }); + return renderWithProvider( + { + throw new Error('Function not implemented.'); + }} + testNetworks={[]} + nonTestNetworks={[]} + defaultSelectedChainIds={[]} + {...props} + />, + store, + ); +}; +describe('EditNetworksModal', () => { + it('should render correctly', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it('shows select all button', async () => { + const { getByLabelText } = render(); + expect(getByLabelText('Select all')).toBeInTheDocument(); + }); +}); From a022cf8acdc8e1b82edb52732197c4b7e2cf3759 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 23 Sep 2024 12:23:31 -0700 Subject: [PATCH 092/125] add actions specs --- ui/store/actions.test.js | 120 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/ui/store/actions.test.js b/ui/store/actions.test.js index 1604f5361eda..0b622c0aeca0 100644 --- a/ui/store/actions.test.js +++ b/ui/store/actions.test.js @@ -13,6 +13,11 @@ import { MetaMetricsNetworkEventSource } from '../../shared/constants/metametric import { ETH_EOA_METHODS } from '../../shared/constants/eth-methods'; import { mockNetworkState } from '../../test/stub/networks'; import { CHAIN_IDS } from '../../shared/constants/network'; +import { + CaveatFactories, + PermissionNames, +} from '../../app/scripts/controllers/permissions'; +import { CaveatTypes } from '../../shared/constants/permissions'; import * as actions from './actions'; import * as actionConstants from './actionConstants'; import { setBackgroundConnection } from './background-connection'; @@ -73,6 +78,12 @@ describe('Actions', () => { background.abortTransactionSigning = sinon.stub(); background.toggleExternalServices = sinon.stub(); background.getStatePatches = sinon.stub().callsFake((cb) => cb(null, [])); + jest.spyOn(background, 'removePermittedChain').mockImplementation(); + jest + .spyOn(background, 'requestAccountsAndChainPermissionsWithId') + .mockImplementation(); + jest.spyOn(background, 'grantPermissions').mockImplementation(); + jest.spyOn(background, 'grantPermissionsIncremental').mockImplementation(); }); describe('#tryUnlockMetamask', () => { @@ -2526,4 +2537,113 @@ describe('Actions', () => { ); }); }); + + describe('removePermittedChain', () => { + it('calls removePermittedChain in the background', async () => { + const store = mockStore(); + + const removePermittedChain = + background.removePermittedChain.mockImplementation((_, __, cb) => cb()); + + setBackgroundConnection(background); + + await store.dispatch(actions.removePermittedChain('test.com', '0x1')); + + expect(removePermittedChain).toHaveBeenCalledWith( + 'test.com', + '0x1', + expect.any(Function), + ); + + expect(store.getActions()).toStrictEqual([]); + }); + }); + + describe('requestAccountsAndChainPermissionsWithId', () => { + it('calls requestAccountsAndChainPermissionsWithId in the background', async () => { + const store = mockStore(); + + const requestAccountsAndChainPermissionsWithId = + background.requestAccountsAndChainPermissionsWithId.mockImplementation( + (_, cb) => cb(), + ); + + setBackgroundConnection(background); + + await store.dispatch( + actions.requestAccountsAndChainPermissionsWithId('test.com'), + ); + + expect(requestAccountsAndChainPermissionsWithId).toHaveBeenCalledWith( + 'test.com', + expect.any(Function), + ); + + expect(store.getActions()).toStrictEqual([]); + }); + }); + + describe('grantPermittedChain', () => { + it('calls grantPermissionsIncremental in the background', async () => { + const store = mockStore(); + + const grantPermissionsIncremental = + background.grantPermissionsIncremental.mockImplementation((_, cb) => + cb(), + ); + + setBackgroundConnection(background); + + await actions.grantPermittedChain('test.com', '0x1'), + expect(grantPermissionsIncremental).toHaveBeenCalledWith( + { + subject: { origin: 'test.com' }, + approvedPermissions: { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]([ + '0x1', + ]), + ], + }, + }, + }, + expect.any(Function), + ); + + expect(store.getActions()).toStrictEqual([]); + }); + }); + + describe('grantPermittedChains', () => { + it('calls grantPermissions in the background', async () => { + const store = mockStore(); + + const grantPermissions = background.grantPermissions.mockImplementation( + (_, cb) => cb(), + ); + + setBackgroundConnection(background); + + await actions.grantPermittedChains('test.com', ['0x1', '0x2']), + expect(grantPermissions).toHaveBeenCalledWith( + { + subject: { origin: 'test.com' }, + approvedPermissions: { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]([ + '0x1', + '0x2', + ]), + ], + }, + }, + }, + expect.any(Function), + ); + + expect(store.getActions()).toStrictEqual([]); + }); + }); }); From 27f434537efed1dd065051f5ea4d578a06251879 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 23 Sep 2024 12:41:41 -0700 Subject: [PATCH 093/125] lint --- ui/store/actions.test.js | 56 +++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/ui/store/actions.test.js b/ui/store/actions.test.js index 0b622c0aeca0..0c3f581c6c22 100644 --- a/ui/store/actions.test.js +++ b/ui/store/actions.test.js @@ -2594,22 +2594,20 @@ describe('Actions', () => { setBackgroundConnection(background); - await actions.grantPermittedChain('test.com', '0x1'), - expect(grantPermissionsIncremental).toHaveBeenCalledWith( - { - subject: { origin: 'test.com' }, - approvedPermissions: { - [PermissionNames.permittedChains]: { - caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching]([ - '0x1', - ]), - ], - }, + await actions.grantPermittedChain('test.com', '0x1'); + expect(grantPermissionsIncremental).toHaveBeenCalledWith( + { + subject: { origin: 'test.com' }, + approvedPermissions: { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching](['0x1']), + ], }, }, - expect.any(Function), - ); + }, + expect.any(Function), + ); expect(store.getActions()).toStrictEqual([]); }); @@ -2625,23 +2623,23 @@ describe('Actions', () => { setBackgroundConnection(background); - await actions.grantPermittedChains('test.com', ['0x1', '0x2']), - expect(grantPermissions).toHaveBeenCalledWith( - { - subject: { origin: 'test.com' }, - approvedPermissions: { - [PermissionNames.permittedChains]: { - caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching]([ - '0x1', - '0x2', - ]), - ], - }, + await actions.grantPermittedChains('test.com', ['0x1', '0x2']); + expect(grantPermissions).toHaveBeenCalledWith( + { + subject: { origin: 'test.com' }, + approvedPermissions: { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]([ + '0x1', + '0x2', + ]), + ], }, }, - expect.any(Function), - ); + }, + expect.any(Function), + ); expect(store.getActions()).toStrictEqual([]); }); From aff9dd3069edf6ab90fc7b16d25ec526ed11e7ff Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 23 Sep 2024 13:27:15 -0700 Subject: [PATCH 094/125] Switch actions.test.js back to sinon --- ui/store/actions.test.js | 130 +++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 60 deletions(-) diff --git a/ui/store/actions.test.js b/ui/store/actions.test.js index 0c3f581c6c22..a391dd1f3e2c 100644 --- a/ui/store/actions.test.js +++ b/ui/store/actions.test.js @@ -78,12 +78,10 @@ describe('Actions', () => { background.abortTransactionSigning = sinon.stub(); background.toggleExternalServices = sinon.stub(); background.getStatePatches = sinon.stub().callsFake((cb) => cb(null, [])); - jest.spyOn(background, 'removePermittedChain').mockImplementation(); - jest - .spyOn(background, 'requestAccountsAndChainPermissionsWithId') - .mockImplementation(); - jest.spyOn(background, 'grantPermissions').mockImplementation(); - jest.spyOn(background, 'grantPermissionsIncremental').mockImplementation(); + background.removePermittedChain = sinon.stub(); + background.requestAccountsAndChainPermissionsWithId = sinon.stub(); + background.grantPermissions = sinon.stub(); + background.grantPermissionsIncremental = sinon.stub(); }); describe('#tryUnlockMetamask', () => { @@ -2539,107 +2537,119 @@ describe('Actions', () => { }); describe('removePermittedChain', () => { + afterEach(() => { + sinon.restore(); + }); + it('calls removePermittedChain in the background', async () => { const store = mockStore(); - const removePermittedChain = - background.removePermittedChain.mockImplementation((_, __, cb) => cb()); - + background.removePermittedChain.callsFake((_, __, cb) => cb()); setBackgroundConnection(background); await store.dispatch(actions.removePermittedChain('test.com', '0x1')); - expect(removePermittedChain).toHaveBeenCalledWith( - 'test.com', - '0x1', - expect.any(Function), - ); - + expect( + background.removePermittedChain.calledWith( + 'test.com', + '0x1', + sinon.match.func, + ), + ).toBe(true); expect(store.getActions()).toStrictEqual([]); }); }); describe('requestAccountsAndChainPermissionsWithId', () => { + afterEach(() => { + sinon.restore(); + }); + it('calls requestAccountsAndChainPermissionsWithId in the background', async () => { const store = mockStore(); - const requestAccountsAndChainPermissionsWithId = - background.requestAccountsAndChainPermissionsWithId.mockImplementation( - (_, cb) => cb(), - ); - + background.requestAccountsAndChainPermissionsWithId.callsFake((_, cb) => + cb(), + ); setBackgroundConnection(background); await store.dispatch( actions.requestAccountsAndChainPermissionsWithId('test.com'), ); - expect(requestAccountsAndChainPermissionsWithId).toHaveBeenCalledWith( - 'test.com', - expect.any(Function), - ); - + expect( + background.requestAccountsAndChainPermissionsWithId.calledWith( + 'test.com', + sinon.match.func, + ), + ).toBe(true); expect(store.getActions()).toStrictEqual([]); }); }); describe('grantPermittedChain', () => { + afterEach(() => { + sinon.restore(); + }); + it('calls grantPermissionsIncremental in the background', async () => { const store = mockStore(); - const grantPermissionsIncremental = - background.grantPermissionsIncremental.mockImplementation((_, cb) => - cb(), - ); - + background.grantPermissionsIncremental.callsFake((_, cb) => cb()); setBackgroundConnection(background); await actions.grantPermittedChain('test.com', '0x1'); - expect(grantPermissionsIncremental).toHaveBeenCalledWith( - { - subject: { origin: 'test.com' }, - approvedPermissions: { - [PermissionNames.permittedChains]: { - caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching](['0x1']), - ], + expect( + background.grantPermissionsIncremental.calledWith( + { + subject: { origin: 'test.com' }, + approvedPermissions: { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]([ + '0x1', + ]), + ], + }, }, }, - }, - expect.any(Function), - ); - + sinon.match.func, + ), + ).toBe(true); expect(store.getActions()).toStrictEqual([]); }); }); describe('grantPermittedChains', () => { + afterEach(() => { + sinon.restore(); + }); + it('calls grantPermissions in the background', async () => { const store = mockStore(); - const grantPermissions = background.grantPermissions.mockImplementation( - (_, cb) => cb(), - ); - + background.grantPermissions.callsFake((_, cb) => cb()); setBackgroundConnection(background); await actions.grantPermittedChains('test.com', ['0x1', '0x2']); - expect(grantPermissions).toHaveBeenCalledWith( - { - subject: { origin: 'test.com' }, - approvedPermissions: { - [PermissionNames.permittedChains]: { - caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching]([ - '0x1', - '0x2', - ]), - ], + expect( + background.grantPermissions.calledWith( + { + subject: { origin: 'test.com' }, + approvedPermissions: { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]([ + '0x1', + '0x2', + ]), + ], + }, }, }, - }, - expect.any(Function), - ); + sinon.match.func, + ), + ).toBe(true); expect(store.getActions()).toStrictEqual([]); }); From dfd85adb18af785451cc6e0c3dc677999aa9dc18 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 23 Sep 2024 13:40:00 -0700 Subject: [PATCH 095/125] add background-api spec --- .../permissions/background-api.test.js | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/app/scripts/controllers/permissions/background-api.test.js b/app/scripts/controllers/permissions/background-api.test.js index b6ba493ba7df..cd3e09edf931 100644 --- a/app/scripts/controllers/permissions/background-api.test.js +++ b/app/scripts/controllers/permissions/background-api.test.js @@ -194,4 +194,95 @@ describe('permission background API methods', () => { ); }); }); + + describe('removePermittedChain', () => { + it('removes a permitted chain', () => { + const permissionController = { + getCaveat: jest.fn().mockImplementationOnce(() => { + return { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1', '0x2'], + }; + }), + revokePermission: jest.fn(), + updateCaveat: jest.fn(), + }; + + getPermissionBackgroundApiMethods( + permissionController, + ).removePermittedChain('foo.com', '0x2'); + + expect(permissionController.getCaveat).toHaveBeenCalledTimes(1); + expect(permissionController.getCaveat).toHaveBeenCalledWith( + 'foo.com', + RestrictedMethods.permittedChains, + CaveatTypes.restrictNetworkSwitching, + ); + + expect(permissionController.revokePermission).not.toHaveBeenCalled(); + + expect(permissionController.updateCaveat).toHaveBeenCalledTimes(1); + expect(permissionController.updateCaveat).toHaveBeenCalledWith( + 'foo.com', + RestrictedMethods.permittedChains, + CaveatTypes.restrictNetworkSwitching, + ['0x1'], + ); + }); + + it('revokes the permittedChains permission if the removed chain is the only permitted chain', () => { + const permissionController = { + getCaveat: jest.fn().mockImplementationOnce(() => { + return { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1'], + }; + }), + revokePermission: jest.fn(), + updateCaveat: jest.fn(), + }; + + getPermissionBackgroundApiMethods( + permissionController, + ).removePermittedChain('foo.com', '0x1'); + + expect(permissionController.getCaveat).toHaveBeenCalledTimes(1); + expect(permissionController.getCaveat).toHaveBeenCalledWith( + 'foo.com', + RestrictedMethods.permittedChains, + CaveatTypes.restrictNetworkSwitching, + ); + + expect(permissionController.revokePermission).toHaveBeenCalledTimes(1); + expect(permissionController.revokePermission).toHaveBeenCalledWith( + 'foo.com', + RestrictedMethods.permittedChains, + ); + + expect(permissionController.updateCaveat).not.toHaveBeenCalled(); + }); + + it('does not call permissionController.updateCaveat if the specified chain is not permitted', () => { + const permissionController = { + getCaveat: jest.fn().mockImplementationOnce(() => { + return { type: CaveatTypes.restrictNetworkSwitching, value: ['0x1'] }; + }), + revokePermission: jest.fn(), + updateCaveat: jest.fn(), + }; + + getPermissionBackgroundApiMethods( + permissionController, + ).removePermittedChain('foo.com', '0x2'); + expect(permissionController.getCaveat).toHaveBeenCalledTimes(1); + expect(permissionController.getCaveat).toHaveBeenCalledWith( + 'foo.com', + RestrictedMethods.permittedChains, + CaveatTypes.restrictNetworkSwitching, + ); + + expect(permissionController.revokePermission).not.toHaveBeenCalled(); + expect(permissionController.updateCaveat).not.toHaveBeenCalled(); + }); + }); }); From e236efd342a999fad04da3cc821fdb05a0489e38 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 23 Sep 2024 13:47:20 -0700 Subject: [PATCH 096/125] update background-api test requestAccountsAndChainPermissionsWithId --- .../permissions/background-api.test.js | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/permissions/background-api.test.js b/app/scripts/controllers/permissions/background-api.test.js index cd3e09edf931..abdd8a8dbd47 100644 --- a/app/scripts/controllers/permissions/background-api.test.js +++ b/app/scripts/controllers/permissions/background-api.test.js @@ -3,7 +3,7 @@ import { RestrictedMethods, } from '../../../../shared/constants/permissions'; import { getPermissionBackgroundApiMethods } from './background-api'; -import { CaveatFactories } from './specifications'; +import { CaveatFactories, PermissionNames } from './specifications'; describe('permission background API methods', () => { const getApprovedPermissions = (accounts) => ({ @@ -195,6 +195,37 @@ describe('permission background API methods', () => { }); }); + describe('requestAccountsAndChainPermissionsWithId', () => { + it('request eth_accounts and permittedChains permissions and returns the request id', async () => { + const permissionController = { + requestPermissions: jest + .fn() + .mockImplementationOnce(async (_, __, { id }) => { + return [null, { id }]; + }), + }; + + const id = await getPermissionBackgroundApiMethods( + permissionController, + ).requestAccountsAndChainPermissionsWithId('foo.com'); + + expect(permissionController.requestPermissions).toHaveBeenCalledTimes(1); + expect(permissionController.requestPermissions).toHaveBeenCalledWith( + { origin: 'foo.com' }, + { + [PermissionNames.eth_accounts]: {}, + [PermissionNames.permittedChains]: {}, + }, + { id: expect.any(String) }, + ); + + expect(id.length > 0).toBe(true); + expect(id).toStrictEqual( + permissionController.requestPermissions.mock.calls[0][2].id, + ); + }); + }); + describe('removePermittedChain', () => { it('removes a permitted chain', () => { const permissionController = { From 171d52e8f9b48c0df250176e98f10d2d733c383c Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 23 Sep 2024 16:33:06 -0700 Subject: [PATCH 097/125] Add edit modal scenarios --- .../edit-accounts-modal.test.tsx | 50 ++++++++++++++++--- .../edit-networks-modal.test.js | 45 +++++++++++++++-- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx index 4188e37f0eca..9ef6846685a0 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx @@ -1,16 +1,18 @@ import React from 'react'; +import { fireEvent } from '@testing-library/react'; import { renderWithProvider } from '../../../../test/jest/rendering'; import mockState from '../../../../test/data/mock-state.json'; import configureStore from '../../../store/store'; +import { MergedInternalAccount } from '../../../selectors/selectors.types'; import { EditAccountsModal } from '.'; const render = ( props: { + onSubmit: (addresses: string[]) => void; onClose: () => void; - activeTabOrigin: string; } = { - onClose: () => jest.fn(), - activeTabOrigin: 'https://test.dapp', + onSubmit: jest.fn(), + onClose: jest.fn(), }, state = {}, ) => { @@ -33,13 +35,16 @@ const render = ( origin: 'https://test.dapp', }, }); + + const accounts = Object.values( + mockState.metamask.internalAccounts.accounts, + ) as unknown as MergedInternalAccount[]; + return renderWithProvider( , store, @@ -55,4 +60,33 @@ describe('EditAccountsModal', () => { const { getByLabelText } = render(); expect(getByLabelText('Select all')).toBeInTheDocument(); }); + + it('calls onSubmit with the selected account addresses when the connect button is clicked', async () => { + const onSubmit = jest.fn(); + const { getByTestId } = render({ + onSubmit, + onClose: jest.fn(), + }); + fireEvent.click(getByTestId('connect-more-accounts-button')); + expect(onSubmit).toHaveBeenCalledWith([ + '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ]); + }); + + it('calls onClose when the connect button is clicked', async () => { + const onClose = jest.fn(); + const { getByTestId } = render({ + onSubmit: jest.fn(), + onClose, + }); + fireEvent.click(getByTestId('connect-more-accounts-button')); + expect(onClose).toHaveBeenCalledWith(); + }); + + it('shows the disconnect text button when nothing is selected', () => { + const { getByLabelText, getByTestId } = render(); + fireEvent.click(getByLabelText('Select all')); + fireEvent.click(getByLabelText('Select all')); + expect(getByTestId('disconnect-accounts-button')).toHaveTextContent('Disconnect') + }) }); diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js index c97a2d380feb..291465dea7e3 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js @@ -1,13 +1,15 @@ import React from 'react'; +import { fireEvent } from '@testing-library/react'; import { renderWithProvider } from '../../../../test/jest/rendering'; import mockState from '../../../../test/data/mock-state.json'; import configureStore from '../../../store/store'; +import { TEST_NETWORKS } from '../../../../shared/constants/network'; import { EditNetworksModal } from '.'; const render = ( props = { - onClose: () => jest.fn(), - activeTabOrigin: 'https://test.dapp', + onSubmit: jest.fn(), + onClose: jest.fn(), }, state = {}, ) => { @@ -30,14 +32,20 @@ const render = ( origin: 'https://test.dapp', }, }); + + const networks = Object.values( + mockState.metamask.networkConfigurationsByChainId, + ).map(({ chainId, name }) => ({ chainId, name })); + return renderWithProvider( { throw new Error('Function not implemented.'); }} - testNetworks={[]} - nonTestNetworks={[]} - defaultSelectedChainIds={[]} + testNetworks={[networks[0]]} + nonTestNetworks={[networks[1]]} + defaultSelectedChainIds={[networks[0].chainId]} + activeTabOrigin="https://test.dapp" {...props} />, store, @@ -53,4 +61,31 @@ describe('EditNetworksModal', () => { const { getByLabelText } = render(); expect(getByLabelText('Select all')).toBeInTheDocument(); }); + + it('calls onSubmit with the selected chain IDs when the connect button is clicked', async () => { + const onSubmit = jest.fn(); + const { getByTestId } = render({ + onSubmit, + onClose: jest.fn(), + }); + fireEvent.click(getByTestId('connect-more-chains-button')); + expect(onSubmit).toHaveBeenCalledWith(['0x1']); + }); + + it('calls onClose when the connect button is clicked', async () => { + const onClose = jest.fn(); + const { getByTestId } = render({ + onSubmit: jest.fn(), + onClose, + }); + fireEvent.click(getByTestId('connect-more-chains-button')); + expect(onClose).toHaveBeenCalledWith(); + }); + + it('shows the disconnect text button when nothing is selected', () => { + const { getByLabelText, getByTestId } = render(); + fireEvent.click(getByLabelText('Select all')); + fireEvent.click(getByLabelText('Select all')); + expect(getByTestId('disconnect-chains-button')).toHaveTextContent('Disconnect') + }) }); From c709725a79028b9fa2efeb9e8d083a99d932a77a Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 24 Sep 2024 13:56:13 +0100 Subject: [PATCH 098/125] lint fix --- .../permission-page-container.component.js | 3 +-- .../edit-accounts-modal/edit-accounts-modal.test.tsx | 6 ++++-- .../edit-networks-modal/edit-networks-modal.test.js | 7 ++++--- ui/store/actions.test.js | 2 +- ui/store/actions.ts | 4 ++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js index 4108dff6e6a1..1b93d9671b84 100644 --- a/ui/components/app/permission-page-container/permission-page-container.component.js +++ b/ui/components/app/permission-page-container/permission-page-container.component.js @@ -13,8 +13,6 @@ import { RestrictedMethods, } from '../../../../shared/constants/permissions'; -import { PermissionNames } from '../../../../app/scripts/controllers/permissions'; - import SnapPrivacyWarning from '../snaps/snap-privacy-warning'; import { getDedupedSnaps } from '../../../helpers/utils/util'; import { containsEthPermissionsAndNonEvmAccount } from '../../../helpers/utils/permissions'; @@ -25,6 +23,7 @@ import { } from '../../../helpers/constants/design-system'; import { Box } from '../../component-library'; import { PermissionPageContainerContent } from '.'; +import { PermissionNames } from '../../../../app/scripts/controllers/permissions'; export default class PermissionPageContainer extends Component { static propTypes = { diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx index 9ef6846685a0..b00e3dc39c3e 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.test.tsx @@ -87,6 +87,8 @@ describe('EditAccountsModal', () => { const { getByLabelText, getByTestId } = render(); fireEvent.click(getByLabelText('Select all')); fireEvent.click(getByLabelText('Select all')); - expect(getByTestId('disconnect-accounts-button')).toHaveTextContent('Disconnect') - }) + expect(getByTestId('disconnect-accounts-button')).toHaveTextContent( + 'Disconnect', + ); + }); }); diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js index 291465dea7e3..4ac721ab2dc0 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.test.js @@ -3,7 +3,6 @@ import { fireEvent } from '@testing-library/react'; import { renderWithProvider } from '../../../../test/jest/rendering'; import mockState from '../../../../test/data/mock-state.json'; import configureStore from '../../../store/store'; -import { TEST_NETWORKS } from '../../../../shared/constants/network'; import { EditNetworksModal } from '.'; const render = ( @@ -86,6 +85,8 @@ describe('EditNetworksModal', () => { const { getByLabelText, getByTestId } = render(); fireEvent.click(getByLabelText('Select all')); fireEvent.click(getByLabelText('Select all')); - expect(getByTestId('disconnect-chains-button')).toHaveTextContent('Disconnect') - }) + expect(getByTestId('disconnect-chains-button')).toHaveTextContent( + 'Disconnect', + ); + }); }); diff --git a/ui/store/actions.test.js b/ui/store/actions.test.js index b9d89cf05292..4495ffd23133 100644 --- a/ui/store/actions.test.js +++ b/ui/store/actions.test.js @@ -17,11 +17,11 @@ import { MetaMetricsNetworkEventSource } from '../../shared/constants/metametric import { ETH_EOA_METHODS } from '../../shared/constants/eth-methods'; import { mockNetworkState } from '../../test/stub/networks'; import { CHAIN_IDS } from '../../shared/constants/network'; +import { CaveatTypes } from '../../shared/constants/permissions'; import { CaveatFactories, PermissionNames, } from '../../app/scripts/controllers/permissions'; -import { CaveatTypes } from '../../shared/constants/permissions'; import * as actions from './actions'; import * as actionConstants from './actionConstants'; import { setBackgroundConnection } from './background-connection'; diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 612f4c0ccea3..d64b3b62f19d 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -119,11 +119,11 @@ import { getMethodDataAsync } from '../../shared/lib/four-byte'; import { DecodedTransactionDataResponse } from '../../shared/types/transaction-decode'; import { LastInteractedConfirmationInfo } from '../pages/confirmations/types/confirm'; import { EndTraceRequest } from '../../shared/lib/trace'; +import { CaveatTypes } from '../../shared/constants/permissions'; import { CaveatFactories, PermissionNames, -} from '../../app/scripts/controllers/permissions/specifications'; -import { CaveatTypes } from '../../shared/constants/permissions'; +} from '../../app/scripts/controllers/permissions'; import * as actionConstants from './actionConstants'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) import { updateCustodyState } from './institutional/institution-actions'; From 62d6bc71b30d69c7082ae7baee90a2df5433a93d Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 24 Sep 2024 14:02:27 +0100 Subject: [PATCH 099/125] lint fix --- .../permission-page-container.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js index 1b93d9671b84..aa87e2c82a74 100644 --- a/ui/components/app/permission-page-container/permission-page-container.component.js +++ b/ui/components/app/permission-page-container/permission-page-container.component.js @@ -22,8 +22,8 @@ import { FlexDirection, } from '../../../helpers/constants/design-system'; import { Box } from '../../component-library'; -import { PermissionPageContainerContent } from '.'; import { PermissionNames } from '../../../../app/scripts/controllers/permissions'; +import { PermissionPageContainerContent } from '.'; export default class PermissionPageContainer extends Component { static propTypes = { From 0d1f0a7df1f531e2b9b29026df1330bf4642d530 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Tue, 24 Sep 2024 14:15:37 +0100 Subject: [PATCH 100/125] updated test for site cell --- ...ite-cell-connection-list-item.test.js.snap | 2 +- .../site-cell-connection-list-item.js | 2 +- .../site-cell-connection-list-item.test.js | 22 +++++++++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/__snapshots__/site-cell-connection-list-item.test.js.snap b/ui/components/multichain/pages/review-permissions-page/site-cell/__snapshots__/site-cell-connection-list-item.test.js.snap index 623b5dd9ccf5..43a161bcb22b 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/__snapshots__/site-cell-connection-list-item.test.js.snap +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/__snapshots__/site-cell-connection-list-item.test.js.snap @@ -4,7 +4,7 @@ exports[`SiteCellConnectionListItem renders correctly with required props 1`] =
- +
`; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js index 87be9be13cfb..85e50b0b0fed 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell-connection-list-item.js @@ -7,7 +7,6 @@ import { Display, FlexDirection, IconColor, - JustifyContent, TextAlign, TextColor, TextVariant, @@ -16,10 +15,10 @@ import { AvatarIcon, AvatarIconSize, Box, + ButtonIcon, + ButtonIconSize, ButtonLink, - Icon, IconName, - IconSize, Text, } from '../../../../component-library'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; @@ -38,7 +37,6 @@ export const SiteCellConnectionListItem = ({ return ( onClick()}>{t('edit')} ) : ( - onClick()} - > - - + size={ButtonIconSize.Sm} + /> )} ); diff --git a/ui/pages/permissions-connect/connect-page/__snapshots__/connect-page.test.tsx.snap b/ui/pages/permissions-connect/connect-page/__snapshots__/connect-page.test.tsx.snap index d44bf2329f43..931b4dac797f 100644 --- a/ui/pages/permissions-connect/connect-page/__snapshots__/connect-page.test.tsx.snap +++ b/ui/pages/permissions-connect/connect-page/__snapshots__/connect-page.test.tsx.snap @@ -35,7 +35,7 @@ exports[`ConnectPage should render correctly 1`] = `
- - - - +