Skip to content

feat(predict): add Positions/Outcomes tab bar to game details#28668

Merged
caieu merged 11 commits intomainfrom
predict/PRED-802-phase-2-positions-outcomes-tab-bar
Apr 10, 2026
Merged

feat(predict): add Positions/Outcomes tab bar to game details#28668
caieu merged 11 commits intomainfrom
predict/PRED-802-phase-2-positions-outcomes-tab-bar

Conversation

@caieu
Copy link
Copy Markdown
Contributor

@caieu caieu commented Apr 10, 2026

Description

Adds a Positions/Outcomes tab bar to the game details screen, gated behind the extendedSportsMarketsLeagues feature flag per league.

What changed:

  • useGameDetailsTabs hook — New hook that reads the extendedSportsMarketsLeagues remote flag, computes tabs (Positions + Outcomes when user has positions, Outcomes-only otherwise), manages active tab state, and determines sticky header indices.
  • PredictGameDetailsContent — Fetches positions as a single source of truth and passes them to both the hook (for tab logic) and PredictGameDetailsTabsContent (for rendering). Renders PredictMarketDetailsTabBar as a direct ScrollView child for sticky behavior.
  • PredictGameDetailsTabsContent — New component with three rendering paths:
    • Flag off + has positions → "Your picks" title + PredictPicks
    • Flag off + no positions → null
    • Flag on + has positions → tabbed view (Positions/Outcomes tabs with sticky header)
    • Flag on + no positions → outcomes placeholder directly (no tab bar)
  • PredictPicks — Refactored to accept positions and claimablePositions as required props instead of fetching internally. Removed "Your picks" title (now owned by parent). Removed internal empty-state check (parent controls visibility).
  • PredictMarketDetailsTabBar — Added optional tabTwStyle prop to allow consumers to override individual tab styling (game details passes flex-1 for 2-tab even distribution).

Changelog

CHANGELOG entry: Added Positions and Outcomes tab bar to sports game detail screens

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/PRED-802

Manual testing steps

Feature: Game details Positions/Outcomes tab bar

  Scenario: User views game details with positions (flag-enabled league)
    Given the user has open positions on a game in an enabled league
    And the extendedSportsMarketsLeagues flag includes that league

    When the user navigates to the game detail screen
    Then a sticky tab bar appears below the chart with "Positions" and "Outcomes" tabs
    And the "Positions" tab is selected by default
    And the user's pick items are displayed

    When the user taps the "Outcomes" tab
    Then the outcomes placeholder is displayed

  Scenario: User views game details without positions (flag-enabled league)
    Given the user has no positions on a game in an enabled league
    And the extendedSportsMarketsLeagues flag includes that league

    When the user navigates to the game detail screen
    Then no tab bar is shown
    And the outcomes placeholder is displayed directly below the chart

  Scenario: User views game details on a non-enabled league
    Given the extendedSportsMarketsLeagues flag does not include the game's league

    When the user navigates to the game detail screen
    Then no tab bar is shown
    And if the user has positions, the "Your picks" title and pick items are displayed
    And if the user has no positions, nothing is shown below the chart

  Scenario: Tab bar stickiness
    Given the user is on a game detail screen with the tab bar visible

    When the user scrolls down past the chart
    Then the tab bar sticks to the top of the screen
    And the tab content scrolls underneath

Screenshots/Recordings

Before

tabsbefore.mp4

After

tabsafter.mp4

Pre-merge author checklist

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
Medium risk: changes game details rendering flow and how positions data is fetched/prop-drilled to drive tabbed UI (including sticky header behavior), gated by a remote feature flag but still impacts a user-facing screen.

Overview
Adds a feature-flagged Positions/Outcomes tab experience to the Predict game details screen, including a sticky PredictMarketDetailsTabBar when the user has positions in an enabled league and an outcomes placeholder when they don’t.

Refactors PredictPicks to be purely presentational by requiring positions/claimablePositions props (no internal fetching or header), and introduces useGameDetailsTabs + PredictGameDetailsTabsContent to centralize tab state/visibility and conditionally render picks vs outcomes content. Updates/expands tests accordingly and removes the prior snapshot test.

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

caieu added 5 commits April 9, 2026 16:52
Extract PredictGameDetailsTabs component to house tab bar logic for
game event details, gated behind a placeholder feature flag. Move
"Your picks" title from PredictPicks into PredictGameDetailsTabs so
PredictPicks renders items only, while the title shows in the
flag-off path. When enabled, the component renders
PredictMarketDetailsTabBar with Positions/Outcomes tabs and an
outcomes placeholder.
…on fetching

Lift position fetching to PredictGameDetailsContent as the single
source of truth, eliminating duplicate usePredictPositions calls from
useGameDetailsTabs and PredictPicks. Replace the placeholder
ENABLE_GAME_TABS constant with the real extendedSportsMarketsLeagues
feature flag gated per league. PredictPicks now receives positions as
required props, and "Your picks" title only renders when positions
exist.
@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.

@metamaskbot metamaskbot added the team-predict Predict team label Apr 10, 2026
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.34884% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.15%. Comparing base (0c26cf4) to head (638d64b).
⚠️ Report is 32 commits behind head on main.

Files with missing lines Patch % Lines
...ctGameDetailsContent/PredictGameDetailsContent.tsx 50.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #28668      +/-   ##
==========================================
+ Coverage   82.13%   82.15%   +0.02%     
==========================================
  Files        4942     4951       +9     
  Lines      129947   130101     +154     
  Branches    28977    29017      +40     
==========================================
+ Hits       106732   106887     +155     
- Misses      15917    15920       +3     
+ Partials     7298     7294       -4     

☔ 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 marked this pull request as ready for review April 10, 2026 15:20
@caieu caieu requested a review from a team as a code owner April 10, 2026 15:20
@github-actions github-actions bot added the risk-medium Moderate testing recommended · Possible bug introduction risk label Apr 10, 2026
@github-actions github-actions bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Apr 10, 2026
@github-actions github-actions bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Apr 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

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

E2E Test Selection:
All 12 changed files are within the Predict feature area (app/components/UI/Predict/). The changes introduce:

  1. A new tab-based navigation system for the game details screen (positions vs outcomes tabs)
  2. Refactoring of PredictPicks to accept positions as props instead of fetching internally
  3. A new useGameDetailsTabs hook controlled by a feature flag (selectExtendedSportsMarketsLeagues)
  4. A new PredictGameDetailsTabsContent component for rendering tab content
  5. A tabTwStyle prop added to PredictMarketDetailsTabBar for flexible styling

These changes affect the Predict market details view - specifically how user positions are displayed and navigated. The SmokePredictions tag directly covers position lifecycle (opening positions, cashing out, claiming winnings) which is the core functionality affected by these UI changes.

Per SmokePredictions tag description: when selecting SmokePredictions, also select SmokeWalletPlatform (Trending section - Predictions is embedded there) and SmokeConfirmations (opening/closing positions are on-chain transactions).

No changes to core infrastructure, navigation, controllers, or shared components that would warrant broader test coverage.

Performance Test Selection:
The changes are UI refactoring within the Predict feature - moving position data fetching from PredictPicks to PredictGameDetailsContent and adding tab navigation. While there is a structural change in data flow, it doesn't introduce new API calls or fundamentally alter rendering performance. The position fetching was already happening, just moved up the component tree. No performance tests are warranted.

View GitHub Actions results

Copy link
Copy Markdown

@cursor cursor bot left a comment

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 8bf539c. Configure here.

@sonarqubecloud
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown
Contributor

E2E Fixture Validation — Schema is up to date
16 value mismatches detected (expected — fixture represents an existing user).
View details

Copy link
Copy Markdown
Contributor

@matallui matallui left a comment

Choose a reason for hiding this comment

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

A couple of questions, but nothing blocking

Comment on lines +64 to +72
const { data: activePositions = [] } = usePredictPositions({
marketId: market.id,
claimable: false,
refetchInterval: 10000,
});
const { data: claimablePositions = [] } = usePredictPositions({
marketId: market.id,
claimable: true,
});
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.

I believe you can fetch both claimable and active if you don't set claimable in this hook. Do we need these 2 separately? I also don't see why we'd need to refetch. We know the user can only get new active positions is they buy/sell, at which point we invalidate the query

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.

I think we can remove the refetch. PredictPicks already handles live position updates internally, so I don’t think we need it.

We also separate claimable positions for two reasons: we don’t need live positions for claimable ones, and we want to show claimable positions after the active ones.

The first part could be moved into the usePredictLivePositions hook, but if we still want to show claimable positions after the active ones in the list, I’m not sure whether that filter should live inside the live positions logic.

Comment on lines +42 to +47
useEffect(() => {
if (!enabled) return;
if (activeTab >= tabs.length) {
setActiveTab(0);
}
}, [tabs, activeTab, enabled]);
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.

is this effect needed?

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.

Not needed after all. Removing on the next task.

@caieu caieu added this pull request to the merge queue Apr 10, 2026
Merged via the queue into main with commit a44ded4 Apr 10, 2026
114 of 115 checks passed
@caieu caieu deleted the predict/PRED-802-phase-2-positions-outcomes-tab-bar branch April 10, 2026 21:46
@github-actions github-actions bot locked and limited conversation to collaborators Apr 10, 2026
@metamaskbot metamaskbot added the release-7.74.0 Issue or pull request that will be included in release 7.74.0 label Apr 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.74.0 Issue or pull request that will be included in release 7.74.0 risk-medium Moderate testing recommended · Possible bug introduction risk size-XL team-predict Predict team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants