-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
feat: Updated TabsList to no longer use ScrollView, but only rendering active tab #21822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
|
|
||
| return contentHeight + emptyStateHeight + padding; | ||
| }, [isHomepageRedesignV1Enabled, formattedDeFiPositions?.length]); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These calculations are needed for the FlashList to work with autoheight. After talking with @Prithpal-Sooriya , there will be a follow up to conditionally not use flashlist for when autoheight is needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I am happy with this being a fast follow. I'll do an initial spike to see what work would be required to conditionally not use a FlashList in this specific state.
| const itemWidth = (availableWidth - gapBetweenItems) / 3; | ||
| const textHeight = 44; | ||
| const rowMarginBottom = 12; | ||
| const estimatedRowHeight = itemWidth + textHeight + rowMarginBottom; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These calculations are needed for the FlashList to work with autoheight. After talking with @Prithpal-Sooriya , there will be a follow up to conditionally not use flashlist for when autoheight is needed
| if (!isHomepageRedesignV1Enabled) return undefined; | ||
| if (claimablePositions.length === 0) return undefined; | ||
| return claimablePositions.length * estimatedPositionItemHeight + 48; | ||
| }, [isHomepageRedesignV1Enabled, claimablePositions.length]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These calculations are needed for the FlashList to work with autoheight. After talking with @kevinbluer , there will be a follow up to conditionally not use flashlist for when autoheight is needed
app/components/UI/Tokens/index.tsx
Outdated
|
|
||
| const estimatedTokenItemHeight = 64; | ||
|
|
||
| const calculatedListHeight = useMemo(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These calculations are needed for the FlashList to work with autoheight. After talking with @Prithpal-Sooriya , there will be a follow up to conditionally not use flashlist for when autoheight is needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This pull request implements a homepage redesign feature (v1) that introduces an auto-height scrolling experience when the feature flag is enabled. The changes modify the main wallet view and various tab components to support dynamic height calculations and conditional scrolling behavior.
Key changes:
- Adds support for an
autoHeightmode in the TabsList component that dynamically adjusts container heights based on content - Implements a feature-flagged redesign (
isHomepageRedesignV1Enabled) that disables nested scrolling and uses calculated heights for tab content - Adds logic to reset the current tab index when the tabs structure changes (e.g., network switching)
Reviewed Changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
app/components/Views/Wallet/index.tsx |
Wraps wallet content in ScrollView with conditional scrolling based on homepage redesign flag; adds tab reset logic |
app/components/Views/TokensFullView/TokensFullView.tsx |
Replaces includesTopInset prop with style prop and adds testID to back button |
app/components/Views/TokensFullView/TokensFullView.test.tsx |
Simplifies back button test using new testID; removes duplicate and unnecessary tests |
app/components/UI/Tokens/index.tsx |
Implements maxItems limit and calculated height for token list when homepage redesign is enabled |
app/components/UI/Tokens/TokenList/index.tsx |
Moves "View all tokens" button into ListFooterComponent; adjusts container classes for homepage redesign |
app/components/UI/Tokens/TokenList/index.test.tsx |
Updates mocks to use selector functions directly instead of string matching |
app/components/UI/Predict/views/PredictTabView/PredictTabView.tsx |
Conditionally disables scrolling and adjusts flex styling for homepage redesign |
app/components/UI/Predict/components/PredictPositions/PredictPositions.tsx |
Calculates and applies fixed heights for position lists when homepage redesign is enabled |
app/components/UI/Perps/components/PerpsLoadingSkeleton/PerpsLoadingSkeleton.tsx |
Adjusts container styling to use min-height instead of flex-1 for homepage redesign |
app/components/UI/Perps/components/PerpsLoadingSkeleton/PerpsLoadingSkeleton.test.tsx |
Adds mock setup for the homepage redesign feature flag |
app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.tsx |
Disables scrolling and adjusts flex styling based on homepage redesign flag |
app/components/UI/NftGrid/NftGrid.tsx |
Implements maxItems limit (18) and calculated height for NFT grid; removes unused props |
app/components/UI/NftGrid/NftGrid.test.tsx |
Updates tests to reflect new maxItems behavior driven by feature flag |
app/components/UI/DeFiPositions/DeFiPositionsList.tsx |
Adds calculated height wrapper for DeFi positions list with homepage redesign |
app/component-library/components-temp/Tabs/TabsList/TabsList.types.ts |
Adds autoHeight prop to TabsList interface |
app/component-library/components-temp/Tabs/TabsList/TabsList.tsx |
Implements auto-height mode with dynamic height measurement and tab content preloading |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
app/component-library/components-temp/Tabs/TabsList/TabsList.tsx
Outdated
Show resolved
Hide resolved
app/component-library/components-temp/Tabs/TabsList/TabsList.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work getting the dynamic tab heights working! Approving to unblock delivery, but flagging critical technical debt for fast follow up.
The TabsList component is now managing 6+ timeout systems and multiple ref Maps, which raises potential memory management concerns:
- The cleanup effect runs expensive operations on every tab change
- Height measurement callbacks may hold onto stale closures after unmount
- Timers for scroll, load, and measurement aren’t coordinated and should be consolidated
- The height effect triggers 4+ times per tab switch due to overly broad dependencies
- A new Map is created on every height change, in a hot render path
The component has also grown in complexity. The height measurement logic alone spans 100+ lines, with tightly coupled effects that will be difficult to maintain or debug safely.
As part of the follow-up work with the @MetaMask/metamask-assets team, I'd recommend exploring ways to simplify the need for this level of complexity. It might also be worth bringing in the Margelo team for a review. They may have ideas on achieving this layout with a lighter implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After a sync with @brianacnguyen, to unblock delivery, we are accepting the complex item size calculations.
We will a have a fast follow to clean this up - discussed in comments:
- https://github.com/MetaMask/metamask-mobile/pull/21822/files#r2473876069
- https://github.com/MetaMask/metamask-mobile/pull/21822/files#r2475981442
If possible can we get some QA testing on this before it gets merged, just to alieviate some concerns or any need to revert this?
0a55e55
|
Testing on the QA build from here:
wallet.scroll.PR.iOS.mp4 |
|
Testing on the QA build from here , the View all NFTs button is missing on iOS. Android build failed, so it cannot be tested wallet.scroll.PR.1.mp4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PerpsTab seems to work fine with these changes. I do notice a behavior where the screen snaps to the view, when the scrolled to tab takes up less vertical space than the previous tab. Unsure if there's anything to do about this?
It's most noticeable when the empty states or "Retry Connection" components are rendered, and you are swiping to that tab from another long list where you have previously scrolled down.
Screen.Recording.2025-10-31.at.12.16.46.PM.mov
885f3b3 to
a17d05f
Compare
- Use InteractionManager to defer content rendering until animations finish - Prevents UI lag during tab transitions - Already-loaded tabs still render immediately - Properly cleanup interaction handles on component unmount
- Mock InteractionManager.runAfterInteractions for test determinism - Update tests to handle deferred content loading behavior - Replace timer-based tests with InteractionManager-based tests - Add tests for interaction cancellation on rapid tab switching - Add test for already-loaded tabs rendering immediately - Update assertions for disabled tabs to check activeIndex instead of DOM - Update snapshots to match new deferred loading behavior
5be8a02 to
266caf6
Compare
|
Testing on the QA build from here:
error.token.list.not.loading.completely.mp4opening.MM.mp4Perps.failed.to.close.position.mp4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests well on iOS simulator and Android physical device. Feel free to ping for re-approval once code coverage is fixed! 👍
…sk/metamask-mobile into fix/tab-individual-height
…to fix/tab-individual-height
|
Added additional tests but sonar cloud still fails. will skip for now |
|





Description
This PR simplifies the
TabsListcomponent by removing complex ScrollView-based tab navigation in favor of a lightweight gesture-based approach.What is the reason for the change?
The previous
TabsListimplementation had significant complexity that was unnecessary for the wallet page use case:This complexity caused:
What is the improvement/solution?
This PR dramatically simplifies TabsList by:
Replacing ScrollView with gesture-based navigation using
react-native-gesture-handlerhiddenCSS classSimplifying tab rendering
hiddenclassStreamlined caching
Reduced code complexity
useTailwind,Dimensions, scroll event typesImproved gesture support
react-native-reanimatedBenefits:
Changelog
CHANGELOG entry: Simplified TabsList component with gesture-based navigation
Related issues
Fixes: https://consensyssoftware.atlassian.net/jira/software/c/projects/DSYS/boards/1888?search=tab&selectedIssue=DSYS-247
Manual testing steps
Screenshots/Recordings
Before
After
Simulator.Screen.Recording.-.iPhone.15.Pro.Max.-.2025-11-02.at.22.56.05.mp4
Pre-merge author checklist
Pre-merge reviewer checklist
Note
Refactors TabsList to use pan-gesture navigation and InteractionManager-driven lazy loading, rendering only loaded/active tabs and updating tests/snapshots accordingly.
TabsList:ScrollViewpaging and scroll handlers withGestureDetectorpan gestures; remove scroll refs/timeouts and layout width logic.InteractionManager.runAfterInteractions; cache loaded tabs; render only active/loaded content; skip disabled tabs; preserve selection bykeyon children changes.goToTabIndex,getCurrentIndex); pass throughtabsBarPropsand testIDs; simplify layout to a singleBoxcontainer.InteractionManager, verify deferred loading, cancellation, immediate loads for cached tabs, and gesture wrapper presence.ScrollView.Written by Cursor Bugbot for commit 2d17260. This will update automatically on new commits. Configure here.