Skip to content

Commit 444261c

Browse files
authored
fix: hide chain avatar beside Popular Networks (#29948) (#30263)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** When multiple networks are enabled, the token list and Activity tabs show the **Popular Networks** label using `totalEnabledNetworksCount > 1`. The network **avatar** was still gated on `!areAllNetworksSelected` for **popular EVM** rows only. After adding a custom network (e.g. Tempo) or leaving one popular chain off, that mismatch showed an **Ethereum** (or first-enabled) icon next to **Popular Networks**, which is incorrect per product expectation ([issue #29948](#29948)). **Change:** show the filter avatar only when we are **not** in the Popular Networks label state **and** not all popular EVM networks are selected—aligned in `BaseControlBar` (Tokens / DeFi control bar) and `ActivityView` (Activity tab). ## **Changelog** CHANGELOG entry: Fixed Popular Networks filter incorrectly showing a chain icon after enabling additional networks (e.g. Tempo). ## **Related issues** Fixes: #29948 ## **Manual testing steps** ```gherkin Feature: Popular Networks filter label and icon Scenario: No icon beside Popular Networks after adding a custom network Given the wallet has "All popular networks" (or equivalent multi-network) enabled with more than one network in the global filter When the user opens Menu > Networks, adds a custom network (e.g. Tempo), and closes the menu Then the Tokens tab network filter shows "Popular Networks" with no chain avatar beside it Scenario: Activity tab matches Tokens behavior Given the same multi-network state as above When the user opens the Activity tab (Transactions) network filter Then the filter shows "Popular networks" with no chain avatar beside it ``` ## **Screenshots/Recordings** <!-- Author will add before/after recordings in this section. --> ### **Before** https://github.com/user-attachments/assets/80528325-e123-4d7d-bf4f-2140c8c49df1 ### **After** https://github.com/user-attachments/assets/0984784e-9114-4a6e-aaab-b118778eec4b ## **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. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk UI-only conditional rendering change that affects when the network filter avatar is shown; main risk is minor visual regression across network-selection edge cases. > > **Overview** > Prevents the network filter from showing a chain `Avatar` when the label is `wallet.popular_networks` (i.e., when `totalEnabledNetworksCount > 1`) in both `BaseControlBar` and `ActivityView`. > > Updates unit tests to assert **no avatar** beside “Popular networks” in multi-network scenarios, while still rendering an avatar for single-network filters when not all popular networks are selected. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit e0a3038. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 66b4d06 commit 444261c

4 files changed

Lines changed: 49 additions & 16 deletions

File tree

app/components/UI/shared/BaseControlBar/BaseControlBar.test.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useNavigation } from '@react-navigation/native';
99
import { strings } from '../../../../../locales/i18n';
1010
import ButtonBase from '../../../../component-library/components/Buttons/Button/foundation/ButtonBase';
1111
import ButtonIcon from '../../../../component-library/components/Buttons/ButtonIcon';
12+
import Avatar from '../../../../component-library/components/Avatars/Avatar';
1213

1314
// Mock dependencies
1415
jest.mock('../../../../util/networks', () => ({
@@ -205,6 +206,8 @@ describe('BaseControlBar', () => {
205206
useNetworksByNamespaceModule.useNetworksByCustomNamespace.mockReturnValue({
206207
areAllNetworksSelected: false,
207208
totalEnabledNetworksCount: 2,
209+
networkCount: 10,
210+
selectedCount: 9,
208211
});
209212
useStylesModule.useStyles.mockReturnValue({ styles: defaultStyles });
210213
useNetworkEnablementModule.useNetworkEnablement.mockReturnValue({
@@ -289,19 +292,28 @@ describe('BaseControlBar', () => {
289292
expect(getByText('wallet.current_network')).toBeTruthy();
290293
});
291294

292-
it('shows network avatar when not all networks selected', () => {
295+
it('does not show network avatar beside "Popular Networks" when multiple networks enabled', () => {
293296
useNetworksByNamespaceModule.useNetworksByCustomNamespace.mockReturnValue(
294297
{
295298
areAllNetworksSelected: false,
296299
totalEnabledNetworksCount: 2,
297300
},
298301
);
299302

300-
renderComponent();
301-
// Avatar should be rendered (tested via component structure)
302-
expect(
303-
useNetworksByNamespaceModule.useNetworksByCustomNamespace,
304-
).toHaveBeenCalled();
303+
const { UNSAFE_queryAllByType } = renderComponent();
304+
expect(UNSAFE_queryAllByType(Avatar)).toHaveLength(0);
305+
});
306+
307+
it('shows network avatar when one network enabled and not all popular selected', () => {
308+
useNetworksByNamespaceModule.useNetworksByCustomNamespace.mockReturnValue(
309+
{
310+
areAllNetworksSelected: false,
311+
totalEnabledNetworksCount: 1,
312+
},
313+
);
314+
315+
const { UNSAFE_getAllByType } = renderComponent();
316+
expect(UNSAFE_getAllByType(Avatar).length).toBeGreaterThan(0);
305317
});
306318

307319
it('does not show network avatar when all networks selected', () => {

app/components/UI/shared/BaseControlBar/BaseControlBar.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ const BaseControlBar: React.FC<BaseControlBarProps> = ({
125125
}, [customIsDisabled]);
126126

127127
const displayAllNetworks = totalEnabledNetworksCount > 1;
128+
const showNetworkFilterAvatar =
129+
!displayAllNetworks && !areAllNetworksSelected;
128130

129131
// Shared navigation handlers
130132
const defaultHandleFilterControls = useCallback(() => {
@@ -148,7 +150,7 @@ const BaseControlBar: React.FC<BaseControlBarProps> = ({
148150
// Shared network label rendering
149151
const renderNetworkLabel = () => (
150152
<View style={styles.networkManagerWrapper}>
151-
{!areAllNetworksSelected && (
153+
{showNetworkFilterAvatar && (
152154
<View style={styles.networkAvatarWrapper}>
153155
<Avatar
154156
variant={AvatarVariant.Network}

app/components/Views/ActivityView/index.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import { useCurrentNetworkInfo } from '../../hooks/useCurrentNetworkInfo';
4141
import {
4242
NetworkType,
4343
useNetworksByCustomNamespace,
44-
useNetworksByNamespace,
4544
} from '../../hooks/useNetworksByNamespace/useNetworksByNamespace';
4645
import { useStyles } from '../../hooks/useStyles';
4746
import ErrorBoundary from '../ErrorBoundary';
@@ -96,15 +95,17 @@ const ActivityView = () => {
9695
const networkName = useSelector(selectNetworkName);
9796

9897
const { enabledNetworks, getNetworkInfo } = useCurrentNetworkInfo();
99-
const { areAllNetworksSelected } = useNetworksByNamespace({
98+
const {
99+
areAllNetworksSelected: areAllEvmPopularNetworksEnabled,
100+
totalEnabledNetworksCount,
101+
} = useNetworksByCustomNamespace({
100102
networkType: NetworkType.Popular,
103+
namespace: KnownCaipNamespace.Eip155,
101104
});
102105

103-
const { areAllNetworksSelected: areAllEvmPopularNetworksEnabled } =
104-
useNetworksByCustomNamespace({
105-
networkType: NetworkType.Popular,
106-
namespace: KnownCaipNamespace.Eip155,
107-
});
106+
const displayAllNetworks = totalEnabledNetworksCount > 1;
107+
const showNetworkFilterAvatar =
108+
!displayAllNetworks && !areAllEvmPopularNetworksEnabled;
108109

109110
const currentNetworkName = getNetworkInfo(0)?.networkName;
110111

@@ -255,7 +256,7 @@ const ActivityView = () => {
255256
label={
256257
<>
257258
<View style={styles.networkManagerWrapper}>
258-
{!areAllNetworksSelected && (
259+
{showNetworkFilterAvatar && (
259260
<Avatar
260261
variant={AvatarVariant.Network}
261262
size={AvatarSize.Xs}
@@ -267,7 +268,7 @@ const ActivityView = () => {
267268
variant={TextVariant.BodyMDMedium}
268269
numberOfLines={1}
269270
>
270-
{enabledNetworks.length > 1
271+
{displayAllNetworks
271272
? strings('wallet.popular_networks')
272273
: (currentNetworkName ??
273274
strings('wallet.current_network'))}

app/components/Views/ActivityView/index.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ jest.mock('../../../core/Engine', () => ({
139139
}));
140140

141141
let mockAreAllEvmPopularNetworksEnabled = false;
142+
/** Must mirror controller: label uses totalEnabledNetworksCount > 1 */
143+
let mockTotalEnabledNetworksCount = 2;
142144

143145
jest.mock('../../hooks/useNetworksByNamespace/useNetworksByNamespace', () => ({
144146
useNetworksByNamespace: () => ({
@@ -151,6 +153,7 @@ jest.mock('../../hooks/useNetworksByNamespace/useNetworksByNamespace', () => ({
151153
useNetworksByCustomNamespace: () => ({
152154
networks: [],
153155
areAllNetworksSelected: mockAreAllEvmPopularNetworksEnabled,
156+
totalEnabledNetworksCount: mockTotalEnabledNetworksCount,
154157
}),
155158
NetworkType: {
156159
Popular: 'popular',
@@ -286,6 +289,7 @@ describe('ActivityView', () => {
286289
mockPerpsEnabled = false;
287290
mockPredictEnabled = false;
288291
mockAreAllEvmPopularNetworksEnabled = false;
292+
mockTotalEnabledNetworksCount = 2;
289293
clearRenderedTabs();
290294
mockRoute.params = {};
291295
});
@@ -301,12 +305,26 @@ describe('ActivityView', () => {
301305
});
302306

303307
it('displays "Popular networks" text when multiple networks are enabled', () => {
308+
mockTotalEnabledNetworksCount = 2;
309+
mockAreAllEvmPopularNetworksEnabled = false;
310+
304311
const { getByText } = renderComponent(mockInitialState);
305312

306313
expect(getByText('Popular networks')).toBeOnTheScreen();
307314
});
308315

316+
it('does not render network avatar beside Popular networks when multiple networks enabled', () => {
317+
mockTotalEnabledNetworksCount = 2;
318+
mockAreAllEvmPopularNetworksEnabled = false;
319+
320+
const { getByText, queryByTestId } = renderComponent(mockInitialState);
321+
322+
expect(getByText('Popular networks')).toBeOnTheScreen();
323+
expect(queryByTestId('network-avatar-image')).toBeNull();
324+
});
325+
309326
it('displays network name when single network is enabled', () => {
327+
mockTotalEnabledNetworksCount = 1;
310328
const singleNetworkInfo = {
311329
enabledNetworks: [{ chainId: '0x1', enabled: true }],
312330
getNetworkInfo: jest.fn(() => ({

0 commit comments

Comments
 (0)