Skip to content

chore(mobile): React Native 0.81.6 (old arch) - #14642

Merged
dylanjeffers merged 4 commits into
chore/mobile-native-release-1.5.188from
chore/mobile-rn-0.81
Oct 9, 2026
Merged

dylanjeffers merged 4 commits into
chore/mobile-native-release-1.5.188from
chore/mobile-rn-0.81

Conversation

@dylanjeffers

@dylanjeffers dylanjeffers commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Phase 1 of the React Native upgrade: 0.79.5 → 0.81.6 with the old architecture still on.

Base: chore/mobile-native-release-1.5.188 (#14654). Rebased onto it after 1.5.187 (#14645, which squashed #14627, #14640 and #14650) merged to main; the two lib-swap commits are gone from this branch. Merge #14647 and #14651 into this branch first, then this into #14654, then #14654 into main as one push (see #14654 for the full order).

Rebase onto 1.5.187 (2026-10-08)

Needs a native release (1.5.188). Don't OTA this JS onto older binaries. Old architecture stays on: Android newArchEnabled=false, iOS RCTNewArchEnabled=false, pods installed with RCT_NEW_ARCH_ENABLED=0.

Two commits, one per step. Each step builds on iOS and Android:

  1. 0.79.5 → 0.80.3
  2. 0.80.3 → 0.81.6 (0.81.6 is the latest 0.81 patch on npm)

Version bumps

Package From To Why
react-native 0.79.5 0.81.6 the upgrade
@react-native/* (babel-preset, eslint-config, metro-config, metro-babel-transformer, typescript-config) 0.79.5 0.81.6 must match RN
@react-native-community/cli, cli-platform-android, cli-platform-ios 18.0.0 20.1.3 CLI line for 0.81; the platform packages were left at 18 on the old ladder branch
react, react-dom, react-test-renderer (whole monorepo, root overrides too) 19.0.0 19.1.4 RN's bundled renderer throws at startup unless react is exactly its version: 19.1.0 for 0.80.3, 19.1.4 for 0.81.6. The old ladder branch used 19.1.8, which would throw "Incompatible React versions" on launch.
@types/react / @types/react-dom 19.0.0 19.1.17 / 19.1.11 match React 19.1
react-native-gesture-handler 2.25.0 2.28.0 2.28 is the first 2.x with official 0.81 support (2.25's table stops at 0.79). Stays on 2.x; v3 drops the old architecture.
@amplitude/analytics-react-native 1.4.11 1.4.14 Kotlin 2.1 (RN 0.80+) turns the deprecated toUpperCase(Locale) in 1.4.11 into a compile error
tiktok-opensdk-react-native ^0.10.7 0.10.7 (pinned) it now carries a patch. 0.10.9 fixes the Android signature, but drops the handleOpenURL/handleUserActivity declarations that SceneDelegate imports

Patches added (patches/):

  • react-native-track-player+4.1.2.patch: Kotlin 2.1 rejects Arguments.fromBundle(Bundle?). Now resolves null when there is no item. 4.1.2 is the last 4.x and has no fixed release.
  • tiktok-opensdk-react-native+0.10.7.patch: onNewIntent(intent: Intent) (RN 0.80 made ActivityEventListener non-null) and reactApplicationContext.currentActivity.

Checked against 0.81 on the old architecture and left alone: Reanimated 3.19.5 (the official table lists 3.19.x for 0.78–0.81 on Paper; Reanimated 4 is out of scope), track-player 4.1.2 (patched above), collapsible-tab-view 8.0.1 (JS only), screens 4.18.0 (0.81 support since 4.14; react-native-screens+4.18.0.patch still applies; 4.25+ drops the old arch), pager-view 6.7.1 (7+ drops the old arch), video 6.18.0, svg 15.15.0 (0.81 support since 15.12.1), google-cast 4.6.2 (5.x is new-arch only), notifications 5.1.0, code-push 12.3.2 (README lists 0.77–0.86; 13.x needs a new OTA history), bootsplash 6.3.11, safe-area-context 5.6.2, keyboard-controller 1.19.0, flash-list 1.8.3, datetimepicker 8.3.0 (builds against 0.81.6).

Template changes (rn-diff-purge 0.79.5 → 0.81.6)

  • Android: Kotlin 2.0.21 → 2.1.20, Gradle 8.13 → 8.14.3, new gradlew/gradlew.bat/wrapper jar, MainApplication uses loadReactNative(this) in place of SoLoader.init plus load(), and edgeToEdgeEnabled=false is added as in the template. The app already draws edge-to-edge through react-native-bars and the SDK 36 target, so this flag changes nothing.
  • AGP comes from RN: 8.8 → 8.11.0, which supports compileSdk 36, so android.suppressUnsupportedCompileSdk=36 is removed.
  • settings.gradle resolves @react-native/gradle-plugin through require.resolve from react-native, not a hard-coded hoisted path. With this lockfile npm nests it under node_modules/react-native/node_modules.
  • Kept: newArchEnabled=false, the AudiusScrypt module (ScryptPackage is still registered), the debug manifest (it adds SYSTEM_ALERT_WINDOW), and android:enableOnBackInvokedCallback="false".
  • Back handling: since 0.80, ReactActivity registers an OnBackPressedCallback when targetSdk ≥ 36, which calls onBackPressed() → JS BackHandler. Our invokeDefaultOnBackPressed override (moveTaskToBack) still runs. The manifest opt-out is kept as asked and can probably go in a later release.
  • iOS: no template change between 0.79.5 and 0.81.6. Info.plist and RichPushExtension/Info.plist now set RCTNewArchEnabled=false, which RN 0.80+ reads at runtime; a missing key means new arch. pod install writes the key and re-sorts the plist each time, so the sorted version is committed.

AppDelegate / UIScene

No code change needed. RCTAppDelegate is marked deprecated in 0.81 in favour of RCTReactNativeFactory, so there is a -Wdeprecated-declarations warning, but every API we use is unchanged: automaticallyLoadReactNativeWindow, rootViewFactory, createRootViewController, setRootView:toRootViewController:, customizeRootView:, bundleURL, and dependencyProvider. didFinishLaunching still creates the factory and skips the window. Moving to RCTReactNativeFactory can wait for the 0.82+ phase.

Verified on a Release build: cold launch, a deep link while killed, and a deep link while backgrounded, on iOS 26.5 and iOS 27. iOS 27 needs UIScene and showed no launch crash.

Web / React 19.1

  • Typecheck: web, common and harmony are clean, and so is mobile.
  • vite build (prod env): succeeds.
  • web vitest: 178 passed, 9 skipped. TrackTile › Public Premium (non-owner) timed out once while the machine was under heavy load, then passed 3 out of 3 when run alone.

Verification matrix (before the rebase; see the bottom for the rebased stack)

iOS 26.5 sim (Release) iOS 27 sim (Release) Android API 36 emu (prodRelease, arm64)
Build ✅ Xcode 27, generic/platform=iOS Simulator same binary ✅ debug keystore
Old arch at runtime ✅ JS thread com.facebook.react.JavaScript – ✅ ReactRootView + "Legacy Architecture" warning
Launch past splash ✅ ✅ ✅ assets://index.android.bundle
Sign-up / sign-in screens (signed in) ✅ ✅ including Create Password
Signed-in feed, scroll ✅ – –
Playback + now playing drawer (open, drag closed) ✅ – –
Background → foreground while playing ✅ audio kept going (0:09 → 0:41) – –
Search ✅ – –
Profile: collapsible header + tab swipe ✅ – –
Deep link, warm ✅ audius://audius ✅ link delivered –
Deep link, cold ✅ audius://deadmau5 ✅ (signed out → sign-on) –
Hardware Back – – ✅ pops Create Password; at root backgrounds the app without killing it
Sign-on footer above nav bar – – ✅
Red JS errors none, apart from a recurring JSON Parse error: Unexpected character: < (not yet traced) none none

The CodePush folder was moved aside or cleared before every cold launch, so all results above are from the bundled JS.

Still needs a real device

  • background audio with the screen locked for more than 2 minutes
  • lock-screen and notification controls
  • Chromecast
  • push notifications

Notes

Verification of the rebased stack (2026-10-08)

Built from a local, unpushed integration branch: #14652's tip (which contains #14647) merged with #14651, on top of #14654.

Check Result
npx tsc --noEmit -p packages/mobile pass
eslint on the touched files / turbo run verify lint pass (1 existing warning in HostRemixContestDrawer.tsx)
cd packages/mobile && npm test 9 suites, 34 tests pass
iOS Release, generic/platform=iOS Simulator, arm64, ENVFILE=.env.prod builds; Info.plist 1.1.201, RCTNewArchEnabled=false; bundle carries 1.5.188
Android assembleProdRelease, arm64-v8a, debug keystore builds; versionName 1.1.537
iOS 26.5 sim, installed over the signed-in app, CodePush moved aside launch to feed; JS thread com.facebook.react.JavaScript (old arch)
Playback from the 2nd feed track plays; play bar tracker moves
Auto-advance seek to 2:43/2:45 on track 2, advanced to track 3
Now playing drawer opens, scrubber advances, tap-to-seek works
Profile tabs swipe Tracks → Albums, tap Reposts
notifee/RNFB APNs token a temporary console log (not committed, rebuilt without it afterwards) printed an 80-byte lowercase APNs token at startup
Android 16 emulator, pm clear Loading JS bundle from "assets://index.android.bundle", "Legacy Architecture" warning, sign-up and sign-in screens render
Android hardware Back not verified: the emulator's system_server was killed by its watchdog three times under host load, so Back never got a clean run
JS errors on iOS only Could not cache profile images (content node timeout)

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: fd4a7e2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@socket-security

socket-security Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Dependency limit exceeded — report not shown.

This pull request scan exceeded the 10,000-dependency limit applied to this scan, so the results are incomplete and may be inaccurate. To avoid reporting false positives, Socket has not posted a report.

Upgrade your plan to raise the dependency limit and get complete reports, or view the partial scan in the dashboard.

Socket is always free for open source. If this is a non-commercial open source project, contact us to request a free Team account.

@dylanjeffers

Copy link
Copy Markdown
Contributor Author

Heads-up from #14643: the Hermes ExceptionsManager fix from #13609 never applied on main, because the script targeted packages/mobile/node_modules/react-native and react-native is hoisted to the root. #14643 replaces the script with patches/react-native+0.79.5.patch.

For this branch, once #14643 merges:

  • Rename patches/react-native+0.79.5.patch → patches/react-native+0.81.6.patch. No content changes are needed. All four replaced lines are identical in 0.81.6, and the 0.79.5 patch already applies cleanly. Its output is byte-identical to a patch generated against 0.81.6. Without the rename, patch-package prints a version-mismatch warning but still succeeds.
  • scripts/postinstall.sh on this branch still calls the old script. Take fix(mobile): apply RN ExceptionsManager Hermes fix via patch-package #14643's version, which deletes that block.

dylanjeffers added a commit that referenced this pull request Oct 8, 2026
…ground (#14644)

## Problem

Signed-in mobile builds log this every time the app comes to the
foreground:

```
[SyntaxError: JSON Parse error: Unexpected character: <]
```

It shows up on Android 16 (RN 0.79.5), the 1.5.186 RC on a Saga (Android
14), and iOS 26.5 (RN 0.81.6, #14642), so it isn't tied to the RN
version.

## Cause

`useResetNotificationBadgeCount` runs on every foreground. It calls
`audiusBackend.clearNotificationBadges`, which POSTs to
`identityservice.audius.co/notifications/clear_badges` and then calls
`.json()` on the response. #14207 removed that route from
identity-service on 2026-04-30, so Express now returns its HTML 404
page:

```
$ curl -X POST https://identityservice.audius.co/notifications/clear_badges
<!DOCTYPE html> ... <pre>Cannot POST /notifications/clear_badges</pre>   -> 404 text/html
```

The `catch` passes the bare error to `console.error`, which produces the
log line above.

## Evidence

I foregrounded the signed-in iOS 26.5 release build on a simulator and
read CFNetwork logs from the unified log:

```
22:28:43.627 [C530 ... url: https://identityservice.audius.co/notifications/clear_badges]
22:28:43.629 Task <4088> setting up Connection 530
22:28:43.793 Task <4088> received response, status 404
22:28:43.795 [com.facebook.react.log:javascript] [SyntaxError: JSON Parse error: Unexpected character: <]
```

Both earlier occurrences that session (17:17:04 and 17:18:05) were also
a 404 from identity, 2 ms before the error. Each one came right after
UIKit cleared the app's deactivation reasons. On iOS,
`useEnterForeground` also fires on `inactive -> active` (Control Center,
the notification shade, the app switcher), so the "about once a minute"
pattern was how often the app was re-activated, not a timer. In the same
session, a process that stayed in the foreground for 5 hours made no
404s and logged no errors.

`clear_badges` is the only identity route in `AudiusBackend` that no
longer exists. I checked every other path it calls against
`identity-service/src/routes`.

## Fix

- Remove the dead `clear_badges` request and
`audiusBackend.clearNotificationBadges`. It had no other callers.
- Keep clearing the local badge (`PushNotifications.setBadgeCount(0)`)
on foreground.

The server-side `PushNotificationBadgeCounts.iosBadgeCount` is now reset
to 0 by pedalboard's `notification_seen` listener
(`apps/notifications/src/notificationSeenListener.ts`) when
notifications are marked seen. This PR doesn't change server behaviour:
the reset on app open has already been gone since #14207.

## Testing

- ESLint passes on the changed files. `tsc` shows no errors in the
changed files.
- I did not rebuild the app to watch the log line disappear. The request
is removed outright, and the repro above was deterministic.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
dylanjeffers added a commit that referenced this pull request Oct 9, 2026
…14643)

## What the patch does

#13609 added a fix to React Native's
`Libraries/Core/ExceptionsManager.js`. It adds a `getErrorStackSafe()`
helper that reads `.stack` inside a try/catch, and returns it only when
it's a string. The helper replaces the three places where the exception
handler reads `.stack` directly:

- `parseErrorStack(e?.stack)` in `reportException`
- `rawStack: e.stack` in `reportException`
- `if (firstArg?.stack)` in `reactConsoleErrorHandler`

On Hermes, reading `.stack` on a value that inherits from `Error` in the
legacy way (`Foo.prototype = new Error()`) throws `Error.stack getter
called with an invalid receiver`. When that happens inside the global
exception handler, the original error gets lost and the handler throws
again. See react/react-native#43636 and facebook/hermes#1496.

## Why it never applied

`packages/mobile/scripts/patch-react-native-exceptions.js` looked for
`packages/mobile/node_modules/react-native/...`. npm hoists react-native
to the root `node_modules` (it's in the root of `package-lock.json` on
both 0.79.5 and 0.81.6). When the file wasn't found, the script ran
`process.exit(0)`, so postinstall printed "Patching React Native
ExceptionsManager" and changed nothing. On main the installed
`ExceptionsManager.js` is byte-identical to the npm tarball, so this fix
has never shipped.

## Fix

- Moved the same edits into `patches/react-native+0.79.5.patch`.
- Deleted the custom script and its block in `scripts/postinstall.sh`.

`scripts/patch-package.sh` already runs root patches with
`--error-on-fail`. So if react-native stops being installed at the root,
or an RN upgrade changes the lines being replaced, `npm install` /
postinstall now fails instead of silently doing nothing. The diff is
identical to the old script's output, generated from a pristine 0.79.5
tarball.

## RN 0.81 (#14642)

All four anchor lines are unchanged in 0.81.6. This patch applies
cleanly to 0.81.6 (output byte-identical to a patch generated against
0.81.6), with patch-package's version-mismatch warning only. The upgrade
PR should rename it to `react-native+0.81.6.patch` once this lands.

## Verification

- patch-package 6.5.1 in a scratch project:
  - applies to pristine 0.79.5 and to 0.81.6, exit 0
  - exits 1 when react-native is missing from `node_modules/`
  - exits 1 when a replaced line has changed (simulated drift)
- `bash scripts/patch-package.sh` in a worktree with cloned
`node_modules`:
  - every patch reports ✔, exit 0
- a second run (the CI cache-hit path, which reruns postinstall) also
exits 0
- The patched file parses with hermes-parser.
- The patched file compiles with `@react-native/babel-preset`; the
output contains the helper and its 3 call sites.
- No native build. This is a JS-only change that Metro bundles, so it
ships in the next build or OTA.

Behavior change to note: this is the first time the fix ships.
`rawStack` and the `firstArg` check now treat a non-string `.stack` as
absent. Before, they passed it through or treated it as truthy.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@dylanjeffers

Copy link
Copy Markdown
Contributor Author

Note for the upgrade: #14643 (merged) adds patches/react-native+0.79.5.patch (ExceptionsManager Hermes fix). When this stack is rebased onto main, rename it to react-native+0.81.6.patch; the anchors are unchanged in 0.81.6 and it applies cleanly.

Base automatically changed from chore/mobile-native-lib-swaps to chore/mobile-native-release-1.5.187 October 9, 2026 00:11
Base automatically changed from chore/mobile-native-release-1.5.187 to main October 9, 2026 00:26
dylanjeffers and others added 2 commits October 8, 2026 17:27
- react-native 0.80.3, @react-native/* 0.80.3, community CLI 19.1.2
- React 19.1.0 across the monorepo. The 0.80.3 renderer throws unless
  react is exactly 19.1.0.
- @amplitude/analytics-react-native 1.4.14: Kotlin 2.1 rejects the
  deprecated toUpperCase in 1.4.11
- patch track-player and tiktok-opensdk for Kotlin 2.1 / RN 0.80
  nullability; pin tiktok-opensdk to 0.10.7 so the patch keeps applying
- Android template: Kotlin 2.1.20, Gradle 8.14.1, loadReactNative
- Info.plist: RCTNewArchEnabled=false, which 0.80 reads at runtime
- replace react-native/types deep imports

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- react-native 0.81.6, @react-native/* 0.81.6, community CLI 20.1.3
- React 19.1.4 across the monorepo; the 0.81.6 renderer requires exactly
  19.1.4
- react-native-gesture-handler 2.28.0, the first 2.x release that
  supports 0.81
- Android template: Gradle 8.14.3, edgeToEdgeEnabled=false, and drop
  suppressUnsupportedCompileSdk now that AGP 8.11 supports API 36
- resolve @react-native/gradle-plugin through react-native, since npm
  nests it under react-native in this lockfile
- type fixes for the TextInput blur event and the ExternalLink spread

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dylanjeffers
dylanjeffers changed the base branch from main to chore/mobile-native-release-1.5.188 October 9, 2026 00:36
dylanjeffers and others added 2 commits October 9, 2026 15:29
…14647)

Prepares react-native-track-player 4.1.2 (Apache-2.0) for the new
architecture. It ships with 1.5.188 on the old architecture, where it
changes nothing.

**Base: `chore/mobile-rn-0.81` (#14642).** Rebased 2026-10-08 after
#14642 moved onto the 1.5.188 release branch (#14654). Merge #14652 into
this branch first, then this into #14642. Don't merge to main on its
own.

We are staying on RNTP 4.1.2. v5 (`@rntp/player`) is commercially
licensed. 4.1.2 is the last 4.x release, and the fixes below were never
published for it.

## Changes (Android only, in
`patches/react-native-track-player+4.1.2.patch`)

**1. Event emission in bridgeless mode**
(doublesymmetry/react-native-track-player#2593, #2619)
- `MusicService.emit()` / `emitList()` read the context from
`reactNativeHost.reactInstanceManager.currentReactContext`. That is null
in bridgeless mode, so every native-to-JS event was dropped: playback
state, active track, remote/notification controls, progress and errors.
- They now use the `reactContext` getter that `HeadlessJsTaskService`
already provides. It returns `ReactInstanceManager.currentReactContext`
on the old architecture (the same call as before) and
`ReactHost.currentReactContext` in bridgeless mode.
- That getter throws if `ReactHost` is null. Emission, `startTask` in
`onStartCommand`, and `super.onDestroy()` now log and carry on instead
of crashing the service. This is the same approach as upstream's fix for
#2619 (ed52dc5, v5 line).

**2. `@ReactMethod`s return `Unit`**
- Every `TrackPlayerModule` method was written as `fun play(callback:
Promise) = scope.launch { ... }`, so each one returned a `Job`.
- The bridgeless TurboModule interop layer rejects a non-void return on
an async method, so it threw while loading the module:
> Unable to parse @ReactMethod annotations from native module:
TrackPlayerModule

On the new architecture the app aborted at startup (found in the spike,
#14648).
- They are now `fun play(callback: Promise) { scope.launch { ... } }`.
The old bridge ignored the return value, so nothing changes there. The
diff touches only the first and last line of each of the 37 methods.

**Not applicable to 4.1.2:**
- #2670 (`BackgroundServiceStartNotAllowedException` while emitting in
the background) and #2662 (Android Auto cold start never starting the
headless task) are v5 issues. In v5, emitting an event starts a
`TrackPlayerTaskService`. In 4.1.2, `emit` never starts a service, and
`onStartCommand` already calls `startTask`, which creates the React host
when there is none. That cold path was checked on the new architecture
in the spike.
- iOS needs no change. The Swift `RCTEventEmitter` module works through
the interop layer as it is.

## Verification (old architecture, `prodRelease` arm64 signed with the
debug keystore, Android 16 emulator)
I tested with a temporary JS harness that was not committed. It wraps
the root component, logs every RNTP event with `console.warn` (visible
in logcat) and in an on-screen overlay, queues 3 public tracks, seeks,
and then seeks near the end. Signed out, `pm clear` before each launch,
and logcat confirmed `Loading JS bundle from
"assets://index.android.bundle"`.

| Check | Result |
|---|---|
| Release build | ✅ |
| Runtime arch | ✅ `bridgeless:false, fabric:false`, "Legacy
Architecture" warning |
| `PlaybackState` / `PlaybackActiveTrackChanged` reach JS | ✅ stopped →
loading → buffering → ready → playing, `index:0` |
| Seek | ✅ `seekTo(60)` → position 60–62.8 |
| Auto-advance | ✅ `active-track-changed` 0 → 1 at the end of track 0,
`getActiveTrackIndex()` = 1 |
| `adb shell cmd media_session dispatch pause/play/next` | ✅
`remote-pause` / `remote-play` / `remote-next` reach JS, and the state
follows |
| Notification shade pause / play / previous | ✅ `remote-pause`,
`remote-play`, `remote-previous` reach JS |
| Screen off and backgrounded for 75 s | ✅ progress events kept
arriving, MediaSession `PLAYING` |
| iOS Release (old arch, 26.5 sim) | ✅ unchanged: events, seek and
auto-advance all work |

The same harness on the new architecture is in the spike PR.

## Still needs a real device
Lock-screen and notification controls, Bluetooth, and background
playback for more than 2 minutes, together with the rest of the 1.5.188
checklist in #14642.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ver (#14651)

Moves the play bar tracker, the now playing scrubber and the track-list
eq bars off JS-driven `Animated` and onto the native driver. JS-only, so
it can ship as an OTA.

## Why
All three used `useNativeDriver: false`, so every frame ran in JS and
committed through `setNativeProps`. On old arch that's cheap. On the new
architecture each one is a full `ShadowTree::commit`, and Reanimated 3's
commit hook clones the tree on every commit. It was half of the CPU
regression in #14648 (blocker 6): playing with the drawer open went from
~15% to ~50%.

## What changed
- `animateLinear` in `utils/animation.ts` runs a long linear animation
as 10 s native-driver segments. The native driver precomputes one frame
per 16 ms, which is why the old code avoided it for track-length
animations (the old comment about Hermes). Each segment starts from the
wall-clock position, so the bar can't drift after the app is
backgrounded.
- `TrackingBar` and `Slider` use it with the start position passed in
explicitly (from `seek` or `TrackPlayer.getProgress()`), instead of
continuing from whatever the JS value was. Seeking, dragging and pausing
behave the same. The scrubber's 100 ms tap-to-seek timing moved to the
native driver too, because a value can't mix drivers. Both stop the
chain on unmount.
- `AnimatedEqBars` animated `height`, which the native driver can't do.
Each bar is now a full-height bar slid down with `translateY` inside a
clip view with the same 2 px radius. The visible shape at every height
matches the old one (rounded top from the bar, rounded bottom from the
clip).

## Base
**Base: `chore/mobile-rn-0.81` (#14642), shipping in native release
1.5.188 (#14654).** Rebased 2026-10-08 after #14642 moved onto the
release branch. Merge this into #14642, not main. The svg
`LinearGradient` it imports is on main since 1.5.187, so it no longer
depends on #14642's code, but it ships with 1.5.188 so the new
native-driver animations get device-tested on the RC builds.

It doesn't overlap the iOS queue fix (#14649, on main): that change is
in `AudioPlayer.tsx` and `addTracksAroundCurrent.ts`, which this PR
doesn't touch.

## Measurements (iOS 26.5 simulator, Release, signed in, 30 s `top`
averages)
| Scenario | Old arch before → after | New arch before → after |
|---|---|---|
| Feed, playing | 17.1% → 12.9% | 50.4% → 15.3% |
| Now playing drawer open, playing | 15.3% → 13.7% | 50.6% → 15.6% |
| Background, playing | 9.5% → 12.2% | 12.2% → 14.5% |

New-arch "after" also includes #14648's svg and Lottie fixes, which
don't affect these screens. Old-arch "after" is the #14642 Release app
with this branch's JS bundle swapped in.

## Testing
- Simulator, both architectures: the bar and the scrubber advance at the
right rate on a 56-minute and a 3-minute track. Tap-to-seek to the
middle lands at 28:02 and keeps going. The play bar is at the right
position after 30 s in the background.
- `tsc` and `eslint` clean on the changed files.
- Not checked by eye: the eq bars. In album track lists, the
`TrackImage` children (eq bars, play icon overlay) don't show on either
architecture, before or after this change. That looks like an existing
bug and is out of scope here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@dylanjeffers
dylanjeffers marked this pull request as ready for review October 9, 2026 22:29
@dylanjeffers
dylanjeffers merged commit e5f2984 into chore/mobile-native-release-1.5.188 Oct 9, 2026
5 checks passed
@dylanjeffers
dylanjeffers deleted the chore/mobile-rn-0.81 branch October 9, 2026 22:29
dylanjeffers added a commit that referenced this pull request Oct 9, 2026
## Summary

Version bump for native release **1.5.188**. Merging it starts the RC
and production native builds.

- `packages/mobile/package.json`: 1.5.187 → 1.5.188 (and the matching
`package-lock.json` entry)
- iOS `CFBundleShortVersionString`: 1.1.200 → 1.1.201
- Android `versionName`: 1.1.536 → 1.1.537

1.5.188 contains (all still on the old architecture):
- #14642: React Native 0.79.5 → 0.81.6, React 19.1.4 monorepo-wide
- #14647: RNTP 4.1.2 patch for bridgeless events and `Unit` module
methods (no change on old arch)
- #14651: playback progress and eq bars on the native driver
- #14652: react-native-svg 15.15.1 and the Lottie `shouldBeRecycled NO`
patch (no change on old arch)

1.5.189 is then only the new-architecture flag flip.

## Stack

```
main
└── chore/mobile-native-release-1.5.188   (this PR)
    └── chore/mobile-rn-0.81              #14642
        ├── fix/mobile-rntp-bridgeless    #14647
        │   └── fix/mobile-new-arch-prereqs  #14652
        └── fix/mobile-native-driver-progress  #14651
```

## Verification of the rebased stack (2026-10-08)
Built from a local, unpushed integration branch: #14652's tip (which
contains #14647) merged with #14651, on top of #14654.

| Check | Result |
|---|---|
| `npx tsc --noEmit -p packages/mobile` | pass |
| eslint on the touched files / `turbo run verify` lint | pass (1
existing warning in `HostRemixContestDrawer.tsx`) |
| `cd packages/mobile && npm test` | 9 suites, 34 tests pass |
| iOS Release, `generic/platform=iOS Simulator`, arm64,
`ENVFILE=.env.prod` | builds; Info.plist 1.1.201,
`RCTNewArchEnabled=false`; bundle carries 1.5.188 |
| Android `assembleProdRelease`, arm64-v8a, debug keystore | builds;
versionName 1.1.537 |
| iOS 26.5 sim, installed over the signed-in app, CodePush moved aside |
launch to feed; JS thread `com.facebook.react.JavaScript` (old arch) |
| Playback from the 2nd feed track | plays; play bar tracker moves |
| Auto-advance | seek to 2:43/2:45 on track 2, advanced to track 3 |
| Now playing drawer | opens, scrubber advances, tap-to-seek works |
| Profile tabs | swipe Tracks → Albums, tap Reposts |
| notifee/RNFB APNs token | a temporary console log (not committed,
rebuilt without it afterwards) printed an 80-byte lowercase APNs token
at startup |
| Android 16 emulator, `pm clear` | `Loading JS bundle from
"assets://index.android.bundle"`, "Legacy Architecture" warning, sign-up
and sign-in screens render |
| Android hardware Back | **not verified**: the emulator's system_server
was killed by its watchdog three times under host load, so Back never
got a clean run |
| JS errors on iOS | only `Could not cache profile images` (content node
timeout) |

## Merge order

1. **Wait until 1.5.187 is live in both stores.** Until then, any native
change on main would go into a 1.5.187 rebuild.
2. **Dispatch a production OTA from main before merging anything
below**, for every store version that can run main's JS:
   ```
gh workflow run mobile.yml --repo AudiusProject/apps --ref main -f
ota_channel=production
   ```
With main at 1.5.187 this publishes to `production/1.5.187`. **Do not**
dispatch `-f binary_version=1.5.186` or `1.5.185` from main any more:
main now carries #14627, which calls
`TurboModuleRegistry.getEnforcing('ReactNativeFs')` at import, so that
JS crashes 1.5.186 and older binaries. If 1.5.186 users still need a
fix, dispatch from a commit before 5c4ee7e.
3. **Land everything on main in one push.** Merge the stack into this
branch from the top down: #14652 into #14647, #14647 and #14651 into
#14642, #14642 into this branch. Then merge this PR into main. Any push
to main that leaves the version unchanged publishes an RC OTA to the
current binary's history, and RN 0.81 JS on a 0.79 binary will not
start. With one push that also changes the version, the version check
skips the OTA and starts the native builds. After that, OTAs route to
`1.5.188` histories.
4. **Device-test the RC builds before submitting:**
- cold launch on both platforms, old arch at runtime (iOS JS thread
`com.facebook.react.JavaScript`; Android "Legacy Architecture" warning)
   - background audio with the screen locked for more than 2 minutes
   - lock-screen and notification controls, Bluetooth, Chromecast
- auto-advance, including starting from the 2nd track of a list
(#14649/#14650)
- play bar and scrubber position: seek, drag, pause/resume, background
then foreground
   - first play does not pause itself (Lottie)
- push: token registered after the 1.5.187 → 1.5.188 update and taps
open the right screen (#14640)
   - Android hardware Back on a pushed screen and at the root
   - gradients and offline downloads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant