Skip to content

Conversation

@weitingsun
Copy link
Contributor

@weitingsun weitingsun commented Dec 18, 2025

Description

Implement OTA Update modal. On iOS, if there are updates available, users can reload the app to see the updates. On Android, the app crashes when we call reloadAsync so we let users know that they need to close and reopen the app to receive the updates

Changelog

CHANGELOG entry: Added OTA updates modal

Related issues

Fixes: #24110

Manual testing steps

Feature: OTA update modal
  In order to keep the app up to date without reinstalling
  As a user
  I want to be prompted to reload when a new OTA update is available

  Background:
    Given the app has launched
    And OTA updates are enabled

  Scenario: Display OTA update modal when a new update is available
    Given a new OTA update has been downloaded in the background
    When I am on the home screen
    Then I see the OTA update modal
    And the modal explains that a new version is ready to use

  Scenario: User chooses to update now
    Given the OTA update modal is visible
    When I tap the "Update now" button
    Then the app reloads to apply the new OTA update

  Scenario: User chooses to update later
    Given the OTA update modal is visible
    When drawer is dismissed
    Then the modal closes
    And the app continues using the current version without reloading

Screenshots/Recordings

Before

After

iOS Android
Simulator Screenshot - iPhone 16 - 2026-01-06 at 03 29 25 Screenshot 2026-01-06 at 3 44 24 AM

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

Adds an OTA-driven update UX using a modal and removes render-blocking during update checks.

  • New OTAUpdatesModal bottom sheet with platform-specific copy and primary action (iOS reload via reloadAsync); tracked with MetaMetricsEvents and covered by tests
  • Refactors useOTAUpdates to: respect feature flag, skip in __DEV__, check/fetch updates, and navigate to OTAUpdatesModal after interactions when isNew is true; no longer reloads automatically or gate-renders via FoxLoader
  • Integrates hook in App and registers route Routes.MODAL.OTA_UPDATES_MODAL; updates App.test.tsx to remove loader assertions and mock useOTAUpdates
  • Adds analytics events (OTA_UPDATES_MODAL_VIEWED, OTA_UPDATES_MODAL_PRIMARY_ACTION_CLICKED) and English i18n strings for modal content

Written by Cursor Bugbot for commit bedde63. This will update automatically on new commits. Configure here.

@github-actions
Copy link
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-mobile-platform Mobile Platform team label Dec 18, 2025
@weitingsun weitingsun self-assigned this Dec 18, 2025
@weitingsun weitingsun marked this pull request as ready for review December 19, 2025 00:24
@github-project-automation github-project-automation bot moved this to Needs dev review in PR review queue Dec 19, 2025
@weitingsun weitingsun requested a review from a team as a code owner December 19, 2025 18:33
return {
isCheckingUpdates,
};
}, [navigation, otaUpdatesEnabled]);
Copy link

Choose a reason for hiding this comment

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

Bug: Missing cleanup for InteractionManager callback on unmount

The useEffect in useOTAUpdates schedules a navigation callback via InteractionManager.runAfterInteractions but doesn't return a cleanup function to cancel it. If the Main component unmounts (e.g., user logs out) while the callback is pending, the navigation to OTAUpdatesModal will still be attempted against a potentially stale navigation context. The runAfterInteractions returns a cancellable handle that could be stored and cancelled in a cleanup function returned from the effect.

Fix in Cursor Fix in Web

return {
isCheckingUpdates,
};
}, [navigation, otaUpdatesEnabled]);
Copy link

Choose a reason for hiding this comment

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

Effect may run multiple times despite documentation

The hook's documentation states it "Runs once when the app initially opens," but the useEffect includes navigation in its dependency array. The navigation object from useNavigation() may change identity between renders, causing the effect to re-run. Without a guard (like a useRef to track if the check already ran), this could trigger multiple checkForUpdateAsync calls and multiple navigations to the OTA modal when the navigation object changes.

Fix in Cursor Fix in Web

});

it('checks for updates when feature flag is enabled', async () => {
mockSelectOtaUpdatesEnabledFlag.mockReturnValue(true);
Copy link

Choose a reason for hiding this comment

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

Tests missing AAA blank line separation (Bugbot Rules)

The unit testing guidelines require every test to follow the AAA pattern with blank line separation between Arrange, Act, and Assert phases. In these tests, there's no blank line between the Arrange step (mockSelectOtaUpdatesEnabledFlag.mockReturnValue(false) or __DEV__ = true) and the Act step (renderHook(...)). This violates the rule: "EVERY test MUST follow the AAA pattern (Arrange, Act, Assert) with blank line separation."

Additional Locations (1)

Fix in Cursor Fix in Web

});

it('checks for updates when feature flag is enabled', async () => {
mockSelectOtaUpdatesEnabledFlag.mockReturnValue(true);
Copy link

Choose a reason for hiding this comment

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

Tests violate AAA pattern blank line separation rule (Bugbot Rules)

The first two tests in this file are missing blank line separation between Arrange and Act phases, violating the unit testing guidelines rule: "EVERY test MUST follow the AAA pattern (Arrange, Act, Assert) with blank line separation." In test does not check for updates when feature flag is disabled, line 84 (Arrange) directly precedes line 85 (Act) with no blank line. Same issue in skips update check in development mode even when feature flag is enabled between lines 93 and 94. The other tests in this file correctly follow the pattern with blank lines between all phases.

Additional Locations (1)

Fix in Cursor Fix in Web

@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2026

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokeCore, SmokeWalletUX
  • Risk Level: medium
  • AI Confidence: 78%
click to see 🤖 AI reasoning details

This PR introduces a new OTA (Over-The-Air) Updates Modal feature with the following key changes:

  1. New OTAUpdatesModal component: A bottom sheet modal that displays when an OTA update is available, with platform-specific behavior (iOS: reload, Android: acknowledge).

  2. Modified useOTAUpdates hook: Changed from blocking app startup with a loading state to non-blocking behavior. Now navigates to the modal instead of auto-reloading.

  3. App.tsx changes: Removed the FoxLoader blocking behavior during OTA checks, added OTAUpdatesModal to navigation stack, and simplified the App component structure.

  4. Analytics events: Added two new events for tracking modal interactions.

  5. Navigation routes: Added new OTA_UPDATES_MODAL route.

Risk Assessment:

  • The changes affect the core App.tsx navigation component, which is central to the app's navigation flow
  • The startup behavior has changed from blocking to non-blocking, which could affect user experience
  • However, the changes are additive (new modal) and simplifying (removing blocking behavior)
  • The MetaMetrics.events.ts changes are just adding new event constants (low risk)
  • No existing E2E tests cover OTA updates functionality

Tag Selection Rationale:

  • SmokeCore: Selected because App.tsx is a core navigation component and the changes affect app startup flow and navigation state
  • SmokeWalletUX: Selected because this is a user experience change (new modal, changed startup behavior) that affects how users interact with the app

The changes are self-contained to the OTA updates feature and don't touch other functional areas like accounts, swaps, confirmations, etc.

View GitHub Actions results

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 6, 2026

Copy link
Contributor

@Cal-L Cal-L left a comment

Choose a reason for hiding this comment

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

LGTM

@github-project-automation github-project-automation bot moved this from Needs dev review to Review finalised - Ready to be merged in PR review queue Jan 6, 2026
@Cal-L Cal-L added this pull request to the merge queue Jan 6, 2026
Merged via the queue into main with commit 218f8fd Jan 6, 2026
91 checks passed
@Cal-L Cal-L deleted the wsun/ota-update-modal branch January 6, 2026 20:32
@github-project-automation github-project-automation bot moved this from Review finalised - Ready to be merged to Merged, Closed or Archived in PR review queue Jan 6, 2026
@github-actions github-actions bot locked and limited conversation to collaborators Jan 6, 2026
@metamaskbot metamaskbot added the release-7.62.0 Issue or pull request that will be included in release 7.62.0 label Jan 6, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.62.0 Issue or pull request that will be included in release 7.62.0 size-L team-mobile-platform Mobile Platform team

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

OTA modal when we receive OTA updates

4 participants