Skip to content

chore(runway): cherry-pick fix(predict): world cup fixes#30983

Merged
runway-github[bot] merged 1 commit into
release/7.80.0from
cherry-pick-7-80-0-2b6abc7
Jun 3, 2026
Merged

chore(runway): cherry-pick fix(predict): world cup fixes#30983
runway-github[bot] merged 1 commit into
release/7.80.0from
cherry-pick-7-80-0-2b6abc7

Conversation

@runway-github

@runway-github runway-github Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

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

2. Broaden the live query (drop series_id)

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.

3. Scoreboard: live status text + shared component

  • 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

CHANGELOG entry: null

Related issues

Fixes:
PRED-942

Manual testing steps

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)

Screenshots/Recordings

Before

After

Screen.Recording.2026-06-01.at.1.35.20.PM.mov

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

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

[2b6abc7](https://github.com/MetaMask/metamask-mobile/commit/2b6abc7a44315f098418d574591e5757d2bbd161)

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 -->
@runway-github runway-github Bot requested a review from a team as a code owner June 3, 2026 02:25
@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-bots Bot team (for MetaMask Bot, Runway Bot, etc.) label Jun 3, 2026
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

⏭️ Smart E2E selection skipped - PR targets a release or stable branch (release/* or stable)

All E2E tests pre-selected.

View GitHub Actions results

@runway-github runway-github Bot merged commit 20655bc into release/7.80.0 Jun 3, 2026
308 of 314 checks passed
@runway-github runway-github Bot deleted the cherry-pick-7-80-0-2b6abc7 branch June 3, 2026 07:14
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 3, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

size-XL team-bots Bot team (for MetaMask Bot, Runway Bot, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants