chore(runway): cherry-pick fix(predict): world cup fixes#30983
Merged
Conversation
This PR bundles several fixes and improvements to the Predict World Cup
experience and the shared sports scoreboard.
Live World Cup soccer markets were being filtered out of the **Live**
tab while the matches were still ongoing.
**Root cause:** live game markets carry `recurrence: DAILY` and a
market-level `endDate` set to the match's *scheduled* end. In
`getVisiblePredictMarket`, the time-based expiry check ran **before**
the game-market check, and for any non-`ended` game it fell through to
the daily `endDate <= now` staleness heuristic. Soccer matches routinely
run past their scheduled `endDate` (stoppage time, halftime, extra time,
penalties), so ongoing games were wrongly treated as expired and hidden.
**Fix:** `isPredictMarketExpiredByTime` is now authoritative for game
markets — a game market expires **only** when the game itself is over
(`status === 'ended'` or an `endTime` is stamped), never via the
scheduled `endDate`. The daily `endDate` heuristic still applies to
non-game daily markets.
The live query now uses `tag_slug` + `live=true` (instead of
`series_id`), which is enough to return all live games. This lets us
repoint the Live UI (e.g. to all soccer leagues) purely via the
`tagSlug` feature flag. `seriesId` has been **removed entirely** from
`PredictWorldCupConfig`, its default flag, schema, and data-config types
since it is no longer read anywhere.
- The scoreboard center status now formats based on the game's
period/elapsed/sport:
- `{period}` at a breaking/terminal period (e.g. "Halftime", "Final")
- `{elapsed}’` for **soccer** during running play (e.g. "75’")
- `{period} • {elapsed}` for other sports during running play (e.g. "Q1
• 8:15")
- Extracted the card's scoreboard UI into the shared
**`PredictSportScoreboard`** component (replacing the legacy
details-screen UI) so the **card and the game details screen render the
same scoreboard**. It supports a `compact` variant for carousel cards.
- Fixed the pulsing "Live" dot alignment so the dot sits to the left of
the centered "Live" label, with the elapsed/status text centered on the
same axis (per Figma).
CHANGELOG entry: null
Fixes:
[PRED-942](https://consensyssoftware.atlassian.net/browse/PRED-942)
```gherkin
Feature: Predict World Cup Live tab
Scenario: A live match that has run past its scheduled end time stays visible
Given the Predict World Cup feature is enabled
And a soccer match is in progress (game status "ongoing")
And the match has already passed its scheduled market endDate (e.g. it is in stoppage/extra time)
When the user opens the World Cup "Live" tab
Then the ongoing match is still listed in the Live tab
And it only disappears once the game reports status "ended"
Scenario: Live query returns games via tag slug
Given the Predict World Cup feature flag tagSlug is set
When the Live tab loads
Then markets are fetched using tag_slug + live=true (no series_id)
And changing tagSlug via feature flag scopes the Live tab to the configured tag
Feature: Sports scoreboard
Scenario: Live status text reflects the game state
Given a live game card or the game details screen
When the game is at a running period
Then soccer shows "{elapsed}’" (e.g. "75’") and other sports show "{period} • {elapsed}" (e.g. "Q1 • 8:15")
And at a break it shows the period label (e.g. "Halftime"), and when finished it shows "Final"
Scenario: Scoreboard is consistent between card and details
Given a sports market
When viewing it as a card and on the game details screen
Then both render the same scoreboard (team logos, scores, live/scheduled/final status, team names)
```
<!-- Live tab empty / ongoing matches missing while games are still in
progress; dot/elapsed misaligned; legacy details scoreboard -->
https://github.com/user-attachments/assets/9a7d9243-7e0f-488f-af17-8eb584515580
- [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.
- [ ] 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.
[PRED-942]:
https://consensyssoftware.atlassian.net/browse/PRED-942?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches market visibility, remote feature-flag parsing, and live
betting UI gating; well-tested but affects what users see during live
matches and World Cup rollout.
>
> **Overview**
> Fixes **Predict World Cup Live** and **sports UI** by aligning game
lifecycle, queries, and a shared scoreboard.
>
> **Live tab / staleness:** Game markets no longer expire from the
scheduled market `endDate` while play is still **ongoing** (e.g.
stoppage/extra time). Expiry uses shared **`isGameEnded`** (`status ===
'ended'`, **`FT`/`VFT`**, or **`endTime`**), matching scoreboard “Final”
and buy-button gating.
>
> **World Cup queries & flags:** Live fetch uses **`tag_slug` +
`live=true`** (drops **`series_id`**). **`seriesId`** is removed from
config/types; **`PredictWorldCupSchema`** uses **`type()`** so
legacy/extra remote keys (e.g. **`seriesId`**) don’t fail parsing and
disable the feature.
>
> **Scoreboard:** Inline card UI moves to **`PredictSportScoreboard`** (
**`compact`**, optional parent **`gameUpdate`** to avoid duplicate
WebSocket). Status text: soccer minutes (**`75’`**), other sports **`Q •
clock`**, breaks/halftime/final via **`getSportLiveStatusText`**. Sport
cards hide buy buttons at full time before **`status`** flips to ended.
Possession/winner trophy UI removed from scoreboard.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
37ef6ba. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
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. |
Contributor
🔍 Smart E2E Test Selection⏭️ Smart E2E selection skipped - PR targets a release or stable branch (release/* or stable) All E2E tests pre-selected. |
vpintorico
approved these changes
Jun 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR bundles several fixes and improvements to the Predict World Cup
experience and the shared sports scoreboard.
1. Live games no longer disappear from the Live tab
Live World Cup soccer markets were being filtered out of the Live
tab while the matches were still ongoing.
Root cause: live game markets carry
recurrence: DAILYand amarket-level
endDateset to the match's scheduled end. IngetVisiblePredictMarket, the time-based expiry check ran beforethe game-market check, and for any non-
endedgame it fell through tothe daily
endDate <= nowstaleness heuristic. Soccer matches routinelyrun past their scheduled
endDate(stoppage time, halftime, extra time,penalties), so ongoing games were wrongly treated as expired and hidden.
Fix:
isPredictMarketExpiredByTimeis now authoritative for gamemarkets — a game market expires only when the game itself is over
(
status === 'ended'or anendTimeis stamped), never via thescheduled
endDate. The dailyendDateheuristic still applies tonon-game daily markets.
2. Broaden the live query (drop
series_id)The live query now uses
tag_slug+live=true(instead ofseries_id), which is enough to return all live games. This lets usrepoint the Live UI (e.g. to all soccer leagues) purely via the
tagSlugfeature flag.seriesIdhas been removed entirely fromPredictWorldCupConfig, its default flag, schema, and data-config typessince it is no longer read anywhere.
3. Scoreboard: live status text + shared component
period/elapsed/sport:
{period}at a breaking/terminal period (e.g. "Halftime", "Final"){elapsed}’for soccer during running play (e.g. "75’"){period} • {elapsed}for other sports during running play (e.g. "Q1• 8:15")
PredictSportScoreboardcomponent (replacing the legacydetails-screen UI) so the card and the game details screen render the
same scoreboard. It supports a
compactvariant for carousel cards.the centered "Live" label, with the elapsed/status text centered on the
same axis (per Figma).
Changelog
CHANGELOG entry: null
Related issues
Fixes:
PRED-942
Manual testing steps
Screenshots/Recordings
Before
After
Screen.Recording.2026-06-01.at.1.35.20.PM.mov
Pre-merge author checklist
Docs and MetaMask Mobile
Coding
Standards.
if applicable
guidelines).
Not required for external contributors.
Pre-merge reviewer checklist
app, test code being changed).
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
Note
Medium Risk
Touches market visibility, remote feature-flag parsing, and live
betting UI gating; well-tested but affects what users see during live
matches and World Cup rollout.
Overview
Fixes Predict World Cup Live and sports UI by aligning game
lifecycle, queries, and a shared scoreboard.
Live tab / staleness: Game markets no longer expire from the
scheduled market
endDatewhile play is still ongoing (e.g.stoppage/extra time). Expiry uses shared
isGameEnded(status === 'ended',FT/VFT, orendTime), matching scoreboard “Final”and buy-button gating.
World Cup queries & flags: Live fetch uses
tag_slug+live=true(dropsseries_id).seriesIdis removed fromconfig/types;
PredictWorldCupSchemausestype()solegacy/extra remote keys (e.g.
seriesId) don’t fail parsing anddisable the feature.
Scoreboard: Inline card UI moves to
PredictSportScoreboard(compact, optional parentgameUpdateto avoid duplicateWebSocket). Status text: soccer minutes (
75’), other sportsQ • clock, breaks/halftime/final viagetSportLiveStatusText. Sportcards hide buy buttons at full time before
statusflips to ended.Possession/winner trophy UI removed from scoreboard.
Reviewed by Cursor Bugbot for commit
37ef6ba. Bugbot is set up for automated
code reviews on this repo. Configure
here.