diff --git a/patches/expo-modules-core@3.0.30.patch b/patches/expo-modules-core@3.0.30.patch deleted file mode 100644 index 1be4787a22..0000000000 --- a/patches/expo-modules-core@3.0.30.patch +++ /dev/null @@ -1,51 +0,0 @@ -diff --git a/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt b/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt -index 47c4d15f6b10bbd77858cfff425cda9a618735b9..afe138d22d566244482498a7be0e14b8454eab96 100644 ---- a/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt -+++ b/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt -@@ -125,6 +125,10 @@ internal fun peekResponseBody( - } - - internal fun shouldParseBody(response: Response): Boolean { -+ if (response.request.url.encodedPath == "/bitdrift_public.protobuf.client.v1.ApiService/Mux") { -+ return false -+ } -+ - // Check for Content-Type - val skipContentTypes = listOf( - "text/event-stream", // Server Sent Events -diff --git a/ios/Core/ExpoBridgeModule.mm b/ios/Core/ExpoBridgeModule.mm -index 2ed1c00f47406e109750cc27ace7e0d88e42c00e..99d0d140eddf95a8db7beb57c61dfcb12c1424b4 100644 ---- a/ios/Core/ExpoBridgeModule.mm -+++ b/ios/Core/ExpoBridgeModule.mm -@@ -7,6 +7,9 @@ - // The runtime executor is included as of React Native 0.74 in bridgeless mode. - #if __has_include() - #import -+#else // React Native <0.74 -+// dispatchBlock:queue: is declared in RCTBridge+Private.h, not the public header. -+#import - #endif // React Native >=0.74 - - @implementation ExpoBridgeModule -@@ -46,7 +49,20 @@ - (void)setBridge:(RCTBridge *)bridge - _appContext.reactBridge = bridge; - - #if !__has_include() -- _appContext._runtime = [EXJavaScriptRuntimeManager runtimeFromBridge:bridge]; -+ // Hop the runtime install (and the prepareRuntime() chain it triggers via -+ // _runtime.didSet) onto RCTJSThread. The original line ran synchronously -+ // on whatever thread called setBridge: - typically the main thread - and -+ // raced JSIExecutor::initializeRuntime() on the JS thread, corrupting -+ // Hermes' Hades GC (HadesGC::writeBarrierSlow EXC_BAD_ACCESS). -+ __weak EXAppContext *weakAppContext = _appContext; -+ __weak RCTBridge *weakBridge = bridge; -+ [bridge dispatchBlock:^{ -+ EXAppContext *strongAppContext = weakAppContext; -+ RCTBridge *strongBridge = weakBridge; -+ if (strongAppContext != nil && strongBridge != nil && strongAppContext._runtime == nil) { -+ strongAppContext._runtime = [EXJavaScriptRuntimeManager runtimeFromBridge:strongBridge]; -+ } -+ } queue:RCTJSThread]; - #endif // React Native <0.74 - } - diff --git a/patches/expo-modules-core@3.0.30.patch.md b/patches/expo-modules-core@3.0.30.patch.md deleted file mode 100644 index 3ce964a7a9..0000000000 --- a/patches/expo-modules-core@3.0.30.patch.md +++ /dev/null @@ -1,26 +0,0 @@ -## expo-modules-core Patch - -This patch contains two unrelated fixes: - -### Android: bitdrift interceptor - -Fixes an issue where bitdrift's API stream gets blocked by the Expo interceptor used to power the devtools. - -### iOS: Hermes startup race in `ExpoBridgeModule.setBridge:` - -On the legacy bridge (old architecture, where `RCTRuntimeExecutor.h` is -absent), `setBridge:` installed the Expo runtime synchronously on whatever -thread called it - typically the main thread, since RN's lazy module-load -path ignores `+requiresMainQueueSetup`. The `_runtime.didSet` then ran -`prepareRuntime()` (JSI mutations) on the main thread while the JS thread was -concurrently inside `JSIExecutor::initializeRuntime()`. Two threads mutating -the same Hermes runtime corrupted Hades GC, producing intermittent -`EXC_BAD_ACCESS` launch crashes (e.g. `HadesGC::writeBarrierSlow`, -`prepareRuntime` / `bindNativePerformanceNow`). - -The fix hops the runtime install onto `RCTJSThread` so all JSI mutation is -serialized on the JS thread. This backports the upstream fix discussed in -expo/expo#45374; the racy `ExpoBridgeModule` is removed entirely in SDK 55+ -(expo/expo#44351), so this patch can be dropped on that upgrade. - -Refs: expo/expo#43003, expo/expo#45374, expo/expo#44351 diff --git a/patches/expo-modules-core@57.0.7.patch b/patches/expo-modules-core@57.0.7.patch new file mode 100644 index 0000000000..7de1337aac --- /dev/null +++ b/patches/expo-modules-core@57.0.7.patch @@ -0,0 +1,15 @@ +diff --git a/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt b/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt +index 47c4d15f6b10bbd77858cfff425cda9a618735b9..afe138d22d566244482498a7be0e14b8454eab96 100644 +--- a/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt ++++ b/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt +@@ -125,6 +125,10 @@ internal fun peekResponseBody( + } + + internal fun shouldParseBody(response: Response): Boolean { ++ if (response.request.url.encodedPath == "/bitdrift_public.protobuf.client.v1.ApiService/Mux") { ++ return false ++ } ++ + // Check for Content-Type + val skipContentTypes = listOf( + "text/event-stream", // Server Sent Events diff --git a/patches/expo-modules-core@57.0.7.patch.md b/patches/expo-modules-core@57.0.7.patch.md new file mode 100644 index 0000000000..56ba6d08bf --- /dev/null +++ b/patches/expo-modules-core@57.0.7.patch.md @@ -0,0 +1,5 @@ +## expo-modules-core Patch + +### Android: bitdrift interceptor + +Fixes an issue where bitdrift's API stream gets blocked by the Expo interceptor used to power the devtools. diff --git a/patches/expo-notifications@57.0.7.patch b/patches/expo-notifications@57.0.7.patch index a0d8e74f65..26ead3bc35 100644 --- a/patches/expo-notifications@57.0.7.patch +++ b/patches/expo-notifications@57.0.7.patch @@ -1,9 +1,9 @@ diff --git a/android/build.gradle b/android/build.gradle -index 7db47bdf190b0790c7bf867fbcfeb594005861be..0f868153edd6ec557730531f61dba7bf26a71742 100644 +index 18a1c56507a1c0da7eb3b6e1f80a1f25a0a8f171..305e10998b99d0c0256930de669e51b5ba4c98c4 100644 --- a/android/build.gradle +++ b/android/build.gradle -@@ -42,6 +42,7 @@ dependencies { - implementation 'com.google.firebase:firebase-messaging:24.0.1' +@@ -43,6 +43,7 @@ dependencies { + implementation 'com.google.firebase:firebase-messaging:25.0.1' implementation 'me.leolin:ShortcutBadger:1.1.22@aar' + implementation project(':expo-background-notification-handler') @@ -124,7 +124,7 @@ index 610d3039cefd589647538ad8ba14587d29fab338..3655fc3121ebc0a97820d9653b61a90b builder.setContentText(content.text) builder.setSubText(content.subText) diff --git a/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt b/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt -index 90ca4ff35132b33dcccb80b90d68572506fef603..9d4cb09b35844805d543acff54772380c732c02c 100644 +index eecdae82e99d2997687e3f3e199c94c3aeffbfe0..216891213fb2eac5a9a382b4f075eadccdcfeb5a 100644 --- a/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt +++ b/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt @@ -3,6 +3,9 @@ package expo.modules.notifications.service.delegates @@ -137,16 +137,16 @@ index 90ca4ff35132b33dcccb80b90d68572506fef603..9d4cb09b35844805d543acff54772380 import expo.modules.interfaces.taskManager.TaskServiceProviderHelper import expo.modules.notifications.notifications.RemoteMessageSerializer import expo.modules.notifications.notifications.background.BackgroundRemoteNotificationTaskConsumer -@@ -18,7 +21,7 @@ import expo.modules.notifications.tokens.interfaces.FirebaseTokenListener - import java.lang.ref.WeakReference +@@ -17,7 +20,7 @@ import expo.modules.notifications.service.interfaces.FirebaseMessagingDelegate + import expo.modules.notifications.tokens.interfaces.FirebaseTokenListener import java.util.* -open class FirebaseMessagingDelegate(protected val context: Context) : FirebaseMessagingDelegate { +open class FirebaseMessagingDelegate(protected val context: Context) : FirebaseMessagingDelegate, BackgroundNotificationHandlerInterface{ companion object { // Unfortunately we cannot save state between instances of a service other way - // than by static properties. Fortunately, using weak references we can -@@ -105,8 +108,19 @@ open class FirebaseMessagingDelegate(protected val context: Context) : FirebaseM + // than by static properties. +@@ -109,8 +112,19 @@ open class FirebaseMessagingDelegate(protected val context: Context) : FirebaseM DebugLogging.logRemoteMessage("FirebaseMessagingDelegate.onMessageReceived: message", remoteMessage) val notification = createNotification(remoteMessage) DebugLogging.logNotification("FirebaseMessagingDelegate.onMessageReceived: notification", notification) diff --git a/patches/react-native@0.81.5.patch b/patches/react-native@0.86.0.patch similarity index 66% rename from patches/react-native@0.81.5.patch rename to patches/react-native@0.86.0.patch index b4c01a1ef3..eb91618aa9 100644 --- a/patches/react-native@0.81.5.patch +++ b/patches/react-native@0.86.0.patch @@ -23,29 +23,19 @@ index c593d9ee2155a826352ebca34845aa5792b2eec3..3c26cd737f21116ff0aa48190e97e6c0 - (void)setFrame:(CGRect)frame { [super setFrame:frame]; -diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.mm -index 0d231bc8aa938da296eb3b981e8ac9595a43b87f..be0a10d9c4de1892fa00bcbf8d63d739b66d8ffe 100644 ---- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.mm -+++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.mm -@@ -76,7 +76,17 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & - return; - } +diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h +index 914a2494a57923fbf185644b7e2bb8aca8848e56..0deac55f22350f5e8377d8963fb1c2434bf6abfd 100644 +--- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h ++++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.h +@@ -19,6 +19,8 @@ NS_ASSUME_NONNULL_BEGIN + */ + @interface RCTPullToRefreshViewComponentView : RCTViewComponentView -- const auto &oldConcreteProps = static_cast(*_props); -+ /* -+ * TODO: Remove after upgrading React Native to 0.82+ (fixed upstream by -+ * facebook/react-native#52615, #52584 and #53231). -+ * Diff against oldProps instead of _props. During the initial-layout replay -+ * from layoutSubviews, _props already holds the new props, so diffing -+ * against it is a no-op and tintColor/progressViewOffset are never applied -+ * on mount (facebook/react-native#56343). oldProps is null-guarded because -+ * the create-mutation path passes nullptr. -+ */ -+ const auto &oldConcreteProps = static_cast( -+ oldProps ? *oldProps : *PullToRefreshViewShadowNode::defaultSharedProps()); - const auto &newConcreteProps = static_cast(*props); ++- (void)beginRefreshingProgrammatically; ++ + @end - if (newConcreteProps.tintColor != oldConcreteProps.tintColor) { + NS_ASSUME_NONNULL_END diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 1494fd225aff1fa0429e917404d6b4ca5fc961c5..d0cce700090245444f8ce51e517d5ceca09526f6 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -76,8 +66,8 @@ index 1494fd225aff1fa0429e917404d6b4ca5fc961c5..d0cce700090245444f8ce51e517d5cec } /* -@@ -1038,6 +1046,11 @@ - (void)_adjustForMaintainVisibleContentPosition - } +@@ -1133,6 +1133,11 @@ - (RCTVirtualViewContainerState *)virtualViewContainerState + return _virtualViewContainerState; } ++ (BOOL)shouldBeRecycled @@ -159,22 +149,6 @@ index 40aaf9c51ebda9fedb1d1db2e9aacec84b4c39c8..1c60164b69762997b3369b46609a0776 RCT_EXPORT_METHOD(setNativeRefreshing : (nonnull NSNumber *)viewTag toRefreshing : (BOOL)refreshing) { [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { -diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.kt b/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.kt -index 8b6571698fc5dd091a0d8980a33bb40295faf305..27c97bfeb6f13907c89f1d85f2bb8b8af7bdfb43 100644 ---- a/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.kt -+++ b/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.kt -@@ -313,8 +313,9 @@ public open class JavaTimerManager( - // We also capture the idleCallbackRunnable to tentatively fix: - // https://github.com/facebook/react-native/issues/44842 - currentIdleCallbackRunnable?.cancel() -- currentIdleCallbackRunnable = IdleCallbackRunnable(frameTimeNanos) -- reactApplicationContext.runOnJSQueueThread(currentIdleCallbackRunnable) -+ val idleCallbackRunnable = IdleCallbackRunnable(frameTimeNanos) -+ currentIdleCallbackRunnable = idleCallbackRunnable -+ reactApplicationContext.runOnJSQueueThread(idleCallbackRunnable) - reactChoreographer.postFrameCallback(ReactChoreographer.CallbackType.IDLE_EVENT, this) - } - } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt index 89b666dcf0258df0702c812600b685463128294c..2b1c3971f0c31a0d7a592b90170e4cc53a8a69dd 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt @@ -205,35 +179,18 @@ index 89b666dcf0258df0702c812600b685463128294c..2b1c3971f0c31a0d7a592b90170e4cc5 var needUpdateClippingRecursive = false diff --git a/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm -index 216bb23beb023ef6c3ae814c17e05bccbda7fc91..6ad5cc1d9ed5b8cd2df08ad77adca56c6bb58ff4 100644 +index ac55304..d99c8f4 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +++ b/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm -@@ -386,9 +386,10 @@ - (TextMeasurement)_measureTextStorage:(NSTextStorage *)textStorage +@@ -389,8 +389,9 @@ - (TextMeasurement)_measureTextStorage:(NSTextStorage *)textStorage size.height = enumeratedLinesHeight; } +- size = (CGSize){ceil(size.width * layoutContext.pointScaleFactor) / layoutContext.pointScaleFactor, +- ceil(size.height * layoutContext.pointScaleFactor) / layoutContext.pointScaleFactor}; + CGFloat epsilon = 0.001; - size = (CGSize){ -- ceil(size.width * layoutContext.pointScaleFactor) / layoutContext.pointScaleFactor, -- ceil(size.height * layoutContext.pointScaleFactor) / layoutContext.pointScaleFactor}; -+ ceil((size.width + epsilon) * layoutContext.pointScaleFactor) / layoutContext.pointScaleFactor, -+ ceil((size.height + epsilon) * layoutContext.pointScaleFactor) / layoutContext.pointScaleFactor}; ++ size = (CGSize){ceil((size.width + epsilon) * layoutContext.pointScaleFactor) / layoutContext.pointScaleFactor, ++ ceil((size.height + epsilon) * layoutContext.pointScaleFactor) / layoutContext.pointScaleFactor}; - __block auto attachments = TextMeasurement::Attachments{}; + NSRange visibleGlyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; -diff --git a/third-party-podspecs/fmt.podspec b/third-party-podspecs/fmt.podspec -index 2f38990e226c13f483aaf1b986302d4094243814..9b02e481e290299be20a6f09c42056ff51695e9b 100644 ---- a/third-party-podspecs/fmt.podspec -+++ b/third-party-podspecs/fmt.podspec -@@ -26,4 +26,11 @@ Pod::Spec.new do |spec| - spec.public_header_files = "include/fmt/*.h" - spec.header_mappings_dir = "include" - spec.source_files = ["include/fmt/*.h", "src/format.cc"] -+ -+ # TODO: Remove after upgrading React Native past 0.83.x -+ # Fix fmt 11.0.2 consteval build error with Xcode 26.4 (facebook/react-native#55601) -+ # Fixed in RN 0.84+ which bumps fmt to a compatible version. -+ spec.prepare_command = <<~SCRIPT -+ perl -i -pe 's/^# define FMT_USE_CONSTEVAL 1$/# define FMT_USE_CONSTEVAL 0/' include/fmt/base.h -+ SCRIPT - end diff --git a/patches/react-native@0.81.5.patch.md b/patches/react-native@0.86.0.patch.md similarity index 87% rename from patches/react-native@0.81.5.patch.md rename to patches/react-native@0.86.0.patch.md index 1121fb7363..d34a684958 100644 --- a/patches/react-native@0.81.5.patch.md +++ b/patches/react-native@0.86.0.patch.md @@ -6,18 +6,11 @@ Patching `RCTRefreshControl.mm` temporarily to play an impact haptic on refresh 17.4, there has been a regression somewhere causing haptics to not play on iOS on refresh. Should monitor for an update in the RN repo: https://github.com/facebook/react-native/issues/43388 -## RCTPullToRefreshViewComponentView.mm Patch - RefreshControl initial props dropped on New Arch +## RefreshControl Path - ScrollForwarder -**TODO: Remove after bumping React Native to 0.82+** (fixed upstream by facebook/react-native#52615, #52584 -and #53231). - -On Fabric, `updateProps` diffs against `_props`, but the initial-layout replay in `layoutSubviews` passes -`_props` as the new props too, so the diff is a no-op and `tintColor`/`progressViewOffset`/`title` are never -applied on mount. This hides the pull-to-refresh spinner behind the floating home header (it stays at offset -0 instead of `headerOffset`). We diff against the `oldProps` argument instead, null-guarded with default -props for the create-mutation path. - -Issue: https://github.com/facebook/react-native/issues/56343 +Patching `RCTRefreshControl.m` and `RCTRefreshControl.h` to add a new `forwarderBeginRefreshing` method to the class. +This method is used by `ExpoScrollForwarder` to initiate a refresh of the underlying `UIScrollView` from inside that +module. ## RCTEnhancedScrollView.mm / RCTScrollViewComponentView.mm Patch - centerContent insets stale after content resize on New Arch diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 731823c695..199f479b2f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -24,7 +24,7 @@ patchedDependencies: 'expo-age-range@0.2.18': patches/expo-age-range@0.2.18.patch 'expo-haptics@57.0.1': patches/expo-haptics@57.0.1.patch 'expo-media-library@57.0.3': patches/expo-media-library@57.0.3.patch - 'expo-modules-core@3.0.30': patches/expo-modules-core@3.0.30.patch + 'expo-modules-core@57.0.7': patches/expo-modules-core@57.0.7.patch 'expo-notifications@57.0.7': patches/expo-notifications@57.0.7.patch 'expo-updates@57.0.10': patches/expo-updates@57.0.10.patch 'react-native-compressor@1.13.0': patches/react-native-compressor@1.13.0.patch @@ -37,11 +37,11 @@ patchedDependencies: 'react-native-svg@15.15.4': patches/react-native-svg@15.15.4.patch 'react-native-view-shot@4.0.3': patches/react-native-view-shot@4.0.3.patch react-native-worklets@0.8.3: patches/react-native-worklets@0.8.3.patch - 'react-native@0.81.5': patches/react-native@0.81.5.patch + 'react-native@0.86.0': patches/react-native@0.86.0.patch minimumReleaseAgeExclude: - '@atproto/*' - - '@bsky.app/*' # todo: remove when old enough + - '@bsky.app/*' - '@oxlint-tsgolint/darwin-arm64@7.0.2001' - '@oxlint-tsgolint/darwin-x64@7.0.2001' - '@oxlint-tsgolint/linux-arm64@7.0.2001'