Skip to content

feat(predict): support series ids in predictMarketHighlights cp-7.81.0#31044

Merged
ghgoodreau merged 4 commits into
mainfrom
feat/predict-series-highlights
Jun 5, 2026
Merged

feat(predict): support series ids in predictMarketHighlights cp-7.81.0#31044
ghgoodreau merged 4 commits into
mainfrom
feat/predict-series-highlights

Conversation

@ghgoodreau

@ghgoodreau ghgoodreau commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Description

Extends the predictMarketHighlights remote feature flag to accept series IDs in addition to market IDs, and renders series-resolved crypto up/down highlights in horizontally-scrolling carousels via a new compact card variant.

Why

Recurring crypto up/down markets (e.g. "BTC Up or Down — 5 Minutes") expose a new market every N minutes. A market-ID pin in predictMarketHighlights therefore goes stale within a single time slot — once the market resolves, the pin disappears, and there is no way to express "always pin the currently-live BTC Up/Down 5m market." Resolving from a series at request time fixes this: the pin automatically rotates to whatever slot is live when the feed is fetched.

Schema change (additive, backward compatible)

export interface PredictMarketHighlight {
  category: string;
  markets?: string[]; // was required, now optional
  series?: string[];  // NEW
}
  • Existing LD flag values that only use markets continue to work unchanged. Both fields are now optional; entries with neither are skipped.
  • The controller's highlight block fetches getMarketsByIds(markets) and getMarketSeries(seriesId) in parallel via Promise.all, then resolves each series response to its currently-live market via the existing findLiveMarket() (with findNearestMarket() as a fallback when no future market is in the fetch window).
  • Resolved series markets pass through the same status === 'open' filter and are marked with isHighlighted: true, identical to the market-ID path.
  • Order: market-ID highlights are prepended first, then series-resolved highlights, then the regular feed. Existing flag values that only use markets keep their original ordering. Dedupe is unified across both paths so a market reachable via both markets and series appears exactly once.

Compact carousel variant

PredictCryptoUpDownMarketCard now has two render variants, keyed off the existing isCarousel prop that PredictMarket forwards to its sibling cards (PredictMarketSingle, PredictMarketMultiple, PredictMarketSportCard):

  • Full (isCarousel=false, the default) — the existing sparkline + target-line / target-price treatment used on PredictFeed, PredictHome, the wallet home carousel, and search.
  • Compact (isCarousel=true) — drops the sparkline and target labels and switches to a height: 100% + flex-column + justifyContent: 'space-between' layout that mirrors PredictMarketSingle / PredictMarketMultiple so the card sits flush with its neighbours in HorizontalCarousel. This is the variant the 5 TrendingView carousel tabs (Now, Macro, RWAs, Crypto, Sports) render for series-resolved highlights.

The compact path also gates useCryptoUpDownChartData and useCryptoTargetPrice with enabled: !isCompact, so the chart historical fetch and live-price websocket subscription don't run for cards that never display them.

Resilience

  • Per-series fetch failures are caught locally with a DevLogger.log, so one unhealthy series entry can't take down a healthy batch.
  • The series fetch reuses the canonical SERIES_MAX_EVENTS (50) for limit. The provider returns markets in endDate ASC order, and a fast recurrence (5m) can produce ~12 past events inside the past buffer alone — too low a limit would clip the live slot.
  • getMarketsByIds keeps its existing fail-hard contract; the previously misleading test "handles getMarketsByIds failure gracefully" (which actually only tested an empty-array result) was renamed accordingly, and a real rejection-propagation test was added.

Test coverage

  • 8 new tests in PredictController.test.ts (live-market resolution, mixed markets+series ordering, nearest-market fallback, empty-series skip, closed-market status filter, throw-silent on single series, partial-batch failure, dedupe across paths)
  • 1 shape-passthrough test in resolvePredictFeatureFlags.test.ts
  • 4 new tests in PredictCryptoUpDownMarketCard.test.tsx covering the compact variant (sparkline not rendered, both chart queries gated with enabled: false, compact skeleton renders, buy sheet still opens)
  • All tests in the touched suites pass.

Changelog

CHANGELOG entry: Added support for pinning the currently-live market of a recurring series (e.g. BTC Up/Down 5m) via the predictMarketHighlights remote feature flag.

Related issues

Fixes: PRED-950

Manual testing steps

Both scenarios assume the predictMarketHighlights LaunchDarkly variation is updated to the new shape. The series ID 10684 corresponds to the Polymarket Gamma BTC Up/Down 5m series (also hardcoded as BTC_UP_DOWN_5M_SERIES_ID in app/components/UI/Predict/constants/btcUpDown5mSeries.ts).

Feature: Series-id highlights in predictMarketHighlights

  Scenario: A series-id highlight resolves to the currently-live market on PredictFeed
    Given the predictMarketHighlights flag is set to
      """
      {
        "enabled": true,
        "minimumVersion": "7.63.0",
        "highlights": [
          { "category": "crypto", "series": ["10684"] }
        ]
      }
      """
    When the user opens the Predict tab and switches to the "Crypto" feed tab
    Then the currently-live BTC Up/Down 5m market is pinned at the top of the feed
    And the pin auto-rotates to the next live slot when the current one resolves
    And the card renders in the full variant (with sparkline + target labels)

  Scenario: A series-id highlight renders the compact variant in TrendingView carousels
    Given the same flag configuration as above
    When the user opens the Explore page and views the "Crypto" or "Now" tab carousel
    Then the BTC Up/Down series card IS pinned in the Explore carousel
    And it renders as the compact variant (no sparkline, same height as its neighbours)
    And no chart-history fetch or live-price websocket subscription is opened for that card

  Scenario: Existing market-id highlights are unaffected
    Given the predictMarketHighlights flag is set to
      """
      {
        "enabled": true,
        "highlights": [
          { "category": "trending", "markets": ["451614"] }
        ]
      }
      """
    When the user opens PredictFeed → Trending and Explore → Now
    Then market 451614 is pinned at the top on both surfaces (market-id path is unchanged)

Screenshots/Recordings

Before

N/A

After

N/A

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
Feed ordering and highlight resolution now depend on series API behavior and remote flag shape; carousel gating changes which data loads on Explore surfaces.

Overview
Adds optional series IDs to predictMarketHighlights so recurring crypto up/down pins resolve to the currently live market via getMarketSeries + findLiveMarket / findNearestMarket, merged with existing market-ID highlights (parallel fetch, dedupe, open-only, market IDs first).

PredictCryptoUpDownMarketCard now honors isCarousel: a compact layout (no sparkline/target UI, full-height flex) and enabled: false on chart/target hooks to avoid unused network work in Explore carousels; full feed cards unchanged with enabled: true on chart data.

Controller tests cover series resolution edge cases; getMarketsByIds failures now reject instead of being mislabeled as graceful handling.

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

@github-actions

github-actions Bot commented Jun 3, 2026

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.

@mm-token-exchange-service mm-token-exchange-service Bot added the team-predict Predict team label Jun 3, 2026
@github-actions github-actions Bot added the size-L label Jun 3, 2026
@ghgoodreau ghgoodreau marked this pull request as ready for review June 4, 2026 01:35
@ghgoodreau ghgoodreau requested a review from a team as a code owner June 4, 2026 01:35
@github-actions github-actions Bot added the risk:medium AI analysis: medium risk label Jun 4, 2026
query?: string;
refresh?: RefreshConfig;
enabled?: boolean;
excludeSeriesHighlights?: boolean;

@matallui matallui Jun 4, 2026

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 there a reason for excluding the highlights from the Explore page? My guess is that you're doing it because we don't have a compact version of those cards that looks good in the Explore page. But if that's the case, we should fix that rather than hide it. These markets bring a lot of value, so we probably want them front and center across the app.

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 crypto up/down series card is taller than the other cards, which is fine in a vertical feed but not the horizontal feed. Excluding it from the trending view would allow us to release it in the PredictView feeds now

@ghgoodreau ghgoodreau enabled auto-merge June 4, 2026 15:02
Extends the predictMarketHighlights remote feature flag to accept series ids alongside market ids. A series-id highlight resolves at request time to its currently-live market via findLiveMarket (with findNearestMarket as fallback), so the pin stays fresh as time slots roll over for recurring crypto up/down markets.

Schema is additive: markets is relaxed to optional, series?: string[] is new. Existing flag values keep working unchanged. Market-id highlights are prepended before series-resolved highlights, and dedup spans both paths.

Per-series fetch failures are caught locally so one bad entry can't take down a healthy batch. getMarketsByIds keeps its existing fail-hard contract, now covered by an explicit test.
Series-resolved crypto up/down highlights now render in the Explore
TrendingView carousels via a compact variant of
`PredictCryptoUpDownMarketCard`, rather than being opted out per surface.
This replaces the prior `excludeSeriesHighlights` plumbing (dropped from
this branch) with a proper carousel-sized render keyed off the existing
`isCarousel` prop that `PredictMarket` already forwards to its sibling
cards.

The compact variant:

- Drops the sparkline and the target-line / target-price label treatment
- Uses `height: 100%` + flex-column + `justifyContent: space-between`,
  mirroring `PredictMarketSingle` / `PredictMarketMultiple`, so the card
  sits flush with its neighbours in `HorizontalCarousel`
- Gates `useCryptoUpDownChartData` and `useCryptoTargetPrice` with
  `enabled: !isCompact` to skip the historical fetch and live websocket
  subscription that the dropped chart would have consumed

`LiveStatus` and `OutcomeButtons` now accept a discriminated `compact`
union: in compact mode they render positionless flex content; in full
mode they keep the existing absolute-positioned layout. The full-mode
render path is unchanged.

Test coverage: 4 new tests cover sparkline-not-rendered, both chart
queries disabled, the compact skeleton (no chart placeholder), and that
the buy sheet still opens. One existing assertion is updated for the
new `enabled: true` option now passed to `useCryptoUpDownChartData` in
the full path.
@ghgoodreau ghgoodreau force-pushed the feat/predict-series-highlights branch from 77b1630 to 97ef710 Compare June 4, 2026 15:58
Drops the `FULL_CARD_HEIGHT` / `FULL_PROGRESS_LOGO_TOP` /
`FULL_LIVE_BADGE_TOP` / `FULL_TITLE_TOP` / `FULL_OUTCOME_BUTTONS_TOP`
constants and the `progressLogoTop` / `liveBadgeTop` / `top` props that
plumbed them into `LiveStatus` and `OutcomeButtons`. Those constants
only existed because the previous commit lifted them out of a now-gone
`CARD_LAYOUT.compact` struct; the full-mode JSX used to carry these
values inline as Tailwind arbitrary values (`h-[319px]`, `top-[112px]`,
`top-[171px]`, `top-[197px]`, `top-[235px]`) and now does so again.

No behavior change — `LiveStatus` and `OutcomeButtons` keep their
`compact?: boolean` discriminator for the compact / full branch, just
without the extra magic-number plumbing.

@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 108a22c. Configure here.

Comment thread app/components/UI/Predict/controllers/PredictController.ts
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.91837% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.99%. Comparing base (000d79f) to head (108a22c).
⚠️ Report is 85 commits behind head on main.

Files with missing lines Patch % Lines
...onents/UI/Predict/controllers/PredictController.ts 93.75% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #31044      +/-   ##
==========================================
+ Coverage   82.86%   82.99%   +0.13%     
==========================================
  Files        5582     5605      +23     
  Lines      144198   144394     +196     
  Branches    33521    33550      +29     
==========================================
+ Hits       119483   119845     +362     
+ Misses      16682    16482     -200     
- Partials     8033     8067      +34     

☔ View full report in Codecov by Harness.
📢 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.

- Drop the flex gap between the LIVE badge and the market title (was
  `gap-2` → now 0; the badge and title now sit flush with only their
  natural text leading between them)
- Tighten the bottom-group gap between the Up/Down buttons and the
  "Resets every …" footer from `gap-3` (12px) to `gap-2` (8px)

Top/bottom padding (`p-4`, 16px) and the flex-driven space between
the title and the buttons row (`justify-between`) are unchanged.
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokePredictions, SmokeWalletPlatform, SmokeConfirmations
  • Selected Performance tags: @PerformancePredict
  • Risk Level: medium
  • AI Confidence: 90%
click to see 🤖 AI reasoning details

E2E Test Selection:
All changes are scoped to the Predictions feature area (app/components/UI/Predict/). Key changes:

  1. PredictController.ts: Enhanced market highlights to support series-based highlights alongside direct market IDs. Added resolveSeriesHighlights() method that fetches series markets and resolves the live/nearest market using findLiveMarket/findNearestMarket. Updated fetchMarkets to run both market ID and series ID resolution in parallel via Promise.all. Also added deduplication logic for highlighted markets.

  2. PredictCryptoUpDownMarketCard.tsx: Added compact carousel variant (isCarousel prop → isCompact). In compact mode: skips chart data and target price queries (performance optimization), renders a simplified layout without sparkline/chart, and uses height: 100% flex layout for carousel integration. This is specifically for the Trending tab carousel.

  3. types/flags.ts: Made markets optional and added optional series field to PredictMarketHighlight — a type-level change that affects how feature flags are parsed.

  4. mocks/remoteFeatureFlagMocks.ts and resolvePredictFeatureFlags.test.ts: Test/mock updates supporting the new series field.

Tag selection:

  • SmokePredictions: Direct impact — PredictController market fetching logic changed, PredictCryptoUpDownMarketCard rendering changed. All prediction flows (open position, cash out, claim winnings, geo restriction) should be validated.
  • SmokeWalletPlatform: Required by SmokePredictions description — the compact carousel variant is specifically for the Trending tab carousel, making this directly relevant.
  • SmokeConfirmations: Required by SmokePredictions description — prediction position transactions go through confirmations.

Performance Test Selection:
The PredictController changes introduce parallel Promise.all resolution for series highlights (potentially more API calls during market list fetching), and the PredictCryptoUpDownMarketCard compact variant skips chart/sparkline data queries. These changes could affect prediction market loading performance — specifically the time to render the market list and carousel. @PerformancePredict covers prediction market list loading and balance display which are directly affected.

View GitHub Actions results

@ghgoodreau ghgoodreau changed the title feat(predict): support series ids in predictMarketHighlights feat(predict): support series ids in predictMarketHighlights cp-7.81.0 Jun 4, 2026
@mm-token-exchange-service mm-token-exchange-service Bot added INVALID-PR-TEMPLATE PR's body doesn't match template and removed INVALID-PR-TEMPLATE PR's body doesn't match template labels Jun 4, 2026
@ghgoodreau ghgoodreau added this pull request to the merge queue Jun 5, 2026
Merged via the queue into main with commit c32e24f Jun 5, 2026
239 of 249 checks passed
@ghgoodreau ghgoodreau deleted the feat/predict-series-highlights branch June 5, 2026 15:54
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 5, 2026
@metamaskbotv2 metamaskbotv2 Bot added the release-7.82.0 Issue or pull request that will be included in release 7.82.0 label Jun 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.82.0 Issue or pull request that will be included in release 7.82.0 risk:medium AI analysis: medium risk size-L team-predict Predict team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants