Skip to content

fix(harmony): align isFirstTime, rollback, and soft reload with android/ios - #587

Closed
sunnylqm wants to merge 4 commits into
masterfrom
fix/harmony-first-time-rollback
Closed

fix(harmony): align isFirstTime, rollback, and soft reload with android/ios#587
sunnylqm wants to merge 4 commits into
masterfrom
fix/harmony-first-time-rollback

Conversation

@sunnylqm

@sunnylqm sunnylqm commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

This PR resolves critical design discrepancies in the HarmonyOS implementation, aligning it with the robust state-machine logic and reload experiences of the iOS and Android platforms:

1. isFirstTime Loading & Rollback Protection

  • Problem: The HarmonyOS module read the raw firstTime value from preferences directly on React Native startup without ever consuming or resetting it. Furthermore, the consume_first_time_on_launch parameter was hardcoded to false. This prevented rollback protection from triggering on crash/failure, and prevented isFirstTime from being a single-use flag.
  • Solution:
    • Set consume_first_time_on_launch = true during getBundleUrl().
    • Introduced a persistent firstLoadMarked key to Preferences when the launch state consumes first time.
    • Implemented consumeFirstLoadMarker() to read and immediately delete the persistent marker.
    • Aligned clearFirstTime() to delete firstLoadMarked.
    • Introduced ignoreRollback as a static memory variable to handle multiple bundle path resolutions during a single application process startup.

2. In-Memory Soft Reload

  • Problem: Previously, reload operations on HarmonyOS triggered a hard application UIAbility restart (terminateSelf + startAbility), causing the window to blink/go white. Both Android and iOS support smooth in-memory soft reloading.
  • Solution:
    • Implemented reloadBridge() to emit the "RELOAD" event to RNOH's devToolsController for seamless in-memory reloading.
    • Added a fallback to restartAbility() if devToolsController is unavailable.
    • Updated PushyFileJSBundleProvider.ets to dynamically fetch the update bundle URL on every call to getURL() / getBundle(), ensuring that when the React Native container reloads, it fetches the newly switched version.

All JS/TS unit tests pass (70/70) and the Harmony HAR compiled successfully.

Summary by CodeRabbit

  • New Features

    • Improved app update handling so reloads can refresh the app more reliably, with a smoother fallback when a live reload isn’t available.
  • Bug Fixes

    • Fixed bundle loading to use the latest available app package path, reducing errors after updates.
    • Improved first-launch behavior so the app better distinguishes an initial run from later launches during update checks.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

UpdateContext gains a static ignoreRollback flag, a firstLoadMarked preferences key managed through extended persistState/runStateOperation options, and a new consumeFirstLoadMarker() method. getBundleUrl passes ignoreRollback to runStateCore and sets the marker on first-time consumption. PushyTurboModule uses consumeFirstLoadMarker() in getConstants() and routes post-switch reloads through a new reloadBridge() helper that prefers devToolsController over restartAbility. PushyFileJSBundleProvider reads the bundle URL dynamically from UpdateContext.getBundleUrl() instead of a stored field.

Changes

First-load marker, ignoreRollback, and reload bridge

Layer / File(s) Summary
UpdateContext: ignoreRollback flag, firstLoadMarked persistence, consumeFirstLoadMarker()
harmony/pushy/src/main/ets/UpdateContext.ts
Adds static ignoreRollback (reset in syncStateWithBinaryVersion), extends persistState and runStateOperation options with markFirstLoadMarker/clearFirstLoadMarker, clears marker in clearFirstTime, reworks getBundleUrl to pass ignoreRollback to runStateCore and set ignoreRollback=true on consumedFirstTime, and adds public consumeFirstLoadMarker().
PushyTurboModule: consumeFirstLoadMarker and reloadBridge
harmony/pushy/src/main/ets/PushyTurboModule.ts
Adds reloadBridge() that emits RELOAD via devToolsController.eventEmitter or falls back to restartAbility(). getConstants() now calls context.consumeFirstLoadMarker(). reloadUpdate() and restartApp() call reloadBridge() instead of restartAbility().
PushyFileJSBundleProvider: dynamic bundle URL
harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets
Constructor no longer stores a bundle path field; getURL() and getBundle() call updateContext.getBundleUrl() dynamically at invocation time.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Poem

🐇 A marker is planted, then plucked from the store,
The rollback flag flips when first-time is no more,
The bridge checks its tools before restarting the show,
The bundle URL blooms wherever we go.
Hop, hop — the context decides, not the field!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main HarmonyOS changes to first-time launch handling, rollback protection, and soft reload behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/harmony-first-time-rollback

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

harmony/pushy/src/main/ets/PushyTurboModule.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: .eslintrc.js » @react-native/eslint-config#overrides[4]:
Environment key "jest/globals" is unknown

at /node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2079:23
at Array.forEach (<anonymous>)
at ConfigValidator.validateEnvironment (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2073:34)
at ConfigValidator.validateConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2223:18)
at CascadingConfigArrayFactory._finalizeConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3985:23)
at CascadingConfigArrayFactory.getConfigArrayForFile (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3791:21)
at FileEnumerator._iterateFilesWithFile (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:368:43)
at FileEnumerator._iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:349:25)
at FileEnumerator.iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:299:59)
at iterateFiles.next (<anonymous>)
harmony/pushy/src/main/ets/UpdateContext.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: .eslintrc.js » @react-native/eslint-config#overrides[4]:
Environment key "jest/globals" is unknown

at /node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2079:23
at Array.forEach (<anonymous>)
at ConfigValidator.validateEnvironment (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2073:34)
at ConfigValidator.validateConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2223:18)
at CascadingConfigArrayFactory._finalizeConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3985:23)
at CascadingConfigArrayFactory.getConfigArrayForFile (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3791:21)
at FileEnumerator._iterateFilesWithFile (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:368:43)
at FileEnumerator._iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:349:25)
at FileEnumerator.iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:299:59)
at iterateFiles.next (<anonymous>)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sunnylqm sunnylqm closed this Jun 30, 2026

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@harmony/pushy/src/main/ets/UpdateContext.ts`:
- Around line 402-408: `UpdateContext` is setting `firstLoadMarked` and
`ignoreRollback` too early, before `launchState.loadVersion` is confirmed and a
verified bundle is returned. Move the `persistState(...)` and
`UpdateContext.ignoreRollback = true` logic so it runs only after the selected
bundle file has been validated and is about to be returned, or explicitly clear
both flags on the missing-file rollback paths before calling `rollBack()`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 00a31086-8fe6-42cc-b21c-eb0c6ea287ea

📥 Commits

Reviewing files that changed from the base of the PR and between 2c864be and 5b691c4.

📒 Files selected for processing (3)
  • harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets
  • harmony/pushy/src/main/ets/PushyTurboModule.ts
  • harmony/pushy/src/main/ets/UpdateContext.ts

Comment on lines 402 to +408
if (launchState.didRollback || launchState.consumedFirstTime) {
this.persistState(launchState);
this.persistState(launchState, {
markFirstLoadMarker: launchState.consumedFirstTime,
});
}
if (launchState.consumedFirstTime) {
UpdateContext.ignoreRollback = true;

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Only mark first-load after the selected bundle is verified.

firstLoadMarked and ignoreRollback are set before the loop confirms launchState.loadVersion exists. If that file is missing, the later rollback path can load a fallback bundle while getConstants() still reports isFirstTime: true for the failed version.

Suggested direction
-    if (launchState.didRollback || launchState.consumedFirstTime) {
+    const shouldMarkFirstLoad = !!launchState.consumedFirstTime;
+    if (launchState.didRollback || shouldMarkFirstLoad) {
       this.persistState(launchState, {
-        markFirstLoadMarker: launchState.consumedFirstTime,
       });
     }
-    if (launchState.consumedFirstTime) {
-      UpdateContext.ignoreRollback = true;
-    }

Then set firstLoadMarked / ignoreRollback only immediately before returning a verified bundleFile, or clear both before calling rollBack() on the missing-file paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harmony/pushy/src/main/ets/UpdateContext.ts` around lines 402 - 408,
`UpdateContext` is setting `firstLoadMarked` and `ignoreRollback` too early,
before `launchState.loadVersion` is confirmed and a verified bundle is returned.
Move the `persistState(...)` and `UpdateContext.ignoreRollback = true` logic so
it runs only after the selected bundle file has been validated and is about to
be returned, or explicitly clear both flags on the missing-file rollback paths
before calling `rollBack()`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant