diff options
author | Hailey <me@haileyok.com> | 2024-12-06 09:52:08 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-06 17:52:08 +0000 |
commit | 1f6acc11abec91972e6e04abd55e09b2a9dc1433 (patch) | |
tree | 3d6a9b1e8a3aa6f06a4700e9b40519439fcb37da /patches | |
parent | 3ab6c435df5dc3d17fe3e2531231ccf012a4860c (diff) | |
download | voidsky-1f6acc11abec91972e6e04abd55e09b2a9dc1433.tar.zst |
clean rn 0.76 upgrade (#6887)
* package upgrades * upgrade system ui * update patches * rename patch * rm * use .set/.set * resolve yarnlock * fix accidentally removed package * fix use permissions hook * fix some type errors * type fixes * more tweaking * clean * Discard changes to src/screens/Onboarding/StepProfile/index.tsx * oops * fix splash * use ios/android in config * Fix tests * add back patch * add to rn patch * fullscreen? * Revert "add to rn patch" This reverts commit 4716d2c643a29fc77b871ca8555d8d78cb4ac427. * try this * test with revert * test * maybe this * fix config * Bump @react-native-picker/picker * Bump some packages * Rm unused * Update lockfile * Rename expo-notifications+0.29.8.patch.md to expo-notifications+0.29.10.patch.md * Update react-native+0.76.3.patch.md * Update react-native+0.76.3.patch.md * Inline splash configs Jumping around the file is annoying and makes it harder to understand how this is structured. * Start fixing Android splash * Downgrade compressor This version isn't building for me due to https://github.com/numandev1/react-native-compressor/issues/322. * Make Android splash empty for now * Work around a bug * Bump the compressor * Bump again * Include splash fixes * Try updating * No custom Android splash * Revert to using icons * welp * Fix sizes * Make sizing work * Bump size --------- Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
Diffstat (limited to 'patches')
20 files changed, 502 insertions, 727 deletions
diff --git a/patches/@lingui+core+4.5.0.patch b/patches/@lingui+core+4.14.1.patch index 8ace93a74..8ace93a74 100644 --- a/patches/@lingui+core+4.5.0.patch +++ b/patches/@lingui+core+4.14.1.patch diff --git a/patches/@sentry+react-native+5.32.0.patch b/patches/@sentry+react-native+5.24.3.patch index a5ccecc21..a5ccecc21 100644 --- a/patches/@sentry+react-native+5.32.0.patch +++ b/patches/@sentry+react-native+5.24.3.patch diff --git a/patches/expo-haptics+12.8.1.md b/patches/expo-haptics+14.0.0.md index afa7395bc..afa7395bc 100644 --- a/patches/expo-haptics+12.8.1.md +++ b/patches/expo-haptics+14.0.0.md diff --git a/patches/expo-haptics+13.0.1.patch b/patches/expo-haptics+14.0.0.patch index 9c7b9a666..9c7b9a666 100644 --- a/patches/expo-haptics+13.0.1.patch +++ b/patches/expo-haptics+14.0.0.patch diff --git a/patches/expo-image-picker+15.0.5.patch b/patches/expo-image-picker+15.0.5.patch deleted file mode 100644 index 962b36612..000000000 --- a/patches/expo-image-picker+15.0.5.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/exporters/RawImageExporter.kt b/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/exporters/RawImageExporter.kt -index f339e5f..fa35e82 100644 ---- a/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/exporters/RawImageExporter.kt -+++ b/node_modules/expo-image-picker/android/src/main/java/expo/modules/imagepicker/exporters/RawImageExporter.kt -@@ -16,7 +16,12 @@ class RawImageExporter : ImageExporter { - copyFile(source, output, contentResolver) - val exifInterface = ExifInterface(output.absolutePath) - val imageRotation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0) -- val isRotatedLandscape = (imageRotation == ExifInterface.ORIENTATION_ROTATE_90 || imageRotation == ExifInterface.ORIENTATION_ROTATE_270) -+ val isRotatedLandscape = ( -+ imageRotation == ExifInterface.ORIENTATION_ROTATE_90 || -+ imageRotation == ExifInterface.ORIENTATION_ROTATE_270 || -+ imageRotation == ExifInterface.ORIENTATION_TRANSPOSE || -+ imageRotation == ExifInterface.ORIENTATION_TRANSVERSE -+ ) - val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } - - BitmapFactory.decodeFile(output.absolutePath, options) diff --git a/patches/expo-modules-core+1.12.11.md b/patches/expo-modules-core+1.12.11.md deleted file mode 100644 index a690c8609..000000000 --- a/patches/expo-modules-core+1.12.11.md +++ /dev/null @@ -1,5 +0,0 @@ -## expo-modules-core Patch - -This patch fixes crashes seen in some Android clients when using intents to open the app. See https://github.com/expo/expo/pull/29513. - -Do not remove this patch until that PR lands in Expo and is released. diff --git a/patches/expo-modules-core+1.12.11.patch b/patches/expo-modules-core+1.12.11.patch deleted file mode 100644 index ea26b821d..000000000 --- a/patches/expo-modules-core+1.12.11.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/node_modules/expo-modules-core/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java b/node_modules/expo-modules-core/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java -index bb74e80..0aa0202 100644 ---- a/node_modules/expo-modules-core/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java -+++ b/node_modules/expo-modules-core/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java -@@ -90,8 +90,8 @@ public class NativeModulesProxy extends ReactContextBaseJavaModule { - mModuleRegistry.ensureIsInitialized(); - - KotlinInteropModuleRegistry kotlinModuleRegistry = getKotlinInteropModuleRegistry(); -- kotlinModuleRegistry.emitOnCreate(); - kotlinModuleRegistry.installJSIInterop(); -+ kotlinModuleRegistry.emitOnCreate(); - - Map<String, Object> constants = new HashMap<>(3); - constants.put(MODULES_CONSTANTS_KEY, new HashMap<>()); -diff --git a/node_modules/expo-modules-core/build/uuid/uuid.js b/node_modules/expo-modules-core/build/uuid/uuid.js -index 109d3fe..c7fce9e 100644 ---- a/node_modules/expo-modules-core/build/uuid/uuid.js -+++ b/node_modules/expo-modules-core/build/uuid/uuid.js -@@ -1,5 +1,7 @@ - import bytesToUuid from './lib/bytesToUuid'; - import { Uuidv5Namespace } from './uuid.types'; -+import { ensureNativeModulesAreInstalled } from '../ensureNativeModulesAreInstalled'; -+ensureNativeModulesAreInstalled(); - const nativeUuidv4 = globalThis?.expo?.uuidv4; - const nativeUuidv5 = globalThis?.expo?.uuidv5; - function uuidv4() { diff --git a/patches/expo-notifications+0.28.7.patch b/patches/expo-notifications+0.28.7.patch deleted file mode 100644 index b71da2ebb..000000000 --- a/patches/expo-notifications+0.28.7.patch +++ /dev/null @@ -1,216 +0,0 @@ -diff --git a/node_modules/expo-notifications/android/build.gradle b/node_modules/expo-notifications/android/build.gradle -index b863077..8b5209e 100644 ---- a/node_modules/expo-notifications/android/build.gradle -+++ b/node_modules/expo-notifications/android/build.gradle -@@ -32,6 +32,7 @@ dependencies { - api 'com.google.firebase:firebase-messaging:22.0.0' - - api '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/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/badge/BadgeHelper.kt b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/badge/BadgeHelper.kt -index 63a46c5..f911834 100644 ---- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/badge/BadgeHelper.kt -+++ b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/badge/BadgeHelper.kt -@@ -1,5 +1,6 @@ - package expo.modules.notifications.badge - -+import android.app.NotificationManager - import android.content.Context - import android.util.Log - import me.leolin.shortcutbadger.ShortcutBadgeException -@@ -12,7 +13,12 @@ object BadgeHelper { - - fun setBadgeCount(context: Context, badgeCount: Int): Boolean { - return try { -- ShortcutBadger.applyCountOrThrow(context.applicationContext, badgeCount) -+ if (badgeCount == 0) { -+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager -+ notificationManager.cancelAll() -+ } else { -+ ShortcutBadger.applyCountOrThrow(context.applicationContext, badgeCount) -+ } - BadgeHelper.badgeCount = badgeCount - true - } catch (e: ShortcutBadgeException) { -diff --git a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/JSONNotificationContentBuilder.java b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/JSONNotificationContentBuilder.java -index 0af7fe0..8f2c8d8 100644 ---- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/JSONNotificationContentBuilder.java -+++ b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/JSONNotificationContentBuilder.java -@@ -14,6 +14,7 @@ import expo.modules.notifications.notifications.enums.NotificationPriority; - import expo.modules.notifications.notifications.model.NotificationContent; - - public class JSONNotificationContentBuilder extends NotificationContent.Builder { -+ private static final String CHANNEL_ID_KEY = "channelId"; - private static final String TITLE_KEY = "title"; - private static final String TEXT_KEY = "message"; - private static final String SUBTITLE_KEY = "subtitle"; -@@ -36,6 +37,7 @@ public class JSONNotificationContentBuilder extends NotificationContent.Builder - - public NotificationContent.Builder setPayload(JSONObject payload) { - this.setTitle(getTitle(payload)) -+ .setChannelId(getChannelId(payload)) - .setSubtitle(getSubtitle(payload)) - .setText(getText(payload)) - .setBody(getBody(payload)) -@@ -60,6 +62,14 @@ public class JSONNotificationContentBuilder extends NotificationContent.Builder - return this; - } - -+ protected String getChannelId(JSONObject payload) { -+ try { -+ return payload.getString(CHANNEL_ID_KEY); -+ } catch (JSONException e) { -+ return null; -+ } -+ } -+ - protected String getTitle(JSONObject payload) { - try { - return payload.getString(TITLE_KEY); -diff --git a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java -index f1fed19..012757b 100644 ---- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java -+++ b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java -@@ -20,6 +20,7 @@ import expo.modules.notifications.notifications.enums.NotificationPriority; - * should be created using {@link NotificationContent.Builder}. - */ - public class NotificationContent implements Parcelable, Serializable { -+ private String mChannelId; - private String mTitle; - private String mText; - private String mSubtitle; -@@ -50,6 +51,11 @@ public class NotificationContent implements Parcelable, Serializable { - } - }; - -+ @Nullable -+ public String getChannelId() { -+ return mChannelId; -+ } -+ - @Nullable - public String getTitle() { - return mTitle; -@@ -121,6 +127,7 @@ public class NotificationContent implements Parcelable, Serializable { - } - - protected NotificationContent(Parcel in) { -+ mChannelId = in.readString(); - mTitle = in.readString(); - mText = in.readString(); - mSubtitle = in.readString(); -@@ -146,6 +153,7 @@ public class NotificationContent implements Parcelable, Serializable { - - @Override - public void writeToParcel(Parcel dest, int flags) { -+ dest.writeString(mChannelId); - dest.writeString(mTitle); - dest.writeString(mText); - dest.writeString(mSubtitle); -@@ -166,6 +174,7 @@ public class NotificationContent implements Parcelable, Serializable { - 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); -@@ -190,6 +199,7 @@ public class NotificationContent implements Parcelable, Serializable { - } - - private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { -+ mChannelId = (String) in.readObject(); - mTitle = (String) in.readObject(); - mText = (String) in.readObject(); - mSubtitle = (String) in.readObject(); -@@ -240,6 +250,7 @@ public class NotificationContent implements Parcelable, Serializable { - } - - public static class Builder { -+ private String mChannelId; - private String mTitle; - private String mText; - private String mSubtitle; -@@ -260,6 +271,11 @@ public class NotificationContent implements Parcelable, Serializable { - useDefaultVibrationPattern(); - } - -+ public Builder setChannelId(String channelId) { -+ mChannelId = channelId; -+ return this; -+ } -+ - public Builder setTitle(String title) { - mTitle = title; - return this; -@@ -336,6 +352,7 @@ public class NotificationContent implements Parcelable, Serializable { - - public NotificationContent build() { - NotificationContent content = new NotificationContent(); -+ content.mChannelId = mChannelId; - content.mTitle = mTitle; - content.mSubtitle = mSubtitle; - content.mText = mText; -diff --git a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.java b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.java -index 6bd9928..ee93d70 100644 ---- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.java -+++ b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.java -@@ -48,6 +48,10 @@ public class ExpoNotificationBuilder extends ChannelAwareNotificationBuilder { - - NotificationContent content = getNotificationContent(); - -+ if (content.getChannelId() != null) { -+ builder.setChannelId(content.getChannelId()); -+ } -+ - builder.setAutoCancel(content.isAutoDismiss()); - builder.setOngoing(content.isSticky()); - -diff --git a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt -index 55b3a8d..1b99d5b 100644 ---- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt -+++ b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt -@@ -12,11 +12,14 @@ import expo.modules.notifications.notifications.model.triggers.FirebaseNotificat - import expo.modules.notifications.service.NotificationsService - import expo.modules.notifications.service.interfaces.FirebaseMessagingDelegate - import expo.modules.notifications.tokens.interfaces.FirebaseTokenListener -+import expo.modules.backgroundnotificationhandler.BackgroundNotificationHandler -+import expo.modules.backgroundnotificationhandler.BackgroundNotificationHandlerInterface -+import expo.modules.backgroundnotificationhandler.ExpoBackgroundNotificationHandlerModule - import org.json.JSONObject - 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 -@@ -89,12 +92,21 @@ open class FirebaseMessagingDelegate(protected val context: Context) : FirebaseM - fun getBackgroundTasks() = sBackgroundTaskConsumerReferences.values.mapNotNull { it.get() } - - override fun onMessageReceived(remoteMessage: RemoteMessage) { -- NotificationsService.receive(context, createNotification(remoteMessage)) -- getBackgroundTasks().forEach { -- it.scheduleJob(RemoteMessageSerializer.toBundle(remoteMessage)) -+ if (!ExpoBackgroundNotificationHandlerModule.isForegrounded) { -+ BackgroundNotificationHandler(context, this).handleMessage(remoteMessage) -+ return -+ } else { -+ showMessage(remoteMessage) -+ getBackgroundTasks().forEach { -+ it.scheduleJob(RemoteMessageSerializer.toBundle(remoteMessage)) -+ } - } - } - -+ override fun showMessage(remoteMessage: RemoteMessage) { -+ NotificationsService.receive(context, createNotification(remoteMessage)) -+ } -+ - protected fun createNotification(remoteMessage: RemoteMessage): Notification { - val identifier = getNotificationIdentifier(remoteMessage) - val payload = JSONObject(remoteMessage.data as Map<*, *>) diff --git a/patches/expo-notifications+0.29.10.patch b/patches/expo-notifications+0.29.10.patch new file mode 100644 index 000000000..a781d5f1a --- /dev/null +++ b/patches/expo-notifications+0.29.10.patch @@ -0,0 +1,169 @@ +diff --git a/node_modules/expo-notifications/android/build.gradle b/node_modules/expo-notifications/android/build.gradle +index 5dd0c61..bf536dd 100644 +--- a/node_modules/expo-notifications/android/build.gradle ++++ b/node_modules/expo-notifications/android/build.gradle +@@ -46,6 +46,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/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/interfaces/INotificationContent.kt b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/interfaces/INotificationContent.kt +index 7b99e6c..45a450d 100644 +--- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/interfaces/INotificationContent.kt ++++ b/node_modules/expo-notifications/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/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java +index 191b64e..fe8b3c5 100644 +--- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java ++++ b/node_modules/expo-notifications/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/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/NotificationData.kt b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/NotificationData.kt +index 39b5aad..e50797d 100644 +--- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/NotificationData.kt ++++ b/node_modules/expo-notifications/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<String, String>) { ++ val channelId: String? ++ get() = data["channelId"] ++ + val title: String? + get() = data["title"] + +diff --git a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/RemoteNotificationContent.kt b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/RemoteNotificationContent.kt +index d2cc6cf..6a48ff2 100644 +--- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/RemoteNotificationContent.kt ++++ b/node_modules/expo-notifications/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/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.kt b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.kt +index 8ca6ec5..57c3599 100644 +--- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.kt ++++ b/node_modules/expo-notifications/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/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt +index 9f22441..5f92f80 100644 +--- a/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt ++++ b/node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/service/delegates/FirebaseMessagingDelegate.kt +@@ -2,6 +2,9 @@ package expo.modules.notifications.service.delegates + + import android.content.Context + 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 +@@ -17,7 +20,8 @@ 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 +@@ -94,8 +98,17 @@ 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(remoteMessage) ++ ++ if (!ExpoBackgroundNotificationHandlerModule.isForegrounded) { ++ BackgroundNotificationHandler(context, this).handleMessage(remoteMessage) ++ } else { ++ showMessage(remoteMessage) ++ runTaskManagerTasks(remoteMessage) ++ } ++ } ++ ++ override fun showMessage(remoteMessage: RemoteMessage) { ++ NotificationsService.receive(context, createNotification(remoteMessage)) + } + + private fun runTaskManagerTasks(remoteMessage: RemoteMessage) { diff --git a/patches/expo-notifications-0.28.7.patch.md b/patches/expo-notifications+0.29.10.patch.md index 05f841725..05f841725 100644 --- a/patches/expo-notifications-0.28.7.patch.md +++ b/patches/expo-notifications+0.29.10.patch.md diff --git a/patches/expo-updates+0.25.14.patch b/patches/expo-updates+0.26.9.patch index 6fc4fc5fc..6fc4fc5fc 100644 --- a/patches/expo-updates+0.25.14.patch +++ b/patches/expo-updates+0.26.9.patch diff --git a/patches/expo-updates+0.25.11.patch.md b/patches/expo-updates+0.26.9.patch.md index 6d5d7093d..6d5d7093d 100644 --- a/patches/expo-updates+0.25.11.patch.md +++ b/patches/expo-updates+0.26.9.patch.md diff --git a/patches/metro+0.80.4.patch b/patches/metro+0.80.4.patch deleted file mode 100644 index f8ef67c84..000000000 --- a/patches/metro+0.80.4.patch +++ /dev/null @@ -1,44 +0,0 @@ -diff --git a/node_modules/metro/src/ModuleGraph/worker/JsFileWrapping.js b/node_modules/metro/src/ModuleGraph/worker/JsFileWrapping.js -index 48a1409..ef185c9 100644 ---- a/node_modules/metro/src/ModuleGraph/worker/JsFileWrapping.js -+++ b/node_modules/metro/src/ModuleGraph/worker/JsFileWrapping.js -@@ -70,14 +70,19 @@ function wrapModule( - importDefaultName, - importAllName, - dependencyMapName, -- globalPrefix -+ globalPrefix, -+ moduleFactoryName - ) { - const params = buildParameters( - importDefaultName, - importAllName, - dependencyMapName - ); -- const factory = functionFromProgram(fileAst.program, params); -+ const factory = functionFromProgram( -+ fileAst.program, -+ params, -+ moduleFactoryName -+ ); - const def = t.callExpression(t.identifier(`${globalPrefix}__d`), [factory]); - const ast = t.file(t.program([t.expressionStatement(def)])); - const requireName = renameRequires(ast); -@@ -107,7 +112,16 @@ function wrapJson(source, globalPrefix) { - "});", - ].join("\n"); - } --function functionFromProgram(program, parameters) { -+const JS_INVALID_IDENT_RE = /[^a-zA-Z0-9$_]/g; -+function functionFromProgram(program, parameters, moduleFactoryName) { -+ let identifier; -+ if (typeof moduleFactoryName === "string" && moduleFactoryName !== "") { -+ // Keep the name readable so it shows up in profiler traces. -+ // Add an unlikely suffix to avoid collisions with the module code. -+ identifier = t.identifier( -+ `${moduleFactoryName.replace(JS_INVALID_IDENT_RE, "_")}__module_factory__` -+ ); -+ } - return t.functionExpression( - undefined, - parameters.map(makeIdentifier), diff --git a/patches/metro-runtime+0.80.4.patch b/patches/metro-runtime+0.80.4.patch deleted file mode 100644 index 65303775d..000000000 --- a/patches/metro-runtime+0.80.4.patch +++ /dev/null @@ -1,50 +0,0 @@ -diff --git a/node_modules/metro-runtime/src/polyfills/require.js b/node_modules/metro-runtime/src/polyfills/require.js -index ce67cb4..eeeae84 100644 ---- a/node_modules/metro-runtime/src/polyfills/require.js -+++ b/node_modules/metro-runtime/src/polyfills/require.js -@@ -22,6 +22,13 @@ global.__c = clear; - global.__registerSegment = registerSegment; - var modules = clear(); - -+if (__DEV__) { -+ // Added by Dan for module init logging. -+ global.__INIT_LOGS__ = [] -+ var initModuleCounter = 0 -+ var initModuleStack = [] -+} -+ - // Don't use a Symbol here, it would pull in an extra polyfill with all sorts of - // additional stuff (e.g. Array.from). - const EMPTY = {}; -@@ -303,7 +310,30 @@ function loadModuleImplementation(moduleId, module) { - throw module.error; - } - if (__DEV__) { -- var Systrace = requireSystrace(); -+ // Added by Dan for module init logging. -+ var Systrace = { -+ beginEvent(label) { -+ let fullLabel = initModuleCounter++ + ' ' + label -+ global.__INIT_LOGS__.push( -+ ' '.repeat(initModuleStack.length) + -+ ' ENTER ' + fullLabel -+ ) -+ initModuleStack.push({ -+ fullLabel, -+ startTime: nativePerformanceNow(), -+ }) -+ }, -+ endEvent() { -+ const res = initModuleStack.pop() -+ const fullLabel = res.fullLabel -+ const startTime = res.startTime -+ const timeElapsed = Math.round(nativePerformanceNow() - startTime) -+ global.__INIT_LOGS__.push( -+ ' '.repeat(initModuleStack.length) + -+ ' LEAVE ' + fullLabel + ' [' + timeElapsed + 'ms]', -+ ) -+ } -+ }; - var Refresh = requireRefresh(); - } - diff --git a/patches/metro-transform-worker+0.80.4.patch b/patches/metro-transform-worker+0.80.4.patch deleted file mode 100644 index 717b7c2d9..000000000 --- a/patches/metro-transform-worker+0.80.4.patch +++ /dev/null @@ -1,41 +0,0 @@ -diff --git a/node_modules/metro-transform-worker/src/index.js b/node_modules/metro-transform-worker/src/index.js -index 9f2e3d2..5222c8e 100644 ---- a/node_modules/metro-transform-worker/src/index.js -+++ b/node_modules/metro-transform-worker/src/index.js -@@ -189,6 +189,10 @@ async function transformJS(file, { config, options, projectRoot }) { - let dependencyMapName = ""; - let dependencies; - let wrappedAst; -+ const minify = -+ options.minify && -+ options.unstable_transformProfile !== "hermes-canary" && -+ options.unstable_transformProfile !== "hermes-stable"; - - // If the module to transform is a script (meaning that is not part of the - // dependency graph and it code will just be prepended to the bundle modules), -@@ -228,19 +232,20 @@ async function transformJS(file, { config, options, projectRoot }) { - if (config.unstable_disableModuleWrapping === true) { - wrappedAst = ast; - } else { -+ let moduleFactoryName; -+ if (options.dev && !minify) { -+ moduleFactoryName = file.filename; -+ } - ({ ast: wrappedAst } = JsFileWrapping.wrapModule( - ast, - importDefault, - importAll, - dependencyMapName, -- config.globalPrefix -+ config.globalPrefix, -+ moduleFactoryName - )); - } - } -- const minify = -- options.minify && -- options.unstable_transformProfile !== "hermes-canary" && -- options.unstable_transformProfile !== "hermes-stable"; - const reserved = []; - if (config.unstable_dependencyMapReservedName != null) { - reserved.push(config.unstable_dependencyMapReservedName); diff --git a/patches/react-native+0.74.1.patch b/patches/react-native+0.74.1.patch deleted file mode 100644 index d9f55aa30..000000000 --- a/patches/react-native+0.74.1.patch +++ /dev/null @@ -1,283 +0,0 @@ -diff --git a/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm b/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm -index caa5540..c5d4e67 100644 ---- a/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm -+++ b/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm -@@ -73,7 +73,7 @@ @implementation RCTFileReaderModule - } else { - NSString *type = [RCTConvert NSString:blob[@"type"]]; - NSString *text = [NSString stringWithFormat:@"data:%@;base64,%@", -- type != nil && [type length] > 0 ? type : @"application/octet-stream", -+ ![type isEqual:[NSNull null]] && [type length] > 0 ? type : @"application/octet-stream", - [data base64EncodedStringWithOptions:0]]; - - resolve(text); -diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm -index b0d71dc..41b9a0e 100644 ---- a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm -+++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm -@@ -377,10 +377,6 @@ - (void)textInputDidBeginEditing - self.backedTextInputView.attributedText = [NSAttributedString new]; - } - -- if (_selectTextOnFocus) { -- [self.backedTextInputView selectAll:nil]; -- } -- - [_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus - reactTag:self.reactTag - text:[self.backedTextInputView.attributedText.string copy] -@@ -611,6 +607,10 @@ - (UIView *)reactAccessibilityElement - - (void)reactFocus - { - [self.backedTextInputView reactFocus]; -+ -+ if (_selectTextOnFocus) { -+ [self.backedTextInputView selectAll:nil]; -+ } - } - - - (void)reactBlur -diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h -index e9b330f..ec5f58c 100644 ---- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h -+++ b/node_modules/react-native/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/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m -index b09e653..d2b4e05 100644 ---- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m -+++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m -@@ -22,6 +22,7 @@ @implementation RCTRefreshControl { - NSString *_title; - UIColor *_titleColor; - CGFloat _progressViewOffset; -+ UIColor *_customTintColor; - } - - - (instancetype)init -@@ -56,6 +57,12 @@ - (void)layoutSubviews - _isInitialRender = false; - } - -+- (void)didMoveToSuperview -+{ -+ [super didMoveToSuperview]; -+ [self setTintColor:_customTintColor]; -+} -+ - - (void)beginRefreshingProgrammatically - { - UInt64 beginRefreshingTimestamp = _currentRefreshingStateTimestamp; -@@ -203,4 +210,58 @@ - (void)refreshControlValueChanged - } - } - -+- (void)setCustomTintColor:(UIColor *)customTintColor -+{ -+ _customTintColor = customTintColor; -+ [self setTintColor:customTintColor]; -+} -+ -+// 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/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m -index 40aaf9c..1c60164 100644 ---- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m -+++ b/node_modules/react-native/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<NSNumber *, UIView *> *viewRegistry) { -diff --git a/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m -index 1aead51..39e6244 100644 ---- a/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m -+++ b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m -@@ -159,31 +159,6 @@ - (BOOL)touchesShouldCancelInContentView:(__unused UIView *)view - return !shouldDisableScrollInteraction; - } - --/* -- * Automatically centers the content such that if the content is smaller than the -- * ScrollView, we force it to be centered, but when you zoom or the content otherwise -- * becomes larger than the ScrollView, there is no padding around the content but it -- * can still fill the whole view. -- */ --- (void)setContentOffset:(CGPoint)contentOffset --{ -- UIView *contentView = [self contentView]; -- if (contentView && _centerContent && !CGSizeEqualToSize(contentView.frame.size, CGSizeZero)) { -- CGSize subviewSize = contentView.frame.size; -- CGSize scrollViewSize = self.bounds.size; -- if (subviewSize.width <= scrollViewSize.width) { -- contentOffset.x = -(scrollViewSize.width - subviewSize.width) / 2.0; -- } -- if (subviewSize.height <= scrollViewSize.height) { -- contentOffset.y = -(scrollViewSize.height - subviewSize.height) / 2.0; -- } -- } -- -- super.contentOffset = CGPointMake( -- RCTSanitizeNaNValue(contentOffset.x, @"scrollView.contentOffset.x"), -- RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y")); --} -- - - (void)setFrame:(CGRect)frame - { - // Preserving and revalidating `contentOffset`. -@@ -427,6 +402,11 @@ - (void)setRemoveClippedSubviews:(__unused BOOL)removeClippedSubviews - // Does nothing - } - -+- (void)setFrame:(CGRect)frame { -+ [super setFrame:frame]; -+ [self centerContentIfNeeded]; -+} -+ - - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex - { - [super insertReactSubview:view atIndex:atIndex]; -@@ -444,6 +424,8 @@ - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex - RCTApplyTransformationAccordingLayoutDirection(_contentView, self.reactLayoutDirection); - [_scrollView addSubview:view]; - } -+ -+ [self centerContentIfNeeded]; - } - - - (void)removeReactSubview:(UIView *)subview -@@ -652,9 +634,46 @@ -(void)delegateMethod : (UIScrollView *)scrollView \ - } - - RCT_SCROLL_EVENT_HANDLER(scrollViewWillBeginDecelerating, onMomentumScrollBegin) --RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll) - RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop) - -+-(void)scrollViewDidZoom : (UIScrollView *)scrollView -+{ -+ [self centerContentIfNeeded]; -+ -+ RCT_SEND_SCROLL_EVENT(onScroll, nil); -+ RCT_FORWARD_SCROLL_EVENT(scrollViewDidZoom : scrollView); -+} -+ -+/* -+ * Automatically centers the content such that if the content is smaller than the -+ * ScrollView, we force it to be centered, but when you zoom or the content otherwise -+ * becomes larger than the ScrollView, there is no padding around the content but it -+ * can still fill the whole view. -+ * -+ * PATCHED: This deviates from the original React Native implementation to fix two issues: -+ * -+ * - The scroll view was swallowing any taps immediately after pinching -+ * - The scroll view was jittering when crossing the full screen threshold while pinching -+ * -+ * This implementation is based on https://petersteinberger.com/blog/2013/how-to-center-uiscrollview/. -+ */ -+-(void)centerContentIfNeeded -+{ -+ if (_scrollView.centerContent && -+ !CGSizeEqualToSize(self.contentSize, CGSizeZero) && -+ !CGSizeEqualToSize(self.bounds.size, CGSizeZero) -+ ) { -+ CGFloat top = 0, left = 0; -+ if (self.contentSize.width < self.bounds.size.width) { -+ left = (self.bounds.size.width - self.contentSize.width) * 0.5f; -+ } -+ if (self.contentSize.height < self.bounds.size.height) { -+ top = (self.bounds.size.height - self.contentSize.height) * 0.5f; -+ } -+ _scrollView.contentInset = UIEdgeInsetsMake(top, left, top, left); -+ } -+} -+ - - (void)addScrollListener:(NSObject<UIScrollViewDelegate> *)scrollListener - { - [_scrollListeners addObject:scrollListener]; -@@ -913,6 +932,7 @@ - (void)updateContentSizeIfNeeded - CGSize contentSize = self.contentSize; - if (!CGSizeEqualToSize(_scrollView.contentSize, contentSize)) { - _scrollView.contentSize = contentSize; -+ [self centerContentIfNeeded]; - } - } - -diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java -index 5f5e1ab..aac00b6 100644 ---- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java -+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.java -@@ -99,8 +99,9 @@ public class JavaTimerManager { - } - - // If the JS thread is busy for multiple frames we cancel any other pending runnable. -- if (mCurrentIdleCallbackRunnable != null) { -- mCurrentIdleCallbackRunnable.cancel(); -+ IdleCallbackRunnable currentRunnable = mCurrentIdleCallbackRunnable; -+ if (currentRunnable != null) { -+ currentRunnable.cancel(); - } - - mCurrentIdleCallbackRunnable = new IdleCallbackRunnable(frameTimeNanos); diff --git a/patches/react-native+0.76.3.patch b/patches/react-native+0.76.3.patch new file mode 100644 index 000000000..6f71097a1 --- /dev/null +++ b/patches/react-native+0.76.3.patch @@ -0,0 +1,331 @@ +diff --git a/node_modules/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts b/node_modules/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts +index 62f52a7..ca30165 100644 +--- a/node_modules/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts ++++ b/node_modules/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts +@@ -426,9 +426,10 @@ export interface ViewStyle extends FlexStyle, ShadowStyleIOS, TransformsStyle { + */ + pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto' | undefined; + isolation?: 'auto' | 'isolate' | undefined; +- cursor?: CursorValue | undefined; ++ cursor?: CursorValue | string | undefined; + boxShadow?: ReadonlyArray<BoxShadowValue> | string | undefined; + filter?: ReadonlyArray<FilterFunction> | string | undefined; ++ transformOrigin?: string | (string | number)[] | undefined; + } + + export type FontVariant = +@@ -536,7 +537,11 @@ export interface TextStyle extends TextStyleIOS, TextStyleAndroid, ViewStyle { + textShadowOffset?: {width: number; height: number} | undefined; + textShadowRadius?: number | undefined; + textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase' | undefined; +- userSelect?: 'auto' | 'none' | 'text' | 'contain' | 'all' | undefined; ++ userSelect?: 'auto' | 'none' | 'text' | 'contain' | 'all' | string | undefined; ++ cursor?: CursorValue | string | undefined; ++ boxShadow?: ReadonlyArray<BoxShadowValue> | string | undefined; ++ filter?: ReadonlyArray<FilterFunction> | string | undefined; ++ transformOrigin?: string | (string | number)[] | undefined; + } + + /** +@@ -558,5 +563,8 @@ export interface ImageStyle extends FlexStyle, ShadowStyleIOS, TransformsStyle { + tintColor?: ColorValue | undefined; + opacity?: AnimatableNumericValue | undefined; + objectFit?: 'cover' | 'contain' | 'fill' | 'scale-down' | undefined; +- cursor?: CursorValue | undefined; ++ cursor?: CursorValue | string | undefined; ++ boxShadow?: ReadonlyArray<BoxShadowValue> | string | undefined; ++ filter?: ReadonlyArray<FilterFunction> | string | undefined; ++ transformOrigin?: string | (string | number)[] | undefined; + } +diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +index 93af874..106f8ec 100644 +--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm ++++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +@@ -66,28 +66,51 @@ - (void)preserveContentOffsetWithBlock:(void (^)())block + * ScrollView, we force it to be centered, but when you zoom or the content otherwise + * becomes larger than the ScrollView, there is no padding around the content but it + * can still fill the whole view. ++ * This implementation is based on https://petersteinberger.com/blog/2013/how-to-center-uiscrollview/. + */ +-- (void)setContentOffset:(CGPoint)contentOffset ++-(void)centerContentIfNeeded + { +- if (_isSetContentOffsetDisabled) { ++ if (!_centerContent) { + return; + } + +- if (_centerContent && !CGSizeEqualToSize(self.contentSize, CGSizeZero)) { +- CGSize scrollViewSize = self.bounds.size; +- if (self.contentSize.width <= scrollViewSize.width) { +- contentOffset.x = -(scrollViewSize.width - self.contentSize.width) / 2.0; +- } +- if (self.contentSize.height <= scrollViewSize.height) { +- contentOffset.y = -(scrollViewSize.height - self.contentSize.height) / 2.0; +- } ++ CGSize contentSize = self.contentSize; ++ CGSize boundsSize = self.bounds.size; ++ if (CGSizeEqualToSize(contentSize, CGSizeZero) || ++ CGSizeEqualToSize(boundsSize, CGSizeZero)) { ++ return; + } + ++ CGFloat top = 0, left = 0; ++ if (contentSize.width < boundsSize.width) { ++ left = (boundsSize.width - contentSize.width) * 0.5f; ++ } ++ if (contentSize.height < boundsSize.height) { ++ top = (boundsSize.height - contentSize.height) * 0.5f; ++ } ++ self.contentInset = UIEdgeInsetsMake(top, left, top, left); ++} ++ ++- (void)setContentOffset:(CGPoint)contentOffset ++{ ++ if (_isSetContentOffsetDisabled) { ++ return; ++ } + super.contentOffset = CGPointMake( + RCTSanitizeNaNValue(contentOffset.x, @"scrollView.contentOffset.x"), + RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y")); + } + ++- (void)setFrame:(CGRect)frame { ++ [super setFrame:frame]; ++ [self centerContentIfNeeded]; ++} ++ ++- (void)didAddSubview:(UIView *)subview { ++ [super didAddSubview:subview]; ++ [self centerContentIfNeeded]; ++} ++ + - (BOOL)touchesShouldCancelInContentView:(UIView *)view + { + if ([_overridingDelegate respondsToSelector:@selector(touchesShouldCancelInContentView:)]) { +@@ -257,6 +280,10 @@ - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView + } + } + ++- (void)scrollViewDidZoom:(__unused UIScrollView *)scrollView { ++ [self centerContentIfNeeded]; ++} ++ + #pragma mark - + + - (BOOL)isHorizontal:(UIScrollView *)scrollView +diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h +index e9b330f..ec5f58c 100644 +--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.h ++++ b/node_modules/react-native/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/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m +index 53bfd04..ff1b1ed 100644 +--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m ++++ b/node_modules/react-native/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/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m +index 40aaf9c..1c60164 100644 +--- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControlManager.m ++++ b/node_modules/react-native/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<NSNumber *, UIView *> *viewRegistry) { +diff --git a/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m +index e9ce48c..ccd9ad6 100644 +--- a/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m ++++ b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m +@@ -159,26 +159,8 @@ - (BOOL)touchesShouldCancelInContentView:(__unused UIView *)view + return !shouldDisableScrollInteraction; + } + +-/* +- * Automatically centers the content such that if the content is smaller than the +- * ScrollView, we force it to be centered, but when you zoom or the content otherwise +- * becomes larger than the ScrollView, there is no padding around the content but it +- * can still fill the whole view. +- */ + - (void)setContentOffset:(CGPoint)contentOffset + { +- UIView *contentView = [self contentView]; +- if (contentView && _centerContent && !CGSizeEqualToSize(contentView.frame.size, CGSizeZero)) { +- CGSize subviewSize = contentView.frame.size; +- CGSize scrollViewSize = self.bounds.size; +- if (subviewSize.width <= scrollViewSize.width) { +- contentOffset.x = -(scrollViewSize.width - subviewSize.width) / 2.0; +- } +- if (subviewSize.height <= scrollViewSize.height) { +- contentOffset.y = -(scrollViewSize.height - subviewSize.height) / 2.0; +- } +- } +- + super.contentOffset = CGPointMake( + RCTSanitizeNaNValue(contentOffset.x, @"scrollView.contentOffset.x"), + RCTSanitizeNaNValue(contentOffset.y, @"scrollView.contentOffset.y")); +@@ -427,6 +409,11 @@ - (void)setRemoveClippedSubviews:(__unused BOOL)removeClippedSubviews + // Does nothing + } + ++- (void)setFrame:(CGRect)frame { ++ [super setFrame:frame]; ++ [self centerContentIfNeeded]; ++} ++ + - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex + { + [super insertReactSubview:view atIndex:atIndex]; +@@ -443,6 +430,8 @@ - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex + _contentView = view; + RCTApplyTransformationAccordingLayoutDirection(_contentView, self.reactLayoutDirection); + [_scrollView addSubview:view]; ++ ++ [self centerContentIfNeeded]; + } + } + +@@ -652,9 +641,46 @@ -(void)delegateMethod : (UIScrollView *)scrollView \ + } + + RCT_SCROLL_EVENT_HANDLER(scrollViewWillBeginDecelerating, onMomentumScrollBegin) +-RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll) + RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop) + ++-(void)scrollViewDidZoom : (UIScrollView *)scrollView ++{ ++ [self centerContentIfNeeded]; ++ ++ RCT_SEND_SCROLL_EVENT(onScroll, nil); ++ RCT_FORWARD_SCROLL_EVENT(scrollViewDidZoom : scrollView); ++} ++ ++/* ++ * Automatically centers the content such that if the content is smaller than the ++ * ScrollView, we force it to be centered, but when you zoom or the content otherwise ++ * becomes larger than the ScrollView, there is no padding around the content but it ++ * can still fill the whole view. ++ * This implementation is based on https://petersteinberger.com/blog/2013/how-to-center-uiscrollview/. ++ */ ++-(void)centerContentIfNeeded ++{ ++ if (!_scrollView.centerContent) { ++ return; ++ } ++ ++ CGSize contentSize = self.contentSize; ++ CGSize boundsSize = self.bounds.size; ++ if (CGSizeEqualToSize(contentSize, CGSizeZero) || ++ CGSizeEqualToSize(boundsSize, CGSizeZero)) { ++ return; ++ } ++ ++ CGFloat top = 0, left = 0; ++ if (contentSize.width < boundsSize.width) { ++ left = (boundsSize.width - contentSize.width) * 0.5f; ++ } ++ if (contentSize.height < boundsSize.height) { ++ top = (boundsSize.height - contentSize.height) * 0.5f; ++ } ++ _scrollView.contentInset = UIEdgeInsetsMake(top, left, top, left); ++} ++ + - (void)addScrollListener:(NSObject<UIScrollViewDelegate> *)scrollListener + { + [_scrollListeners addObject:scrollListener]; +@@ -933,6 +959,7 @@ - (void)updateContentSizeIfNeeded + CGSize contentSize = self.contentSize; + if (!CGSizeEqualToSize(_scrollView.contentSize, contentSize)) { + _scrollView.contentSize = contentSize; ++ [self centerContentIfNeeded]; + } + } + diff --git a/patches/react-native+0.74.1.patch.md b/patches/react-native+0.76.3.patch.md index 84953df2b..eacb9f267 100644 --- a/patches/react-native+0.74.1.patch.md +++ b/patches/react-native+0.76.3.patch.md @@ -12,9 +12,6 @@ Patching `RCTRefreshControl.m` and `RCTRefreshControl.h` to add a new `forwarder This method is used by `ExpoScrollForwarder` to initiate a refresh of the underlying `UIScrollView` from inside that module. +## ScrollView centerContent fix -## TextInput Patch - `selectTextOnFocus` fix - -Patching `RCTBaseTextInputView.m` to fix an issue where `selectTextOnFocus` does not work as expected on iOS 17. This -patch _only_ fixes the Paper version of `TextInput`. If we migrate to Fabric and the fix has not been made upstream, -we can apply the same fix. See https://github.com/facebook/react-native/pull/44307. +Includes https://github.com/facebook/react-native/pull/47591 early. Delete when it's in a release. diff --git a/patches/react-native-image-crop-picker+0.41.2.patch b/patches/react-native-image-crop-picker+0.41.2.patch deleted file mode 100644 index 7f1865a5e..000000000 --- a/patches/react-native-image-crop-picker+0.41.2.patch +++ /dev/null @@ -1,39 +0,0 @@ -diff --git a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java -index 5de0845..fa477e7 100644 ---- a/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java -+++ b/node_modules/react-native-image-crop-picker/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java -@@ -21,6 +21,7 @@ import android.webkit.MimeTypeMap; - - import androidx.core.app.ActivityCompat; - import androidx.core.content.FileProvider; -+import androidx.exifinterface.media.ExifInterface; - - import com.facebook.react.bridge.ActivityEventListener; - import com.facebook.react.bridge.Callback; -@@ -678,6 +679,15 @@ class PickerModule extends ReactContextBaseJavaModule implements ActivityEventLi - throw new Exception("Cannot select remote files"); - } - BitmapFactory.Options original = validateImage(path); -+ ExifInterface originalExif = new ExifInterface(path); -+ int orientation = originalExif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1); -+ boolean invertDimensions = ( -+ orientation == ExifInterface.ORIENTATION_ROTATE_90 || -+ orientation == ExifInterface.ORIENTATION_ROTATE_270 || -+ orientation == ExifInterface.ORIENTATION_TRANSPOSE || -+ orientation == ExifInterface.ORIENTATION_TRANSVERSE -+ ); -+ - - // if compression options are provided image will be compressed. If none options is provided, - // then original image will be returned -@@ -687,8 +697,8 @@ class PickerModule extends ReactContextBaseJavaModule implements ActivityEventLi - long modificationDate = new File(path).lastModified(); - - image.putString("path", "file://" + compressedImagePath); -- image.putInt("width", options.outWidth); -- image.putInt("height", options.outHeight); -+ image.putInt("width", invertDimensions ? options.outHeight : options.outWidth); -+ image.putInt("height", invertDimensions ? options.outWidth : options.outHeight); - image.putString("mime", options.outMimeType); - image.putInt("size", (int) new File(compressedImagePath).length()); - image.putString("modificationDate", String.valueOf(modificationDate)); diff --git a/patches/react-native-svg+15.3.0.patch b/patches/react-native-svg+15.9.0.patch index 54540023f..54540023f 100644 --- a/patches/react-native-svg+15.3.0.patch +++ b/patches/react-native-svg+15.9.0.patch |