From de05544551660786a2151187eb86a872f4bdb449 Mon Sep 17 00:00:00 2001 From: Nick Gambino <35090461+gambinish@users.noreply.github.com> Date: Tue, 12 Nov 2024 11:57:29 -0800 Subject: [PATCH] feat: Token Network Filter UI [Mobile] (#11808) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR introduces token network filter UI component. It lives behind a feature flag `PORTFOLIO_VIEW` in order to allow this to get merged while backend changes are in flight. Integration with the multichain asset list will occur in a separate PR. We'll likely want to add additional e2e tests when that happens. ### Running this branch `yarn && yarn setup` `PORTFOLIO_VIEW=1 yarn watch` `yarn start:ios` or `i` from the watch process Please ensure that this PR looks okay both with and without the feature flag running. I have introduced a horizontal scroll view because there simply was not enough screen real estate to make the network filter look okay with truncated text. ## **Related issues** Fixes: ## **Manual testing steps** 1. Run app with feature flag 2. Go to main Portfolio view 3. Pressing the network filter should trigger the bottom sheet, allowing the user to select different filter options. ## **Screenshots/Recordings** https://github.com/user-attachments/assets/636d1627-5da3-4826-83a1-924ff4f28603 ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.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. --------- Signed-off-by: Kai Huang Co-authored-by: salimtb Co-authored-by: Vince Howard Co-authored-by: sethkfman <10342624+sethkfman@users.noreply.github.com> Co-authored-by: runway-github[bot] <73448015+runway-github[bot]@users.noreply.github.com> Co-authored-by: MetaMask Bot <37885440+metamaskbot@users.noreply.github.com> Co-authored-by: metamaskbot Co-authored-by: tommasini <46944231+tommasini@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Xiaoming Wang <7315988+dawnseeker8@users.noreply.github.com> Co-authored-by: Nico MASSART Co-authored-by: Xiaoming Wang Co-authored-by: Nicholas Ellul Co-authored-by: legobt <6wbvkn0j@anonaddy.me> Co-authored-by: OGPoyraz Co-authored-by: Pedro Pablo Aste Kompen Co-authored-by: legobeat <109787230+legobeat@users.noreply.github.com> Co-authored-by: tommasini Co-authored-by: Bryan Fullam Co-authored-by: Mpendulo Ndlovu Co-authored-by: Kaihuang72490 <147628638+Kaihuang72490@users.noreply.github.com> Co-authored-by: Cal-L Co-authored-by: Cal Leung Co-authored-by: sahar-fehri Co-authored-by: Matthew Walsh --- app/components/Nav/App/index.js | 7 +- .../TokenFilterBottomSheet.test.tsx | 113 ++++++++++++++++++ .../TokenFilterBottomSheet.tsx | 87 ++++++++++++++ .../TokenSortBottomSheet.test.tsx | 2 +- .../TokenSortBottomSheet.tsx | 2 +- .../UI/Tokens/TokensBottomSheet/index.ts | 9 +- .../Tokens/__snapshots__/index.test.tsx.snap | 33 ++++- app/components/UI/Tokens/index.test.tsx | 4 +- app/components/UI/Tokens/index.tsx | 82 ++++++++++--- app/components/UI/Tokens/styles.ts | 29 ++++- app/constants/navigation/Routes.ts | 1 + app/selectors/preferencesController.ts | 6 + app/util/test/initial-background-state.json | 1 + bitrise.yml | 3 + e2e/selectors/wallet/WalletView.selectors.js | 1 + locales/languages/en.json | 3 + ...tamask+preferences-controller+13.1.0.patch | 50 ++++++-- 17 files changed, 390 insertions(+), 43 deletions(-) create mode 100644 app/components/UI/Tokens/TokensBottomSheet/TokenFilterBottomSheet.test.tsx create mode 100644 app/components/UI/Tokens/TokensBottomSheet/TokenFilterBottomSheet.tsx diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index 91b7ff961ab..32ec40e7203 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -57,7 +57,8 @@ import Toast, { ToastContext, } from '../../../component-library/components/Toast'; import AccountSelector from '../../../components/Views/AccountSelector'; -import TokenSortBottomSheet from '../../../components/UI/Tokens/TokensBottomSheet/TokenSortBottomSheet.tsx'; +import { TokenSortBottomSheet } from '../../../components/UI/Tokens/TokensBottomSheet/TokenSortBottomSheet.tsx'; +import { TokenFilterBottomSheet } from '../../../components/UI/Tokens/TokensBottomSheet/TokenFilterBottomSheet.tsx'; import AccountConnect from '../../../components/Views/AccountConnect'; import AccountPermissions from '../../../components/Views/AccountPermissions'; import { AccountPermissionsScreens } from '../../../components/Views/AccountPermissions/AccountPermissions.types'; @@ -442,6 +443,10 @@ const RootModalFlow = () => ( name={Routes.SHEET.TOKEN_SORT} component={TokenSortBottomSheet} /> + ({ + useSelector: jest.fn(), +})); + +jest.mock('../../../../util/theme', () => ({ + useTheme: jest.fn(() => ({ colors: {} })), +})); + +jest.mock('../../../../core/Engine', () => ({ + context: { + PreferencesController: { + setTokenNetworkFilter: jest.fn(), + }, + }, +})); + +jest.mock('@react-navigation/native', () => { + const reactNavigationModule = jest.requireActual('@react-navigation/native'); + return { + ...reactNavigationModule, + useNavigation: () => ({ + navigate: jest.fn(), + goBack: jest.fn(), + }), + }; +}); + +jest.mock('react-native-safe-area-context', () => { + // copied from BottomSheetDialog.test.tsx + const inset = { top: 1, right: 2, bottom: 3, left: 4 }; + const frame = { width: 5, height: 6, x: 7, y: 8 }; + return { + SafeAreaProvider: jest.fn().mockImplementation(({ children }) => children), + SafeAreaConsumer: jest + .fn() + .mockImplementation(({ children }) => children(inset)), + useSafeAreaInsets: jest.fn().mockImplementation(() => inset), + useSafeAreaFrame: jest.fn().mockImplementation(() => frame), + }; +}); + +describe('TokenFilterBottomSheet', () => { + beforeEach(() => { + (useSelector as jest.Mock).mockImplementation((selector) => { + if (selector === selectChainId) { + return '0x1'; // default chain ID + } else if (selector === selectTokenNetworkFilter) { + return {}; // default to show all networks + } + return null; + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('renders correctly with the default option (All Networks) selected', () => { + const { queryByText } = render(); + + expect(queryByText('All Networks')).toBeTruthy(); + expect(queryByText('Current Network')).toBeTruthy(); + }); + + it('sets filter to All Networks and closes bottom sheet when first option is pressed', async () => { + const { queryByText } = render(); + + fireEvent.press(queryByText('All Networks')); + + await waitFor(() => { + expect( + Engine.context.PreferencesController.setTokenNetworkFilter, + ).toHaveBeenCalledWith({}); + }); + }); + + it('sets filter to Current Network and closes bottom sheet when second option is pressed', async () => { + const { queryByText } = render(); + + fireEvent.press(queryByText('Current Network')); + + await waitFor(() => { + expect( + Engine.context.PreferencesController.setTokenNetworkFilter, + ).toHaveBeenCalledWith({ + '0x1': true, + }); + }); + }); + + it('displays the correct selection based on tokenNetworkFilter', () => { + (useSelector as jest.Mock).mockImplementation((selector) => { + if (selector === selectChainId) { + return '0x1'; + } else if (selector === selectTokenNetworkFilter) { + return { '0x1': true }; // filter by current network + } + return null; + }); + + const { queryByText } = render(); + + expect(queryByText('Current Network')).toBeTruthy(); + }); +}); diff --git a/app/components/UI/Tokens/TokensBottomSheet/TokenFilterBottomSheet.tsx b/app/components/UI/Tokens/TokensBottomSheet/TokenFilterBottomSheet.tsx new file mode 100644 index 00000000000..4720c09660b --- /dev/null +++ b/app/components/UI/Tokens/TokensBottomSheet/TokenFilterBottomSheet.tsx @@ -0,0 +1,87 @@ +import React, { useRef } from 'react'; +import { useSelector } from 'react-redux'; +import { selectChainId } from '../../../../selectors/networkController'; +import { selectTokenNetworkFilter } from '../../../../selectors/preferencesController'; +import BottomSheet, { + BottomSheetRef, +} from '../../../../component-library/components/BottomSheets/BottomSheet'; +import { useTheme } from '../../../../util/theme'; +import createStyles from '../styles'; +import Engine from '../../../../core/Engine'; +import { View } from 'react-native'; +import Text, { + TextVariant, +} from '../../../../component-library/components/Texts/Text'; +import ListItemSelect from '../../../../component-library/components/List/ListItemSelect'; +import { VerticalAlignment } from '../../../../component-library/components/List/ListItem'; +import { strings } from '../../../../../locales/i18n'; + +enum FilterOption { + AllNetworks = 0, + CurrentNetwork = 1, +} + +const TokenFilterBottomSheet = () => { + const sheetRef = useRef(null); + const { colors } = useTheme(); + const styles = createStyles(colors); + + const chainId = useSelector(selectChainId); + const tokenNetworkFilter = useSelector(selectTokenNetworkFilter); + + const onFilterControlsBottomSheetPress = (option: FilterOption) => { + const { PreferencesController } = Engine.context; + switch (option) { + case FilterOption.AllNetworks: + PreferencesController.setTokenNetworkFilter({}); + sheetRef.current?.onCloseBottomSheet(); + break; + case FilterOption.CurrentNetwork: + PreferencesController.setTokenNetworkFilter({ + [chainId]: true, + }); + sheetRef.current?.onCloseBottomSheet(); + break; + default: + break; + } + }; + + const isSelectedNetwork = Boolean(tokenNetworkFilter?.[chainId]); + + return ( + + + + {strings('wallet.filter_by')} + + + onFilterControlsBottomSheetPress(FilterOption.AllNetworks) + } + isSelected={!isSelectedNetwork} + gap={8} + verticalAlignment={VerticalAlignment.Center} + > + + {strings('wallet.all_networks')} + + + + onFilterControlsBottomSheetPress(FilterOption.CurrentNetwork) + } + isSelected={isSelectedNetwork} + gap={8} + verticalAlignment={VerticalAlignment.Center} + > + + {strings('wallet.current_network')} + + + + + ); +}; + +export { TokenFilterBottomSheet }; diff --git a/app/components/UI/Tokens/TokensBottomSheet/TokenSortBottomSheet.test.tsx b/app/components/UI/Tokens/TokensBottomSheet/TokenSortBottomSheet.test.tsx index a7ca4c24109..d451e30613a 100644 --- a/app/components/UI/Tokens/TokensBottomSheet/TokenSortBottomSheet.test.tsx +++ b/app/components/UI/Tokens/TokensBottomSheet/TokenSortBottomSheet.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render, fireEvent, waitFor } from '@testing-library/react-native'; -import TokenSortBottomSheet from './TokenSortBottomSheet'; +import { TokenSortBottomSheet } from './TokenSortBottomSheet'; import { useSelector } from 'react-redux'; import Engine from '../../../../core/Engine'; import { selectTokenSortConfig } from '../../../../selectors/preferencesController'; diff --git a/app/components/UI/Tokens/TokensBottomSheet/TokenSortBottomSheet.tsx b/app/components/UI/Tokens/TokensBottomSheet/TokenSortBottomSheet.tsx index bdee8f734d3..1fce0a394a3 100644 --- a/app/components/UI/Tokens/TokensBottomSheet/TokenSortBottomSheet.tsx +++ b/app/components/UI/Tokens/TokensBottomSheet/TokenSortBottomSheet.tsx @@ -101,4 +101,4 @@ const TokenSortBottomSheet = () => { ); }; -export default TokenSortBottomSheet; +export { TokenSortBottomSheet }; diff --git a/app/components/UI/Tokens/TokensBottomSheet/index.ts b/app/components/UI/Tokens/TokensBottomSheet/index.ts index 0f9bc12f503..c96c7c2199b 100644 --- a/app/components/UI/Tokens/TokensBottomSheet/index.ts +++ b/app/components/UI/Tokens/TokensBottomSheet/index.ts @@ -5,4 +5,11 @@ export const createTokensBottomSheetNavDetails = createNavigationDetails( Routes.MODAL.ROOT_MODAL_FLOW, Routes.SHEET.TOKEN_SORT, ); -export { default } from './TokenSortBottomSheet'; + +export const createTokenBottomSheetFilterNavDetails = createNavigationDetails( + Routes.MODAL.ROOT_MODAL_FLOW, + Routes.SHEET.TOKEN_FILTER, +); + +export { TokenSortBottomSheet } from './TokenSortBottomSheet'; +export { TokenFilterBottomSheet } from './TokenFilterBottomSheet'; diff --git a/app/components/UI/Tokens/__snapshots__/index.test.tsx.snap b/app/components/UI/Tokens/__snapshots__/index.test.tsx.snap index 4968a992595..b1fa6c4ca1f 100644 --- a/app/components/UI/Tokens/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/Tokens/__snapshots__/index.test.tsx.snap @@ -317,8 +317,8 @@ exports[`Tokens should hide zero balance tokens when setting is on 1`] = ` "flexDirection": "row", "justifyContent": "space-between", "paddingBottom": 16, - "paddingLeft": 16, - "paddingRight": 16, + "paddingLeft": 8, + "paddingRight": 8, "paddingTop": 8, } } @@ -340,9 +340,13 @@ exports[`Tokens should hide zero balance tokens when setting is on 1`] = ` "flexDirection": "row", "height": 40, "justifyContent": "center", + "marginLeft": 5, + "marginRight": 5, + "maxWidth": "60%", "paddingHorizontal": 16, } } + testID="sort-by" > { }); it('triggers bottom sheet when sort controls are pressed', async () => { - const { getByText } = renderComponent(initialState); + const { getByTestId } = renderComponent(initialState); - await fireEvent.press(getByText('Sort by')); + await fireEvent.press(getByTestId(WalletViewSelectorsIDs.SORT_BY)); await waitFor(() => { expect(createTokensBottomSheetNavDetails).toHaveBeenCalledWith({}); diff --git a/app/components/UI/Tokens/index.tsx b/app/components/UI/Tokens/index.tsx index 6f16616cc3f..a19a8cbaa92 100644 --- a/app/components/UI/Tokens/index.tsx +++ b/app/components/UI/Tokens/index.tsx @@ -1,5 +1,5 @@ import React, { useRef, useState, LegacyRef, useMemo } from 'react'; -import { View } from 'react-native'; +import { View, Text } from 'react-native'; import ActionSheet from '@metamask/react-native-actionsheet'; import { useSelector } from 'react-redux'; import useTokenBalancesController from '../../hooks/useTokenBalancesController/useTokenBalancesController'; @@ -21,7 +21,10 @@ import { TokenI, TokensI } from './types'; import { WalletViewSelectorsIDs } from '../../../../e2e/selectors/wallet/WalletView.selectors'; import { strings } from '../../../../locales/i18n'; import { IconName } from '../../../component-library/components/Icons/Icon'; -import { selectTokenSortConfig } from '../../../selectors/preferencesController'; +import { + selectTokenNetworkFilter, + selectTokenSortConfig, +} from '../../../selectors/preferencesController'; import { deriveBalanceFromAssetMarketDetails, sortAssets } from './util'; import { useNavigation } from '@react-navigation/native'; import { StackNavigationProp } from '@react-navigation/stack'; @@ -31,8 +34,13 @@ import { selectConversionRate, selectCurrentCurrency, } from '../../../selectors/currencyRateController'; -import { createTokensBottomSheetNavDetails } from './TokensBottomSheet'; +import { + createTokenBottomSheetFilterNavDetails, + createTokensBottomSheetNavDetails, +} from './TokensBottomSheet'; import ButtonBase from '../../../component-library/components/Buttons/Button/foundation/ButtonBase'; +import { selectNetworkName } from '../../../selectors/networkInfos'; +import ButtonIcon from '../../../component-library/components/Buttons/ButtonIcon'; // this will be imported from TokenRatesController when it is exported from there // PR: https://github.com/MetaMask/core/pull/4622 @@ -74,6 +82,7 @@ const Tokens: React.FC = ({ tokens }) => { const { trackEvent, createEventBuilder } = useMetrics(); const { data: tokenBalances } = useTokenBalancesController(); const tokenSortConfig = useSelector(selectTokenSortConfig); + const tokenNetworkFilter = useSelector(selectTokenNetworkFilter); const chainId = useSelector(selectChainId); const networkConfigurationsByChainId = useSelector( selectNetworkConfigurations, @@ -85,6 +94,7 @@ const Tokens: React.FC = ({ tokens }) => { const tokenExchangeRates = useSelector(selectContractExchangeRates); const currentCurrency = useSelector(selectCurrentCurrency); const conversionRate = useSelector(selectConversionRate); + const networkName = useSelector(selectNetworkName); const nativeCurrencies = [ ...new Set( Object.values(networkConfigurationsByChainId).map( @@ -151,6 +161,10 @@ const Tokens: React.FC = ({ tokens }) => { } }; + const showFilterControls = () => { + navigation.navigate(...createTokenBottomSheetFilterNavDetails({})); + }; + const showSortControls = () => { navigation.navigate(...createTokensBottomSheetNavDetails({})); }; @@ -225,25 +239,61 @@ const Tokens: React.FC = ({ tokens }) => { const onActionSheetPress = (index: number) => index === 0 ? removeToken() : null; + const isTokenFilterEnabled = process.env.PORTFOLIO_VIEW === 'true'; + return ( - - + {isTokenFilterEnabled ? ( + + + {tokenNetworkFilter[chainId] + ? networkName ?? strings('wallet.current_network') + : strings('wallet.all_networks')} + + } + onPress={showFilterControls} + endIconName={IconName.ArrowDown} + style={styles.controlButton} + /> + + + + + + ) : ( + <> + + + + )} {tokensList && ( actionBarWrapper: { flexDirection: 'row', justifyContent: 'space-between', - paddingLeft: 16, - paddingRight: 16, + paddingLeft: 8, + paddingRight: 8, paddingBottom: 16, paddingTop: 8, }, + controlButtonOuterWrapper: { + flexDirection: 'row', + width: '100%', + justifyContent: 'space-between', + }, + controlButtonInnerWrapper: { + flexDirection: 'row', + }, controlButton: { backgroundColor: colors.background.default, borderColor: colors.border.default, borderStyle: 'solid', borderWidth: 1, + marginLeft: 5, + marginRight: 5, + maxWidth: '60%', + }, + controlButtonText: { + color: colors.text.default, + }, + controlIconButton: { + backgroundColor: colors.background.default, + borderColor: colors.border.default, + borderStyle: 'solid', + borderWidth: 1, + marginLeft: 5, + marginRight: 5, + borderRadius: 50, + width: 50, + height: 40, }, balanceContainer: { flexDirection: 'row', diff --git a/app/constants/navigation/Routes.ts b/app/constants/navigation/Routes.ts index 1a5197f213b..008fb429b5d 100644 --- a/app/constants/navigation/Routes.ts +++ b/app/constants/navigation/Routes.ts @@ -114,6 +114,7 @@ const Routes = { ORIGIN_SPAM_MODAL: 'OriginSpamModal', TOOLTIP_MODAL: 'tooltipModal', TOKEN_SORT: 'TokenSort', + TOKEN_FILTER: 'TokenFilter', CHANGE_IN_SIMULATION_MODAL: 'ChangeInSimulationModal', }, BROWSER: { diff --git a/app/selectors/preferencesController.ts b/app/selectors/preferencesController.ts index ba9c5388335..85ddc0b1025 100644 --- a/app/selectors/preferencesController.ts +++ b/app/selectors/preferencesController.ts @@ -47,6 +47,12 @@ export const selectTokenSortConfig = createSelector( preferencesControllerState.tokenSortConfig, ); +export const selectTokenNetworkFilter = createSelector( + selectPreferencesControllerState, + (preferencesControllerState: PreferencesState) => + preferencesControllerState.tokenNetworkFilter, +); + // isMultiAccountBalancesEnabled is a patched property - ref patches/@metamask+preferences-controller+2.1.0.patch export const selectIsMultiAccountBalancesEnabled = createSelector( selectPreferencesControllerState, diff --git a/app/util/test/initial-background-state.json b/app/util/test/initial-background-state.json index 483d29373a4..5e26fc63afb 100644 --- a/app/util/test/initial-background-state.json +++ b/app/util/test/initial-background-state.json @@ -197,6 +197,7 @@ "order": "dsc", "sortCallback": "stringNumeric" }, + "tokenNetworkFilter": {}, "privacyMode": false }, "TokenBalancesController": { diff --git a/bitrise.yml b/bitrise.yml index 78e924668d1..124b3e2daf6 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -1518,6 +1518,9 @@ app: - opts: is_expand: false MM_NETWORK_UI_REDESIGN_ENABLED: false + - opts: + is_expand: false + PORTFOLIO_VIEW: false - opts: is_expand: false MM_MULTICHAIN_V1_ENABLED: false diff --git a/e2e/selectors/wallet/WalletView.selectors.js b/e2e/selectors/wallet/WalletView.selectors.js index 5b02e02cf55..4586664a92c 100644 --- a/e2e/selectors/wallet/WalletView.selectors.js +++ b/e2e/selectors/wallet/WalletView.selectors.js @@ -12,6 +12,7 @@ export const WalletViewSelectorsIDs = { STAKE_BUTTON: 'stake-button', IMPORT_NFT_BUTTON: 'import-collectible-button', IMPORT_TOKEN_BUTTON: 'import-token-button', + IMPORT_TOKEN_BUTTON_LINK: 'import-token-button-link', NAVBAR_NETWORK_BUTTON: 'open-networks-button', NAVBAR_NETWORK_TEXT: 'open-networks-text', NFT_TAB_CONTAINER: 'collectible-contracts', diff --git a/locales/languages/en.json b/locales/languages/en.json index 7f7c89f1e55..1345810012d 100644 --- a/locales/languages/en.json +++ b/locales/languages/en.json @@ -439,6 +439,9 @@ }, "import": "Import", "sort_by": "Sort by", + "filter_by": "Filter by", + "current_network": "Current Network", + "all_networks": "All Networks", "declining_balance": "Declining balance ({{currency}} high-low)", "alphabetically": "Alphabetically (A-Z)", "add_to_get_started": "Add crypto to get started", diff --git a/patches/@metamask+preferences-controller+13.1.0.patch b/patches/@metamask+preferences-controller+13.1.0.patch index 1ad700b6326..95e65bb7e0f 100644 --- a/patches/@metamask+preferences-controller+13.1.0.patch +++ b/patches/@metamask+preferences-controller+13.1.0.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/@metamask/preferences-controller/dist/PreferencesController.cjs b/node_modules/@metamask/preferences-controller/dist/PreferencesController.cjs -index 97182f2..634de2e 100644 +index 97182f2..107ef23 100644 --- a/node_modules/@metamask/preferences-controller/dist/PreferencesController.cjs +++ b/node_modules/@metamask/preferences-controller/dist/PreferencesController.cjs @@ -17,7 +17,7 @@ const metadata = { @@ -11,18 +11,19 @@ index 97182f2..634de2e 100644 securityAlertsEnabled: { persist: true, anonymous: true }, selectedAddress: { persist: true, anonymous: false }, showTestNetworks: { persist: true, anonymous: true }, -@@ -26,6 +26,10 @@ const metadata = { +@@ -26,6 +26,11 @@ const metadata = { useTokenDetection: { persist: true, anonymous: true }, smartTransactionsOptInStatus: { persist: true, anonymous: false }, useTransactionSimulations: { persist: true, anonymous: true }, + useSafeChainsListValidation: { persist: true, anonymous: true }, + showMultiRpcModal: { persist: false, anonymous: false }, + tokenSortConfig: { persist: true, anonymous: false }, ++ tokenNetworkFilter: { persist: true, anonymous: false }, + privacyMode: { persist: true, anonymous: true }, }; const name = 'PreferencesController'; /** -@@ -41,7 +45,7 @@ function getDefaultPreferencesState() { +@@ -41,7 +46,7 @@ function getDefaultPreferencesState() { isIpfsGatewayEnabled: true, isMultiAccountBalancesEnabled: true, lostIdentities: {}, @@ -31,7 +32,7 @@ index 97182f2..634de2e 100644 securityAlertsEnabled: false, selectedAddress: '', showIncomingTransactions: { -@@ -71,6 +75,14 @@ function getDefaultPreferencesState() { +@@ -71,6 +76,15 @@ function getDefaultPreferencesState() { useTokenDetection: true, smartTransactionsOptInStatus: false, useTransactionSimulations: true, @@ -42,11 +43,12 @@ index 97182f2..634de2e 100644 + order: 'dsc', + sortCallback: 'stringNumeric' + }, ++ tokenNetworkFilter: {}, + privacyMode: false, }; } exports.getDefaultPreferencesState = getDefaultPreferencesState; -@@ -209,22 +221,22 @@ class PreferencesController extends base_controller_1.BaseController { +@@ -209,22 +223,22 @@ class PreferencesController extends base_controller_1.BaseController { * @param useNftDetection - Boolean indicating user preference on NFT detection. */ setUseNftDetection(useNftDetection) { @@ -76,7 +78,7 @@ index 97182f2..634de2e 100644 state.useNftDetection = false; } }); -@@ -305,6 +317,49 @@ class PreferencesController extends base_controller_1.BaseController { +@@ -305,6 +319,59 @@ class PreferencesController extends base_controller_1.BaseController { state.useTransactionSimulations = useTransactionSimulations; }); } @@ -114,6 +116,16 @@ index 97182f2..634de2e 100644 + }); + } + /** ++ * Set the token network filter configuration setting. ++ * ++ * @param tokenNetworkFilter - Object describing token sort configuration. ++ */ ++ setTokenNetworkFilter(tokenNetworkFilter) { ++ this.update((state) => { ++ state.tokenNetworkFilter = tokenNetworkFilter; ++ }); ++ } ++ /** + * A setter for the user preferences to enable/disable privacy mode. + * + * @param privacyMode - true to enable privacy mode, false to disable it. @@ -127,7 +139,7 @@ index 97182f2..634de2e 100644 exports.PreferencesController = PreferencesController; _PreferencesController_instances = new WeakSet(), _PreferencesController_syncIdentities = function _PreferencesController_syncIdentities(addresses) { diff --git a/node_modules/@metamask/preferences-controller/dist/PreferencesController.d.cts b/node_modules/@metamask/preferences-controller/dist/PreferencesController.d.cts -index 04a9d6f..77a9548 100644 +index 04a9d6f..5885d5b 100644 --- a/node_modules/@metamask/preferences-controller/dist/PreferencesController.d.cts +++ b/node_modules/@metamask/preferences-controller/dist/PreferencesController.d.cts @@ -65,7 +65,7 @@ export type PreferencesState = { @@ -139,7 +151,7 @@ index 04a9d6f..77a9548 100644 /** * Controls whether "security alerts" are enabled */ -@@ -100,6 +100,22 @@ export type PreferencesState = { +@@ -100,6 +100,26 @@ export type PreferencesState = { * Controls whether transaction simulations are enabled */ useTransactionSimulations: boolean; @@ -152,17 +164,21 @@ index 04a9d6f..77a9548 100644 + */ + showMultiRpcModal: boolean; + /** -+ * Controls whether Multi rpc modal is displayed or not ++ * Controls token sorting controls + */ + tokenSortConfig: Record; + /** ++ * Controls token filtering controls ++ */ ++ tokenNetworkFilter: Record; ++ /** + * Controls whether balance and assets are hidden or not + */ + privacyMode: boolean; }; declare const name = "PreferencesController"; export type PreferencesControllerGetStateAction = ControllerGetStateAction; -@@ -120,7 +136,7 @@ export declare function getDefaultPreferencesState(): { +@@ -120,7 +140,7 @@ export declare function getDefaultPreferencesState(): { isIpfsGatewayEnabled: boolean; isMultiAccountBalancesEnabled: boolean; lostIdentities: {}; @@ -171,18 +187,19 @@ index 04a9d6f..77a9548 100644 securityAlertsEnabled: boolean; selectedAddress: string; showIncomingTransactions: { -@@ -150,6 +166,10 @@ export declare function getDefaultPreferencesState(): { +@@ -150,6 +170,11 @@ export declare function getDefaultPreferencesState(): { useTokenDetection: boolean; smartTransactionsOptInStatus: boolean; useTransactionSimulations: boolean; + useSafeChainsListValidation: boolean; + showMultiRpcModal: boolean; + tokenSortConfig: Record; ++ tokenNetworkFilter: Record; + privacyMode: boolean; }; /** * Controller that stores shared settings and exposes convenience methods -@@ -218,11 +238,11 @@ export declare class PreferencesController extends BaseController): void; + /** ++ * Set the token sort configuration setting. ++ * ++ * @param tokenNetworkFilter - Object describing token sort configuration. ++ */ ++ setTokenNetworkFilter(tokenNetworkFilter: Record): void; ++ /** + * A setter for the user preferences to enable/disable privacy mode. + * + * @param privacyMode - true to enable privacy mode, false to disable it. @@ -227,3 +250,4 @@ index 04a9d6f..77a9548 100644 } export default PreferencesController; //# sourceMappingURL=PreferencesController.d.cts.map +\ No newline at end of file