diff --git a/test/e2e/flask/multi-srp/import-srp.spec.ts b/test/e2e/flask/multi-srp/import-srp.spec.ts
index ecf560c82b66..c9f737c56ed8 100644
--- a/test/e2e/flask/multi-srp/import-srp.spec.ts
+++ b/test/e2e/flask/multi-srp/import-srp.spec.ts
@@ -40,7 +40,7 @@ describe('Multi SRP - Import SRP', function (this: Suite) {
},
async (driver: Driver) => {
const accountListPage = new AccountListPage(driver);
- await accountListPage.checkAccountBelongsToSrp('Account 2', 2);
+ await accountListPage.checkAccountBelongsToSrp('Account 1', 2);
},
);
});
diff --git a/test/e2e/page-objects/pages/account-list-page.ts b/test/e2e/page-objects/pages/account-list-page.ts
index 8ed190d4760b..9cfcc24c12ba 100644
--- a/test/e2e/page-objects/pages/account-list-page.ts
+++ b/test/e2e/page-objects/pages/account-list-page.ts
@@ -404,7 +404,15 @@ class AccountListPage {
const createMultichainAccountButtons = await this.driver.findElements(
this.addMultichainAccountButton,
);
- await createMultichainAccountButtons[options?.srpIndex ?? 0].click();
+ const buttonIndex = options?.srpIndex ?? 0;
+ await createMultichainAccountButtons[buttonIndex].click();
+
+ // Wait for the account creation to complete by waiting for loading state to finish
+ // The button shows "Adding account..." during loading and "Add account" when done
+ await this.driver.assertElementNotPresent({
+ css: this.addMultichainAccountButton,
+ text: 'Adding account...',
+ });
}
/**
@@ -1058,6 +1066,7 @@ class AccountListPage {
if (srpIndex === 0) {
throw new Error('SRP index must be > 0');
}
+
const srps = await this.driver.findElements('.select-srp__container');
const selectedSrp = srps[srpIndex - 1];
const showAccountsButton = await this.driver.waitForSelector(
diff --git a/ui/components/multichain/multi-srp/srp-list/index.scss b/ui/components/multichain/multi-srp/srp-list/index.scss
index 6838850bea9b..e90c51f247b0 100644
--- a/ui/components/multichain/multi-srp/srp-list/index.scss
+++ b/ui/components/multichain/multi-srp/srp-list/index.scss
@@ -28,6 +28,6 @@
&__account-name {
overflow: hidden;
white-space: nowrap;
- max-width: 80px;
+ max-width: 120px;
}
}
diff --git a/ui/components/multichain/multi-srp/srp-list/index.ts b/ui/components/multichain/multi-srp/srp-list/index.ts
index 1675f9c1e48b..b27d5479e1d1 100644
--- a/ui/components/multichain/multi-srp/srp-list/index.ts
+++ b/ui/components/multichain/multi-srp/srp-list/index.ts
@@ -1 +1,2 @@
export { SrpList } from './srp-list';
+export { SrpListItem } from './srp-list-item';
diff --git a/ui/components/multichain/multi-srp/srp-list/srp-card.test.tsx b/ui/components/multichain/multi-srp/srp-list/srp-card.test.tsx
new file mode 100644
index 000000000000..26a563b56ed4
--- /dev/null
+++ b/ui/components/multichain/multi-srp/srp-list/srp-card.test.tsx
@@ -0,0 +1,99 @@
+import React from 'react';
+import configureMockStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+import { fireEvent } from '@testing-library/react';
+
+import { KeyringTypes } from '@metamask/keyring-controller';
+import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';
+
+import { renderWithProvider } from '../../../../../test/lib/render-helpers-navigate';
+import mockState from '../../../../../test/data/mock-state.json';
+import { FirstTimeFlowType } from '../../../../../shared/constants/onboarding';
+import { SrpCard } from './srp-card';
+
+const mockWalletId = 'mock-wallet-id' as AccountWalletId;
+const mockTotalFiatBalance = '$100.00';
+
+const mocks = {
+ useSingleWalletAccountsBalanceCallback: jest
+ .fn()
+ .mockReturnValue((_: AccountGroupId) => mockTotalFiatBalance),
+ onActionComplete: jest.fn(),
+ useWalletInfoCallback: jest.fn().mockReturnValue({
+ multichainAccounts: [
+ {
+ id: 'mock-account-id-1' as AccountGroupId,
+ metadata: {
+ name: 'Mock Account 1',
+ },
+ },
+ ],
+ keyringId: '01JKAF3DSGM3AB87EM9N0K41AJ',
+ isSRPBackedUp: true,
+ }),
+};
+
+jest.mock('../../../../hooks/multichain-accounts/useWalletBalance', () => ({
+ useSingleWalletAccountsBalanceCallback: (walletId: AccountWalletId) =>
+ mocks.useSingleWalletAccountsBalanceCallback(walletId),
+}));
+
+jest.mock('../../../../hooks/multichain-accounts/useWalletInfo', () => ({
+ useWalletInfo: (walletId: AccountWalletId) =>
+ mocks.useWalletInfoCallback(walletId),
+}));
+
+const mockSecondHdKeyring = {
+ accounts: [],
+ type: KeyringTypes.hd,
+ metadata: {
+ id: '01JN31PKMJ3ANWYFJZM3Z8MYT4',
+ name: '',
+ },
+};
+
+const render = (shouldTriggerBackup: boolean) => {
+ const store = configureMockStore([thunk])({
+ ...mockState,
+ metamask: {
+ ...mockState.metamask,
+ keyrings: [...mockState.metamask.keyrings, mockSecondHdKeyring],
+ firstTimeFlowType: FirstTimeFlowType.create,
+ seedPhraseBackedUp: false,
+ },
+ });
+
+ return renderWithProvider(
+ ,
+ store,
+ );
+};
+
+describe('SrpCard', () => {
+ it('renders the secret recovery phrases card', () => {
+ const { getByText } = render(false);
+ expect(getByText('Secret Recovery Phrase 1')).toBeInTheDocument();
+ });
+
+ it('shows/hides accounts when clicking show/hide text', () => {
+ const { getByText } = render(false);
+ const showAccountsButton = getByText('Show 1 account');
+ fireEvent.click(showAccountsButton);
+ expect(getByText('Hide 1 account')).toBeInTheDocument();
+ });
+
+ it('calls onActionComplete when clicking a keyring', () => {
+ const { getByTestId } = render(true);
+ const firstKeyringId = mockState.metamask.keyrings[0].metadata.id;
+
+ const keyring = getByTestId(`hd-keyring-${firstKeyringId}`);
+ fireEvent.click(keyring);
+
+ expect(mocks.onActionComplete).toHaveBeenCalledWith(firstKeyringId, true);
+ });
+});
diff --git a/ui/components/multichain/multi-srp/srp-list/srp-card.tsx b/ui/components/multichain/multi-srp/srp-list/srp-card.tsx
new file mode 100644
index 000000000000..b14792dff107
--- /dev/null
+++ b/ui/components/multichain/multi-srp/srp-list/srp-card.tsx
@@ -0,0 +1,174 @@
+import React, { useState, useContext, useCallback } from 'react';
+import type { AccountWalletId } from '@metamask/account-api';
+
+import { MetaMetricsContext } from '../../../../contexts/metametrics';
+import { useWalletInfo } from '../../../../hooks/multichain-accounts/useWalletInfo';
+import { useI18nContext } from '../../../../hooks/useI18nContext';
+import { useSingleWalletAccountsBalanceCallback } from '../../../../hooks/multichain-accounts/useWalletBalance';
+import {
+ MetaMetricsEventCategory,
+ MetaMetricsEventName,
+} from '../../../../../shared/constants/metametrics';
+
+import Card from '../../../ui/card';
+import {
+ Box,
+ IconName,
+ Icon,
+ Text,
+ IconSize,
+} from '../../../component-library';
+import {
+ JustifyContent,
+ Display,
+ TextColor,
+ FlexDirection,
+ AlignItems,
+ BlockSize,
+ TextVariant,
+ IconColor,
+} from '../../../../helpers/constants/design-system';
+import { SrpListItem } from './srp-list-item';
+
+/**
+ * Props for the SrpCard component.
+ */
+type SrpCardProps = {
+ index: number;
+ walletId: AccountWalletId;
+ shouldTriggerBackup: boolean;
+ onActionComplete: (id: string, triggerBackup?: boolean) => void;
+ isSettingsPage?: boolean;
+ hideShowAccounts?: boolean;
+};
+
+export const SrpCard = ({
+ index,
+ walletId,
+ shouldTriggerBackup,
+ onActionComplete,
+ isSettingsPage = false,
+ hideShowAccounts = false,
+}: SrpCardProps) => {
+ const t = useI18nContext();
+ const trackEvent = useContext(MetaMetricsContext);
+ const { multichainAccounts, keyringId } = useWalletInfo(walletId);
+ const [showAccounts, setShowAccounts] = useState(false);
+ const walletAccountBalance = useSingleWalletAccountsBalanceCallback(walletId);
+
+ const showHideText = useCallback(
+ (numberOfAccounts: number): string => {
+ if (numberOfAccounts > 1) {
+ return showAccounts
+ ? t('SrpListHideAccounts', [numberOfAccounts])
+ : t('SrpListShowAccounts', [numberOfAccounts]);
+ }
+ return showAccounts
+ ? t('SrpListHideSingleAccount', [numberOfAccounts])
+ : t('SrpListShowSingleAccount', [numberOfAccounts]);
+ },
+ [showAccounts, t],
+ );
+
+ return (
+ {
+ trackEvent({
+ category: MetaMetricsEventCategory.Accounts,
+ event: MetaMetricsEventName.SecretRecoveryPhrasePickerClicked,
+ properties: {
+ // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ button_type: 'srp_select',
+ },
+ });
+ keyringId && onActionComplete(keyringId, shouldTriggerBackup);
+ }}
+ className="select-srp__container"
+ marginBottom={3}
+ >
+
+
+
+ {t('srpListName', [index + 1])}
+
+ {!hideShowAccounts && (
+ {
+ event.stopPropagation();
+ trackEvent({
+ category: MetaMetricsEventCategory.Accounts,
+ event: MetaMetricsEventName.SecretRecoveryPhrasePickerClicked,
+ properties: {
+ // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ button_type: 'details',
+ },
+ });
+ setShowAccounts((prevState) => !prevState);
+ }}
+ >
+ {showHideText(multichainAccounts.length)}
+
+ )}
+
+
+ {isSettingsPage && (
+
+ {shouldTriggerBackup
+ ? t('srpListStateNotBackedUp')
+ : t('srpListStateBackedUp')}
+
+ )}
+
+
+
+ {showAccounts && (
+
+
+ {multichainAccounts.map((group) => {
+ return (
+
+ );
+ })}
+
+ )}
+
+ );
+};
diff --git a/ui/components/multichain/multi-srp/srp-list/srp-list-item.test.tsx b/ui/components/multichain/multi-srp/srp-list/srp-list-item.test.tsx
index 6488258270d8..52fe212fb6e9 100644
--- a/ui/components/multichain/multi-srp/srp-list/srp-list-item.test.tsx
+++ b/ui/components/multichain/multi-srp/srp-list/srp-list-item.test.tsx
@@ -1,65 +1,42 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
+import { type AccountGroupId } from '@metamask/account-api';
+
import mockState from '../../../../../test/data/mock-state.json';
-import { createMockInternalAccount } from '../../../../../test/jest/mocks';
-import { InternalAccountWithBalance } from '../../../../selectors';
import { renderWithProvider } from '../../../../../test/lib/render-helpers-navigate';
-import { shortenAddress } from '../../../../helpers/utils/util';
import { SrpListItem } from './srp-list-item';
-const mockTotalFiatBalance = '100';
-const mockAccount: InternalAccountWithBalance = {
- ...createMockInternalAccount({
- name: 'Test Account',
- address: '0xB1BAF6A2f4A808937bb97a2F12CCF08F1233e3D9',
- }),
- balance: mockTotalFiatBalance,
-};
-
-const mocks = {
- useMultichainAccountTotalFiatBalance: jest.fn().mockReturnValue({
- totalFiatBalance: mockTotalFiatBalance,
- }),
-};
-
-jest.mock('../../../../hooks/useMultichainAccountTotalFiatBalance', () => ({
- useMultichainAccountTotalFiatBalance: (account: InternalAccountWithBalance) =>
- mocks.useMultichainAccountTotalFiatBalance(account),
-}));
-jest.mock('../../../../helpers/utils/util', () => ({
- ...jest.requireActual('../../../../helpers/utils/util'),
-}));
+const mockAccountId = 'mock-account-id' as AccountGroupId;
+const mockAccountName = 'Mock Account Name';
+const mockBalance = '$100.00';
const render = () => {
const store = configureMockStore([thunk])(mockState);
- return renderWithProvider(, store);
+ return renderWithProvider(
+ ,
+ store,
+ );
};
describe('SrpListItem', () => {
- beforeEach(() => {
- // Reset mock implementations before each test
- mocks.useMultichainAccountTotalFiatBalance.mockReturnValue({
- totalFiatBalance: mockTotalFiatBalance,
- });
- });
-
afterEach(() => {
jest.clearAllMocks();
});
- it('renders account name and shortened address', () => {
+ it('renders account name', () => {
const { getByText } = render();
- expect(getByText(mockAccount.metadata.name)).toBeInTheDocument();
- expect(getByText(shortenAddress(mockAccount.address))).toBeInTheDocument();
+ expect(getByText(mockAccountName)).toBeInTheDocument();
});
it('calls useMultichainAccountTotalFiatBalance with correct account', () => {
- render();
+ const { getByText } = render();
- expect(mocks.useMultichainAccountTotalFiatBalance).toHaveBeenCalledWith(
- mockAccount,
- );
+ expect(getByText(mockBalance)).toBeInTheDocument();
});
});
diff --git a/ui/components/multichain/multi-srp/srp-list/srp-list-item.tsx b/ui/components/multichain/multi-srp/srp-list/srp-list-item.tsx
index 938e03135b93..a1f5a215aaa1 100644
--- a/ui/components/multichain/multi-srp/srp-list/srp-list-item.tsx
+++ b/ui/components/multichain/multi-srp/srp-list/srp-list-item.tsx
@@ -1,48 +1,36 @@
-import React, { useMemo } from 'react';
+import React from 'react';
import { useSelector } from 'react-redux';
-import { isEvmAccountType } from '@metamask/keyring-api';
-import {
- AvatarAccount,
- AvatarAccountSize,
-} from '@metamask/design-system-react';
-import { InternalAccountWithBalance } from '../../../../selectors';
-import { useMultichainAccountTotalFiatBalance } from '../../../../hooks/useMultichainAccountTotalFiatBalance';
+import { type AccountGroupId } from '@metamask/account-api';
+import { AvatarAccountSize } from '@metamask/design-system-react';
+import { PreferredAvatar } from '../../../app/preferred-avatar';
import {
Display,
FlexDirection,
AlignItems,
JustifyContent,
TextVariant,
- TextColor,
} from '../../../../helpers/constants/design-system';
-import { shortenAddress } from '../../../../helpers/utils/util';
-import UserPreferencedCurrencyDisplay from '../../../app/user-preferenced-currency-display';
+import { getIconSeedAddressByAccountGroupId } from '../../../../selectors/multichain-accounts/account-tree';
import { Text, Box } from '../../../component-library';
-// eslint-disable-next-line import/no-restricted-paths
-import { normalizeSafeAddress } from '../../../../../app/scripts/lib/multichain/address';
-import { getMultichainAggregatedBalance } from '../../../../selectors/assets';
type SrpListItemProps = {
- account: InternalAccountWithBalance;
+ accountId: AccountGroupId;
+ accountName: string;
+ balance: string;
};
-export const SrpListItem = ({ account }: SrpListItemProps) => {
- const { totalFiatBalance } = useMultichainAccountTotalFiatBalance(account);
- const isEvmAccount = isEvmAccountType(account.type);
- const multichainAggregatedBalance = useSelector((state) =>
- getMultichainAggregatedBalance(state, account),
+export const SrpListItem = ({
+ accountId,
+ accountName,
+ balance,
+}: SrpListItemProps) => {
+ const seedAddress = useSelector((state) =>
+ getIconSeedAddressByAccountGroupId(state, accountId),
);
- const balance = useMemo(() => {
- if (isEvmAccount) {
- return totalFiatBalance;
- }
- return multichainAggregatedBalance.toString();
- }, [isEvmAccount, multichainAggregatedBalance, totalFiatBalance]);
-
return (
{
flexDirection={FlexDirection.Row}
alignItems={AlignItems.center}
>
-
+
- {account.metadata.name}
-
-
- {shortenAddress(normalizeSafeAddress(account.address))}
+ {accountName}
-
-
-
+ {balance}
);
};
diff --git a/ui/components/multichain/multi-srp/srp-list/srp-list.test.tsx b/ui/components/multichain/multi-srp/srp-list/srp-list.test.tsx
index 8bd26ab7abc9..3d8075dcb69f 100644
--- a/ui/components/multichain/multi-srp/srp-list/srp-list.test.tsx
+++ b/ui/components/multichain/multi-srp/srp-list/srp-list.test.tsx
@@ -3,26 +3,30 @@ import { fireEvent } from '@testing-library/react';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { KeyringTypes } from '@metamask/keyring-controller';
+import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';
import { renderWithProvider } from '../../../../../test/lib/render-helpers-navigate';
import mockState from '../../../../../test/data/mock-state.json';
-import { InternalAccountWithBalance } from '../../../../selectors';
-import { shortenAddress } from '../../../../helpers/utils/util';
-// eslint-disable-next-line import/no-restricted-paths
-import { normalizeSafeAddress } from '../../../../../app/scripts/lib/multichain/address';
import { FirstTimeFlowType } from '../../../../../shared/constants/onboarding';
import { SrpList } from './srp-list';
-const mockTotalFiatBalance = '100';
+const mockTotalFiatBalance = '$100.00';
+
const mocks = {
- useMultichainAccountTotalFiatBalance: jest.fn().mockReturnValue({
- totalFiatBalance: mockTotalFiatBalance,
- }),
+ useSingleWalletAccountsBalanceCallback: jest
+ .fn()
+ .mockReturnValue((_: AccountGroupId) => mockTotalFiatBalance),
onActionComplete: jest.fn(),
+ useWalletInfoCallback: jest.fn(),
};
-jest.mock('../../../../hooks/useMultichainAccountTotalFiatBalance', () => ({
- useMultichainAccountTotalFiatBalance: (account: InternalAccountWithBalance) =>
- mocks.useMultichainAccountTotalFiatBalance(account),
+jest.mock('../../../../hooks/multichain-accounts/useWalletBalance', () => ({
+ useSingleWalletAccountsBalanceCallback: (walletId: AccountWalletId) =>
+ mocks.useSingleWalletAccountsBalanceCallback(walletId),
+}));
+
+jest.mock('../../../../hooks/multichain-accounts/useWalletInfo', () => ({
+ useWalletInfo: (walletId: AccountWalletId) =>
+ mocks.useWalletInfoCallback(walletId),
}));
const mockSecondHdKeyring = {
@@ -34,12 +38,73 @@ const mockSecondHdKeyring = {
},
};
+// Second wallet entry for accountTree to match the second keyring
+const mockSecondWallet = {
+ id: 'entropy:01JN31PKMJ3ANWYFJZM3Z8MYT4' as AccountWalletId,
+ type: 'entropy',
+ groups: {
+ 'entropy:01JN31PKMJ3ANWYFJZM3Z8MYT4/0': {
+ id: 'entropy:01JN31PKMJ3ANWYFJZM3Z8MYT4/0' as AccountGroupId,
+ type: 'multichain-account',
+ accounts: ['mock-account-id-2'],
+ metadata: {
+ name: 'Account 2',
+ entropy: { groupIndex: 0 },
+ hidden: false,
+ pinned: false,
+ },
+ },
+ },
+ metadata: {
+ name: 'Wallet 2',
+ entropy: { id: '01JN31PKMJ3ANWYFJZM3Z8MYT4' },
+ },
+};
+
const render = () => {
+ // Set up useWalletInfo mock to return correct data for each wallet
+ mocks.useWalletInfoCallback.mockImplementation(
+ (walletId: AccountWalletId) => {
+ if (walletId === 'entropy:01JKAF3DSGM3AB87EM9N0K41AJ') {
+ return {
+ multichainAccounts: [
+ {
+ id: 'entropy:01JKAF3DSGM3AB87EM9N0K41AJ/0' as AccountGroupId,
+ metadata: { name: 'Account 1' },
+ },
+ ],
+ keyringId: '01JKAF3DSGM3AB87EM9N0K41AJ',
+ isSRPBackedUp: false,
+ };
+ }
+ if (walletId === 'entropy:01JN31PKMJ3ANWYFJZM3Z8MYT4') {
+ return {
+ multichainAccounts: [
+ {
+ id: 'entropy:01JN31PKMJ3ANWYFJZM3Z8MYT4/0' as AccountGroupId,
+ metadata: { name: 'Account 2' },
+ },
+ ],
+ keyringId: '01JN31PKMJ3ANWYFJZM3Z8MYT4',
+ isSRPBackedUp: true,
+ };
+ }
+ return { multichainAccounts: [], keyringId: undefined };
+ },
+ );
+
const store = configureMockStore([thunk])({
...mockState,
metamask: {
...mockState.metamask,
keyrings: [...mockState.metamask.keyrings, mockSecondHdKeyring],
+ accountTree: {
+ ...mockState.metamask.accountTree,
+ wallets: {
+ ...mockState.metamask.accountTree.wallets,
+ 'entropy:01JN31PKMJ3ANWYFJZM3Z8MYT4': mockSecondWallet,
+ },
+ },
firstTimeFlowType: FirstTimeFlowType.create,
seedPhraseBackedUp: false,
},
@@ -58,13 +123,6 @@ describe('SrpList', () => {
expect(getByText('Secret Recovery Phrase 2')).toBeInTheDocument();
});
- it('shows/hides accounts when clicking show/hide text', () => {
- const { getByText } = render();
- const showAccountsButton = getByText('Show 2 accounts');
- fireEvent.click(showAccountsButton);
- expect(getByText('Hide 2 accounts')).toBeInTheDocument();
- });
-
it('calls onActionComplete when clicking a keyring', () => {
const { getByTestId } = render();
const firstKeyringId = mockState.metamask.keyrings[0].metadata.id;
@@ -74,28 +132,4 @@ describe('SrpList', () => {
expect(mocks.onActionComplete).toHaveBeenCalledWith(firstKeyringId, true);
});
-
- it('displays the correct accounts for a keyring and ensures no duplicates', () => {
- const { getByText, getAllByText } = render();
- const firstKeyringAccounts = mockState.metamask.keyrings[0].accounts;
- const account1Address = firstKeyringAccounts[0];
- const account2Address = firstKeyringAccounts[1];
-
- const showAccountsButton = getByText('Show 2 accounts');
- fireEvent.click(showAccountsButton);
-
- const shortenedAccount1 = shortenAddress(
- normalizeSafeAddress(account1Address),
- );
- const shortenedAccount2 = shortenAddress(
- normalizeSafeAddress(account2Address),
- );
-
- expect(getByText(shortenedAccount1)).toBeInTheDocument();
- expect(getByText(shortenedAccount2)).toBeInTheDocument();
-
- // Ensure no duplicates by checking the count of each shortened address.
- expect(getAllByText(shortenedAccount1).length).toBe(1);
- expect(getAllByText(shortenedAccount2).length).toBe(1);
- });
});
diff --git a/ui/components/multichain/multi-srp/srp-list/srp-list.tsx b/ui/components/multichain/multi-srp/srp-list/srp-list.tsx
index a109f7b6bf18..8b02f6b53b92 100644
--- a/ui/components/multichain/multi-srp/srp-list/srp-list.tsx
+++ b/ui/components/multichain/multi-srp/srp-list/srp-list.tsx
@@ -1,35 +1,12 @@
-import React, { useContext, useMemo, useState } from 'react';
+import React from 'react';
import { useSelector } from 'react-redux';
import classnames from 'classnames';
-import Card from '../../../ui/card';
-import {
- Box,
- IconName,
- Icon,
- Text,
- IconSize,
-} from '../../../component-library';
-import {
- JustifyContent,
- Display,
- TextColor,
- FlexDirection,
- AlignItems,
- BlockSize,
- TextVariant,
- IconColor,
-} from '../../../../helpers/constants/design-system';
-import { getMetaMaskAccounts } from '../../../../selectors/selectors';
-import { InternalAccountWithBalance } from '../../../../selectors/selectors.types';
-import { useI18nContext } from '../../../../hooks/useI18nContext';
-import {
- MetaMetricsEventCategory,
- MetaMetricsEventName,
-} from '../../../../../shared/constants/metametrics';
-import { MetaMetricsContext } from '../../../../contexts/metametrics';
-import { useHdKeyringsWithSnapAccounts } from '../../../../hooks/multi-srp/useHdKeyringsWithSnapAccounts';
+import { AccountWalletType } from '@metamask/account-api';
+
+import { getWalletIdsByType } from '../../../../selectors/multichain-accounts/account-tree';
import { getIsPrimarySeedPhraseBackedUp } from '../../../../ducks/metamask/metamask';
-import { SrpListItem } from './srp-list-item';
+import { Box } from '../../../component-library';
+import { SrpCard } from './srp-card';
export const SrpList = ({
onActionComplete,
@@ -40,38 +17,14 @@ export const SrpList = ({
isSettingsPage?: boolean;
hideShowAccounts?: boolean;
}) => {
- const t = useI18nContext();
- const trackEvent = useContext(MetaMetricsContext);
- const hdKeyringsWithSnapAccounts = useHdKeyringsWithSnapAccounts();
-
const isPrimarySeedPhraseBackedUp = useSelector(
getIsPrimarySeedPhraseBackedUp,
);
- // This selector will return accounts with nonEVM balances as well.
- const accountsWithBalances: Record =
- useSelector(getMetaMaskAccounts);
-
- const showAccountsInitState = useMemo(
- () => new Array(hdKeyringsWithSnapAccounts.length).fill(hideShowAccounts),
- [hdKeyringsWithSnapAccounts, hideShowAccounts],
- );
-
- const [showAccounts, setShowAccounts] = useState(
- showAccountsInitState,
+ const walletIdsFromStore = useSelector((state) =>
+ getWalletIdsByType(state, AccountWalletType.Entropy),
);
- const showHideText = (index: number, numberOfAccounts: number): string => {
- if (numberOfAccounts > 1) {
- return showAccounts[index]
- ? t('SrpListHideAccounts', [numberOfAccounts])
- : t('SrpListShowAccounts', [numberOfAccounts]);
- }
- return showAccounts[index]
- ? t('SrpListHideSingleAccount', [numberOfAccounts])
- : t('SrpListShowSingleAccount', [numberOfAccounts]);
- };
-
return (
- {hdKeyringsWithSnapAccounts.map((keyring, index) => {
+ {walletIdsFromStore.map((walletId, index) => {
// We only consider the first(primary) keyring for the backup reminder.
const shouldTriggerBackup = !isPrimarySeedPhraseBackedUp && index === 0;
return (
- {
- trackEvent({
- category: MetaMetricsEventCategory.Accounts,
- event: MetaMetricsEventName.SecretRecoveryPhrasePickerClicked,
- properties: {
- // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860
- // eslint-disable-next-line @typescript-eslint/naming-convention
- button_type: 'srp_select',
- },
- });
- onActionComplete(keyring.metadata.id, shouldTriggerBackup);
- }}
- className="select-srp__container"
- marginBottom={3}
- >
-
-
-
- {t('srpListName', [index + 1])}
-
- {!hideShowAccounts && (
- {
- event.stopPropagation();
- trackEvent({
- category: MetaMetricsEventCategory.Accounts,
- event:
- MetaMetricsEventName.SecretRecoveryPhrasePickerClicked,
- properties: {
- // TODO: Fix in https://github.com/MetaMask/metamask-extension/issues/31860
- // eslint-disable-next-line @typescript-eslint/naming-convention
- button_type: 'details',
- },
- });
- setShowAccounts((prevState) => {
- const accountListLength =
- hdKeyringsWithSnapAccounts.length;
- let newState = prevState;
- if (accountListLength > prevState.length) {
- // Extend the state with `false` for new accounts
- newState = [
- ...prevState,
- ...Array(accountListLength - prevState.length).fill(
- false,
- ),
- ];
- }
- return newState.map((value, i) =>
- i === index ? !value : value,
- );
- });
- }}
- >
- {showHideText(index, keyring.accounts.length)}
-
- )}
-
-
- {isSettingsPage && (
-
- {shouldTriggerBackup
- ? t('srpListStateNotBackedUp')
- : t('srpListStateBackedUp')}
-
- )}
-
-
-
- {showAccounts[index] && (
-
-
- {keyring.accounts.map((address: string) => {
- const account = accountsWithBalances[address];
- return (
-
- );
- })}
-
- )}
-
+
);
})}
diff --git a/ui/selectors/multichain-accounts/account-tree.ts b/ui/selectors/multichain-accounts/account-tree.ts
index 236e99693b79..073415f65b4a 100644
--- a/ui/selectors/multichain-accounts/account-tree.ts
+++ b/ui/selectors/multichain-accounts/account-tree.ts
@@ -6,7 +6,10 @@ import {
import { isEvmAccountType, EthAccountType } from '@metamask/keyring-api';
import { AccountId } from '@metamask/accounts-controller';
import { createSelector } from 'reselect';
-import { AccountGroupObject } from '@metamask/account-tree-controller';
+import {
+ type AccountWalletObject,
+ type AccountGroupObject,
+} from '@metamask/account-tree-controller';
import { InternalAccount } from '@metamask/keyring-internal-api';
import {
type Hex,
@@ -906,3 +909,35 @@ export const getIconSeedAddressesByAccountGroups = (
return seedAddresses;
};
+
+/**
+ * Retrieve wallet IDs from account tree state by type.
+ * In case no type is provided it returns all wallet IDs.
+ *
+ * @param state - Redux state.
+ * @param state.metamask - MetaMask state object.
+ * @param state.metamask.accountTree - Account tree state object.
+ * @param walletId - The ID of the wallet to retrieve.
+ * @returns Wallet object from account tree state.
+ */
+export const getWalletIdsByType = createSelector(
+ (state: MultichainAccountsState) => state.metamask?.accountTree?.wallets,
+ (_, walletType?: AccountWalletType) => walletType,
+ (
+ wallets: Record,
+ walletType?: AccountWalletType,
+ ): AccountWalletId[] => {
+ if (!wallets) {
+ return [];
+ }
+
+ if (!walletType) {
+ // return all wallet IDs if no type is specified
+ return Object.keys(wallets) as AccountWalletId[];
+ }
+
+ return Object.entries(wallets)
+ .filter(([, wallet]) => wallet.type === walletType)
+ .map(([walletId]) => walletId) as AccountWalletId[];
+ },
+);