Skip to content

feat: hub page discovery tabs A/B test #29193

Merged
vinnyhoward merged 38 commits into
mainfrom
feat-tmcu-591-homepage-hub-tabs-ui
May 6, 2026
Merged

feat: hub page discovery tabs A/B test #29193
vinnyhoward merged 38 commits into
mainfrom
feat-tmcu-591-homepage-hub-tabs-ui

Conversation

@vinnyhoward

@vinnyhoward vinnyhoward commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Description

Implements the treatment UI for the coreMCU589AbtestHubPageDiscoveryTabs A/B test

When the treatment variant is active, the homepage replaces the standard scrollable layout with a top-tab navigation bar exposing three verticals:

  • Portfolio: existing homepage sections with balance header and pull-to-refresh
  • Perpetuals: PerpsHomeView wrapped in connection + stream providers
  • Predictions: PredictFeed wrapped in preview sheet provider

When 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 tabs

  • TabsList — design-system tab primitive with keepMounted support. Perpetuals and Predictions both use keepMounted={false}:

    • Performance: both screens have a heavy hydration cost on first mount; keeping them alive while invisible wastes resources on tabs the user may never visit
    • Open connections: PerpsHomeView establishes WebSocket channels for live market data via its stream providers on mount; leaving these running in the background wastes bandwidth and server-side connection slots
    • Memory: PredictFeed and its preview sheet provider hold feed state that can safely be discarded between visits
    • Portfolio uses keepMounted={true} (the default) since it is the landing tab and its scroll position and section state should survive switching away and back
  • Wallet header animates up/down on scroll via Reanimated shared values; icon collapse and gradient opacity are synced via TabIconAnimationContext

  • Scroll event forwarding keeps existing HOME_VIEWED section analytics working in the Portfolio tab

  • A/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

Feature: Hub Page Discovery Tabs

  Scenario: treatment variant shows top tabs
    Given the coreMCU589AbtestHubPageDiscoveryTabs flag is set to "treatment"

    When user opens the app to the Wallet screen
    Then three tabs are visible: Portfolio, Perpetuals, Predictions
    And switching between tabs is smooth with no layout shift
    And the wallet header hides on scroll and restores on tab switch
    And pull-to-refresh works on the Portfolio tab

  Scenario: control variant preserves existing layout
    Given the coreMCU589AbtestHubPageDiscoveryTabs flag is set to "control"

    When user opens the app to the Wallet screen
    Then the existing homepage layout is shown with no tabs

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)

  • 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 to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production performance metrics

For performance guidelines and tooling, see the Performance Guide.

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.

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 HomepageDiscoveryTabs experience 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 emits onHeaderHiddenChange to sync sibling animations.

Updates Wallet to 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. PerpsHomeView and PredictFeed gain hideHeader and header-sync props, and useFeedScrollManager can 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.

- 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
@github-actions

Copy link
Copy Markdown
Contributor

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.

interface DiscoveryTabViewProps {
tabLabel: string;
tabIcon?: IconName;
keepMounted?: boolean;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread app/components/UI/Predict/hooks/useDiscoveryScrollManager.ts Outdated
…underline, re-enable A/B variant gating, and add test coverage for keepMounted and new PerpsHomeView props
@codecov-commenter

codecov-commenter commented Apr 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.27193% with 131 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.83%. Comparing base (a614776) to head (44ae1da).
⚠️ Report is 79 commits behind head on main.

Files with missing lines Patch % Lines
...y/components-temp/Tabs/TabsIconBar/TabsIconBar.tsx 53.14% 45 Missing and 22 partials ⚠️
...components-temp/Tabs/TabsIconList/TabsIconList.tsx 67.96% 19 Missing and 14 partials ⚠️
...ts/HomepageDiscoveryTabs/HomepageDiscoveryTabs.tsx 72.22% 8 Missing and 7 partials ⚠️
app/components/Views/Wallet/index.tsx 64.51% 4 Missing and 7 partials ⚠️
...omponents/UI/Predict/hooks/useFeedScrollManager.ts 0.00% 2 Missing and 1 partial ⚠️
...y/components-temp/Tabs/TabsIconTab/TabsIconTab.tsx 95.23% 0 Missing and 1 partial ⚠️
...nts/UI/Perps/Views/PerpsHomeView/PerpsHomeView.tsx 92.85% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

caieu
caieu previously approved these changes May 5, 2026
@vinnyhoward vinnyhoward dismissed stale reviews from caieu and gambinish via 8c011c8 May 6, 2026 00:30

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread app/components/UI/Perps/Views/PerpsHomeView/PerpsHomeView.tsx
pull Bot pushed a commit to Reality2byte/metamask-mobile that referenced this pull request May 6, 2026
…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 -->
@vinnyhoward vinnyhoward added the skip-sonar-cloud Only used for bypassing sonar cloud when failures are not relevant to the changes. label May 6, 2026
@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokeWalletPlatform, SmokePerps, SmokePredictions, SmokeConfirmations, SmokeSwap, SmokeStake, SmokeMoney, SmokeAccounts, SmokeNetworkAbstractions
  • Selected Performance tags: @PerformanceLaunch, @PerformanceAssetLoading
  • Risk Level: high
  • AI Confidence: 82%
click to see 🤖 AI reasoning details

E2E 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:

  1. Wallet/index.tsx (core wallet view): This is the central screen that virtually all E2E tests interact with. The changes add:

    • A new A/B test branch that renders HomepageDiscoveryTabs instead of the standard Homepage
    • Reanimated-based animated header (hide/show on scroll) using useSharedValue and useAnimatedStyle
    • New layout structure with walletHeaderRoot, portfolioHeader, portfolioHeaderBase content splits
    • Even in the control path, the content structure has been refactored (bannerContent, portfolioHeaderBase extracted)
    • This refactoring could affect test selectors like WALLET_SCROLL_VIEW, WALLET_HEADER_ROOT, etc.
  2. PerpsHomeView.tsx: Modified to accept new props and uses Reanimated.ScrollView instead of ScrollView. The header is now conditionally rendered (hideHeader prop). This directly affects SmokePerps tests.

  3. PredictFeed.tsx: Modified to accept hideHeader and onHeaderHiddenChange props. This directly affects SmokePredictions tests.

  4. HomepageDiscoveryTabs (new): Integrates Portfolio (Homepage), Perpetuals (PerpsHomeView), and Predictions (PredictFeed) into a single tabbed view. This is the Trending tab concept from SmokeWalletPlatform.

  5. useDiscoveryScrollManager (new hook): Scroll-driven header animation logic using Reanimated worklets.

  6. useFeedScrollManager.ts: Updated with onHeaderHiddenChange callback.

Tags selected:

  • SmokeWalletPlatform: Primary tag - Wallet view is heavily modified, Trending/discovery tabs are the core feature, transaction history display affected
  • SmokePerps: PerpsHomeView directly modified with new props and Reanimated.ScrollView
  • SmokePredictions: PredictFeed directly modified with new props
  • SmokeConfirmations: Per tag descriptions, SmokePerps and SmokePredictions require SmokeConfirmations (on-chain transactions)
  • SmokeSwap: Per tag descriptions, SmokePerps integrates with SmokeSwap for trading category entry
  • SmokeStake: Wallet view changes could affect stake flows that originate from wallet actions
  • SmokeMoney: Wallet home changes affect buy/sell entry points; per tag description, wallet home changes warrant SmokeWalletPlatform which is already selected
  • SmokeAccounts: Account selector and wallet header changes could affect account management flows
  • SmokeNetworkAbstractions: Network selector is part of the wallet header area that was restructured

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:
The Wallet/index.tsx changes introduce Reanimated animations (useSharedValue, useAnimatedStyle) to the wallet header, which is rendered on every app launch and wallet view. The new HomepageDiscoveryTabs component adds a tabbed layout with lazy-mounted content (Portfolio, Perpetuals, Predictions) and gradient overlays. These changes could impact: (1) App launch performance - the wallet header now has animated styles computed on every render; (2) Asset loading performance - the portfolio tab now uses Reanimated.ScrollView with scroll handlers, and the content structure has been refactored. The @PerformanceLaunch tag covers cold/warm start and time-to-interactive, and @PerformanceAssetLoading covers token list rendering and balance fetching which are part of the Portfolio tab content.

View GitHub Actions results

@sonarqubecloud

sonarqubecloud Bot commented May 6, 2026

Copy link
Copy Markdown

@vinnyhoward vinnyhoward removed the skip-sonar-cloud Only used for bypassing sonar cloud when failures are not relevant to the changes. label May 6, 2026
@vinnyhoward vinnyhoward added this pull request to the merge queue May 6, 2026
Merged via the queue into main with commit b283dbd May 6, 2026
108 of 109 checks passed
@vinnyhoward vinnyhoward deleted the feat-tmcu-591-homepage-hub-tabs-ui branch May 6, 2026 17:44
@github-actions github-actions Bot locked and limited conversation to collaborators May 6, 2026
@metamaskbotv2 metamaskbotv2 Bot added the release-7.77.0 Issue or pull request that will be included in release 7.77.0 label May 6, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.77.0 Issue or pull request that will be included in release 7.77.0 size-XL team-mobile-ux Mobile UX team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants