feat: hub page discovery tabs A/B test #29193
Conversation
- Add HomepageDiscoveryTabs component with Portfolio, Perpetuals, and Predictions tabs - Wire A/B test gate in Wallet — treatment renders discovery tabs, control renders existing homepage - Lazy-mount Perps and Predictions inline as tab content with hideHeader prop to suppress duplicate headers - Extend TabsList/TabsBar/Tab to support per-tab icons using local icon system - Register portfolio and predict custom SVG icons - Add per-tab gradient overlay that bleeds into wallet header with color per tab - Fix Perps safe area top inset when rendered as embedded tab
|
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. |
…tmcu-591-homepage-hub-tabs-ui
…tmcu-591-homepage-hub-tabs-ui
| interface DiscoveryTabViewProps { | ||
| tabLabel: string; | ||
| tabIcon?: IconName; | ||
| keepMounted?: boolean; |
There was a problem hiding this comment.
I suspect that this is at least part of the issue. TabList defaults to keepMounted true. So if we omit this, each tab remains mounted (including perps and predict tabs)
I know that the perps views call several websocket channels to fetch data, so we are certainly wasting bandwidth by keeping these views mounted even if not visible.
What do you think about making this keepMounted property required, and passing it in as false?
There was a problem hiding this comment.
The tradeoff is that Perps takes ~1 second to fully mount and render on each visit, which would be noticeable every time the user switches to that tab. keepMounted={true} was intentional, it trades bandwidth for a faster tab switch UX. The sluggishness in dev mode is mostly JS thread overhead that isn't present in release builds, so the current behavior feels acceptable there. I do like the idea of opting out of websocket connections though, once we solve the 1 second mounting issue I think we should do this. We currently do it for the Predict screen.
There was a problem hiding this comment.
Hmm, we have a preloader that "warms" the perps tab to prevent this kind of remount if it happens within a given grace period. Is this still an issue after your changes to optimize the useWithdrawRequests selector?
There was a problem hiding this comment.
Yeah doesn't seem to be an issue anymore, I'm testing it now. I've added keepMounted={false} for Perps and things seem fast. We won't have to worry about idle listeners
…underline, re-enable A/B variant gating, and add test coverage for keepMounted and new PerpsHomeView props
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #29193 +/- ##
==========================================
- Coverage 81.84% 81.83% -0.02%
==========================================
Files 5244 5262 +18
Lines 138676 139424 +748
Branches 31462 31645 +183
==========================================
+ Hits 113503 114097 +594
- Misses 17450 17541 +91
- Partials 7723 7786 +63 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…tmcu-591-homepage-hub-tabs-ui
…tmcu-591-homepage-hub-tabs-ui
…tmcu-591-homepage-hub-tabs-ui
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8c011c8. Configure here.
…etection, and collapse animation (MetaMask#29684) ## **Description** Adds new `TabsIcon` components to `components-temp` and new icons to the component library as part of the hub page discovery tabs A/B test. Extracted into its own PR so it can be reviewed and merged independently before the feature PR lands. --- ### `TabsIcon` Component Family **`TabsIconTab`** Tab item that renders an icon above a label. Icon and label have active/disabled color states. Supports a `fillWidth` prop for equal-width layouts and animated icon collapse via `TabsIconAnimationContext`. **`TabsIconBar`** Tab bar built for icon+label tabs. Features: - Automatic overflow detection — switches between a fixed layout and a horizontally scrollable `ScrollView` - Animated sliding underline indicator - `fillWidth` mode for equal-width tabs - Height collapse animation for hiding the bar on scroll **`TabsIconList`** Lazy-mounting tab list that manages active state, swipe gestures, and `InteractionManager`-based content loading. Per-tab `keepMounted` prop controls whether inactive tab content stays mounted or is unmounted when the tab loses focus. **`TabsIconAnimationContext`** React context that carries an `Animated.Value` (`0` = icons expanded, `1` = collapsed) from a parent provider down to `TabsIconTab` without prop drilling. --- ### Icons Adds three new SVG icons and registers them in `Icon.assets.ts` / `Icon.types.ts`: | Icon | `IconName` | |---|---| | Portfolio | `IconName.Portfolio` | | Predict | `IconName.Predict` | | Candlestick | `IconName.Candlestick` | ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-591 ## **Manual testing steps** ```gherkin Feature: Tabs component library Scenario: user navigates between tabs Given the app is open on any screen using TabsList When user taps each tab Then the animated underline slides to the active tab And the correct tab content is displayed Scenario: tabs overflow to scrollable mode Given a TabsBar with more tabs than fit the container width When the screen renders Then the tab bar becomes horizontally scrollable And the active tab is scrolled into view automatically Scenario: new icons render correctly Given any view that uses the Icon component When IconName.Portfolio or IconName.Predict is passed Then the correct SVG icon is displayed ``` ## **Screenshots/Recordings** Take from the feature branch using these components [here](MetaMask#29193) ### iOS Dark Mode https://github.com/user-attachments/assets/7df6a46d-b3b3-44cc-a697-b796581dd759 Light Mode (No Gradient) https://github.com/user-attachments/assets/97b1f901-6092-42f9-926c-e8e6785c6f4e ### Android Dark Mode https://github.com/user-attachments/assets/4232f3fc-a47c-4516-93c2-104e4a3fb5cb Light Mode (No Gradient) https://github.com/user-attachments/assets/3f11dd8f-696f-438e-9867-1b08dc69200d ### **Before** `~` ### **After** `~` ## **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) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] 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 - [x] 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** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] 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. [TMCU-591]: https://consensyssoftware.atlassian.net/browse/TMCU-591?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Adds new tab UI components plus shared layout/list hooks involving animations, gesture handling, and InteractionManager-based lazy loading; regressions would primarily impact navigation/UX in screens adopting these new components. Also extends the global `IconName` enum/assets, which can affect consumers if mis-registered. > > **Overview** > Adds a new `TabsIcon` component family under `components-temp`: > > `TabsIconTab` renders icon+label tabs with active/disabled states and optional icon collapse via `TabIconAnimationContext`, `TabsIconBar` manages underline animation plus *auto scroll overflow detection* and optional height collapse, and `TabsIconList` composes the bar with lazy-mounted tab content, disabled-tab handling, swipe gestures, and a small imperative ref API. > > Introduces reusable hooks `useTabsBarLayout` (measures tab/container layouts, toggles scroll mode, drives underline animations, and resets/cleans up on layout/tab changes) and `useTabsList` (active index normalization, InteractionManager-backed lazy loading with timeout fallback, key-preserving tab updates, and swipe-to-switch). > > Extends the icon library by registering new SVGs and `IconName` entries (`Group`, `Portfolio`, `Predictions`) and updates the `candlestick.svg` asset. Comprehensive unit tests are added for the new components and hooks. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ffa57ba. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…tmcu-591-homepage-hub-tabs-ui
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection: The changes in this PR introduce a significant new feature: HomepageDiscoveryTabs, a tabbed navigation hub (Portfolio, Perpetuals, Predictions) embedded in the Wallet view, controlled by an A/B test flag. Key impacts:
Tags selected:
The changes are behind an A/B test flag (treatment vs control), so the control path is preserved. However, even the control path has been refactored (content structure changes), which could affect existing tests that rely on specific component hierarchies or test IDs. Performance Test Selection: |
|




Description
Implements the treatment UI for the
coreMCU589AbtestHubPageDiscoveryTabsA/B testWhen the treatment variant is active, the homepage replaces the standard scrollable layout with a top-tab navigation bar exposing three verticals:
PerpsHomeViewwrapped in connection + stream providersPredictFeedwrapped in preview sheet providerWhen the control variant is active (or the flag is absent), the existing homepage layout is fully preserved.
Key implementation details:
HomepageDiscoveryTabs: new component that owns tab layout, per-tab gradient crossfade, and wallet header hide/show coordination across all three tabsTabsList— design-system tab primitive withkeepMountedsupport. Perpetuals and Predictions both usekeepMounted={false}:PerpsHomeViewestablishes WebSocket channels for live market data via its stream providers on mount; leaving these running in the background wastes bandwidth and server-side connection slotsPredictFeedand its preview sheet provider hold feed state that can safely be discarded between visitskeepMounted={true}(the default) since it is the landing tab and its scroll position and section state should survive switching away and backWallet header animates up/down on scroll via Reanimated shared values; icon collapse and gradient opacity are synced via
TabIconAnimationContextScroll event forwarding keeps existing
HOME_VIEWEDsection analytics working in the Portfolio tabA/B gating is handled by
useABTest(HUB_PAGE_DISCOVERY_TABS_AB_KEY)Figma Design for reference. Only difference between this UI and the Figma is that the tabs take up the entire space
Changelog
CHANGELOG entry:null
Related issues
Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-591
Manual testing steps
Screenshots/Recordings
iOS
Dark Mode
dark_mode_ios.mov
Light Mode (No Gradient)
light_mode_ios.mov
Android
Dark Mode
dark_mode_android.mov
Light Mode (No Gradient)
light_mode_android.mov
Before
~After
~Pre-merge author checklist
Performance checks (if applicable)
trace()for usage andaddTokenfor an exampleFor performance guidelines and tooling, see the Performance Guide.
Pre-merge reviewer checklist
Note
Medium Risk
Introduces a new A/B-test-gated navigation structure and shared header animation/scroll handling on the Wallet home screen, which could affect core navigation and scroll/refresh behavior. Risk is mitigated by control-path preservation and added unit coverage, but Reanimated worklet interactions and tab switching edge cases remain sensitive.
Overview
When the Hub Page Discovery Tabs A/B test is in treatment (and homepage sections are enabled), Wallet home now renders a new top-level
HomepageDiscoveryTabsexperience with three tabs: Portfolio, Perpetuals (PerpsHomeView), and Predictions (PredictFeed). The control path preserves the existing scroll layout.Adds
useDiscoveryScrollManager, a Reanimated-backed hook that hides/shows the shared Wallet header based on scroll threshold, restores per-tab header state on tab entry, forwards scroll events back to JS (for analytics), and emitsonHeaderHiddenChangeto sync sibling animations.Updates
Walletto measure/animate the header via shared values, pass refresh/portfolio header/scroll callbacks into the new tabs view, and crossfade a dark-mode gradient overlay per active tab.PerpsHomeViewandPredictFeedgainhideHeaderand header-sync props, anduseFeedScrollManagercan now notify header hidden/show changes; tests are added/updated across these components and the new hook.Reviewed by Cursor Bugbot for commit 38e679d. Bugbot is set up for automated code reviews on this repo. Configure here.