Skip to content

feat(predict): add remote feed banner - #33478

Merged
ghgoodreau merged 3 commits into
mainfrom
PRED-850
Jul 24, 2026
Merged

feat(predict): add remote feed banner#33478
ghgoodreau merged 3 commits into
mainfrom
PRED-850

Conversation

@ghgoodreau

@ghgoodreau ghgoodreau commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Description

Predict currently has no targeted, remotely managed way to display operational or promotional messages inside its feed. This adds a version-gated predictFeedBanner JSON flag that can publish, update, move, or remove one active message without an app release.

The remote payload controls a stable message ID, title, description, MMDS severity, optional dismissal, and a named insertion position. Named slots cover both the current PredictFeed header and the redesigned PredictHome sections. Dismissals are persisted under the message ID, so updated content can be shown again by publishing a new ID.

The banner uses MMDS BannerAlert for semantic light/dark styling. The existing World Cup banner remains mounted in its current position and retains its existing visibility, analytics, and navigation behavior.

Example payload:

{
  "enabled": true,
  "minimumVersion": "8.5.0",
  "id": "predict-maintenance-2026-07-20",
  "title": "Scheduled maintenance",
  "description": "Predict will be temporarily unavailable at 18:00 UTC.",
  "position": "after-world-cup-banner",
  "severity": "warning",
  "dismissible": true
}

Supported positions: after-balance, after-featured-carousel, after-world-cup-banner, before-portfolio, after-portfolio, after-live-now, after-categories, after-popular-today, and after-trending.

Supported severities: neutral, info, success, warning, and danger.

Changelog

CHANGELOG entry: Added remotely configurable messages to the Predict feed

Related issues

Refs: PRED-850

Manual testing steps

Feature: Remotely configurable Predict feed banner

  Scenario: Enabled message is displayed
    Given predictFeedBanner contains valid content and enabled is true
    When the user opens Predict
    Then the message is shown at its configured feed position

  Scenario: Disabled message is hidden
    Given predictFeedBanner enabled is false
    When the user opens Predict
    Then no remote Predict feed message is shown

  Scenario: Dismissible message is closed
    Given a dismissible remote message is visible
    When the user presses its close button
    Then that message ID remains hidden after reopening Predict

  Scenario: World Cup banner remains unchanged
    Given the World Cup main feed banner is enabled
    When the user opens the existing Predict feed
    Then the World Cup banner retains its current content and interaction behavior

Automated coverage:

  • yarn jest app/components/UI/Predict/schemas/flags.test.ts app/components/UI/Predict/selectors/featureFlags/index.test.ts app/components/UI/Predict/components/PredictFeedBanner/PredictFeedBanner.test.tsx app/components/UI/Predict/views/PredictFeed/PredictFeed.test.tsx app/components/UI/Predict/components/PredictWorldCupMainFeedBanner/PredictWorldCupMainFeedBanner.test.tsx tests/feature-flags/feature-flag-registry.test.ts --runInBand --coverage=false
  • yarn jest -c jest.config.view.js app/components/UI/Predict/views/PredictHome/PredictHome.view.test.tsx --runInBand --silent --coverage=false
  • yarn lint:tsc

Device-matrix testing remains pending while this PR is in draft.

Screenshots/Recordings

Before

N/A - Predict has no existing remote feed message module.

After

LaunchDarkly check on local dev env + remote launchdarkly:

Screenshot 2026-07-20 at 3 03 06 PM

Test render of all variants:

Screenshot 2026-07-20 at 3 03 18 PM

Compared to Figma designs:

Screenshot 2026-07-20 at 3 04 19 PM

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
    • Pending: the ticket owner will complete device-matrix testing before marking this PR ready for review.
    • Ideally on a mid-range device; emulator is acceptable
  • I've tested with a power user scenario
    • N/A: Feed message behavior does not depend on account or token scale.
    • 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
    • N/A: This UI module introduces no long-running operation or new trace boundary.
    • See trace() for usage and addToken for an example

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

Low Risk
User-facing messaging only, with strict remote parsing and fallbacks to disabled defaults; reuses existing banner dismissal state with no auth or payment changes.

Overview
Introduces remotely configurable operational messages in Predict via a new predictFeedBanner feature flag (registered with safe disabled defaults). Remote JSON drives message id, title, description, severity, dismissible behavior, minimumVersion gating, and a single placement among named feed positions.

A new PredictFeedBanner component reads validated config from selectPredictFeedBannerConfig, renders MMDS BannerAlert only when enabled and the slot position matches, and optionally persists dismissals through the existing dismissBanner flow keyed by predict-feed-banner:{id} so a new message id can show again after an old one was closed.

PredictFeed mounts banner slots after balance, featured carousel, and World Cup banner; PredictHome mounts slots around portfolio and each major section. Schema validation, selector fallbacks, component tests, feed/home integration tests, and view tests cover flag parsing and rendering behavior.

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

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

@metamask-ci metamask-ci Bot added the team-predict Predict team label Jul 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Flaky unit test detection

Run history flaky detection

View recent run history

Historical failure rate is a hint, not proof — review each suggestion in context. See the flaky-test-detection skill for the full pattern reference and manual audit workflow.

Failures / runs sampled per window:

File 7d 15d 30d
app/components/UI/Predict/views/PredictFeed/PredictFeed.test.tsx 0/164 0/164 0/369

AI-detected flaky patterns

app/components/UI/Predict/views/PredictFeed/PredictFeed.test.tsx

  • J9 — Module-level mutable let binding not reset in beforeEach (high)
    • The module-level let mockIsWorldCupMainFeedTabEnabled = false is mutated inside several 'World Cup tab feature flag' tests (e.g. mockIsWorldCupMainFeedTabEnabled = true). The beforeEach block calls jest.clearAllMocks() but that only resets mock call history — it does NOT restore the value of a plain let variable. If a test that sets mockIsWorldCupMainFeedTabEnabled = true runs before a test that expects it to be false (e.g. 'does not render World Cup tab when flag is disabled'), the latter will fail intermittently depending on test execution order (e.g. when run with --randomize).
    • Suggested fix in app/components/UI/Predict/views/PredictFeed/PredictFeed.test.tsx:108:
      -let mockIsWorldCupMainFeedTabEnabled = false;
      -// ...
      -  beforeEach(() => {
      -    jest.clearAllMocks();
      -    mockUseNavigation.mockReturnValue(mockNavigation);
      -    mockUseRoute.mockReturnValue({
      -      params: {
      -    // ...
      -  });
      -  afterEach(() => {
      -    jest.clearAllMocks();
      -  });
      +let mockIsWorldCupMainFeedTabEnabled = false;
      +// ...
      +  beforeEach(() => {
      +    jest.clearAllMocks();
      +    mockIsWorldCupMainFeedTabEnabled = false; // reset mutable binding
      +    mockUseNavigation.mockReturnValue(mockNavigation);
      +    mockUseRoute.mockReturnValue({
      +      params: {
      +    // ...
      +  });
  • J9 — Module-level mutable object binding not reset in beforeEach (high)
    • mockHotTabFlag is a module-level const object whose properties are mutated directly in tests (e.g. mockHotTabFlag.enabled = true, mockHotTabFlag.queryParams = 'tag_id=149&order=volume24hr'). jest.clearAllMocks() in beforeEach only resets Jest mock call history — it does NOT restore mutated plain-object properties. If a 'hot tab enabled' test runs before a test that expects mockHotTabFlag.enabled to be false (e.g. 'does not render Hot tab when flag is disabled', 'renders all six category tabs'), the latter will see stale true and fail intermittently under --randomize.
    • Suggested fix in app/components/UI/Predict/views/PredictFeed/PredictFeed.test.tsx:112:
      -const mockHotTabFlag: { enabled: boolean; queryParams?: string } = {
      -  enabled: false,
      -  queryParams: undefined,
      -};
      -// ...
      -  beforeEach(() => {
      -    jest.clearAllMocks();
      -    mockUseNavigation.mockReturnValue(mockNavigation);
      -    // ...
      -  });
      +const mockHotTabFlag: { enabled: boolean; queryParams?: string } = {
      +  enabled: false,
      +  queryParams: undefined,
      +};
      +// ...
      +  beforeEach(() => {
      +    jest.clearAllMocks();
      +    mockHotTabFlag.enabled = false;       // reset mutable object properties
      +    mockHotTabFlag.queryParams = undefined;
      +    mockIsWorldCupMainFeedTabEnabled = false;
      +    mockUseNavigation.mockReturnValue(mockNavigation);
      +    // ...
      +  });

This check is informational only and does not block merging.

@ghgoodreau
ghgoodreau marked this pull request as ready for review July 20, 2026 19:04
@ghgoodreau
ghgoodreau requested review from a team as code owners July 20, 2026 19:04
@github-actions github-actions Bot added the risk:low AI analysis: low risk label Jul 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚡ Performance Test Results

ℹ️ Performance test results are currently non-blocking and will not block this PR.

All tests passed · 3 tests · 1 device

📱 Devices tested (1)

Android: Google Pixel 8 Pro (v14.0)

✅ Passed Tests (3)
Test Platform Device Duration Team Recording
Predict Deposit - Complete Flow Performance Android Google Pixel 8 Pro (v14.0) 11.09s @team-predict 📹 Watch
Predict Market Details - Complete Flow Performance Android Google Pixel 8 Pro (v14.0) 6.62s @team-predict 📹 Watch
Predict Available Balance - Complete Flow Performance Android Google Pixel 8 Pro (v14.0) 1.37s @team-predict 📹 Watch

Branch: PRED-850 · Build: Normal · Commit: 88859e5 · View full run

@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: 88%
click to see 🤖 AI reasoning details

E2E Test Selection:
This PR introduces a new PredictFeedBanner component and its supporting infrastructure (feature flag, types, schemas, selectors, constants) for the Predict feature area.

Key changes:

  1. Feature flag registry (tests/feature-flags/feature-flag-registry.ts): Adds predictFeedBanner remote feature flag (disabled by default, inProd: false).
  2. New PredictFeedBanner component: A configurable banner alert rendered at various positions in the Predict feed/home views, controlled by the remote feature flag.
  3. PredictFeed.tsx: Adds PredictFeedBanner at 3 positions (AfterBalance, AfterFeaturedCarousel, AfterWorldCupBanner).
  4. PredictHome.tsx: Adds PredictFeedBanner at 6 positions (BeforePortfolio, AfterPortfolio, AfterLiveNow, AfterCategories, AfterPopularToday, AfterTrending).
  5. Supporting files: New types, schemas, constants, and selectors for the banner feature flag.

Tag selection rationale:

  • SmokePredictions: Directly affected — the Predict feed and home views are modified with new banner components. The banner is disabled by default but the structural changes to PredictFeed and PredictHome need validation.
  • SmokeWalletPlatform: Required per SmokePredictions tag description — "Predictions is also a section inside the Trending tab (SmokeWalletPlatform); changes to Predictions views affect Trending."
  • SmokeConfirmations: Required per SmokePredictions tag description — "opening/closing positions are on-chain transactions" and "When selecting SmokePredictions, also select SmokeConfirmations."

The feature flag is disabled by default so the banner won't render in normal test conditions, but the structural changes to PredictFeed and PredictHome warrant validation to ensure no regressions in the existing Predict UI layout.

Performance Test Selection:
The PredictFeedBanner component returns null when the feature flag is disabled (which is the default state: enabled: false, inProd: false). There are no changes to performance-sensitive rendering paths, data fetching, or app launch flows. The new component adds minimal overhead only when the flag is enabled. No performance test tags are warranted.

View GitHub Actions results

@sonarqubecloud

Copy link
Copy Markdown

@matallui
matallui requested a review from MarioAslau July 22, 2026 11:00
@ghgoodreau
ghgoodreau enabled auto-merge July 24, 2026 14:28
@ghgoodreau
ghgoodreau added this pull request to the merge queue Jul 24, 2026
Merged via the queue into main with commit b641a82 Jul 24, 2026
180 checks passed
@ghgoodreau
ghgoodreau deleted the PRED-850 branch July 24, 2026 17:15
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 24, 2026
@metamask-ci metamask-ci Bot added the release-8.6.0 Issue or pull request that will be included in release 8.6.0 label Jul 24, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants