From 9e4ca948be6df27ec0064c9c6c94a81e9d22e4c8 Mon Sep 17 00:00:00 2001 From: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com> Date: Tue, 6 Oct 2026 07:37:33 -0700 Subject: [PATCH] Mobile: Fixes #16589: Upgrade React Native to v0.86, Expo to v57 (#16636) Co-authored-by: Laurent Cozic --- .github/workflows/ui-tests.yml | 2 +- .../patches/expo-npm-57.0.23-a2b19bd138.patch | 17 + ...-quick-actions-npm-0.3.13-eb0753e0b7.patch | 56 + ...tive-rsa-native-npm-2.0.5-4664e1ce79.patch | 47 + ...-sqlite-storage-npm-6.0.1-8369d747bd.patch | 37 - .../rn-fetch-blob-npm-0.12.0-cf02e3c544.patch | 25 +- package.json | 1 - packages/app-mobile/.gitignore | 3 + packages/app-mobile/android/app/build.gradle | 2 +- .../android/app/src/main/AndroidManifest.xml | 12 + .../java/net/cozic/joplin/MainApplication.kt | 26 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- packages/app-mobile/android/gradlew | 3 - packages/app-mobile/android/gradlew.bat | 8 +- packages/app-mobile/app.json | 3 +- .../accessibility/AccessibleView.test.tsx | 22 +- .../buttons/FloatingActionButton.tsx | 13 +- .../voiceTyping/AudioRecordingBanner.tsx | 12 +- packages/app-mobile/ios/AppDelegate.swift | 48 +- .../ios/Joplin.xcodeproj/project.pbxproj | 62 +- packages/app-mobile/ios/Joplin/Info.plist | 17 + packages/app-mobile/ios/Podfile | 13 +- packages/app-mobile/ios/Podfile.lock | 1163 +++--- packages/app-mobile/ios/PrivacyInfo.xcprivacy | 20 +- packages/app-mobile/ios/SceneDelegate.swift | 25 + packages/app-mobile/jest.config.js | 2 +- packages/app-mobile/jest.setup.js | 8 +- packages/app-mobile/package.json | 50 +- .../services/AlarmServiceDriver.ios.ts | 12 +- .../app-mobile/utils/shim-init-react/index.ts | 22 +- packages/app-mobile/web/webpack.config.ts | 2 + packages/lib/services/e2ee/cryptoTestUtils.ts | 30 + .../android/build.gradle | 2 +- packages/react-native-saf-x/src/index.ts | 36 +- packages/whisper-voice-typing/package.json | 8 +- yarn.lock | 3103 +++++++++++------ 36 files changed, 3093 insertions(+), 1821 deletions(-) create mode 100644 .yarn/patches/expo-npm-57.0.23-a2b19bd138.patch create mode 100644 .yarn/patches/react-native-quick-actions-npm-0.3.13-eb0753e0b7.patch create mode 100644 .yarn/patches/react-native-rsa-native-npm-2.0.5-4664e1ce79.patch delete mode 100644 .yarn/patches/react-native-sqlite-storage-npm-6.0.1-8369d747bd.patch create mode 100644 packages/app-mobile/ios/SceneDelegate.swift diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index e39c57038a..f3cb025755 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -47,7 +47,7 @@ jobs: retention-days: 7 Mobile: if: github.repository == 'laurent22/joplin' - runs-on: macos-15 + runs-on: macos-26 steps: - uses: actions/checkout@v6 - name: Setup build environment diff --git a/.yarn/patches/expo-npm-57.0.23-a2b19bd138.patch b/.yarn/patches/expo-npm-57.0.23-a2b19bd138.patch new file mode 100644 index 0000000000..44bf984453 --- /dev/null +++ b/.yarn/patches/expo-npm-57.0.23-a2b19bd138.patch @@ -0,0 +1,17 @@ +diff --git a/types/react-native-web.d.ts b/types/react-native-web.d.ts +index d6159045aa4b1f6864037cfc1c001a68605b416d..5950b82015f82f7840cf3bd2fc88e81dd0ff789c 100644 +--- a/types/react-native-web.d.ts ++++ b/types/react-native-web.d.ts +@@ -3,7 +3,11 @@ + import { StyleProp } from 'react-native'; + import * as RN from 'react-native'; + +-declare module 'react-native' { ++// Expo's extended react-native types are out-of-sync with upstream. ++// This causes TypeScript errors when compiling. These errors only occur ++// when strict null checks is disabled, and so do not occur with the default React Native ++// and Expo TypeScript config. ++declare module 'react-native-disabled' { + /** + * View + */ diff --git a/.yarn/patches/react-native-quick-actions-npm-0.3.13-eb0753e0b7.patch b/.yarn/patches/react-native-quick-actions-npm-0.3.13-eb0753e0b7.patch new file mode 100644 index 0000000000..a67a8024a0 --- /dev/null +++ b/.yarn/patches/react-native-quick-actions-npm-0.3.13-eb0753e0b7.patch @@ -0,0 +1,56 @@ +# This patch makes react-native-quick-actions compatible with the iOS UIKit +# scene-based lifecycle. Requires calling `setInitialAction` on app load when +# started from a quick action. +diff --git a/RNQuickAction/RNQuickAction/RNQuickActionManager.h b/RNQuickAction/RNQuickAction/RNQuickActionManager.h +index ad4baad075513115892bd120a278f1ba664480b7..7a366391a48e77d7b08eb2741d2a499a267304b8 100644 +--- a/RNQuickAction/RNQuickAction/RNQuickActionManager.h ++++ b/RNQuickAction/RNQuickAction/RNQuickActionManager.h +@@ -10,4 +10,5 @@ + + @interface RNQuickActionManager : NSObject + +(void) onQuickActionPress:(UIApplicationShortcutItem *) shortcutItem completionHandler:(void (^)(BOOL succeeded)) completionHandler; +++(void) setInitialAction:(UIApplicationShortcutItem *) shortcutItem; + @end +diff --git a/RNQuickAction/RNQuickAction/RNQuickActionManager.m b/RNQuickAction/RNQuickAction/RNQuickActionManager.m +index 8db8cab947349763a9c3d9d6004868b675b9c7ef..3217a35634ad457e33756b895b32d80f3fa11178 100644 +--- a/RNQuickAction/RNQuickAction/RNQuickActionManager.m ++++ b/RNQuickAction/RNQuickAction/RNQuickActionManager.m +@@ -31,6 +31,7 @@ @implementation RNQuickActionManager + RCT_EXPORT_MODULE(); + + @synthesize bridge = _bridge; ++UIApplicationShortcutItem *lastInitialAction_ = NULL; + + - (instancetype)init + { +@@ -39,6 +40,7 @@ - (instancetype)init + selector:@selector(handleQuickActionPress:) + name:RCTShortcutItemClicked + object:nil]; ++ _initialAction = lastInitialAction_; + } + return self; + } +@@ -59,7 +61,9 @@ - (void)dealloc + - (void)setBridge:(RCTBridge *)bridge + { + _bridge = bridge; +- _initialAction = [bridge.launchOptions[UIApplicationLaunchOptionsShortcutItemKey] copy]; ++ // Patch: The initial action must now be set through `setInitialAction`: ++ // _initialAction = NULL; ++ // _initialAction = [bridge.launchOptions[UIApplicationLaunchOptionsShortcutItemKey] copy]; + } + + // Map user passed array of UIApplicationShortcutItem +@@ -155,6 +159,11 @@ + (void)onQuickActionPress:(UIApplicationShortcutItem *) shortcutItem completion + completionHandler(YES); + } + +++ (void)setInitialAction:(UIApplicationShortcutItem *) shortcutItem ++{ ++ lastInitialAction_ = [shortcutItem copy]; ++} ++ + - (void)handleQuickActionPress:(NSNotification *) notification + { + [_bridge.eventDispatcher sendDeviceEventWithName:@"quickActionShortcut" diff --git a/.yarn/patches/react-native-rsa-native-npm-2.0.5-4664e1ce79.patch b/.yarn/patches/react-native-rsa-native-npm-2.0.5-4664e1ce79.patch new file mode 100644 index 0000000000..d72167a4c0 --- /dev/null +++ b/.yarn/patches/react-native-rsa-native-npm-2.0.5-4664e1ce79.patch @@ -0,0 +1,47 @@ +# Fixes the following error: +# A problem occurred evaluating project ':react-native-rsa-native'. +# > Could not find method jcenter() for arguments [] on repository container of type org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler. +# +# See https://github.com/amitaymolko/react-native-rsa-native/issues/175 +diff --git a/android/bin/build.gradle b/android/bin/build.gradle +index b5f54a631f4bed61973c77c2a6b27c5df0b26d17..28f38ca5966097f798f3f3607ded55dd4fe9b6d1 100644 +--- a/android/bin/build.gradle ++++ b/android/bin/build.gradle +@@ -1,7 +1,6 @@ + buildscript { + repositories { + maven { url "https://maven.google.com" } +- jcenter() + google() + maven { + url 'https://maven.google.com/' +@@ -41,7 +40,7 @@ allprojects { + repositories { + mavenLocal() + maven { url "https://maven.google.com" } +- jcenter() ++ mavenCentral() + google() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm +diff --git a/android/build.gradle b/android/build.gradle +index 82f60d3d862258e50023076b0db6e792bd2a2503..9e4606903f2dcf4ee7bb49dc878a080023ed314c 100644 +--- a/android/build.gradle ++++ b/android/build.gradle +@@ -9,7 +9,6 @@ buildscript { + if (project == rootProject) { + repositories { + google() +- jcenter() + maven { + url "https://maven.fabric.io/public" + } +@@ -45,7 +44,7 @@ allprojects { + repositories { + mavenLocal() + google() +- jcenter() ++ mavenCentral() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$rootDir/../node_modules/react-native/android" diff --git a/.yarn/patches/react-native-sqlite-storage-npm-6.0.1-8369d747bd.patch b/.yarn/patches/react-native-sqlite-storage-npm-6.0.1-8369d747bd.patch deleted file mode 100644 index 27bbb478d5..0000000000 --- a/.yarn/patches/react-native-sqlite-storage-npm-6.0.1-8369d747bd.patch +++ /dev/null @@ -1,37 +0,0 @@ -diff --git a/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java b/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java -index 4f2391b..f7cc433 100644 ---- a/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java -+++ b/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java -@@ -8,11 +8,14 @@ - package org.pgsqlite; - - import android.annotation.SuppressLint; -+import android.database.AbstractWindowedCursor; - import android.database.Cursor; -+import android.database.CursorWindow; - import android.database.sqlite.SQLiteDatabase; - import android.database.sqlite.SQLiteException; - import android.database.sqlite.SQLiteStatement; - import android.content.Context; -+import android.os.Build; - import android.util.Base64; - - import java.io.Closeable; -@@ -808,6 +811,17 @@ public class SQLitePlugin extends ReactContextBaseJavaModule { - throw ex; - } - -+ // To try to fix the error "Row too big to fit into CursorWindow" when using sqlite binary bundled with the device -+ // https://github.com/andpor/react-native-sqlite-storage/issues/364#issuecomment-526423153 -+ // https://github.com/laurent22/joplin/issues/1767#issuecomment-515617991 -+ -+ if (cur != null && Build.VERSION.SDK_INT >= 28) { -+ CursorWindow cw = new CursorWindow(null, 50 * 1024 * 1024); -+ AbstractWindowedCursor ac = (AbstractWindowedCursor) cur; -+ ac.setWindow(cw); -+ cur = ac; -+ } -+ - // If query result has rows - if (cur != null && cur.moveToFirst()) { - WritableArray rowsArrayResult = Arguments.createArray(); diff --git a/.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch b/.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch index 01764ea8b8..511eb8dc92 100644 --- a/.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch +++ b/.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch @@ -1,6 +1,25 @@ -# Fixes sync issues caused by locale-sensitive lowercasing -# of HTTP headers. -# See https://github.com/laurent22/joplin/issues/10681 +# Fixes the following errors: +# - Could not find method jcenter() for arguments [] on repository container of type org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler. +# - Sync issues caused by locale-sensitive lowercasing of HTTP headers (see https://github.com/laurent22/joplin/issues/10681). +diff --git a/android/build.gradle b/android/build.gradle +index a4ca7a421bff66bcc378c1092474bac6b7a82738..d2527c7e695d09dac3069fecd14931eae33a03e7 100644 +--- a/android/build.gradle ++++ b/android/build.gradle +@@ -6,13 +6,12 @@ def safeExtGet(prop, fallback) { + + repositories { + mavenCentral() +- jcenter() + google() + } + + buildscript { + repositories { +- jcenter() ++ mavenCentral() + google() + } + dependencies { diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java index 8ac9e7a855162cefbf99024eb013c8a3b11de1ec..1c639cf9d84821b6ffc132960e2d1c044bedbd48 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java diff --git a/package.json b/package.json index 9bcc60c4e5..69f007847e 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,6 @@ "pdfjs-dist": "patch:pdfjs-dist@npm%3A3.11.174#./.yarn/patches/pdfjs-dist-npm-3.11.174-67f2fee6d6.patch", "chokidar@^2.0.0": "3.6.0", "rn-fetch-blob@0.12.0": "patch:rn-fetch-blob@npm%3A0.12.0#./.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch", - "react-native-sqlite-storage@6.0.1": "patch:react-native-sqlite-storage@npm%3A6.0.1#./.yarn/patches/react-native-sqlite-storage-npm-6.0.1-8369d747bd.patch", "react-native-paper@5.13.1": "patch:react-native-paper@npm%3A5.13.1#./.yarn/patches/react-native-paper-npm-5.13.1-f153e542e2.patch", "react-native-popup-menu@0.17.0": "patch:react-native-popup-menu@npm%3A0.17.0#./.yarn/patches/react-native-popup-menu-npm-0.17.0-8b745d88dd.patch", "react-native@0.79.2": "patch:react-native@npm%3A0.79.2#./.yarn/patches/react-native-npm-0.79.2-9db13eddfe.patch", diff --git a/packages/app-mobile/.gitignore b/packages/app-mobile/.gitignore index 63fb3d630f..5007a767c9 100644 --- a/packages/app-mobile/.gitignore +++ b/packages/app-mobile/.gitignore @@ -53,6 +53,9 @@ yarn-error.log **/fastlane/screenshots **/fastlane/test_output +# generated inline modules +app/src/main/java/inline/ + # Bundle artifact *.jsbundle diff --git a/packages/app-mobile/android/app/build.gradle b/packages/app-mobile/android/app/build.gradle index f35e8ed797..6bc217445e 100644 --- a/packages/app-mobile/android/app/build.gradle +++ b/packages/app-mobile/android/app/build.gradle @@ -11,7 +11,7 @@ def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath() react { entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim()) reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() - hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc" + hermesCommand = new File(["node", "--print", "require.resolve('hermes-compiler/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/hermesc/%OS-BIN%/hermesc" codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() enableBundleCompression = (findProperty('android.enableBundleCompression') ?: false).toBoolean() diff --git a/packages/app-mobile/android/app/src/main/AndroidManifest.xml b/packages/app-mobile/android/app/src/main/AndroidManifest.xml index 29fb9430a8..5ef3914977 100644 --- a/packages/app-mobile/android/app/src/main/AndroidManifest.xml +++ b/packages/app-mobile/android/app/src/main/AndroidManifest.xml @@ -10,7 +10,12 @@ + + + + @@ -56,6 +61,13 @@ + + +