Skip to content

fix(predict): prevent buy/sell sheet from being cropped in HomepageDiscoveryTabs#30219

Merged
vinnyhoward merged 9 commits into
mainfrom
fix-tmcu-752-predict-sheet-homepage-discovery-tabs
May 20, 2026
Merged

fix(predict): prevent buy/sell sheet from being cropped in HomepageDiscoveryTabs#30219
vinnyhoward merged 9 commits into
mainfrom
fix-tmcu-752-predict-sheet-homepage-discovery-tabs

Conversation

@vinnyhoward

@vinnyhoward vinnyhoward commented May 14, 2026

Copy link
Copy Markdown
Contributor

Description

When PredictFeed is rendered inside HomepageDiscoveryTabs (behind the coreMCU589AbtestHubPageDiscoveryTabs A/B test), the PredictPreviewSheetProvider bottom sheet is cropped by the tab layout and unusable.

Changes:

  • Added disableBottomSheet prop to PredictPreviewSheetProvider — when true, openBuySheet/openSellSheet navigate to the full-screen bet slip via Routes.PREDICT.ROOT instead of opening the sheet; HomepageDiscoveryTabs passes this prop for the Predictions tab
  • Replaced the _providerMounted boolean singleton with a reference counter (_providerSheetModeCount) to correctly handle concurrent providers (e.g. PredictScreenStack + HomepageDiscoveryTabs both mounted); renamed the exported helper to shouldSuppressLegacyOrderFailureToast to reflect actual semantics — a disableBottomSheet provider must not suppress the failure toast
  • Added beforeRemove listener to PredictBuyPreview for swipe/hardware-back dismiss tracking in screen mode, gated behind a trackSwipeDismiss route param set only by disableBottomSheet navigations — preserves prior behavior for the pre-existing flagless path
  • Fixed extra bottom safe-area padding in PredictFeed and PerpsHomeView when rendered as tabs (hideHeader=trueedges=[])

Changelog

CHANGELOG entry:null

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-752 https://consensyssoftware.atlassian.net/browse/TMCU-766

Manual testing steps

Feature: Predict buy sheet in HomepageDiscoveryTabs

  Scenario: user opens a prediction from the Predictions tab
    Given the coreMCU589AbtestHubPageDiscoveryTabs flag is set to treatment
    And the user is on the Predictions tab

    When user taps a prediction outcome
    Then the full-screen buy preview opens instead of a bottom sheet

  Scenario: order fails in the background with disableBottomSheet enabled
    Given the user opened and dismissed the buy preview
    When the order fails in the background
    Then a failure toast is shown

Screenshots/Recordings

Routing Fix

Simulator.Screen.Recording.-.iPhone.17.Pro.Max.-.2026-05-19.at.16.58.07.mov

Before

broken.mov

After

Simulator.Screen.Recording.-.iPhone.17.Pro.Max.-.2026-05-19.at.16.58.07.mov

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
Changes Predict buy/sell routing and dismissal analytics/Toast suppression behavior based on new disableBottomSheet mode, which could affect navigation flows and user-facing toasts if misconfigured. Updates are well-tested but touch cross-cutting UI/analytics logic used in multiple entry points.

Overview
Fixes Predict bet slip being cropped inside HomepageDiscoveryTabs by adding disableBottomSheet to PredictPreviewSheetProvider so buy/sell actions navigate to full-screen previews (via Routes.PREDICT.ROOT) instead of opening a bottom sheet.

Refines order-failure toast suppression by replacing a singleton “provider mounted” boolean with a sheet-mode provider refcount and renaming the helper to shouldSuppressLegacyOrderFailureToast, ensuring providers mounted with disableBottomSheet do not suppress the legacy failure toast.

Adds screen-mode swipe/back dismissal tracking for PredictBuyPreview behind a new trackSwipeDismiss route param (only set by disableBottomSheet navigations) to avoid double-counting and keep analytics changes scoped to the discovery-tabs flow.

Also adjusts SafeAreaView edges in PredictFeed and PerpsHomeView when hideHeader is true to avoid extra bottom padding in tab layouts.

Reviewed by Cursor Bugbot for commit 9565740. Bugbot is set up for automated code reviews on this repo. Configure here.

@vinnyhoward vinnyhoward requested review from a team as code owners May 14, 2026 22:45
@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.

*/
const clearErrorTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const dismissedWithErrorRef = useRef(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.

Navigate via ROOT so the screen resolves correctly

@vinnyhoward vinnyhoward added team-mobile-ux Mobile UX team team-predict Predict team labels May 15, 2026
@vinnyhoward vinnyhoward changed the title fix(predict): prevent buy/sell sheet from being cropped in HomepageDiscoveryTabs fix(predict): prevent buy/sell sheet from being cropped in HomepageDiscoveryTabs cp-7.78.0 May 15, 2026
wachunei
wachunei previously approved these changes May 15, 2026

@MarioAslau MarioAslau 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.

High-priority issues

H1. Multi-provider race on _providerInSheetMode / _providerMounted globals

PredictPreviewSheetProvider is mounted in two places:

  • app/components/UI/Predict/routes/index.tsx (PredictScreenStack, default → sheet mode)
  • app/components/Views/Homepage/components/HomepageDiscoveryTabs/HomepageDiscoveryTabs.tsx (now disableBottomSheet)

When the user is on the Predictions tab inside HomepageDiscoveryTabs and taps a market, openBuySheet calls navigation.navigate(Routes.PREDICT.ROOT, { screen: BUY_PREVIEW, params }). This pushes onto the root navigator and mounts PredictScreenStack, which mounts a second PredictPreviewSheetProvider. For a window of time both providers are mounted, but the singleton booleans:

let _providerMounted = false;

/**
 * Returns whether `PredictPreviewSheetProvider` is currently mounted somewhere
 ...
 */
export function isPredictSheetProviderMounted(): boolean {
  return _providerMounted;
}

…cannot represent two providers simultaneously. The mount/unmount effects race, so:

  • After the inner provider unmounts (e.g., user pops BUY_PREVIEW), it sets _providerMounted = false and _providerInSheetMode = false, even though the outer (disableBottomSheet) provider is still mounted.
  • After the inner provider mounts, it sets _providerInSheetMode = true, even though the outer one is disableBottomSheet.

In today's code this happens to produce the correct toast behavior in the most common flow because the outer provider's retry-toast effect short-circuits on disableBottomSheet. But the design is fragile, any future consumer of these globals (or any reordering of useEffect runs in StrictMode/dev) can flip behavior silently. It's also genuinely incorrect: there is a Predict provider mounted, and during the interval the inner stack is mounted the outer one is implicitly "in sheet mode" from the global's perspective, even though it explicitly opted out.

Suggested fix: replace the two booleans with reference counters, e.g.:

let _providerMountedCount = 0;
let _providerSheetModeMountedCount = 0;
export function isPredictSheetProviderMounted() {
  return _providerSheetModeMountedCount > 0;
}

…incremented on mount and decremented on unmount based on disableBottomSheet. This makes the behavior independent of mount/unmount ordering when multiple providers coexist.


H2. Analytics behavior change in PredictBuyPreview screen mode is broader than the stated scope

The PR description says the beforeRemove listener exists for screen mode in the new disableBottomSheet flow. The implementation, however, registers the listener for every screen-mode mount of PredictBuyPreview:

useEffect(() => {
  if (isSheetMode) return;
  return addListener('beforeRemove', () => {
    if (!predictBuyPreviewOrderInitiatedRef.current) {
      const dismissalMethod = predictBuyPreviewDismissedViaBackRef.current
        ? PredictDismissalMethod.BACK_BUTTON
        : PredictDismissalMethod.SWIPE;
      Engine.context.PredictController.trackBetslipDismissed({ ... });
    }
  });
}, [addListener, isSheetMode, analyticsProperties, transactionActiveAbTests]);

That includes the pre-existing path where bottomSheetEnabled === false (LD flag off) i.e., users who were already navigating to the full-screen buy preview before this PR. Previously those users only fired Predict Betslip Dismissed when they tapped the in-screen back button; swipe/hardware-back dismissals were silent. After this PR, those swipe/hardware-back dismissals will start firing SWIPE events.

This may well be desirable (it's arguably a correctness improvement and matches what usePredictBuyActions.ts already does for the AnyToken path), but the PR title and description frame it as a fix for the Hub Page Discovery Tabs A/B test only. Reviewers/analytics owners may be missing that the change ships analytics for all flagless users on this path. Two options:

  • Gate the listener on props.mode === 'screen' && /* something distinguishing the new flow */ so the analytics change is scoped to the new flow, or
  • Keep the broader change but call it out explicitly in the changelog/Jira and confirm with the analytics team that the new event volume is expected.

Medium-priority issues

M1. isPredictSheetProviderMounted() no longer matches its name

The function is documented as "whether PredictPreviewSheetProvider is currently mounted somewhere in the tree", but after this PR it returns _providerMounted && _providerInSheetMode:

let _providerMounted = false;
let _providerInSheetMode = false;

export function isPredictSheetProviderMounted(): boolean {
  return _providerMounted && _providerInSheetMode;
}

A disableBottomSheet-mounted provider is still mounted, but the function now lies about that. The only caller (usePredictToastRegistrations) actually uses this boolean to mean "will the provider's retry toast cover this failure?", not "is the provider mounted?". Rename to reflect the new semantics — e.g. shouldSuppressLegacyOrderFailureToast() or isPredictSheetProviderInSheetMode() — and update the doc comment. This avoids the next caller misinterpreting the function and (per H1) re-introducing the silent-failure bug that motivated this fix.

M2. Misleading inline comment in the back-button handler

        onPress={() => {
          predictBuyPreviewDismissedViaBackRef.current = true;
          if (isSheetMode) {
            // Sheet dismissals are tracked in PredictPreviewSheetContext.onBuyDismiss.
            Engine.context.PredictController.trackBetslipDismissed({
              ...
            });
            onClose?.();
          } else {
            // Screen mode: beforeRemove listener owns dismiss tracking.
            goBack();
          }
        }}

The first comment ("Sheet dismissals are tracked in PredictPreviewSheetContext.onBuyDismiss") sits directly above a block that does call trackBetslipDismissed. The intent is "swipe/hardware-back sheet dismissals are tracked elsewhere; this branch handles only the BACK_BUTTON sheet path", but as written it reads like a contradiction. Reword to something like:

// Sheet mode: this branch handles the explicit BACK_BUTTON dismissal.
// Swipe / hardware-back dismissals are handled by onBuyDismiss in PredictPreviewSheetContext.

M3. lastBuyParamsRef.current = params is a dead store under disableBottomSheet

const openBuySheet = useCallback(
  (params: PredictBuyPreviewParams) => {
    lastBuyParamsRef.current = params;          // ← always set
    if (bottomSheetEnabled && !disableBottomSheet) {
      ...
    } else {
      navigation.navigate(Routes.PREDICT.ROOT, {
        screen: Routes.PREDICT.MODALS.BUY_PREVIEW,
        params,
      });
    }
  },
  ...,
);

The retry-toast effect that consumes lastBuyParamsRef.current short-circuits on disableBottomSheet, so when disableBottomSheet === true we are storing params we will never read. Two minor concerns:

  • We hold a long-lived strong reference to params.market, outcome, outcomeToken, etc., for the lifetime of the provider, even though we'll never use them.
  • It muddies the retry contract: a future maintainer could be tempted to read lastBuyParamsRef from a code path that doesn't gate on disableBottomSheet and re-introduce the same silent-failure bug.

Either gate the assignment on !disableBottomSheet, or comment the intentional dead-store.

M4. Missing test: feature-flag-off + disableBottomSheet interaction

The new tests cover disableBottomSheet=true while bottomSheetEnabled is true, and the existing tests cover bottomSheetEnabled=false with no disableBottomSheet. There's no test for bottomSheetEnabled=false with disableBottomSheet=true. In that combo, both branches force navigation, but _providerInSheetMode is now driven purely by disableBottomSheet (i.e. false), regardless of the runtime feature flag — which is what we want, but should be locked in with a test so a refactor of the flag selectors can't regress it.

M5. JSDoc on shared module-level refs is now stale

/**
 * Module-level ref so PredictPreviewSheetContext can distinguish a programmatic
 * back-button dismiss from a swipe/hardware-back dismiss when onBuyDismiss fires.
 * Set to true in the back-button handler, reset to false by the sheet context
 * after consuming it.
 */
export const predictBuyPreviewDismissedViaBackRef = { current: false };

The doc now lies on three counts after this PR:

  1. The ref is also consumed by the new beforeRemove listener in screen mode (PredictBuyPreview.tsx), not just by onBuyDismiss.
  2. It is also consumed by usePredictBuyActions.ts for the AnyToken screen-mode path.
  3. It is now reset on mount by PredictBuyPreview itself (line 121), in addition to being reset by onBuyDismiss.

Update the JSDoc to reflect that this is shared module-level state with three consumers, and document the reset contract to reduce future foot-guns.

…-area padding

- Replace _providerMounted/_providerInSheetMode booleans with a reference
  counter (_providerSheetModeCount) to avoid last-writer-wins race when
  PredictPreviewSheetProvider is mounted in both PredictScreenStack and
  HomepageDiscoveryTabs simultaneously
- Rename isPredictSheetProviderMounted → shouldSuppressLegacyOrderFailureToast
  to reflect actual semantics (checks sheet-mode, not mount status)
- Gate beforeRemove swipe-dismiss analytics behind trackSwipeDismiss route
  param so the change is scoped to the disableBottomSheet flow and does not
  change event volume for the pre-existing flagless screen-mode path
- Move lastBuyParamsRef assignment inside the sheet-mode branch to eliminate
  dead store when disableBottomSheet is active
- Fix extra bottom safe-area padding in PredictFeed and PerpsHomeView when
  rendered as tabs inside HomepageDiscoveryTabs (hideHeader=true → edges=[])
@vinnyhoward vinnyhoward requested a review from a team as a code owner May 19, 2026 16:50
@vinnyhoward vinnyhoward changed the title fix(predict): prevent buy/sell sheet from being cropped in HomepageDiscoveryTabs cp-7.78.0 fix(predict): prevent buy/sell sheet from being cropped in HomepageDiscoveryTabs May 19, 2026
@vinnyhoward

Copy link
Copy Markdown
Contributor Author

@MarioAslau I have addressed the issues you've found in this 3a47acf

Comment thread app/components/UI/Predict/views/PredictBuyPreview/PredictBuyPreview.tsx Outdated
Comment thread app/components/UI/Predict/views/PredictFeed/PredictFeed.tsx
…mcu-752-predict-sheet-homepage-discovery-tabs
return (
<SafeAreaView
edges={{ bottom: 'additive' }}
edges={hideHeader ? [] : { bottom: 'additive' }}

@vinnyhoward vinnyhoward May 19, 2026

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.

This ternary is needed to solve the bottom padding issue

Before After
Before After

style={styles.container}
edges={hideHeader ? { bottom: 'additive' } : undefined}
>
<SafeAreaView style={styles.container} edges={hideHeader ? [] : undefined}>

@vinnyhoward vinnyhoward May 19, 2026

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.

This ternary is needed to solve the bottom padding issue

Before After
Before After

@vinnyhoward vinnyhoward added the team-perps Perps team label May 19, 2026

@MarioAslau MarioAslau 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.

High-priority issues

H1. hadEnteredAmount analytics value is now inconsistent across dismissal paths

Five distinct dismissal paths now emit Predict Betslip Dismissed, but they read hadEnteredAmount from two different sources:

Path Source of hadEnteredAmount
Sheet-mode, back-button currentValue > 0 (instantaneous)
Sheet-mode, swipe / hardware-back (onBuyDismiss) predictBuyPreviewSessionRef.hadEnteredAmount (sticky)
Screen-mode + trackSwipeDismiss, back-button (via beforeRemove) predictBuyPreviewSessionRef.hadEnteredAmount (sticky)
Screen-mode + trackSwipeDismiss, swipe / hardware-back predictBuyPreviewSessionRef.hadEnteredAmount (sticky)
Screen-mode flagless, back-button currentValue > 0 (instantaneous)

The session ref is "sticky": once currentValue > 0 it flips to true for the rest of the session and is never set back to false (only reset on mount/unmount):

useEffect(() => {
  if (currentValue > 0) {
    predictBuyPreviewSessionRef.hadEnteredAmount = true;
  }
  ...
}, [currentValue, isCalculating]);

So a user who types $10, clears it back to $0, then taps back will report:

  • hadEnteredAmount = false in sheet-mode back-button and screen-mode flagless back-button
  • hadEnteredAmount = true in the new screen-mode-trackSwipeDismiss back-button (which defers to beforeRemove)

The same physical action (tap back-button) reports different analytics depending on which presentation mode the user happens to be in. This will skew the had_entered_amount distribution for the HomepageDiscoveryTabs cohort relative to the rest of Predict and contaminate the A/B test signal the PR is meant to support.

Suggested fix: pick one semantic and apply it everywhere. The sticky semantic is arguably the more useful one ("did the user ever engage?"), but whichever is chosen, all five paths should agree. At minimum, the new beforeRemove listener and the back-button handler should read from the same source when the user is in screen mode.


H2. beforeRemove listener fires on every pop, not just dismissals

useEffect(() => {
  if (isSheetMode || !trackSwipeDismiss) return;
  return addListener('beforeRemove', () => {
    if (!predictBuyPreviewOrderInitiatedRef.current) {
      const dismissalMethod = predictBuyPreviewDismissedViaBackRef.current
        ? PredictDismissalMethod.BACK_BUTTON
        : PredictDismissalMethod.SWIPE;
      Engine.context.PredictController.trackBetslipDismissed({ ... });
    }
  });
}, [addListener, isSheetMode, trackSwipeDismiss, ...]);

beforeRemove fires for any removal of the screen from the navigation stack, including programmatic StackActions.pop() triggered by a successful order:

useEffect(() => {
  if (result?.success) {
    if (isSheetMode) {
      onClose?.();
    } else {
      dispatch(StackActions.pop());   // ← will trigger beforeRemove
    }
  }
}, [dispatch, result, isSheetMode, onClose]);

The !predictBuyPreviewOrderInitiatedRef.current gate inside the listener is what's supposed to prevent the false-positive dismissal event in this flow. That works as long as onPlaceBet always runs first:

const onPlaceBet = useCallback(async () => {
  if (!preview || isBelowMinimum || isInsufficientBalance) return;
  predictBuyPreviewOrderInitiatedRef.current = true;   // ← gate setter
  await placeOrder({ analyticsProperties, preview });
}, ...);

But the gate is fragile in a few ways:

  1. The ref is module-level and shared with PredictBuyWithAnyToken / usePredictBuyActions. If a previous PredictBuyWithAnyToken mount left the ref true and a PredictBuyPreview mounts in screen-mode-trackSwipeDismiss immediately after, the beforeRemove listener will silently swallow the first dismissal until the mount-time reset on line 125 lands. The mount-time reset does run, so the dominant ordering is safe — but the cross-component coupling is hard to verify by reading the code.
  2. There's no test that asserts "successful order does not emit a SWIPE dismissal event". The test that comes closest only checks the negative case for predictBuyPreviewOrderInitiatedRef.current = true (line 2599) by manually toggling the ref. A higher-fidelity test would render the component, drive placeOrder to success, and assert trackBetslipDismissed was not called.

Suggested fix: add the integration-level test described above. Optionally, replace predictBuyPreviewOrderInitiatedRef with a stronger signal (e.g. checking result?.success or the e.data?.action.type === 'POP' style guard inside the listener).


H3. Screen-mode flagless BACK_BUTTON tracking is now gated on orderInitiatedRef — quietly different from previous behavior

Before this PR, the screen-mode back-button handler fired BACK_BUTTON unconditionally:

- // (previous main):
- predictBuyPreviewDismissedViaBackRef.current = true;
- Engine.context.PredictController.trackBetslipDismissed({
-   ...,
-   dismissalMethod: PredictDismissalMethod.BACK_BUTTON,
- });
- if (isSheetMode) onClose?.();
- else goBack();

After 8e24485, the flagless path now gates the event on !predictBuyPreviewOrderInitiatedRef.current:

} else {
  // Screen mode (flagless path): beforeRemove listener is not
  // registered, so track back-button dismissal directly here.
  if (!predictBuyPreviewOrderInitiatedRef.current) {
    Engine.context.PredictController.trackBetslipDismissed({
      analyticsProperties,
      dismissalMethod: PredictDismissalMethod.BACK_BUTTON,
      hadEnteredAmount: currentValue > 0,
      timeOnScreenMs: Date.now() - mountTimestampRef.current,
      activeAbTests: transactionActiveAbTests,
    });
  }
  goBack();
}

This is a behavior change for all existing users of the flagless screen-mode path (i.e. users who already see the full-screen preview because selectPredictBottomSheetEnabledFlag is false):

  • Before: tap Place Bet → bet in-flight → tap back-button → BACK_BUTTON event fires.
  • After: same sequence → event suppressed.

It's plausible that this is the desired semantic (the user committed; calling that a "dismissal" is dubious), but:

  1. The sheet-mode back-button handler (line 381) was not updated symmetrically — it still fires BACK_BUTTON unconditionally even when orderInitiatedRef === true. So we now have:
    • Sheet-mode back-button after Place Bet: fires BACK_BUTTON event
    • Screen-mode back-button after Place Bet: silently swallows the event
  2. The PR description doesn't call out this analytics change for the flagless cohort.
  3. There is no test for this branch — the new describe('beforeRemove dismiss tracking (screen mode)', ...) block (line 2549) covers the trackSwipeDismiss=true cases, but not the new gated flagless back-button path.

Suggested fix: either revert the gate on the flagless back-button (keep prior unconditional firing), or apply the same gate to the sheet-mode back-button so all three back-button paths are consistent. Either way, add a test that locks in the chosen semantic for the flagless path, and mention the change in the PR body so the analytics team isn't surprised.

@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 25b2bb1. Configure here.

Comment thread app/components/UI/Predict/views/PredictBuyPreview/PredictBuyPreview.tsx Outdated
@github-actions github-actions Bot added size-L and removed size-M labels May 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokePredictions, SmokePerps, SmokeWalletPlatform, SmokeConfirmations
  • Selected Performance tags: None (no tests recommended)
  • Risk Level: medium
  • AI Confidence: 88%
click to see 🤖 AI reasoning details

E2E Test Selection:

Changes Overview:

  1. PerpsHomeView.tsx — Minor SafeAreaView edge fix: { bottom: 'additive' }[] when hideHeader is true. This affects the Perps home view layout when embedded in HomepageDiscoveryTabs (the Trending tab A/B test). Requires SmokePerps validation.

  2. PredictPreviewSheetContext.tsx — Significant refactor: replaces single boolean _providerMounted with a reference counter _providerSheetModeCount to support multiple simultaneous providers. Adds disableBottomSheet prop that routes navigation to full-screen Predict stack instead of bottom sheet. Renames exported function isPredictSheetProviderMountedshouldSuppressLegacyOrderFailureToast. This changes navigation behavior for the Predictions buy/sell preview flow when embedded in HomepageDiscoveryTabs.

  3. HomepageDiscoveryTabs.tsx — Passes disableBottomSheet to PredictPreviewSheetProvider, activating the new navigation path for Predictions within the Trending tab.

  4. PredictBuyPreview.tsx — Adds trackSwipeDismiss param support, adds beforeRemove listener for screen-mode swipe tracking, fixes analytics double-counting, resets predictBuyPreviewDismissedViaBackRef on mount. Changes affect buy preview dismissal analytics and navigation.

  5. PredictFeed.tsx — SafeAreaView edge fix: only applies { bottom: 'additive' } when not in hideHeader mode.

  6. usePredictToastRegistrations.tsx — Updates import to use renamed function shouldSuppressLegacyOrderFailureToast.

  7. navigation.ts — Adds trackSwipeDismiss param to PredictBuyPreviewParams.

  8. contexts/index.ts — Updates export name.

Tag Selection Rationale:

  • SmokePredictions: Directly affected — buy/sell preview navigation, toast suppression logic, and feed layout all changed.
  • SmokeWalletPlatform: Required per SmokePredictions tag description (Predictions is a section inside Trending tab). HomepageDiscoveryTabs.tsx is also directly modified.
  • SmokeConfirmations: Required per SmokePredictions tag description (opening/closing positions are on-chain transactions that go through confirmations).
  • SmokePerps: PerpsHomeView SafeAreaView edge fix affects Perps layout. Per SmokePerps tag description, also requires SmokeWalletPlatform (already selected) and SmokeConfirmations (already selected).

Performance Test Selection:
The changes are primarily logic-level refactors (reference counter, navigation routing, analytics tracking) and minor SafeAreaView edge fixes. None of these changes affect rendering performance, data loading, list rendering, or app startup in ways that would meaningfully impact performance metrics. No performance tests are warranted.

View GitHub Actions results

@sonarqubecloud

Copy link
Copy Markdown

@MarioAslau MarioAslau 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.

LGTM

@vinnyhoward vinnyhoward added this pull request to the merge queue May 20, 2026
Merged via the queue into main with commit e0019e2 May 20, 2026
172 of 175 checks passed
@vinnyhoward vinnyhoward deleted the fix-tmcu-752-predict-sheet-homepage-discovery-tabs branch May 20, 2026 15:35
@github-actions github-actions Bot locked and limited conversation to collaborators May 20, 2026
@metamaskbotv2 metamaskbotv2 Bot added the release-7.79.0 Issue or pull request that will be included in release 7.79.0 label May 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.79.0 Issue or pull request that will be included in release 7.79.0 size-L team-mobile-ux Mobile UX team team-perps Perps team team-predict Predict team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants