From ac01992a7656e25e9febcd08d1020dd538cebc3d Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 14 May 2026 14:48:37 +0300 Subject: [PATCH] run script --- patches/@discord__bottom-sheet@4.6.1.patch | 13 + patches/@sentry__react-native@6.20.0.patch | 33 ++ patches/expo-glass-effect@55.0.8.patch | 60 +++ patches/expo-haptics@15.0.8.patch | 13 + patches/expo-image-picker@17.0.11.patch | 29 ++ patches/expo-image@3.0.11.patch | 103 +++++ patches/expo-media-library@18.2.1.patch | 13 + patches/expo-modules-core@3.0.30.patch | 15 + patches/expo-notifications@0.32.17.patch | 170 ++++++++ patches/expo-paste-input@0.1.15.patch | 135 ++++++ patches/expo-updates@29.0.17.patch | 26 ++ patches/react-native-compressor@1.13.0.patch | 59 +++ patches/react-native-date-picker@5.0.13.patch | 17 + .../react-native-drawer-layout@4.2.3.patch | 38 ++ ...ct-native-keyboard-controller@1.21.7.patch | 48 +++ patches/react-native-pager-view@6.8.0.patch | 30 ++ patches/react-native-reanimated@3.19.1.patch | 390 ++++++++++++++++++ patches/react-native-svg@15.12.1.patch | 57 +++ patches/react-native-uitextview@1.4.0.patch | 35 ++ patches/react-native-view-shot@4.0.3.patch | 28 ++ patches/react-native@0.81.5.patch | 168 ++++++++ patches/sonner-native@0.21.0.patch | 22 + pnpm-workspace.yaml | 23 ++ 23 files changed, 1525 insertions(+) create mode 100644 patches/@discord__bottom-sheet@4.6.1.patch create mode 100644 patches/@sentry__react-native@6.20.0.patch create mode 100644 patches/expo-glass-effect@55.0.8.patch create mode 100644 patches/expo-haptics@15.0.8.patch create mode 100644 patches/expo-image-picker@17.0.11.patch create mode 100644 patches/expo-image@3.0.11.patch create mode 100644 patches/expo-media-library@18.2.1.patch create mode 100644 patches/expo-modules-core@3.0.30.patch create mode 100644 patches/expo-notifications@0.32.17.patch create mode 100644 patches/expo-paste-input@0.1.15.patch create mode 100644 patches/expo-updates@29.0.17.patch create mode 100644 patches/react-native-compressor@1.13.0.patch create mode 100644 patches/react-native-date-picker@5.0.13.patch create mode 100644 patches/react-native-drawer-layout@4.2.3.patch create mode 100644 patches/react-native-keyboard-controller@1.21.7.patch create mode 100644 patches/react-native-pager-view@6.8.0.patch create mode 100644 patches/react-native-reanimated@3.19.1.patch create mode 100644 patches/react-native-svg@15.12.1.patch create mode 100644 patches/react-native-uitextview@1.4.0.patch create mode 100644 patches/react-native-view-shot@4.0.3.patch create mode 100644 patches/react-native@0.81.5.patch create mode 100644 patches/sonner-native@0.21.0.patch diff --git a/patches/@discord__bottom-sheet@4.6.1.patch b/patches/@discord__bottom-sheet@4.6.1.patch new file mode 100644 index 0000000000..133052e5d6 --- /dev/null +++ b/patches/@discord__bottom-sheet@4.6.1.patch @@ -0,0 +1,13 @@ +diff --git a/src/hooks/useStableCallback.ts b/src/hooks/useStableCallback.ts +index 1c788ab72351c21c5aea178d7b416741aa940e1f..d30f330ab1f082ea0477c19e2fa05b7a0b1a3e0d 100644 +--- a/src/hooks/useStableCallback.ts ++++ b/src/hooks/useStableCallback.ts +@@ -6,7 +6,7 @@ type Callback = (...args: any[]) => any; + * https://gist.github.com/JakeCoxon/c7ebf6e6496f8468226fd36b596e1985 + */ + export const useStableCallback = (callback: Callback) => { +- const callbackRef = useRef(); ++ const callbackRef = useRef(undefined); + const memoCallback = useCallback( + (...args: any) => callbackRef.current && callbackRef.current(...args), + [] diff --git a/patches/@sentry__react-native@6.20.0.patch b/patches/@sentry__react-native@6.20.0.patch new file mode 100644 index 0000000000..e8869040be --- /dev/null +++ b/patches/@sentry__react-native@6.20.0.patch @@ -0,0 +1,33 @@ +diff --git a/dist/js/tools/sentryMetroSerializer.js b/dist/js/tools/sentryMetroSerializer.js +index d7f2350196fc008f9f1ce530e224fe3d052347e9..4ce7b6614e38c8af747ebcf166dba556affea3de 100644 +--- a/dist/js/tools/sentryMetroSerializer.js ++++ b/dist/js/tools/sentryMetroSerializer.js +@@ -12,12 +12,9 @@ exports.createSentryMetroSerializer = exports.unstable_beforeAssetSerializationP + const crypto = require("crypto"); + const utils_1 = require("./utils"); + const utils_2 = require("./vendor/metro/utils"); +-let countLines; +-try { +- countLines = require('metro/private/lib/countLines'); +-} +-catch (e) { +- countLines = require('metro/src/lib/countLines'); ++const newline = /\r\n?|\n|\u2028|\u2029/g; ++function countLines(string) { ++ return (string.match(newline) || []).length + 1; + } + const DEBUG_ID_PLACE_HOLDER = '__debug_id_place_holder__'; + const DEBUG_ID_MODULE_PATH = '__debugid__'; +diff --git a/scripts/expo-upload-sourcemaps.js b/scripts/expo-upload-sourcemaps.js +index b3783b572171482778d31a96b8d6ebadbcc8783b..d5e3e45477c07b5419285237372d99ddd83c56a6 100755 +--- a/scripts/expo-upload-sourcemaps.js ++++ b/scripts/expo-upload-sourcemaps.js +@@ -218,7 +218,7 @@ for (const [assetGroupName, assets] of Object.entries(groupedAssets)) { + + const isHermes = assets.find(asset => asset.endsWith('.hbc')); + const windowsCallback = process.platform === "win32" ? 'node ' : ''; +- execSync(`${windowsCallback}${sentryCliBin} sourcemaps upload ${isHermes ? '--debug-id-reference' : ''} ${assets.join(' ')}`, { ++ execSync(`${windowsCallback}${sentryCliBin} sourcemaps upload ${isHermes ? '--debug-id-reference' : ''} ${assets.join(' ')} --dist ${process.env.SENTRY_DIST}`, { + env: { + ...process.env, + [SENTRY_PROJECT]: sentryProject, diff --git a/patches/expo-glass-effect@55.0.8.patch b/patches/expo-glass-effect@55.0.8.patch new file mode 100644 index 0000000000..b793fe04d4 --- /dev/null +++ b/patches/expo-glass-effect@55.0.8.patch @@ -0,0 +1,60 @@ +diff --git a/ios/GlassContainer.swift b/ios/GlassContainer.swift +index 61fb67cdfa2022f57524ddde05096067055e9ee6..b2d111ef8a724b8e7d4404f3d40efce3bd6fbb6d 100644 +--- a/ios/GlassContainer.swift ++++ b/ios/GlassContainer.swift +@@ -1,6 +1,7 @@ + // Copyright 2022-present 650 Industries. All rights reserved. + + import ExpoModulesCore ++import React + + public final class GlassContainer: ExpoView { + private var containerEffect: Any? +@@ -46,11 +47,19 @@ public final class GlassContainer: ExpoView { + } + } + +- public override func mountChildComponentView(_ childComponentView: UIView, index: Int) { ++ // Paper: redirect children into the container effect's contentView ++ public override func didUpdateReactSubviews() { ++ for subview in self.reactSubviews() { ++ containerEffectView.contentView.addSubview(subview) ++ } ++ } ++ ++ // Fabric: redirect children into the container effect's contentView ++ @objc public func mountChildComponentView(_ childComponentView: UIView, index: Int) { + containerEffectView.contentView.insertSubview(childComponentView, at: index) + } + +- public override func unmountChildComponentView(_ childComponentView: UIView, index: Int) { ++ @objc public func unmountChildComponentView(_ childComponentView: UIView, index: Int) { + childComponentView.removeFromSuperview() + } + } +diff --git a/ios/GlassView.swift b/ios/GlassView.swift +index 35cd8f320009a9e28fdbb2f55cc409734ba98f40..9587306b6fac3455ab27a5289eb62a711aa50c03 100644 +--- a/ios/GlassView.swift ++++ b/ios/GlassView.swift +@@ -271,11 +271,19 @@ public final class GlassView: ExpoView { + #endif + } + } +- public override func mountChildComponentView(_ childComponentView: UIView, index: Int) { ++ // Paper: redirect children into the glass effect's contentView ++ public override func didUpdateReactSubviews() { ++ for subview in self.reactSubviews() { ++ glassEffectView.contentView.addSubview(subview) ++ } ++ } ++ ++ // Fabric: redirect children into the glass effect's contentView ++ @objc public func mountChildComponentView(_ childComponentView: UIView, index: Int) { + glassEffectView.contentView.insertSubview(childComponentView, at: index) + } + +- public override func unmountChildComponentView(_ childComponentView: UIView, index: Int) { ++ @objc public func unmountChildComponentView(_ childComponentView: UIView, index: Int) { + childComponentView.removeFromSuperview() + } + } diff --git a/patches/expo-haptics@15.0.8.patch b/patches/expo-haptics@15.0.8.patch new file mode 100644 index 0000000000..b2fe0e713b --- /dev/null +++ b/patches/expo-haptics@15.0.8.patch @@ -0,0 +1,13 @@ +diff --git a/android/src/main/java/expo/modules/haptics/HapticsModule.kt b/android/src/main/java/expo/modules/haptics/HapticsModule.kt +index 6102727daafe198ac1170fdef8aea74df0b740d2..bfd4e6d2e730575062394e302c37e00ade20e1ce 100644 +--- a/android/src/main/java/expo/modules/haptics/HapticsModule.kt ++++ b/android/src/main/java/expo/modules/haptics/HapticsModule.kt +@@ -48,7 +48,7 @@ class HapticsModule : Module() { + + private fun vibrate(type: HapticsVibrationType) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { +- vibrator.vibrate(VibrationEffect.createWaveform(type.timings, type.amplitudes, -1)) ++ vibrator.vibrate(VibrationEffect.createWaveform(type.oldSDKPattern, intArrayOf(0, 100), -1)) + } else { + @Suppress("DEPRECATION") + vibrator.vibrate(type.oldSDKPattern, -1) diff --git a/patches/expo-image-picker@17.0.11.patch b/patches/expo-image-picker@17.0.11.patch new file mode 100644 index 0000000000..9ee0f51cad --- /dev/null +++ b/patches/expo-image-picker@17.0.11.patch @@ -0,0 +1,29 @@ +diff --git a/ios/MediaHandler.swift b/ios/MediaHandler.swift +index 6e4fbe1b3921173a1cc4e6eff626b0749e8bab14..e334abb680a8965acb1f6bbdfbc1325ad35edf3a 100644 +--- a/ios/MediaHandler.swift ++++ b/ios/MediaHandler.swift +@@ -310,10 +310,12 @@ internal struct MediaHandler { + let fileExtension = getFileExtension(from: originalFilename) + let destinationUrl = try generateUrl(withFileExtension: fileExtension) + ++ let resourceOptions = PHAssetResourceRequestOptions() ++ resourceOptions.isNetworkAccessAllowed = true + try await PHAssetResourceManager.default().writeData( + for: resource, + toFile: destinationUrl, +- options: nil ++ options: resourceOptions + ) + + let mimeType = getMimeType(from: destinationUrl.pathExtension) +@@ -389,7 +391,9 @@ internal struct MediaHandler { + + // Stream the resource into our cache directory. This API is asynchronous but doesn't require + // a temporary file like `loadFileRepresentation`. +- try await PHAssetResourceManager.default().writeData(for: resource, toFile: destinationUrl, options: nil) ++ let resourceOptions = PHAssetResourceRequestOptions() ++ resourceOptions.isNetworkAccessAllowed = true ++ try await PHAssetResourceManager.default().writeData(for: resource, toFile: destinationUrl, options: resourceOptions) + + // Build and return the result using the helper. + let mimeType = getMimeType(from: destinationUrl.pathExtension) diff --git a/patches/expo-image@3.0.11.patch b/patches/expo-image@3.0.11.patch new file mode 100644 index 0000000000..c132d6869e --- /dev/null +++ b/patches/expo-image@3.0.11.patch @@ -0,0 +1,103 @@ +diff --git a/build/Image.types.d.ts b/build/Image.types.d.ts +index 022ae487e65ae9d624f8a4be262b01944928f250..416504fe18ecd00fdc713faca23485fb292caf2c 100644 +--- a/build/Image.types.d.ts ++++ b/build/Image.types.d.ts +@@ -152,6 +152,16 @@ export interface ImageProps extends Omit { + * @default 'normal' + */ + priority?: 'low' | 'normal' | 'high' | null; ++ /** ++ * The loading behavior for the image. Maps to the native HTML `loading` attribute on web. ++ * ++ * - `'lazy'` - Defers loading until the image is near the viewport. ++ * - `'eager'` - Loads the image immediately. ++ * ++ * @default undefined ++ * @platform web ++ */ ++ loading?: 'lazy' | 'eager' | null; + /** + * Determines whether to cache the image and where: on the disk, in the memory or both. + * +diff --git a/src/ExpoImage.web.tsx b/src/ExpoImage.web.tsx +index 2a49ff00649b374b86fa780653a4b0d6f13a4a57..1c3de93ef280bf6f3c27bed34c794b8ff59e3db3 100644 +--- a/src/ExpoImage.web.tsx ++++ b/src/ExpoImage.web.tsx +@@ -70,6 +70,7 @@ export default function ExpoImage({ + onLoadEnd, + onDisplay, + priority, ++ loading, + blurRadius, + recyclingKey, + style, +@@ -118,6 +119,7 @@ export default function ExpoImage({ + accessibilityLabel={accessibilityLabel ?? alt} + cachePolicy={cachePolicy} + priority={priority} ++ loading={loading} + tintColor={tintColor} + /> + ), +@@ -149,6 +151,7 @@ export default function ExpoImage({ + className={className} + cachePolicy={cachePolicy} + priority={priority} ++ loading={loading} + contentPosition={selectedSource ? contentPosition : { top: '50%', left: '50%' }} + hashPlaceholderContentPosition={contentPosition} + hashPlaceholderStyle={imageHashStyle} +diff --git a/src/Image.types.ts b/src/Image.types.ts +index 9dec0e7aee61dfaa73a3cfa535d08259c9dad209..61c162114977dff35b633ac6b1f3d59e373bbbfe 100644 +--- a/src/Image.types.ts ++++ b/src/Image.types.ts +@@ -178,6 +178,17 @@ export interface ImageProps extends Omit { + */ + priority?: 'low' | 'normal' | 'high' | null; + ++ /** ++ * The loading behavior for the image. Maps to the native HTML `loading` attribute on web. ++ * ++ * - `'lazy'` - Defers loading until the image is near the viewport. ++ * - `'eager'` - Loads the image immediately. ++ * ++ * @default undefined ++ * @platform web ++ */ ++ loading?: 'lazy' | 'eager' | null; ++ + /** + * Determines whether to cache the image and where: on the disk, in the memory or both. + * +diff --git a/src/web/ImageWrapper.tsx b/src/web/ImageWrapper.tsx +index e8f891d525892f8d3668e34cf96cefccfbfc49f6..89a5cb1e3a8574fc3bd1d0361895a610e3165dfb 100644 +--- a/src/web/ImageWrapper.tsx ++++ b/src/web/ImageWrapper.tsx +@@ -30,6 +30,7 @@ const ImageWrapper = React.forwardRef( + contentPosition, + hashPlaceholderContentPosition, + priority, ++ loading, + style, + hashPlaceholderStyle, + tintColor, +@@ -82,6 +83,7 @@ const ImageWrapper = React.forwardRef( + // @ts-ignore + // eslint-disable-next-line react/no-unknown-property + fetchPriority={getFetchPriorityFromImagePriority(priority || 'normal')} ++ loading={loading || undefined} + {...getImageWrapperEventHandler(events, sourceWithHeaders)} + {...getImgPropsFromSource(source)} + {...props} +diff --git a/src/web/ImageWrapper.types.ts b/src/web/ImageWrapper.types.ts +index 19bbe2f15999124cda0ca7c81b3ebcf289f522f8..179837f803a3d315c85075895f4191631b37eda7 100644 +--- a/src/web/ImageWrapper.types.ts ++++ b/src/web/ImageWrapper.types.ts +@@ -29,6 +29,7 @@ export type ImageWrapperProps = { + contentPosition?: ImageContentPositionObject; + hashPlaceholderContentPosition?: ImageContentPositionObject; + priority?: string | null; ++ loading?: 'lazy' | 'eager' | null; + style: CSSProperties; + tintColor?: string | null; + hashPlaceholderStyle?: CSSProperties; diff --git a/patches/expo-media-library@18.2.1.patch b/patches/expo-media-library@18.2.1.patch new file mode 100644 index 0000000000..5033f7ea7a --- /dev/null +++ b/patches/expo-media-library@18.2.1.patch @@ -0,0 +1,13 @@ +diff --git a/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt b/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt +index 97c19ff07a49dc80adce7f100eb205b2045dc115..a9a520617eb74caddbbd9cbd15635f113017ab65 100644 +--- a/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt ++++ b/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt +@@ -109,7 +109,7 @@ class MediaLibraryModule : Module() { + } + + AsyncFunction("createAssetAsync") Coroutine { localUri: String, albumId: String? -> +- requireSystemPermissions() ++ // requireSystemPermissions() + return@Coroutine createAssetWithAlbumId(context, localUri, true, albumId) + } + diff --git a/patches/expo-modules-core@3.0.30.patch b/patches/expo-modules-core@3.0.30.patch new file mode 100644 index 0000000000..7de1337aac --- /dev/null +++ b/patches/expo-modules-core@3.0.30.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-notifications@0.32.17.patch b/patches/expo-notifications@0.32.17.patch new file mode 100644 index 0000000000..a0d8e74f65 --- /dev/null +++ b/patches/expo-notifications@0.32.17.patch @@ -0,0 +1,170 @@ +diff --git a/android/build.gradle b/android/build.gradle +index 7db47bdf190b0790c7bf867fbcfeb594005861be..0f868153edd6ec557730531f61dba7bf26a71742 100644 +--- a/android/build.gradle ++++ b/android/build.gradle +@@ -42,6 +42,7 @@ dependencies { + implementation 'com.google.firebase:firebase-messaging:24.0.1' + + implementation 'me.leolin:ShortcutBadger:1.1.22@aar' ++ implementation project(':expo-background-notification-handler') + + if (project.findProject(':expo-modules-test-core')) { + testImplementation project(':expo-modules-test-core') +diff --git a/android/src/main/java/expo/modules/notifications/notifications/interfaces/INotificationContent.kt b/android/src/main/java/expo/modules/notifications/notifications/interfaces/INotificationContent.kt +index 7b99e6cf8ee1b0de07a79fe4486c49ff25e489c6..45a450da1c5fff13461b77d3a7b20d0217035687 100644 +--- a/android/src/main/java/expo/modules/notifications/notifications/interfaces/INotificationContent.kt ++++ b/android/src/main/java/expo/modules/notifications/notifications/interfaces/INotificationContent.kt +@@ -15,6 +15,7 @@ import org.json.JSONObject + * This interface exists to provide a common API for both classes. + * */ + interface INotificationContent : Parcelable { ++ val channelId: String? + val title: String? + val text: String? + val subText: String? +diff --git a/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java b/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java +index 191b64e101c03eeec37920c2d6582bfd50d8b902..fe8b3c51d6f7c661484467f0d820836145824676 100644 +--- a/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java ++++ b/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java +@@ -35,6 +35,7 @@ import kotlin.coroutines.Continuation; + * Refactoring this class may require a migration strategy for the data stored in SharedPreferences. + */ + public class NotificationContent implements Parcelable, Serializable, INotificationContent { ++ private String mChannelId; + private String mTitle; + private String mText; + private String mSubtitle; +@@ -65,6 +66,11 @@ public class NotificationContent implements Parcelable, Serializable, INotificat + } + }; + ++ @Nullable ++ public String getChannelId() { ++ return mChannelId; ++ } ++ + @Nullable + public String getTitle() { + return mTitle; +@@ -158,6 +164,7 @@ public class NotificationContent implements Parcelable, Serializable, INotificat + } + + protected NotificationContent(Parcel in) { ++ mChannelId = in.readString(); + mTitle = in.readString(); + mText = in.readString(); + mSubtitle = in.readString(); +@@ -183,6 +190,7 @@ public class NotificationContent implements Parcelable, Serializable, INotificat + + @Override + public void writeToParcel(Parcel dest, int flags) { ++ dest.writeString(mChannelId); + dest.writeString(mTitle); + dest.writeString(mText); + dest.writeString(mSubtitle); +@@ -203,6 +211,7 @@ public class NotificationContent implements Parcelable, Serializable, INotificat + private static final long serialVersionUID = 397666843266836802L; + + private void writeObject(java.io.ObjectOutputStream out) throws IOException { ++ out.writeObject(mChannelId); + out.writeObject(mTitle); + out.writeObject(mText); + out.writeObject(mSubtitle); +@@ -285,6 +294,11 @@ public class NotificationContent implements Parcelable, Serializable, INotificat + useDefaultVibrationPattern(); + } + ++ public Builder setChannelId(String channelId) { ++ content.mChannelId = channelId; ++ return this; ++ } ++ + public Builder setTitle(String title) { + content.mTitle = title; + return this; +diff --git a/android/src/main/java/expo/modules/notifications/notifications/model/NotificationData.kt b/android/src/main/java/expo/modules/notifications/notifications/model/NotificationData.kt +index 3af254c6b5fecb8f850c023422db4d80bd4815a5..3c77e9d774f2d8fcfa5d34dc7b088958676cba71 100644 +--- a/android/src/main/java/expo/modules/notifications/notifications/model/NotificationData.kt ++++ b/android/src/main/java/expo/modules/notifications/notifications/model/NotificationData.kt +@@ -11,6 +11,9 @@ import org.json.JSONObject + * */ + @JvmInline + value class NotificationData(private val data: Map) { ++ val channelId: String? ++ get() = data["channelId"] ++ + val title: String? + get() = data["title"] + +diff --git a/android/src/main/java/expo/modules/notifications/notifications/model/RemoteNotificationContent.kt b/android/src/main/java/expo/modules/notifications/notifications/model/RemoteNotificationContent.kt +index cdbc237948ae9de7e85aeb3902e724c44557453c..6d970066decfa47d68299a5bd8febd953f9c2060 100644 +--- a/android/src/main/java/expo/modules/notifications/notifications/model/RemoteNotificationContent.kt ++++ b/android/src/main/java/expo/modules/notifications/notifications/model/RemoteNotificationContent.kt +@@ -31,6 +31,8 @@ class RemoteNotificationContent(private val remoteMessage: RemoteMessage) : INot + return remoteMessage.notification?.imageUrl != null + } + ++ override val channelId = remoteMessage.notification?.channelId ?: notificationData.channelId ++ + override val title = remoteMessage.notification?.title ?: notificationData.title + + override val text = remoteMessage.notification?.body ?: notificationData.message +diff --git a/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.kt b/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.kt +index 610d3039cefd589647538ad8ba14587d29fab338..3655fc3121ebc0a97820d9653b61a90bd7c34fc2 100644 +--- a/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.kt ++++ b/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.kt +@@ -101,6 +101,9 @@ open class ExpoNotificationBuilder( + builder.setOngoing(content.isSticky) + + // see "Notification anatomy" https://developer.android.com/develop/ui/views/notifications#Templates ++ content.channelId?.let { ++ builder.setChannelId(it) ++ } + builder.setContentTitle(content.title) + 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 +--- 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 + import android.content.Context + import android.os.Bundle + import com.google.firebase.messaging.RemoteMessage ++import expo.modules.backgroundnotificationhandler.BackgroundNotificationHandler ++import expo.modules.backgroundnotificationhandler.BackgroundNotificationHandlerInterface ++import expo.modules.backgroundnotificationhandler.ExpoBackgroundNotificationHandlerModule + 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 + 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 + DebugLogging.logRemoteMessage("FirebaseMessagingDelegate.onMessageReceived: message", remoteMessage) + val notification = createNotification(remoteMessage) + DebugLogging.logNotification("FirebaseMessagingDelegate.onMessageReceived: notification", notification) +- NotificationsService.receive(context, notification) +- runTaskManagerTasks(context.applicationContext, RemoteMessageSerializer.toBundle(remoteMessage)) ++ if (!ExpoBackgroundNotificationHandlerModule.isForegrounded) { ++ BackgroundNotificationHandler(context, this).handleMessage(remoteMessage) ++ } else { ++ NotificationsService.receive(context, notification) ++ runTaskManagerTasks( ++ context.applicationContext, ++ RemoteMessageSerializer.toBundle(remoteMessage) ++ ) ++ } ++ } ++ ++ override fun showMessage(remoteMessage: RemoteMessage) { ++ NotificationsService.receive(context, createNotification(remoteMessage)) + } + + protected fun createNotification(remoteMessage: RemoteMessage): Notification { diff --git a/patches/expo-paste-input@0.1.15.patch b/patches/expo-paste-input@0.1.15.patch new file mode 100644 index 0000000000..bd234598e5 --- /dev/null +++ b/patches/expo-paste-input@0.1.15.patch @@ -0,0 +1,135 @@ +diff --git a/ios/ExpoPasteInputView.swift b/ios/ExpoPasteInputView.swift +index 2164aec4ec1d8380fd690850aec0931e4f65194d..d216db6d2927c9cbb1eb5e10783ff1cc83c103e7 100644 +--- a/ios/ExpoPasteInputView.swift ++++ b/ios/ExpoPasteInputView.swift +@@ -511,14 +511,17 @@ class ExpoPasteInputView: ExpoView { + var attachmentRanges: [NSRange] = [] + var mediaPayloads: [MediaPayload] = [] + ++ // Only track ranges for attachments we successfully extract a real payload ++ // from. Attachments without a payload (e.g. iOS dictation placeholders) ++ // are left alone — sanitizing them would delete characters the system ++ // manages itself, and emitting "unsupported" would raise a spurious error. + attributedText.enumerateAttribute(.attachment, in: NSRange(location: 0, length: attributedText.length), options: []) { value, range, _ in + guard let attachment = value as? NSTextAttachment else { + return + } + +- attachmentRanges.append(range) +- + if let payload = self.extractMediaPayload(from: attachment, textView: textView, range: range) { ++ attachmentRanges.append(range) + mediaPayloads.append(payload) + } + } +@@ -529,9 +532,8 @@ class ExpoPasteInputView: ExpoView { + return + } + +- attachmentRanges.append(range) +- + if let payload = self.extractMediaPayload(from: adaptiveGlyph) { ++ attachmentRanges.append(range) + mediaPayloads.append(payload) + } + } +@@ -539,17 +541,12 @@ class ExpoPasteInputView: ExpoView { + + attachmentRanges = uniqueRanges(attachmentRanges) + +- guard !attachmentRanges.isEmpty else { ++ guard !mediaPayloads.isEmpty else { + return + } + + sanitizeAttachments(in: textView, ranges: attachmentRanges) + +- guard !mediaPayloads.isEmpty else { +- handleUnsupportedPaste() +- return +- } +- + emitImagesAsync(for: mediaPayloads) + } + +@@ -651,6 +648,11 @@ class ExpoPasteInputView: ExpoView { + } + + private func extractMediaPayload(from attachment: NSTextAttachment, textView: UITextView, range: NSRange) -> MediaPayload? { ++ // Only accept attachments that carry real image payloads. We intentionally ++ // do not fall back to `image(forBounds:)` or rendering the text view's ++ // hierarchy, because system-inserted attachments (e.g. the iOS dictation ++ // placeholder) draw themselves via those paths and would cause us to ++ // emit a screenshot of the composer as a "pasted image". + if let fileWrapperData = attachment.fileWrapper?.regularFileContents, + let payload = extractMediaPayload(fromData: fileWrapperData) { + return payload +@@ -667,20 +669,6 @@ class ExpoPasteInputView: ExpoView { + return .image(image) + } + +- let attachmentBounds = attachment.bounds.size.width > 0 && attachment.bounds.size.height > 0 +- ? attachment.bounds +- : CGRect(origin: .zero, size: CGSize(width: 128, height: 128)) +- +- if let image = attachment.image(forBounds: attachmentBounds, textContainer: textView.textContainer, characterIndex: range.location), +- image.size.width > 0, +- image.size.height > 0 { +- return .image(image) +- } +- +- if let renderedImage = renderTextAttachment(in: textView, range: range) { +- return .image(renderedImage) +- } +- + return nil + } + +@@ -701,47 +689,6 @@ class ExpoPasteInputView: ExpoView { + return .imageData(data) + } + +- private func renderTextAttachment(in textView: UITextView, range: NSRange) -> UIImage? { +- let glyphRange = textView.layoutManager.glyphRange(forCharacterRange: range, actualCharacterRange: nil) +- var rect = textView.layoutManager.boundingRect(forGlyphRange: glyphRange, in: textView.textContainer) +- +- rect.origin.x += textView.textContainerInset.left - textView.contentOffset.x +- rect.origin.y += textView.textContainerInset.top - textView.contentOffset.y +- rect = rect.integral +- +- guard rect.width > 1, rect.height > 1 else { +- return nil +- } +- +- let format = UIGraphicsImageRendererFormat.default() +- format.scale = textView.window?.screen.scale ?? UIScreen.main.scale +- format.opaque = false +- +- let image = UIGraphicsImageRenderer(size: rect.size, format: format).image { _ in +- let drawRect = CGRect( +- origin: CGPoint(x: -rect.origin.x, y: -rect.origin.y), +- size: textView.bounds.size +- ) +- +- if textView.window != nil { +- textView.drawHierarchy(in: drawRect, afterScreenUpdates: false) +- } else { +- guard let context = UIGraphicsGetCurrentContext() else { +- return +- } +- +- context.translateBy(x: -rect.origin.x, y: -rect.origin.y) +- textView.layer.render(in: context) +- } +- } +- +- guard image.size.width > 0, image.size.height > 0 else { +- return nil +- } +- +- return image +- } +- + @available(iOS 18.0, *) + private func handleAdaptiveImageGlyphInsertion(_ adaptiveGlyph: NSAdaptiveImageGlyph) -> Bool { + guard let payload = extractMediaPayload(from: adaptiveGlyph) else { diff --git a/patches/expo-updates@29.0.17.patch b/patches/expo-updates@29.0.17.patch new file mode 100644 index 0000000000..a07c4d2f23 --- /dev/null +++ b/patches/expo-updates@29.0.17.patch @@ -0,0 +1,26 @@ +diff --git a/ios/EXUpdates/Update/ExpoUpdatesUpdate.swift b/ios/EXUpdates/Update/ExpoUpdatesUpdate.swift +index 68086bd9963675e71fcc4008df693fed9110cd3a..78c776191ac9071ff0057fd9d045ca1e01011dcf 100644 +--- a/ios/EXUpdates/Update/ExpoUpdatesUpdate.swift ++++ b/ios/EXUpdates/Update/ExpoUpdatesUpdate.swift +@@ -78,13 +78,20 @@ public final class ExpoUpdatesUpdate: Update { + status = UpdateStatus.StatusPending + } + ++ // Instead of relying on various hacks to get the correct format for the specific ++ // platform on the backend, we can just add this little patch.. ++ let dateFormatter = DateFormatter() ++ dateFormatter.locale = Locale(identifier: "en_US_POSIX") ++ dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" ++ let date = dateFormatter.date(from:commitTime) ?? RCTConvert.nsDate(commitTime)! ++ + return Update( + manifest: manifest, + config: config, + database: database, + updateId: uuid, + scopeKey: config.scopeKey, +- commitTime: RCTConvert.nsDate(commitTime), ++ commitTime: date, + runtimeVersion: runtimeVersion, + keep: true, + status: status, diff --git a/patches/react-native-compressor@1.13.0.patch b/patches/react-native-compressor@1.13.0.patch new file mode 100644 index 0000000000..1a0f4c61d7 --- /dev/null +++ b/patches/react-native-compressor@1.13.0.patch @@ -0,0 +1,59 @@ +diff --git a/android/build.gradle b/android/build.gradle +index 5071139f8ee5fbba085d2afe3b2093de8eda915c..84bee34a238c6510169f6b6bdb0fda0594c77136 100644 +--- a/android/build.gradle ++++ b/android/build.gradle +@@ -115,7 +115,6 @@ dependencies { + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" + implementation 'org.mp4parser:isoparser:1.9.56' +- implementation 'com.github.banketree:AndroidLame-kotlin:v0.0.1' + implementation 'javazoom:jlayer:1.0.1' + } + +diff --git a/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt b/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +deleted file mode 100644 +index 9292d3ee50776bd9d7760b8dcf6d123d44b4e31b..0000000000000000000000000000000000000000 +diff --git a/android/src/main/java/com/reactnativecompressor/Audio/AudioExtractor.kt b/android/src/main/java/com/reactnativecompressor/Audio/AudioExtractor.kt +deleted file mode 100644 +index c6551828014437a14dc8f2f19488b647dba1bbe1..0000000000000000000000000000000000000000 +diff --git a/android/src/main/java/com/reactnativecompressor/Audio/AudioHelper.kt b/android/src/main/java/com/reactnativecompressor/Audio/AudioHelper.kt +deleted file mode 100644 +index 42040b4916573463415ef2f57789b3c4fa25d135..0000000000000000000000000000000000000000 +diff --git a/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt b/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt +index 446d4fb8b69e7cfdb51b29603aa2d52aac1ab8c8..f02190992dac823b6bbf2d77880a25adc48f16c7 100644 +--- a/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt ++++ b/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt +@@ -11,7 +11,9 @@ class AudioMain(private val reactContext: ReactApplicationContext) { + promise: Promise) { + try { + +- AudioCompressor.CompressAudio(fileUrl,optionMap,reactContext,promise) ++ // Skip compression on Android to avoid libandroidlame dependency ++ // Return the original file URL without compression ++ promise.resolve(fileUrl) + } catch (ex: Exception) { + promise.reject(ex) + } +diff --git a/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt b/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +index c14b727e930f4114765bfbe15b742ddcdeaa392f..1198908fcc66eeeea5e537085d7632a0d4b04545 100644 +--- a/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt ++++ b/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +@@ -7,7 +7,6 @@ import android.provider.OpenableColumns + import android.util.Log + import com.facebook.react.bridge.Promise + import com.facebook.react.bridge.ReactApplicationContext +-import com.reactnativecompressor.Audio.AudioCompressor + import com.reactnativecompressor.Video.VideoCompressor.CompressionListener + import com.reactnativecompressor.Video.VideoCompressor.VideoCompressorClass + import java.io.FileNotFoundException +@@ -152,10 +151,6 @@ object Utils { + } + } + +- fun addLog(log: String) { +- Log.d(AudioCompressor.TAG, log) +- } +- + val exifAttributes = arrayOf( + "FNumber", + "ApertureValue", diff --git a/patches/react-native-date-picker@5.0.13.patch b/patches/react-native-date-picker@5.0.13.patch new file mode 100644 index 0000000000..c6c0ccba72 --- /dev/null +++ b/patches/react-native-date-picker@5.0.13.patch @@ -0,0 +1,17 @@ +diff --git a/ios/RNDatePicker.h b/ios/RNDatePicker.h +index 480746eb7acfbe86f67547d9e1de7a5be4d5faf2..13d30cb547195993dfcb4005cc0d248de9ac391a 100644 +--- a/ios/RNDatePicker.h ++++ b/ios/RNDatePicker.h +@@ -15,6 +15,7 @@ NS_ASSUME_NONNULL_END + #else + #import "DatePicker.h" + #import ++#include + + @interface RNDatePicker : DatePicker + +@@ -22,4 +23,3 @@ NS_ASSUME_NONNULL_END + @end + + #endif +- diff --git a/patches/react-native-drawer-layout@4.2.3.patch b/patches/react-native-drawer-layout@4.2.3.patch new file mode 100644 index 0000000000..bb4b3381b1 --- /dev/null +++ b/patches/react-native-drawer-layout@4.2.3.patch @@ -0,0 +1,38 @@ +diff --git a/lib/module/views/Drawer.native.js b/lib/module/views/Drawer.native.js +index 38470a658878a63919186257041098747ec55473..b9d25798c9be229efeb676166f89bfbc72615793 100644 +--- a/lib/module/views/Drawer.native.js ++++ b/lib/module/views/Drawer.native.js +@@ -124,15 +124,21 @@ export function Drawer({ + } + onTransitionEnd?.(!open); + }); ++ const animatingTo = useSharedValue(null) + const toggleDrawer = React.useCallback((open, velocity) => { + 'worklet'; + ++ if (animatingTo.value === (open ? 'open' : 'close')) { ++ return; ++ } ++ + const translateX = getDrawerTranslationX(open); + if (velocity === undefined) { + runOnJS(onAnimationStart)(open); + } + touchStartX.value = 0; + touchX.value = 0; ++ animatingTo.value = open ? 'open' : 'close'; + translationX.value = withSpring(translateX, { + velocity, + stiffness: 1000, +@@ -142,7 +148,10 @@ export function Drawer({ + restDisplacementThreshold: 0.01, + restSpeedThreshold: 0.01, + reduceMotion: ReduceMotion.Never +- }, finished => runOnJS(onAnimationEnd)(open, finished)); ++ }, finished => { ++ animatingTo.value = null; ++ runOnJS(onAnimationEnd)(open, finished); ++ }); + if (open) { + runOnJS(onOpen)(); + } else { diff --git a/patches/react-native-keyboard-controller@1.21.7.patch b/patches/react-native-keyboard-controller@1.21.7.patch new file mode 100644 index 0000000000..209a551b23 --- /dev/null +++ b/patches/react-native-keyboard-controller@1.21.7.patch @@ -0,0 +1,48 @@ +diff --git a/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts b/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts +index 0f6d7c67a307885310ab184fdf9e7a5c7b296825..1e0bdd5b1d3b1eceb47bfb0050da84061fd7f77c 100644 +--- a/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts ++++ b/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts +@@ -1,8 +1,6 @@ + import { useCallback } from "react"; +-import { Platform } from "react-native"; + import { scrollTo, useAnimatedReaction } from "react-native-reanimated"; + +-import { IS_FABRIC } from "../../../architecture"; + import { isScrollAtEnd, shouldShiftContent } from "../useChatKeyboard/helpers"; + + import type { KeyboardLiftBehavior } from "../useChatKeyboard/types"; +@@ -52,7 +50,6 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { + scroll, + layout, + size, +- contentOffsetY, + inverted, + keyboardLiftBehavior, + freeze, +@@ -62,20 +59,14 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { + (target: number) => { + "worklet"; + +- if (contentOffsetY && IS_FABRIC) { +- // eslint-disable-next-line react-compiler/react-compiler +- contentOffsetY.value = target; +- } else if (Platform.OS === "android") { +- // Defer scrollTo so the animatedProps inset commit lands first; +- // otherwise the native ScrollView clamps to the old range. +- requestAnimationFrame(() => { +- scrollTo(scrollViewRef, 0, target, false); +- }); +- } else { ++ // Always defer scrollTo so the animatedProps inset commit lands first; ++ // otherwise the native ScrollView clamps contentOffset to the old ++ // contentInset range (iOS Fabric) or the old contentInsetBottom (Android). ++ requestAnimationFrame(() => { + scrollTo(scrollViewRef, 0, target, false); +- } ++ }); + }, +- [scrollViewRef, contentOffsetY], ++ [scrollViewRef], + ); + + useAnimatedReaction( diff --git a/patches/react-native-pager-view@6.8.0.patch b/patches/react-native-pager-view@6.8.0.patch new file mode 100644 index 0000000000..4444e60985 --- /dev/null +++ b/patches/react-native-pager-view@6.8.0.patch @@ -0,0 +1,30 @@ +diff --git a/ios/RNCPagerView.m b/ios/RNCPagerView.m +index adfc7c6f2224b898a02319d352bb4fe11a18fd7e..939bb801c5b0ca6f93b77cb0507c19d137e08e77 100644 +--- a/ios/RNCPagerView.m ++++ b/ios/RNCPagerView.m +@@ -498,6 +498,25 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogni + return YES; + } + ++ // iOS 26+ full-screen back gesture (interactiveContentPopGestureRecognizer) ++ if (@available(iOS 26, *)) { ++ if (gestureRecognizer == self.panGestureRecognizer && ++ otherGestureRecognizer == self.reactViewController.navigationController.interactiveContentPopGestureRecognizer) { ++ UIPanGestureRecognizer* panGestureRecognizer = (UIPanGestureRecognizer*) gestureRecognizer; ++ CGPoint velocity = [panGestureRecognizer velocityInView:self]; ++ BOOL isLTR = [self isLtrLayout]; ++ BOOL isBackGesture = (isLTR && velocity.x > 0) || (!isLTR && velocity.x < 0); ++ ++ if (self.currentIndex == 0 && isBackGesture) { ++ self.scrollView.panGestureRecognizer.enabled = false; ++ } else { ++ self.scrollView.panGestureRecognizer.enabled = self.scrollEnabled; ++ } ++ ++ return YES; ++ } ++ } ++ + self.scrollView.panGestureRecognizer.enabled = self.scrollEnabled; + return NO; + } diff --git a/patches/react-native-reanimated@3.19.1.patch b/patches/react-native-reanimated@3.19.1.patch new file mode 100644 index 0000000000..8cc124d14b --- /dev/null +++ b/patches/react-native-reanimated@3.19.1.patch @@ -0,0 +1,390 @@ +diff --git a/lib/module/component/PerformanceMonitor.js b/lib/module/component/PerformanceMonitor.js +index 9c98d6cc395419f50969753a4e4c7962b7be588f..3686a97280ac540efa0814ac4dea40358860a76f 100644 +--- a/lib/module/component/PerformanceMonitor.js ++++ b/lib/module/component/PerformanceMonitor.js +@@ -1,125 +1,5 @@ + 'use strict'; + +-import React, { useEffect, useRef } from 'react'; +-import { StyleSheet, TextInput, View } from 'react-native'; +-import { addWhitelistedNativeProps } from "../ConfigHelper.js"; +-import { createAnimatedComponent } from "../createAnimatedComponent/index.js"; +-import { useAnimatedProps, useFrameCallback, useSharedValue } from "../hook/index.js"; +-function createCircularDoublesBuffer(size) { +- 'worklet'; +- +- return { +- next: 0, +- buffer: new Float32Array(size), +- size, +- count: 0, +- push(value) { +- const oldValue = this.buffer[this.next]; +- const oldCount = this.count; +- this.buffer[this.next] = value; +- this.next = (this.next + 1) % this.size; +- this.count = Math.min(this.size, this.count + 1); +- return oldCount === this.size ? oldValue : null; +- }, +- front() { +- const notEmpty = this.count > 0; +- if (notEmpty) { +- const current = this.next - 1; +- const index = current < 0 ? this.size - 1 : current; +- return this.buffer[index]; +- } +- return null; +- }, +- back() { +- const notEmpty = this.count > 0; +- return notEmpty ? this.buffer[this.next] : null; +- } +- }; +-} +-const DEFAULT_BUFFER_SIZE = 20; +-addWhitelistedNativeProps({ +- text: true +-}); +-const AnimatedTextInput = createAnimatedComponent(TextInput); +-function loopAnimationFrame(fn) { +- let lastTime = 0; +- function loop() { +- requestAnimationFrame(time => { +- if (lastTime > 0) { +- fn(lastTime, time); +- } +- lastTime = time; +- requestAnimationFrame(loop); +- }); +- } +- loop(); +-} +-function getFps(renderTimeInMs) { +- 'worklet'; +- +- return 1000 / renderTimeInMs; +-} +-function completeBufferRoutine(buffer, timestamp) { +- 'worklet'; +- +- timestamp = Math.round(timestamp); +- const droppedTimestamp = buffer.push(timestamp) ?? timestamp; +- const measuredRangeDuration = timestamp - droppedTimestamp; +- return getFps(measuredRangeDuration / buffer.count); +-} +-function JsPerformance({ +- smoothingFrames +-}) { +- const jsFps = useSharedValue(null); +- const totalRenderTime = useSharedValue(0); +- const circularBuffer = useRef(createCircularDoublesBuffer(smoothingFrames)); +- useEffect(() => { +- loopAnimationFrame((_, timestamp) => { +- timestamp = Math.round(timestamp); +- const currentFps = completeBufferRoutine(circularBuffer.current, timestamp); +- +- // JS fps have to be measured every 2nd frame, +- // thus 2x multiplication has to occur here +- jsFps.value = (currentFps * 2).toFixed(0); +- }); +- }, [jsFps, totalRenderTime]); +- const animatedProps = useAnimatedProps(() => { +- const text = 'JS: ' + (jsFps.value ?? 'N/A') + ' '; +- return { +- text, +- defaultValue: text +- }; +- }); +- return +- +- ; +-} +-function UiPerformance({ +- smoothingFrames +-}) { +- const uiFps = useSharedValue(null); +- const circularBuffer = useSharedValue(null); +- useFrameCallback(({ +- timestamp +- }) => { +- if (circularBuffer.value === null) { +- circularBuffer.value = createCircularDoublesBuffer(smoothingFrames); +- } +- timestamp = Math.round(timestamp); +- const currentFps = completeBufferRoutine(circularBuffer.value, timestamp); +- uiFps.value = currentFps.toFixed(0); +- }); +- const animatedProps = useAnimatedProps(() => { +- const text = 'UI: ' + (uiFps.value ?? 'N/A') + ' '; +- return { +- text, +- defaultValue: text +- }; +- }); +- return +- +- ; +-} + /** + * A component that lets you measure fps values on JS and UI threads on both the + * Paper and Fabric architectures. +@@ -127,38 +7,7 @@ function UiPerformance({ + * @param smoothingFrames - Determines amount of saved frames which will be used + * for fps value smoothing. + */ +-export function PerformanceMonitor({ +- smoothingFrames = DEFAULT_BUFFER_SIZE +-}) { +- return +- +- +- ; ++export function PerformanceMonitor() { ++ return null; + } +-const styles = StyleSheet.create({ +- monitor: { +- flexDirection: 'row', +- position: 'absolute', +- backgroundColor: '#0006', +- zIndex: 1000 +- }, +- header: { +- fontSize: 14, +- color: '#ffff', +- paddingHorizontal: 5 +- }, +- text: { +- fontSize: 13, +- fontVariant: ['tabular-nums'], +- color: '#ffff', +- fontFamily: 'monospace', +- paddingHorizontal: 3 +- }, +- container: { +- alignItems: 'center', +- justifyContent: 'center', +- flexDirection: 'row', +- flexWrap: 'wrap' +- } +-}); + //# sourceMappingURL=PerformanceMonitor.js.map +\ No newline at end of file +diff --git a/src/component/PerformanceMonitor.tsx b/src/component/PerformanceMonitor.tsx +index ff8fc8a947a0aeb959e21ec061882c3d190a2ce0..34dde79727765623df80dbcdab5016de6fd5d82c 100644 +--- a/src/component/PerformanceMonitor.tsx ++++ b/src/component/PerformanceMonitor.tsx +@@ -1,170 +1,5 @@ + 'use strict'; + +-import React, { useEffect, useRef } from 'react'; +-import { StyleSheet, TextInput, View } from 'react-native'; +- +-import { addWhitelistedNativeProps } from '../ConfigHelper'; +-import { createAnimatedComponent } from '../createAnimatedComponent'; +-import type { FrameInfo } from '../frameCallback'; +-import { useAnimatedProps, useFrameCallback, useSharedValue } from '../hook'; +- +-type CircularBuffer = ReturnType; +-function createCircularDoublesBuffer(size: number) { +- 'worklet'; +- +- return { +- next: 0 as number, +- buffer: new Float32Array(size), +- size, +- count: 0 as number, +- +- push(value: number): number | null { +- const oldValue = this.buffer[this.next]; +- const oldCount = this.count; +- this.buffer[this.next] = value; +- +- this.next = (this.next + 1) % this.size; +- this.count = Math.min(this.size, this.count + 1); +- return oldCount === this.size ? oldValue : null; +- }, +- +- front(): number | null { +- const notEmpty = this.count > 0; +- if (notEmpty) { +- const current = this.next - 1; +- const index = current < 0 ? this.size - 1 : current; +- return this.buffer[index]; +- } +- return null; +- }, +- +- back(): number | null { +- const notEmpty = this.count > 0; +- return notEmpty ? this.buffer[this.next] : null; +- }, +- }; +-} +- +-const DEFAULT_BUFFER_SIZE = 20; +-addWhitelistedNativeProps({ text: true }); +-const AnimatedTextInput = createAnimatedComponent(TextInput); +- +-function loopAnimationFrame(fn: (lastTime: number, time: number) => void) { +- let lastTime = 0; +- +- function loop() { +- requestAnimationFrame((time) => { +- if (lastTime > 0) { +- fn(lastTime, time); +- } +- lastTime = time; +- requestAnimationFrame(loop); +- }); +- } +- +- loop(); +-} +- +-function getFps(renderTimeInMs: number): number { +- 'worklet'; +- return 1000 / renderTimeInMs; +-} +- +-function completeBufferRoutine( +- buffer: CircularBuffer, +- timestamp: number +-): number { +- 'worklet'; +- timestamp = Math.round(timestamp); +- +- const droppedTimestamp = buffer.push(timestamp) ?? timestamp; +- +- const measuredRangeDuration = timestamp - droppedTimestamp; +- +- return getFps(measuredRangeDuration / buffer.count); +-} +- +-function JsPerformance({ smoothingFrames }: { smoothingFrames: number }) { +- const jsFps = useSharedValue(null); +- const totalRenderTime = useSharedValue(0); +- const circularBuffer = useRef( +- createCircularDoublesBuffer(smoothingFrames) +- ); +- +- useEffect(() => { +- loopAnimationFrame((_, timestamp) => { +- timestamp = Math.round(timestamp); +- +- const currentFps = completeBufferRoutine( +- circularBuffer.current, +- timestamp +- ); +- +- // JS fps have to be measured every 2nd frame, +- // thus 2x multiplication has to occur here +- jsFps.value = (currentFps * 2).toFixed(0); +- }); +- }, [jsFps, totalRenderTime]); +- +- const animatedProps = useAnimatedProps(() => { +- const text = 'JS: ' + (jsFps.value ?? 'N/A') + ' '; +- return { text, defaultValue: text }; +- }); +- +- return ( +- +- +- +- ); +-} +- +-function UiPerformance({ smoothingFrames }: { smoothingFrames: number }) { +- const uiFps = useSharedValue(null); +- const circularBuffer = useSharedValue(null); +- +- useFrameCallback(({ timestamp }: FrameInfo) => { +- if (circularBuffer.value === null) { +- circularBuffer.value = createCircularDoublesBuffer(smoothingFrames); +- } +- +- timestamp = Math.round(timestamp); +- +- const currentFps = completeBufferRoutine(circularBuffer.value, timestamp); +- +- uiFps.value = currentFps.toFixed(0); +- }); +- +- const animatedProps = useAnimatedProps(() => { +- const text = 'UI: ' + (uiFps.value ?? 'N/A') + ' '; +- return { text, defaultValue: text }; +- }); +- +- return ( +- +- +- +- ); +-} +- +-export type PerformanceMonitorProps = { +- /** +- * Sets amount of previous frames used for smoothing at highest expectedFps. +- * +- * Automatically scales down at lower frame rates. +- * +- * Affects jumpiness of the FPS measurements value. +- */ +- smoothingFrames?: number; +-}; +- + /** + * A component that lets you measure fps values on JS and UI threads on both the + * Paper and Fabric architectures. +@@ -172,40 +7,6 @@ export type PerformanceMonitorProps = { + * @param smoothingFrames - Determines amount of saved frames which will be used + * for fps value smoothing. + */ +-export function PerformanceMonitor({ +- smoothingFrames = DEFAULT_BUFFER_SIZE, +-}: PerformanceMonitorProps) { +- return ( +- +- +- +- +- ); ++export function PerformanceMonitor() { ++ return null; + } +- +-const styles = StyleSheet.create({ +- monitor: { +- flexDirection: 'row', +- position: 'absolute', +- backgroundColor: '#0006', +- zIndex: 1000, +- }, +- header: { +- fontSize: 14, +- color: '#ffff', +- paddingHorizontal: 5, +- }, +- text: { +- fontSize: 13, +- fontVariant: ['tabular-nums'], +- color: '#ffff', +- fontFamily: 'monospace', +- paddingHorizontal: 3, +- }, +- container: { +- alignItems: 'center', +- justifyContent: 'center', +- flexDirection: 'row', +- flexWrap: 'wrap', +- }, +-}); diff --git a/patches/react-native-svg@15.12.1.patch b/patches/react-native-svg@15.12.1.patch new file mode 100644 index 0000000000..299ac13106 --- /dev/null +++ b/patches/react-native-svg@15.12.1.patch @@ -0,0 +1,57 @@ +diff --git a/android/src/main/java/com/horcrux/svg/PathView.java b/android/src/main/java/com/horcrux/svg/PathView.java +index 06829bd00dbded262e1871aaf6db3ae0cfa9d1b1..1b158185a6d7e907aa18ddc806902fcc2bd51027 100644 +--- a/android/src/main/java/com/horcrux/svg/PathView.java ++++ b/android/src/main/java/com/horcrux/svg/PathView.java +@@ -14,17 +14,33 @@ import android.graphics.Paint; + import android.graphics.Path; + import com.facebook.react.bridge.ReactContext; + ++import java.util.ArrayList; ++import java.util.HashMap; ++ ++class ParsedPath { ++ final Path path; ++ final ArrayList elements; ++ ++ ParsedPath(Path path, ArrayList elements) { ++ this.path = path; ++ this.elements = elements; ++ } ++} ++ + @SuppressLint("ViewConstructor") + class PathView extends RenderableView { + private Path mPath; + ++ // This grows forever but for our use case (static icons) it's ok. ++ private static final HashMap sPathCache = new HashMap<>(); ++ + public PathView(ReactContext reactContext) { + super(reactContext); + PathParser.mScale = mScale; + mPath = new Path(); + } + +- public void setD(String d) { ++ void setDByParsing(String d) { + mPath = PathParser.parse(d); + elements = PathParser.elements; + for (PathElement elem : elements) { +@@ -33,6 +49,17 @@ class PathView extends RenderableView { + point.y *= mScale; + } + } ++ } ++ ++ public void setD(String d) { ++ ParsedPath cached = sPathCache.get(d); ++ if (cached != null) { ++ mPath = cached.path; ++ elements = cached.elements; ++ } else { ++ setDByParsing(d); ++ sPathCache.put(d, new ParsedPath(mPath, elements)); ++ } + invalidate(); + } + diff --git a/patches/react-native-uitextview@1.4.0.patch b/patches/react-native-uitextview@1.4.0.patch new file mode 100644 index 0000000000..ea2a967cde --- /dev/null +++ b/patches/react-native-uitextview@1.4.0.patch @@ -0,0 +1,35 @@ +diff --git a/ios/RNUITextViewShadow.swift b/ios/RNUITextViewShadow.swift +index c34ba712ca628ed8cf2db0f9fc332810ec86d34d..3602856dc8cd926b5321b4ecb109be9c00a23fe6 100644 +--- a/ios/RNUITextViewShadow.swift ++++ b/ios/RNUITextViewShadow.swift +@@ -159,13 +159,25 @@ class RNUITextViewShadow: RCTShadowView { + let maxSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(MAXFLOAT)) + let textSize = self.attributedText.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, context: nil) + +- var totalLines = self.lineHeight == 0.0 ? 0 : Int(ceil(textSize.height / self.lineHeight)) +- +- if self.numberOfLines != 0, totalLines > self.numberOfLines { +- totalLines = self.numberOfLines ++ var finalHeight: CGFloat ++ ++ if self.numberOfLines != 0 && self.lineHeight != 0.0 { ++ // numberOfLines is set with custom line height - need to calculate lines and snap to lineHeight multiples ++ // NOTE: this calculation can be inaccurate with fractional font sizes ++ var totalLines = Int(ceil(textSize.height / self.lineHeight)) ++ if totalLines > self.numberOfLines { ++ totalLines = self.numberOfLines ++ } ++ finalHeight = CGFloat(totalLines) * self.lineHeight ++ } else { ++ // Either no numberOfLines limit, or no custom lineHeight - use actual text height ++ // (numberOfLines without custom lineHeight is handled by the UITextView's textContainer.maximumNumberOfLines) ++ finalHeight = textSize.height + } + +- self.frameSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(CGFloat(totalLines) * self.lineHeight)) ++ finalHeight = ceil(finalHeight) ++ ++ self.frameSize = CGSize(width: CGFloat(maxWidth), height: finalHeight) + return YGSize(width: Float(self.frameSize.width), height: Float(self.frameSize.height)) + } + diff --git a/patches/react-native-view-shot@4.0.3.patch b/patches/react-native-view-shot@4.0.3.patch new file mode 100644 index 0000000000..a7fa2fbe97 --- /dev/null +++ b/patches/react-native-view-shot@4.0.3.patch @@ -0,0 +1,28 @@ +diff --git a/src/index.js b/src/index.js +index fa76d7e1272e7fbe4bbd153104db127f1f6eecad..018b6860b7fa02d498d73b5fd06028bae99abedb 100644 +--- a/src/index.js ++++ b/src/index.js +@@ -125,13 +125,17 @@ export function captureRef( + } + } + if (typeof view !== "number") { +- const node = findNodeHandle(view); +- if (!node) { +- return Promise.reject( +- new Error("findNodeHandle failed to resolve view=" + String(view)) +- ); ++ if (Platform.OS == 'web') { ++ view = view; ++ } else { ++ const node = findNodeHandle(view); ++ if (!node) { ++ return Promise.reject( ++ new Error("findNodeHandle failed to resolve view=" + String(view)) ++ ); ++ } ++ view = node; + } +- view = node; + } + const { options, errors } = validateOptions(optionsObject); + if (__DEV__ && errors.length > 0) { diff --git a/patches/react-native@0.81.5.patch b/patches/react-native@0.81.5.patch new file mode 100644 index 0000000000..a71008d2d1 --- /dev/null +++ b/patches/react-native@0.81.5.patch @@ -0,0 +1,168 @@ +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 + ++- (void)beginRefreshingProgrammatically; ++ + @end + + NS_ASSUME_NONNULL_END +diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +index 1494fd225aff1fa0429e917404d6b4ca5fc961c5..df643f5c844ad2e684de5161528eba17f4a188d0 100644 +--- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm ++++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +@@ -1038,6 +1038,11 @@ - (void)_adjustForMaintainVisibleContentPosition + } + } + +++ (BOOL)shouldBeRecycled ++{ ++ return NO; ++} ++ + @end + + Class RCTScrollViewCls(void) +diff --git a/React/Views/RefreshControl/RCTRefreshControl.h b/React/Views/RefreshControl/RCTRefreshControl.h +index e9b330fa7c29c42653a3b0191d0f8a1b13b2d3de..ec5f58c887bfd949f1279ef1c31352e0b465e9ec 100644 +--- a/React/Views/RefreshControl/RCTRefreshControl.h ++++ b/React/Views/RefreshControl/RCTRefreshControl.h +@@ -15,5 +15,8 @@ + @property (nonatomic, copy) NSString *title; + @property (nonatomic, copy) RCTDirectEventBlock onRefresh; + @property (nonatomic, weak) UIScrollView *scrollView; ++@property (nonatomic, copy) UIColor *customTintColor; ++ ++- (void)forwarderBeginRefreshing; + + @end +diff --git a/React/Views/RefreshControl/RCTRefreshControl.m b/React/Views/RefreshControl/RCTRefreshControl.m +index 53bfd04703502d5b8e932c47a528bb03cd79d330..ff1b1ed5e060bcf0d91528c6d3c2c5c8acf24967 100644 +--- a/React/Views/RefreshControl/RCTRefreshControl.m ++++ b/React/Views/RefreshControl/RCTRefreshControl.m +@@ -23,6 +23,7 @@ @implementation RCTRefreshControl { + UIColor *_titleColor; + CGFloat _progressViewOffset; + BOOL _hasMovedToWindow; ++ UIColor *_customTintColor; + } + + - (instancetype)init +@@ -58,6 +59,12 @@ - (void)layoutSubviews + _isInitialRender = false; + } + ++- (void)didMoveToSuperview ++{ ++ [super didMoveToSuperview]; ++ [self setTintColor:_customTintColor]; ++} ++ + - (void)didMoveToWindow + { + [super didMoveToWindow]; +@@ -221,4 +228,50 @@ - (void)refreshControlValueChanged + } + } + ++// Fix for https://github.com/facebook/react-native/issues/43388 ++// A bug in iOS 17.4 causes the haptic to not play when refreshing if the tintColor ++// is set before the refresh control gets added to the scrollview. We'll call this ++// function whenever the superview changes. We'll also call it if the value of customTintColor ++// changes. ++- (void)setTintColor:(UIColor *)tintColor ++{ ++ if ([self.superview isKindOfClass:[UIScrollView class]] && self.tintColor != tintColor) { ++ [super setTintColor:tintColor]; ++ } ++} ++ ++// This method is used by Bluesky's ExpoScrollForwarder. This allows other React Native ++// libraries to perform a refresh of a scrollview and access the refresh control's onRefresh ++// function. ++- (void)forwarderBeginRefreshing ++{ ++ _refreshingProgrammatically = NO; ++ ++ [self sizeToFit]; ++ ++ if (!self.scrollView) { ++ return; ++ } ++ ++ UIScrollView *scrollView = (UIScrollView *)self.scrollView; ++ ++ [UIView animateWithDuration:0.3 ++ delay:0 ++ options:UIViewAnimationOptionBeginFromCurrentState ++ animations:^(void) { ++ // Whenever we call this method, the scrollview will always be at a position of ++ // -130 or less. Scrolling back to -65 simulates the default behavior of RCTRefreshControl ++ [scrollView setContentOffset:CGPointMake(0, -65)]; ++ } ++ completion:^(__unused BOOL finished) { ++ [super beginRefreshing]; ++ [self setCurrentRefreshingState:super.refreshing]; ++ ++ if (self->_onRefresh) { ++ self->_onRefresh(nil); ++ } ++ } ++ ]; ++} ++ + @end +diff --git a/React/Views/RefreshControl/RCTRefreshControlManager.m b/React/Views/RefreshControl/RCTRefreshControlManager.m +index 40aaf9c51ebda9fedb1d1db2e9aacec84b4c39c8..1c60164b69762997b3369b46609a07768a06bad3 100644 +--- a/React/Views/RefreshControl/RCTRefreshControlManager.m ++++ b/React/Views/RefreshControl/RCTRefreshControlManager.m +@@ -22,11 +22,12 @@ - (UIView *)view + + RCT_EXPORT_VIEW_PROPERTY(onRefresh, RCTDirectEventBlock) + RCT_EXPORT_VIEW_PROPERTY(refreshing, BOOL) +-RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) + RCT_EXPORT_VIEW_PROPERTY(title, NSString) + RCT_EXPORT_VIEW_PROPERTY(titleColor, UIColor) + RCT_EXPORT_VIEW_PROPERTY(progressViewOffset, CGFloat) + ++RCT_REMAP_VIEW_PROPERTY(tintColor, customTintColor, UIColor) ++ + 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/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/sonner-native@0.21.0.patch b/patches/sonner-native@0.21.0.patch new file mode 100644 index 0000000000..8b6ce6fcf1 --- /dev/null +++ b/patches/sonner-native@0.21.0.patch @@ -0,0 +1,22 @@ +diff --git a/lib/commonjs/toast.js b/lib/commonjs/toast.js +index 121816a452339c1088aeba87928ff63a0bdacca5..47e74bce47323201f0bb4e5ed9dc55b373c07b97 100644 +--- a/lib/commonjs/toast.js ++++ b/lib/commonjs/toast.js +@@ -264,7 +264,7 @@ const Toast = exports.Toast = /*#__PURE__*/React.forwardRef(({ + ...toastSwipeHandlerProps, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, { + entering: entering, +- exiting: exiting, ++ exiting: _reactNative.Platform.OS === 'android' ? undefined : exiting, + children: jsx + }) + }); +@@ -274,7 +274,7 @@ const Toast = exports.Toast = /*#__PURE__*/React.forwardRef(({ + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, { + style: [unstyled ? undefined : elevationStyle, defaultStyles.toast, toastStyleCtx, styles?.toast, style, wiggleAnimationStyle], + entering: entering, +- exiting: exiting, ++ exiting: _reactNative.Platform.OS === 'android' ? undefined : exiting, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { + style: [defaultStyles.toastContent, toastContentStyleCtx, styles?.toastContent], + children: [promiseOptions || variant === 'loading' ? 'loading' in icons ? icons.loading : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {}) : icon ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 749babd619..bfd1aaa0b2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -17,3 +17,26 @@ allowBuilds: "core-js-pure": true "esbuild": true "unrs-resolver": true +patchedDependencies: + '@discord/bottom-sheet@4.6.1': patches/@discord__bottom-sheet@4.6.1.patch + '@sentry/react-native@6.20.0': patches/@sentry__react-native@6.20.0.patch + expo-glass-effect@55.0.8: patches/expo-glass-effect@55.0.8.patch + expo-haptics@15.0.8: patches/expo-haptics@15.0.8.patch + expo-image-picker@17.0.11: patches/expo-image-picker@17.0.11.patch + expo-image@3.0.11: patches/expo-image@3.0.11.patch + expo-media-library@18.2.1: patches/expo-media-library@18.2.1.patch + expo-modules-core@3.0.30: patches/expo-modules-core@3.0.30.patch + expo-notifications@0.32.17: patches/expo-notifications@0.32.17.patch + expo-paste-input@0.1.15: patches/expo-paste-input@0.1.15.patch + expo-updates@29.0.17: patches/expo-updates@29.0.17.patch + react-native-compressor@1.13.0: patches/react-native-compressor@1.13.0.patch + react-native-date-picker@5.0.13: patches/react-native-date-picker@5.0.13.patch + react-native-drawer-layout@4.2.3: patches/react-native-drawer-layout@4.2.3.patch + react-native-keyboard-controller@1.21.7: patches/react-native-keyboard-controller@1.21.7.patch + react-native-pager-view@6.8.0: patches/react-native-pager-view@6.8.0.patch + react-native-reanimated@3.19.1: patches/react-native-reanimated@3.19.1.patch + react-native-svg@15.12.1: patches/react-native-svg@15.12.1.patch + react-native-uitextview@1.4.0: patches/react-native-uitextview@1.4.0.patch + react-native-view-shot@4.0.3: patches/react-native-view-shot@4.0.3.patch + react-native@0.81.5: patches/react-native@0.81.5.patch + sonner-native@0.21.0: patches/sonner-native@0.21.0.patch