diff --git a/__mocks__/multiformats/cid.js b/__mocks__/multiformats/cid.js new file mode 100644 index 0000000000..788505c189 --- /dev/null +++ b/__mocks__/multiformats/cid.js @@ -0,0 +1,3 @@ +export const CID = jest.fn().mockImplementation(() => { + return {} +}) diff --git a/__mocks__/multiformats/hashes/hasher.js b/__mocks__/multiformats/hashes/hasher.js new file mode 100644 index 0000000000..a525f1310c --- /dev/null +++ b/__mocks__/multiformats/hashes/hasher.js @@ -0,0 +1,7 @@ +export const from = jest.fn().mockImplementation(() => { + return { + digest: jest.fn().mockImplementation(() => { + return Promise.resolve('') + }), + } +}) diff --git a/__tests__/lib/string.test.ts b/__tests__/lib/string.test.ts index e212ef5c9b..b018da2361 100644 --- a/__tests__/lib/string.test.ts +++ b/__tests__/lib/string.test.ts @@ -1,11 +1,11 @@ import {RichText} from '@atproto/api' -import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player' +import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player' import { createStarterPackGooglePlayUri, createStarterPackLinkFromAndroidReferrer, parseStarterPackUri, -} from 'lib/strings/starter-pack' +} from '#/lib/strings/starter-pack' import {cleanError} from '../../src/lib/strings/errors' import {createFullHandle, makeValidHandle} from '../../src/lib/strings/handles' import {enforceLen} from '../../src/lib/strings/helpers' diff --git a/app.config.js b/app.config.js index 0e30313294..dae8ca38cc 100644 --- a/app.config.js +++ b/app.config.js @@ -193,6 +193,7 @@ module.exports = function (_config) { plugins: [ 'expo-video', 'expo-localization', + 'expo-web-browser', [ 'react-native-edge-to-edge', {android: {enforceNavigationBarContrast: false}}, @@ -242,10 +243,8 @@ module.exports = function (_config) { './plugins/withAndroidStylesAccentColorPlugin.js', './plugins/withAndroidDayNightThemePlugin.js', './plugins/withAndroidNoJitpackPlugin.js', - './plugins/withNoBundleCompression.js', './plugins/shareExtension/withShareExtensions.js', './plugins/notificationsExtension/withNotificationsExtension.js', - './plugins/withAppDelegateReferrer.js', [ 'expo-font', { diff --git a/babel.config.js b/babel.config.js index ac872648fe..43e7eb54f3 100644 --- a/babel.config.js +++ b/babel.config.js @@ -17,7 +17,7 @@ module.exports = function (api) { ], plugins: [ 'macros', - ['babel-plugin-react-compiler', {target: '18'}], + ['babel-plugin-react-compiler', {target: '19'}], [ 'module:react-native-dotenv', { @@ -37,10 +37,6 @@ module.exports = function (api) { alias: { // This needs to be mirrored in tsconfig.json '#': './src', - lib: './src/lib', - platform: './src/platform', - state: './src/state', - view: './src/view', crypto: './src/platform/crypto.ts', }, }, diff --git a/docs/build.md b/docs/build.md index 7a29ab3553..7817dd0957 100644 --- a/docs/build.md +++ b/docs/build.md @@ -15,7 +15,7 @@ This is NOT required when developing for web. - Set up your environment [using the expo instructions](https://docs.expo.dev/guides/local-app-development/). - make sure that the JAVA_HOME points to the zulu-17 directory in your `.zshrc` or `.bashrc` file: `export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home`. DO NOT use another JDK or you will encounter build errors. -- If you're running macOS, make sure you are running the correct versions of Ruby and Cocoapods:- +- If you're running macOS, make sure you are running the correct versions of Ruby and Cocoapods:- - If you are using Apple Silicon and this is the first time you are building for RN 0.74+, you may need to run: - `arch -arm64 brew install llvm` - `sudo gem install ffi` @@ -33,7 +33,7 @@ This is NOT required when developing for web. - After initial setup: - Copy `google-services.json.example` to `google-services.json` or provide your own `google-services.json`. (A real firebase project is NOT required) - `npx expo prebuild` -> you will also need to run this anytime `app.json` or native `package.json` deps change - + ### Running the Native App - iOS: `yarn ios` @@ -162,6 +162,9 @@ See [testing.md](./testing.md). `./platform/polyfills.*.ts` adds polyfills to the environment. Currently, this includes: - TextEncoder / TextDecoder +- react-native-url-polyfill +- Array#findLast (on web) +- atob (on native) ### Sentry sourcemaps diff --git a/metro.config.js b/metro.config.js index b180813363..dcdead3f5d 100644 --- a/metro.config.js +++ b/metro.config.js @@ -16,22 +16,21 @@ if (process.env.BSKY_PROFILE) { cfg.resolver.assetExts = [...cfg.resolver.assetExts, 'woff2'] +// Enabled by default in RN 0.79+, but this breaks Lingui + others +cfg.resolver.unstable_enablePackageExports = false + cfg.resolver.resolveRequest = (context, moduleName, platform) => { // HACK: manually resolve a few packages that use `exports` in `package.json`. // A proper solution is to enable `unstable_enablePackageExports` but this needs careful testing. if (moduleName.startsWith('multiformats/hashes/hasher')) { return context.resolveRequest( context, - 'multiformats/dist/src/hashes/hasher', + 'multiformats/cjs/src/hashes/hasher', platform, ) } if (moduleName.startsWith('multiformats/cid')) { - return context.resolveRequest( - context, - 'multiformats/dist/src/cid', - platform, - ) + return context.resolveRequest(context, 'multiformats/cjs/src/cid', platform) } if (moduleName === '@ipld/dag-cbor') { return context.resolveRequest(context, '@ipld/dag-cbor/src', platform) diff --git a/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt b/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt index 5df163b640..31eb739a66 100644 --- a/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt +++ b/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt @@ -317,6 +317,9 @@ class BottomSheetView( // View overrides to pass to DialogRootViewGroup instead override fun dispatchProvideStructure(structure: ViewStructure?) { + if (structure == null) { + return + } dialogRootViewGroup.dispatchProvideStructure(structure) } diff --git a/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/DialogRootViewGroup.kt b/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/DialogRootViewGroup.kt index c022924e99..4b02bae9f8 100644 --- a/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/DialogRootViewGroup.kt +++ b/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/DialogRootViewGroup.kt @@ -139,19 +139,7 @@ class DialogRootViewGroup( return super.onHoverEvent(event) } - @Deprecated("Deprecated in Java") - override fun onChildStartedNativeGesture(ev: MotionEvent?) { - eventDispatcher?.let { - if (ev != null) { - jSTouchDispatcher.onChildStartedNativeGesture(ev, it) - } - } - } - - override fun onChildStartedNativeGesture( - childView: View, - ev: MotionEvent, - ) { + override fun onChildStartedNativeGesture(childView: View?, ev: MotionEvent) { eventDispatcher?.let { jSTouchDispatcher.onChildStartedNativeGesture(ev, it) } jSPointerDispatcher?.onChildStartedNativeGesture(childView, ev, eventDispatcher) } diff --git a/modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifView.podspec b/modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifView.podspec index 352812640b..7640a5fc7c 100644 --- a/modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifView.podspec +++ b/modules/expo-bluesky-gif-view/ios/ExpoBlueskyGifView.podspec @@ -10,7 +10,7 @@ Pod::Spec.new do |s| s.static_framework = true s.dependency 'ExpoModulesCore' - s.dependency 'SDWebImage', '~> 5.19.1' + s.dependency 'SDWebImage', '~> 5.21.0' s.dependency 'SDWebImageWebPCoder', '~> 0.14.6' # Swift/Objective-C compatibility diff --git a/modules/expo-emoji-picker/LICENSE b/modules/expo-emoji-picker/LICENSE new file mode 100644 index 0000000000..aa0254ccbe --- /dev/null +++ b/modules/expo-emoji-picker/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2025 Alan Hughes + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/modules/expo-emoji-picker/README.md b/modules/expo-emoji-picker/README.md new file mode 100644 index 0000000000..d2fb3ae050 --- /dev/null +++ b/modules/expo-emoji-picker/README.md @@ -0,0 +1,3 @@ +# expo-emoji-picker + +Based on [react-native-emoji-popup](https://github.com/okwasniewski/react-native-emoji-popup) and [expo-emoji-picker](https://github.com/alanjhughes/expo-emoji-picker) diff --git a/modules/expo-emoji-picker/android/build.gradle b/modules/expo-emoji-picker/android/build.gradle new file mode 100644 index 0000000000..d34288dc79 --- /dev/null +++ b/modules/expo-emoji-picker/android/build.gradle @@ -0,0 +1,46 @@ +apply plugin: 'com.android.library' + +group = 'expo.community.modules.emojipicker' +version = '0.1.0' + +def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle") +apply from: expoModulesCorePlugin +applyKotlinExpoModulesCorePlugin() +useCoreDependencies() +useExpoPublishing() + +// If you want to use the managed Android SDK versions from expo-modules-core, set this to true. +// The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code. +// Most of the time, you may like to manage the Android SDK versions yourself. +def useManagedAndroidSdkVersions = false +if (useManagedAndroidSdkVersions) { + useDefaultAndroidSdkVersions() +} else { + buildscript { + // Simple helper that allows the root project to override versions declared by this library. + ext.safeExtGet = { prop, fallback -> + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback + } + } + project.android { + compileSdkVersion safeExtGet("compileSdkVersion", 34) + defaultConfig { + minSdkVersion safeExtGet("minSdkVersion", 21) + targetSdkVersion safeExtGet("targetSdkVersion", 34) + } + } +} + +android { + namespace "expo.community.modules.emojipicker" + defaultConfig { + versionCode 1 + versionName "0.1.0" + } + lintOptions { + abortOnError false + } + dependencies { + implementation "androidx.emoji2:emoji2-emojipicker:1.5.0" + } +} diff --git a/modules/expo-emoji-picker/android/src/main/AndroidManifest.xml b/modules/expo-emoji-picker/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..bdae66c8f5 --- /dev/null +++ b/modules/expo-emoji-picker/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/modules/expo-emoji-picker/android/src/main/java/expo/community/modules/emojipicker/EmojiPickerModule.kt b/modules/expo-emoji-picker/android/src/main/java/expo/community/modules/emojipicker/EmojiPickerModule.kt new file mode 100644 index 0000000000..ebdc7a35e4 --- /dev/null +++ b/modules/expo-emoji-picker/android/src/main/java/expo/community/modules/emojipicker/EmojiPickerModule.kt @@ -0,0 +1,15 @@ +package expo.community.modules.emojipicker + +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition +import java.net.URL + +class EmojiPickerModule : Module() { + override fun definition() = ModuleDefinition { + Name("EmojiPicker") + + View(EmojiPickerModuleView::class) { + Events("onEmojiSelected") + } + } +} diff --git a/modules/expo-emoji-picker/android/src/main/java/expo/community/modules/emojipicker/EmojiPickerModuleView.kt b/modules/expo-emoji-picker/android/src/main/java/expo/community/modules/emojipicker/EmojiPickerModuleView.kt new file mode 100644 index 0000000000..a6e5a066fe --- /dev/null +++ b/modules/expo-emoji-picker/android/src/main/java/expo/community/modules/emojipicker/EmojiPickerModuleView.kt @@ -0,0 +1,40 @@ +package expo.community.modules.emojipicker + +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.Configuration +import androidx.emoji2.emojipicker.EmojiPickerView +import expo.modules.kotlin.AppContext +import expo.modules.kotlin.viewevent.EventDispatcher +import expo.modules.kotlin.views.ExpoView + + +@SuppressLint("ViewConstructor") +class EmojiPickerModuleView(context: Context, appContext: AppContext) : + ExpoView(context, appContext) { + private var emojiView: EmojiPickerView = EmojiPickerView(context) + private val onEmojiSelected by EventDispatcher() + + init { + setupView() + } + + private fun setupView() { + addView( + emojiView, LayoutParams( + LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT + ) + ) + + emojiView.setOnEmojiPickedListener { emoji -> + onEmojiSelected(mapOf("emoji" to emoji.emoji)) + } + } + + override fun onConfigurationChanged(newConfig: Configuration?) { + super.onConfigurationChanged(newConfig) + removeView(emojiView) + setupView() + } +} diff --git a/modules/expo-emoji-picker/expo-module.config.json b/modules/expo-emoji-picker/expo-module.config.json new file mode 100644 index 0000000000..7576240d10 --- /dev/null +++ b/modules/expo-emoji-picker/expo-module.config.json @@ -0,0 +1,9 @@ +{ + "platforms": ["apple", "android"], + "apple": { + "modules": ["EmojiPickerModule"] + }, + "android": { + "modules": ["expo.community.modules.emojipicker.EmojiPickerModule"] + } +} diff --git a/modules/expo-emoji-picker/index.ts b/modules/expo-emoji-picker/index.ts new file mode 100644 index 0000000000..a555b730ab --- /dev/null +++ b/modules/expo-emoji-picker/index.ts @@ -0,0 +1,3 @@ +export {default as EmojiPicker} from './src/EmojiPicker' +export {default} from './src/EmojiPickerModule' +export * from './src/EmojiPickerModule.types' diff --git a/modules/expo-emoji-picker/ios/EmojiPickerModule.podspec b/modules/expo-emoji-picker/ios/EmojiPickerModule.podspec new file mode 100644 index 0000000000..e6a982a70e --- /dev/null +++ b/modules/expo-emoji-picker/ios/EmojiPickerModule.podspec @@ -0,0 +1,25 @@ +Pod::Spec.new do |s| + s.name = 'EmojiPickerModule' + s.version = '1.0.0' + s.summary = 'An emoji picker for use in Bluesky' + s.description = 'An emoji picker for use in Bluesky' + s.author = 'alanjhughes' + s.homepage = 'https://github.com/bluesky-social/social-app' + s.platforms = { + :ios => '15.1', + :tvos => '15.1' + } + s.source = { git: '' } + s.swift_version = '5.4' + s.static_framework = true + + s.dependency 'ExpoModulesCore' + s.dependency 'MCEmojiPicker' + + # Swift/Objective-C compatibility + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + } + + s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}" +end diff --git a/modules/expo-emoji-picker/ios/EmojiPickerModule.swift b/modules/expo-emoji-picker/ios/EmojiPickerModule.swift new file mode 100644 index 0000000000..57b6f959a9 --- /dev/null +++ b/modules/expo-emoji-picker/ios/EmojiPickerModule.swift @@ -0,0 +1,11 @@ +import ExpoModulesCore + +public class EmojiPickerModule: Module { + public func definition() -> ModuleDefinition { + Name("EmojiPicker") + + View(EmojiPickerView.self) { + Events("onEmojiSelected") + } + } +} diff --git a/modules/expo-emoji-picker/ios/EmojiPickerView.swift b/modules/expo-emoji-picker/ios/EmojiPickerView.swift new file mode 100644 index 0000000000..65089a2af6 --- /dev/null +++ b/modules/expo-emoji-picker/ios/EmojiPickerView.swift @@ -0,0 +1,30 @@ +import ExpoModulesCore +import WebKit +import MCEmojiPicker + +class EmojiPickerView: ExpoView, MCEmojiPickerDelegate { + let onEmojiSelected = EventDispatcher() + + override func layoutSubviews() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) + self.addGestureRecognizer(tapGesture) + } + + @objc func handleTap(_ gesture: UITapGestureRecognizer) { + presentEmojiPicker() + } + + func presentEmojiPicker() { + let emojiPicker = MCEmojiPickerViewController() + let reactRootVC = reactViewController() + emojiPicker.sourceView = self + emojiPicker.delegate = self + reactRootVC?.present(emojiPicker, animated: true) + } + + func didGetEmoji(emoji: String) { + onEmojiSelected([ + "emoji": emoji + ]) + } +} diff --git a/modules/expo-emoji-picker/src/EmojiPicker.android.tsx b/modules/expo-emoji-picker/src/EmojiPicker.android.tsx new file mode 100644 index 0000000000..d8493b72e3 --- /dev/null +++ b/modules/expo-emoji-picker/src/EmojiPicker.android.tsx @@ -0,0 +1,28 @@ +import {useMemo} from 'react' +import {useColorScheme} from 'react-native' + +import {type EmojiPickerViewProps} from './EmojiPickerModule.types' +import EmojiPickerNativeView from './EmojiPickerView' + +const EmojiPicker = ({onEmojiSelected}: EmojiPickerViewProps) => { + const scheme = useColorScheme() + const styles = useMemo( + () => + ({ + flex: 1, + width: '100%', + backgroundColor: scheme === 'dark' ? '#000' : '#fff', + } as const), + [scheme], + ) + + return ( + { + onEmojiSelected(emoji) + }} + style={styles} + /> + ) +} +export default EmojiPicker diff --git a/modules/expo-emoji-picker/src/EmojiPicker.tsx b/modules/expo-emoji-picker/src/EmojiPicker.tsx new file mode 100644 index 0000000000..8fee11df78 --- /dev/null +++ b/modules/expo-emoji-picker/src/EmojiPicker.tsx @@ -0,0 +1,15 @@ +import {type EmojiPickerViewProps} from './EmojiPickerModule.types' +import EmojiPickerNativeView from './EmojiPickerView' + +const EmojiPicker = ({children, onEmojiSelected}: EmojiPickerViewProps) => { + return ( + { + onEmojiSelected(emoji) + }}> + {children} + + ) +} + +export default EmojiPicker diff --git a/modules/expo-emoji-picker/src/EmojiPickerModule.ts b/modules/expo-emoji-picker/src/EmojiPickerModule.ts new file mode 100644 index 0000000000..a5b04e662a --- /dev/null +++ b/modules/expo-emoji-picker/src/EmojiPickerModule.ts @@ -0,0 +1,5 @@ +import {NativeModule, requireNativeModule} from 'expo' + +declare class EmojiPickerModule extends NativeModule {} + +export default requireNativeModule('EmojiPicker') diff --git a/modules/expo-emoji-picker/src/EmojiPickerModule.types.ts b/modules/expo-emoji-picker/src/EmojiPickerModule.types.ts new file mode 100644 index 0000000000..14690a38a6 --- /dev/null +++ b/modules/expo-emoji-picker/src/EmojiPickerModule.types.ts @@ -0,0 +1,20 @@ +import {type ViewProps} from 'react-native' + +export type EmojiSelectionListener = (event: { + nativeEvent: SelectionEvent +}) => void + +export type SelectionEvent = { + emoji: string +} + +export type EmojiPickerViewProps = ViewProps & { + /* + * Callback that will be called when an emoji is selected. + */ + onEmojiSelected: (emoji: string) => void +} + +export type EmojiPickerNativeViewProps = ViewProps & { + onEmojiSelected: EmojiSelectionListener +} diff --git a/modules/expo-emoji-picker/src/EmojiPickerView.tsx b/modules/expo-emoji-picker/src/EmojiPickerView.tsx new file mode 100644 index 0000000000..0dff25fb55 --- /dev/null +++ b/modules/expo-emoji-picker/src/EmojiPickerView.tsx @@ -0,0 +1,21 @@ +import {requireNativeView} from 'expo' +import type * as React from 'react' + +import { + type EmojiPickerNativeViewProps, + type EmojiPickerViewProps, +} from './EmojiPickerModule.types' + +const NativeView: React.ComponentType = + requireNativeView('EmojiPicker') + +export default function EmojiPicker(props: EmojiPickerViewProps) { + return ( + { + props.onEmojiSelected(nativeEvent.emoji) + }} + /> + ) +} diff --git a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift index 15993ef29e..e6ebef1d33 100644 --- a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift +++ b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift @@ -9,7 +9,7 @@ class ExpoScrollForwarderView: ExpoView, UIGestureRecognizerDelegate { } } - private var rctScrollView: RCTScrollView? + private var scrollView: UIScrollView? private var rctRefreshCtrl: RCTRefreshControl? private var cancelGestureRecognizers: [UIGestureRecognizer]? private var animTimer: Timer? @@ -68,7 +68,7 @@ class ExpoScrollForwarderView: ExpoView, UIGestureRecognizerDelegate { } @IBAction func callOnPan(_ sender: UIPanGestureRecognizer) { - guard let rctsv = self.rctScrollView, let sv = rctsv.scrollView else { + guard let sv = self.scrollView else { return } @@ -113,7 +113,7 @@ class ExpoScrollForwarderView: ExpoView, UIGestureRecognizerDelegate { } func startDecayAnimation(_ translation: CGFloat, _ velocity: CGFloat) { - guard let sv = self.rctScrollView?.scrollView else { + guard let sv = self.scrollView else { return } @@ -160,32 +160,49 @@ class ExpoScrollForwarderView: ExpoView, UIGestureRecognizerDelegate { return offset } + + private func findScrollView(in view: UIView, foundCount: Int) -> UIScrollView? { + var foundCount = foundCount + if let sv = view as? UIScrollView { return sv } + for child in view.subviews { + if let found = findScrollView(in: child, foundCount: foundCount) { + if foundCount == 1 { + print("found sv: \(found)") +// return found + } else { + print("found sv: \(found)") + foundCount += 1 + } + } + } + return nil + } func tryFindScrollView() { - guard let scrollViewTag = scrollViewTag else { - return - } - // Before we switch to a different scrollview, we always want to remove the cancel gesture recognizer. // Otherwise we might end up with duplicates when we switch back to that scrollview. self.removeCancelGestureRecognizers() + + guard let sv = self.findScrollView(in: self.superview!.superview!.superview!, foundCount: 0) else { + print("⚠️ ExpoScrollForwarder: couldn’t find UIScrollView under tag \(tag)") + return + } - self.rctScrollView = self.appContext? - .findView(withTag: scrollViewTag, ofType: RCTScrollView.self) - self.rctRefreshCtrl = self.rctScrollView?.scrollView.refreshControl as? RCTRefreshControl + self.scrollView = sv + self.rctRefreshCtrl = sv.refreshControl as? RCTRefreshControl self.addCancelGestureRecognizers() } func addCancelGestureRecognizers() { self.cancelGestureRecognizers?.forEach { r in - self.rctScrollView?.scrollView?.addGestureRecognizer(r) + self.scrollView?.addGestureRecognizer(r) } } func removeCancelGestureRecognizers() { self.cancelGestureRecognizers?.forEach { r in - self.rctScrollView?.scrollView?.removeGestureRecognizer(r) + self.scrollView?.removeGestureRecognizer(r) } } @@ -202,7 +219,7 @@ class ExpoScrollForwarderView: ExpoView, UIGestureRecognizerDelegate { } func scrollToOffset(_ offset: Int, animated: Bool = true) { - self.rctScrollView?.scroll(toOffset: CGPoint(x: 0, y: offset), animated: animated) + self.scrollView?.scrollRectToVisible(CGRect(x: 0, y: offset, width: 0, height: 0), animated: animated) } func stopTimer() { diff --git a/modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts b/modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts index 26b9e7553a..3f1e4a63da 100644 --- a/modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts +++ b/modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts @@ -1,5 +1,3 @@ -import React from 'react' - export interface ExpoScrollForwarderViewProps { scrollViewTag: number | null children: React.ReactNode diff --git a/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx b/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx index 21a2b9fb26..18bdb25c8a 100644 --- a/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx +++ b/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx @@ -1,7 +1,6 @@ -import * as React from 'react' import {requireNativeViewManager} from 'expo-modules-core' -import {ExpoScrollForwarderViewProps} from './ExpoScrollForwarder.types' +import {type ExpoScrollForwarderViewProps} from './ExpoScrollForwarder.types' const NativeView: React.ComponentType = requireNativeViewManager('ExpoScrollForwarder') diff --git a/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx b/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx index 0f5d01c130..d66eef7cbd 100644 --- a/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx +++ b/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx @@ -1,6 +1,4 @@ -import React from 'react' - -import {ExpoScrollForwarderViewProps} from './ExpoScrollForwarder.types' +import {type ExpoScrollForwarderViewProps} from './ExpoScrollForwarder.types' export function ExpoScrollForwarderView({ children, diff --git a/package.json b/package.json index 2ed14061f4..0d96e688cb 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,17 @@ "node": ">=20" }, "packageManager": "yarn@1.22.22", + "expo": { + "autolinking": { + "android": { + "buildFromSource": [ + "expo-notifications", + "expo-haptics", + "expo-media-library" + ] + } + } + }, "scripts": { "prepare": "is-ci || husky install", "postinstall": "patch-package && yarn intl:compile", @@ -58,13 +69,13 @@ "icons:optimize": "svgo -f ./assets/icons" }, "dependencies": { - "@atproto/api": "^0.15.3", + "@atproto/api": "^0.15.5", "@bitdrift/react-native": "^0.6.8", "@braintree/sanitize-url": "^6.0.2", "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", "@emoji-mart/react": "^1.1.1", - "@expo/html-elements": "^0.4.2", - "@expo/webpack-config": "^19.0.0", + "@expo/html-elements": "^0.12.4", + "@expo/webpack-config": "^19.0.1", "@floating-ui/dom": "^1.6.3", "@floating-ui/react-dom": "^2.0.8", "@formatjs/intl-locale": "^4.2.8", @@ -80,9 +91,9 @@ "@mattermost/react-native-paste-input": "^0.7.1", "@miblanchard/react-native-slider": "^2.3.1", "@mozzius/expo-dynamic-app-icon": "^1.5.0", - "@react-native-async-storage/async-storage": "1.23.1", - "@react-native-menu/menu": "^1.1.7", - "@react-native-picker/picker": "2.10.3", + "@react-native-async-storage/async-storage": "2.1.2", + "@react-native-menu/menu": "^1.2.3", + "@react-native-picker/picker": "2.11.0", "@react-navigation/bottom-tabs": "^6.5.20", "@react-navigation/drawer": "^6.6.15", "@react-navigation/native": "^6.1.17", @@ -119,33 +130,33 @@ "emoji-mart": "^5.5.2", "emoji-regex": "^10.4.0", "eventemitter3": "^5.0.1", - "expo": "~52.0.42", - "expo-application": "~6.0.2", - "expo-blur": "^14.0.3", - "expo-build-properties": "~0.13.2", - "expo-camera": "~16.0.18", - "expo-clipboard": "~7.0.1", - "expo-dev-client": "~5.0.19", - "expo-device": "~7.0.3", - "expo-file-system": "~18.0.12", - "expo-font": "~13.0.4", - "expo-haptics": "~14.0.1", - "expo-image": "~2.0.7", - "expo-image-manipulator": "~13.0.6", - "expo-image-picker": "~16.0.6", - "expo-linear-gradient": "~14.0.2", - "expo-linking": "~7.0.5", - "expo-localization": "~16.0.1", - "expo-media-library": "~17.0.6", - "expo-notifications": "~0.29.14", - "expo-screen-orientation": "~8.0.4", - "expo-sharing": "~13.0.1", - "expo-splash-screen": "~0.29.22", - "expo-system-ui": "~4.0.9", - "expo-task-manager": "~12.0.6", - "expo-updates": "~0.27.4", - "expo-video": "~2.0.6", - "expo-web-browser": "~14.0.2", + "expo": "^53.0.5", + "expo-application": "~6.1.4", + "expo-blur": "~14.1.4", + "expo-build-properties": "~0.14.6", + "expo-camera": "~16.1.6", + "expo-clipboard": "~7.1.4", + "expo-dev-client": "~5.1.7", + "expo-device": "~7.1.4", + "expo-file-system": "~18.1.8", + "expo-font": "~13.3.0", + "expo-haptics": "~14.1.4", + "expo-image": "~2.1.6", + "expo-image-manipulator": "~13.1.5", + "expo-image-picker": "~16.1.4", + "expo-linear-gradient": "~14.1.4", + "expo-linking": "~7.1.4", + "expo-localization": "~16.1.5", + "expo-media-library": "~17.1.6", + "expo-notifications": "~0.31.1", + "expo-screen-orientation": "~8.1.5", + "expo-sharing": "~13.1.5", + "expo-splash-screen": "~0.30.8", + "expo-system-ui": "~5.0.7", + "expo-task-manager": "~13.1.5", + "expo-updates": "~0.28.12", + "expo-video": "~2.1.8", + "expo-web-browser": "~14.1.6", "fast-text-encoding": "^1.0.6", "history": "^5.3.0", "hls.js": "^1.5.11", @@ -157,47 +168,47 @@ "lodash.isequal": "^4.5.0", "lodash.shuffle": "^4.2.0", "lodash.throttle": "^4.1.1", - "multiformats": "^13.1.0", + "multiformats": "9.9.0", "nanoid": "^5.0.5", "normalize-url": "^8.0.0", "patch-package": "^6.5.1", "postinstall-postinstall": "^2.1.0", "psl": "^1.9.0", "radix-ui": "^1.2.0", - "react": "18.3.1", - "react-compiler-runtime": "19.0.0-beta-a7bf2bd-20241110", - "react-dom": "18.3.1", + "react": "19.0.0", + "react-compiler-runtime": "^19.1.0-rc.1", + "react-dom": "19.0.0", "react-image-crop": "^11.0.7", - "react-keyed-flatten-children": "^3.0.0", - "react-native": "0.76.9", - "react-native-compressor": "1.10.3", - "react-native-date-picker": "^5.0.7", - "react-native-drawer-layout": "^4.1.1", + "react-is": "19", + "react-keyed-flatten-children": "^5.0.0", + "react-native": "0.79.2", + "react-native-compressor": "1.11.0", + "react-native-date-picker": "^5.0.12", + "react-native-drawer-layout": "^4.1.6", "react-native-edge-to-edge": "^1.6.0", - "react-native-emoji-popup": "^0.1.2", - "react-native-gesture-handler": "2.20.2", + "react-native-gesture-handler": "2.25.0", "react-native-get-random-values": "~1.11.0", - "react-native-image-crop-picker": "^0.41.6", + "react-native-image-crop-picker": "^0.42.0", "react-native-ios-context-menu": "^1.15.3", "react-native-keyboard-controller": "^1.17.1", "react-native-mmkv": "^2.12.2", - "react-native-pager-view": "6.5.1", + "react-native-pager-view": "6.7.1", "react-native-picker-select": "^9.3.1", "react-native-progress": "bluesky-social/react-native-progress", "react-native-qrcode-styled": "^0.3.3", - "react-native-reanimated": "~3.16.1", + "react-native-reanimated": "~3.17.5", "react-native-root-siblings": "^4.1.1", - "react-native-safe-area-context": "4.12.0", - "react-native-screens": "~4.4.0", - "react-native-svg": "15.8.0", + "react-native-safe-area-context": "5.4.0", + "react-native-screens": "~4.10.0", + "react-native-svg": "15.11.2", "react-native-uitextview": "^1.4.0", "react-native-url-polyfill": "^1.3.0", - "react-native-uuid": "^2.0.2", + "react-native-uuid": "^2.0.3", "react-native-view-shot": "^4.0.3", - "react-native-web": "~0.19.13", + "react-native-web": "~0.20.0", "react-native-web-webview": "^1.0.2", - "react-native-webview": "13.12.5", - "react-remove-scroll-bar": "^2.3.6", + "react-native-webview": "13.13.5", + "react-remove-scroll-bar": "^2.3.8", "react-responsive": "^9.0.2", "react-textarea-autosize": "^8.5.3", "rn-fetch-blob": "^0.12.0", @@ -209,77 +220,74 @@ "zod": "^3.20.2" }, "devDependencies": { - "@atproto/dev-env": "^0.3.87", + "@atproto/dev-env": "^0.3.128", "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "@babel/runtime": "^7.26.0", - "@did-plc/server": "^0.0.1", - "@expo/config-plugins": "9.0.10", + "@expo/config-plugins": "~10.0.2", "@lingui/cli": "^4.14.1", "@lingui/macro": "^4.14.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", - "@react-native/eslint-config": "^0.76.9", - "@react-native/typescript-config": "^0.76.9", + "@react-native/eslint-config": "^0.79.2", + "@react-native/typescript-config": "^0.79.2", "@sentry/webpack-plugin": "^3.2.2", "@testing-library/jest-native": "^5.4.3", - "@testing-library/react-native": "^12.8.1", + "@testing-library/react-native": "^13.2.0", "@types/jest": "^29.4.0", "@types/lodash.chunk": "^4.2.7", "@types/lodash.debounce": "^4.0.7", "@types/lodash.isequal": "^4.5.6", "@types/lodash.shuffle": "^4.2.7", "@types/psl": "^1.1.1", - "@types/react-dom": "^18.3.1", + "@types/react-dom": "^19.1.2", "@types/react-responsive": "^8.0.5", - "@types/react-test-renderer": "^17.0.1", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "babel-jest": "^29.7.0", "babel-plugin-macros": "^3.1.0", "babel-plugin-module-resolver": "^5.0.2", - "babel-plugin-react-compiler": "19.0.0-beta-a7bf2bd-20241110", - "babel-preset-expo": "^12.0.2", + "babel-plugin-react-compiler": "^19.1.0-rc.1", + "babel-preset-expo": "~13.1.11", "eslint": "^8.19.0", "eslint-plugin-bsky-internal": "link:./eslint", "eslint-plugin-ft-flow": "^2.0.3", "eslint-plugin-import": "^2.31.0", "eslint-plugin-lingui": "^0.2.0", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-compiler": "19.0.0-beta-a7bf2bd-20241110", + "eslint-plugin-react-compiler": "^19.1.0-rc.1", "eslint-plugin-react-native-a11y": "^3.3.0", "eslint-plugin-simple-import-sort": "^12.0.0", "file-loader": "6.2.0", "husky": "^8.0.3", "is-ci": "^3.0.1", "jest": "^29.7.0", - "jest-expo": "~52.0.6", + "jest-expo": "~53.0.3", "jest-junit": "^16.0.0", "lint-staged": "^13.2.3", "lockfile-lint": "^4.14.0", - "metro-react-native-babel-preset": "^0.76.9", + "metro-react-native-babel-preset": "^0.77.0", "prettier": "^2.8.3", "react-native-dotenv": "^3.4.11", "react-refresh": "^0.14.0", - "react-test-renderer": "18.2.0", "svgo": "^3.3.2", "ts-node": "^10.9.1", - "typescript": "^5.7.2", + "typescript": "~5.8.3", "webpack-bundle-analyzer": "^4.10.1" }, "resolutions": { "@expo/image-utils": "0.6.3", - "@react-native/babel-preset": "0.76.9", - "@react-native/normalize-colors": "0.76.1", + "@react-native/babel-preset": "0.79.2", + "@react-native/normalize-colors": "0.79.2", "@types/react": "^18", "**/expo-constants": "17.0.3", "**/expo-device": "7.0.1", - "**/zod": "3.23.8" + "**/zod": "3.23.8", + "**/multiformats": "9.9.0" }, "jest": { "preset": "jest-expo/ios", "setupFilesAfterEnv": [ - "./jest/jestSetup.js", - "@testing-library/jest-native/extend-expect" + "./jest/jestSetup.js" ], "moduleFileExtensions": [ "ts", diff --git a/patches/expo-haptics+14.0.1.patch b/patches/expo-haptics+14.1.4.patch similarity index 100% rename from patches/expo-haptics+14.0.1.patch rename to patches/expo-haptics+14.1.4.patch diff --git a/patches/expo-haptics+14.0.0.md b/patches/expo-haptics+14.1.4.patch.md similarity index 100% rename from patches/expo-haptics+14.0.0.md rename to patches/expo-haptics+14.1.4.patch.md diff --git a/patches/expo-image-manipulator+13.0.6.patch b/patches/expo-image-manipulator+13.0.6.patch deleted file mode 100644 index 02d4d5fb9a..0000000000 --- a/patches/expo-image-manipulator+13.0.6.patch +++ /dev/null @@ -1,101 +0,0 @@ -diff --git a/node_modules/expo-image-manipulator/src/ImageManipulator.web.ts b/node_modules/expo-image-manipulator/src/ImageManipulator.web.ts -new file mode 100644 -index 0000000..babbb3b ---- /dev/null -+++ b/node_modules/expo-image-manipulator/src/ImageManipulator.web.ts -@@ -0,0 +1,60 @@ -+import { useReleasingSharedObject } from 'expo-modules-core'; -+import { SharedRef } from 'expo-modules-core/types'; -+ -+import { Action, ImageResult, SaveFormat, SaveOptions } from './ImageManipulator.types'; -+import { ImageManipulatorContext } from './ImageManipulatorContext'; -+import ExpoImageManipulator from './NativeImageManipulatorModule'; -+import { validateArguments } from './validators'; -+ -+// @needsAudit -+/** -+ * Manipulate the image provided via `uri`. Available modifications are rotating, flipping (mirroring), -+ * resizing and cropping. Each invocation results in a new file. With one invocation you can provide -+ * a set of actions to perform over the image. Overwriting the source file would not have an effect -+ * in displaying the result as images are cached. -+ * @param uri URI of the file to manipulate. Should be on the local file system or a base64 data URI. -+ * @param actions An array of objects representing manipulation options. Each object should have -+ * __only one__ of the keys that corresponds to specific transformation. -+ * @param saveOptions A map defining how modified image should be saved. -+ * @return Promise which fulfils with [`ImageResult`](#imageresult) object. -+ * @deprecated It has been replaced by the new, contextual and object-oriented API. -+ * Use [`ImageManipulator.manipulate`](#manipulateuri) or [`useImageManipulator`](#useimagemanipulatoruri) instead. -+ */ -+export async function manipulateAsync( -+ uri: string, -+ actions: Action[] = [], -+ saveOptions: SaveOptions = {} -+): Promise { -+ validateArguments(uri, actions, saveOptions); -+ -+ const { format = SaveFormat.JPEG, ...rest } = saveOptions; -+ const context = ExpoImageManipulator.manipulate(uri); -+ -+ for (const action of actions) { -+ if ('resize' in action) { -+ context.resize(action.resize); -+ } else if ('rotate' in action) { -+ context.rotate(action.rotate); -+ } else if ('flip' in action) { -+ context.flip(action.flip); -+ } else if ('crop' in action) { -+ context.crop(action.crop); -+ } else if ('extent' in action && context.extent) { -+ context.extent(action.extent); -+ } -+ } -+ const image = await context.renderAsync(saveOptions.compress); -+ const result = await image.saveAsync({ format, ...rest }); -+ -+ // These shared objects will not be used anymore, so free up some memory. -+ context.release(); -+ image.release(); -+ -+ return result; -+} -+ -+export function useImageManipulator(source: string | SharedRef<'image'>): ImageManipulatorContext { -+ return useReleasingSharedObject(() => ExpoImageManipulator.manipulate(source), [source]); -+} -+ -+export { ExpoImageManipulator as ImageManipulator }; -diff --git a/node_modules/expo-image-manipulator/src/ImageManipulatorContext.ts b/node_modules/expo-image-manipulator/src/ImageManipulatorContext.ts -index 120d8d3..f8aa49c 100644 ---- a/node_modules/expo-image-manipulator/src/ImageManipulatorContext.ts -+++ b/node_modules/expo-image-manipulator/src/ImageManipulatorContext.ts -@@ -52,7 +52,7 @@ export declare class ImageManipulatorContext extends SharedObject { - /** - * Awaits for all manipulation tasks to finish and resolves with a reference to the resulted native image. - */ -- renderAsync(): Promise; -+ renderAsync(compress?: number): Promise; - } - - export default ExpoImageManipulator.Context as typeof ImageManipulatorContext; -diff --git a/node_modules/expo-image-manipulator/src/web/ImageManipulatorContext.web.ts b/node_modules/expo-image-manipulator/src/web/ImageManipulatorContext.web.ts -index 428848c..363a57a 100644 ---- a/node_modules/expo-image-manipulator/src/web/ImageManipulatorContext.web.ts -+++ b/node_modules/expo-image-manipulator/src/web/ImageManipulatorContext.web.ts -@@ -41,7 +41,7 @@ export default class ImageManipulatorContext extends SharedObject { - return this; - } - -- async renderAsync(): Promise { -+ async renderAsync(compress?: number): Promise { - const canvas = await this.currentTask; - - return new Promise((resolve) => { -@@ -49,7 +49,7 @@ export default class ImageManipulatorContext extends SharedObject { - const url = blob ? URL.createObjectURL(blob) : canvas.toDataURL(); - - resolve(new ImageManipulatorImageRef(url, canvas.width, canvas.height)); -- }); -+ }, typeof compress === 'number' ? 'image/jpeg' : undefined, compress); - }); - } - diff --git a/patches/expo-media-library+17.0.6.patch b/patches/expo-media-library+17.1.6.patch similarity index 50% rename from patches/expo-media-library+17.0.6.patch rename to patches/expo-media-library+17.1.6.patch index 7ef57f7687..4fa853ec3a 100644 --- a/patches/expo-media-library+17.0.6.patch +++ b/patches/expo-media-library+17.1.6.patch @@ -1,19 +1,19 @@ diff --git a/node_modules/expo-media-library/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt b/node_modules/expo-media-library/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt -index 64e6efe..e61485e 100644 +index f1255e8..a9b49e5 100644 --- a/node_modules/expo-media-library/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt +++ b/node_modules/expo-media-library/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt -@@ -111,12 +111,12 @@ class MediaLibraryModule : Module() { +@@ -112,11 +112,9 @@ class MediaLibraryModule : Module() { } - AsyncFunction("createAssetAsync") { localUri: String, promise: Promise -> + AsyncFunction("createAssetAsync") { localUri: String, albumId: String?, promise: Promise -> - throwUnlessPermissionsGranted { -+ //throwUnlessPermissionsGranted { - withModuleScope(promise) { - CreateAsset(context, localUri, promise) - .execute() - } -- } -+ //} +- withModuleScope(promise) { +- CreateAssetWithAlbumId(context, localUri, promise, true, albumId) +- .execute() +- } ++ withModuleScope(promise) { ++ CreateAssetWithAlbumId(context, localUri, promise, true, albumId) ++ .execute() + } } - AsyncFunction("addAssetsToAlbumAsync") { assetsId: List, albumId: String, copyToAlbum: Boolean, promise: Promise -> diff --git a/patches/expo-modules-core+2.2.3.patch b/patches/expo-modules-core+2.3.12.patch similarity index 100% rename from patches/expo-modules-core+2.2.3.patch rename to patches/expo-modules-core+2.3.12.patch diff --git a/patches/expo-modules-core+2.1.2.md b/patches/expo-modules-core+2.3.12.patch.md similarity index 100% rename from patches/expo-modules-core+2.1.2.md rename to patches/expo-modules-core+2.3.12.patch.md diff --git a/patches/expo-notifications+0.29.14.patch b/patches/expo-notifications+0.29.14.patch deleted file mode 100644 index dd70cc781b..0000000000 --- a/patches/expo-notifications+0.29.14.patch +++ /dev/null @@ -1,169 +0,0 @@ -diff --git a/node_modules/expo-notifications/android/build.gradle b/node_modules/expo-notifications/android/build.gradle -index 13bffbb..5ebbede 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) { -+ 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.31.1.patch b/patches/expo-notifications+0.31.1.patch new file mode 100644 index 0000000000..56e639a261 --- /dev/null +++ b/patches/expo-notifications+0.31.1.patch @@ -0,0 +1,992 @@ +diff --git a/node_modules/expo-notifications/android/.gradle/8.10/checksums/checksums.lock b/node_modules/expo-notifications/android/.gradle/8.10/checksums/checksums.lock +new file mode 100644 +index 0000000..883ef6a +Binary files /dev/null and b/node_modules/expo-notifications/android/.gradle/8.10/checksums/checksums.lock differ +diff --git a/node_modules/expo-notifications/android/.gradle/8.10/dependencies-accessors/gc.properties b/node_modules/expo-notifications/android/.gradle/8.10/dependencies-accessors/gc.properties +new file mode 100644 +index 0000000..e69de29 +diff --git a/node_modules/expo-notifications/android/.gradle/8.10/fileChanges/last-build.bin b/node_modules/expo-notifications/android/.gradle/8.10/fileChanges/last-build.bin +new file mode 100644 +index 0000000..f76dd23 +Binary files /dev/null and b/node_modules/expo-notifications/android/.gradle/8.10/fileChanges/last-build.bin differ +diff --git a/node_modules/expo-notifications/android/.gradle/8.10/fileHashes/fileHashes.lock b/node_modules/expo-notifications/android/.gradle/8.10/fileHashes/fileHashes.lock +new file mode 100644 +index 0000000..774caf7 +Binary files /dev/null and b/node_modules/expo-notifications/android/.gradle/8.10/fileHashes/fileHashes.lock differ +diff --git a/node_modules/expo-notifications/android/.gradle/8.10/gc.properties b/node_modules/expo-notifications/android/.gradle/8.10/gc.properties +new file mode 100644 +index 0000000..e69de29 +diff --git a/node_modules/expo-notifications/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/node_modules/expo-notifications/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +new file mode 100644 +index 0000000..a3c1514 +Binary files /dev/null and b/node_modules/expo-notifications/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ +diff --git a/node_modules/expo-notifications/android/.gradle/buildOutputCleanup/cache.properties b/node_modules/expo-notifications/android/.gradle/buildOutputCleanup/cache.properties +new file mode 100644 +index 0000000..0e5b4da +--- /dev/null ++++ b/node_modules/expo-notifications/android/.gradle/buildOutputCleanup/cache.properties +@@ -0,0 +1,2 @@ ++#Thu Apr 24 20:44:36 PDT 2025 ++gradle.version=8.10 +diff --git a/node_modules/expo-notifications/android/.gradle/config.properties b/node_modules/expo-notifications/android/.gradle/config.properties +new file mode 100644 +index 0000000..0bd71c6 +--- /dev/null ++++ b/node_modules/expo-notifications/android/.gradle/config.properties +@@ -0,0 +1,2 @@ ++#Thu Apr 24 20:44:32 PDT 2025 ++java.home=/Applications/Android Studio.app/Contents/jbr/Contents/Home +diff --git a/node_modules/expo-notifications/android/.gradle/vcs-1/gc.properties b/node_modules/expo-notifications/android/.gradle/vcs-1/gc.properties +new file mode 100644 +index 0000000..e69de29 +diff --git a/node_modules/expo-notifications/android/.idea/.gitignore b/node_modules/expo-notifications/android/.idea/.gitignore +new file mode 100644 +index 0000000..26d3352 +--- /dev/null ++++ b/node_modules/expo-notifications/android/.idea/.gitignore +@@ -0,0 +1,3 @@ ++# Default ignored files ++/shelf/ ++/workspace.xml +diff --git a/node_modules/expo-notifications/android/.idea/AndroidProjectSystem.xml b/node_modules/expo-notifications/android/.idea/AndroidProjectSystem.xml +new file mode 100644 +index 0000000..4a53bee +--- /dev/null ++++ b/node_modules/expo-notifications/android/.idea/AndroidProjectSystem.xml +@@ -0,0 +1,6 @@ ++ ++ ++ ++ ++ +\ No newline at end of file +diff --git a/node_modules/expo-notifications/android/.idea/caches/deviceStreaming.xml b/node_modules/expo-notifications/android/.idea/caches/deviceStreaming.xml +new file mode 100644 +index 0000000..9e9ba09 +--- /dev/null ++++ b/node_modules/expo-notifications/android/.idea/caches/deviceStreaming.xml +@@ -0,0 +1,607 @@ ++ ++ ++ ++ ++ ++ +\ No newline at end of file +diff --git a/node_modules/expo-notifications/android/.idea/gradle.xml b/node_modules/expo-notifications/android/.idea/gradle.xml +new file mode 100644 +index 0000000..b838237 +--- /dev/null ++++ b/node_modules/expo-notifications/android/.idea/gradle.xml +@@ -0,0 +1,12 @@ ++ ++ ++ ++ ++ ++ +\ No newline at end of file +diff --git a/node_modules/expo-notifications/android/.idea/migrations.xml b/node_modules/expo-notifications/android/.idea/migrations.xml +new file mode 100644 +index 0000000..f8051a6 +--- /dev/null ++++ b/node_modules/expo-notifications/android/.idea/migrations.xml +@@ -0,0 +1,10 @@ ++ ++ ++ ++ ++ ++ +\ No newline at end of file +diff --git a/node_modules/expo-notifications/android/.idea/misc.xml b/node_modules/expo-notifications/android/.idea/misc.xml +new file mode 100644 +index 0000000..3040d03 +--- /dev/null ++++ b/node_modules/expo-notifications/android/.idea/misc.xml +@@ -0,0 +1,10 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ +\ No newline at end of file +diff --git a/node_modules/expo-notifications/android/.idea/runConfigurations.xml b/node_modules/expo-notifications/android/.idea/runConfigurations.xml +new file mode 100644 +index 0000000..16660f1 +--- /dev/null ++++ b/node_modules/expo-notifications/android/.idea/runConfigurations.xml +@@ -0,0 +1,17 @@ ++ ++ ++ ++ ++ ++ +\ No newline at end of file +diff --git a/node_modules/expo-notifications/android/.idea/workspace.xml b/node_modules/expo-notifications/android/.idea/workspace.xml +new file mode 100644 +index 0000000..df26928 +--- /dev/null ++++ b/node_modules/expo-notifications/android/.idea/workspace.xml +@@ -0,0 +1,47 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ 1745552672693 ++ ++ ++ ++ +\ No newline at end of file +diff --git a/node_modules/expo-notifications/android/build.gradle b/node_modules/expo-notifications/android/build.gradle +index bc479ee..1ebfa00 100644 +--- a/node_modules/expo-notifications/android/build.gradle ++++ b/node_modules/expo-notifications/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/node_modules/expo-notifications/android/local.properties b/node_modules/expo-notifications/android/local.properties +new file mode 100644 +index 0000000..ab4c86d +--- /dev/null ++++ b/node_modules/expo-notifications/android/local.properties +@@ -0,0 +1,8 @@ ++## This file must *NOT* be checked into Version Control Systems, ++# as it contains information specific to your local configuration. ++# ++# Location of the SDK. This is only used by Gradle. ++# For customization when using a Version Control System, please read the ++# header note. ++#Thu Apr 24 20:44:32 PDT 2025 ++sdk.dir=/Users/hailey/Library/Android/sdk +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 3af254c..3c77e9d 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) { ++ 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 98f003f..2f745e8 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 90ca4ff..9d4cb09 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 +@@ -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-notifications+0.29.11.patch.md b/patches/expo-notifications+0.31.1.patch.md similarity index 100% rename from patches/expo-notifications+0.29.11.patch.md rename to patches/expo-notifications+0.31.1.patch.md diff --git a/patches/expo-updates+0.27.4.patch b/patches/expo-updates+0.28.12.patch similarity index 100% rename from patches/expo-updates+0.27.4.patch rename to patches/expo-updates+0.28.12.patch diff --git a/patches/expo-updates+0.26.10.patch.md b/patches/expo-updates+0.28.12.patch.md similarity index 100% rename from patches/expo-updates+0.26.10.patch.md rename to patches/expo-updates+0.28.12.patch.md diff --git a/patches/react-native+0.76.9.patch b/patches/react-native+0.76.9.patch deleted file mode 100644 index 7e96d875c4..0000000000 --- a/patches/react-native+0.76.9.patch +++ /dev/null @@ -1,366 +0,0 @@ -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 | string | undefined; - filter?: ReadonlyArray | 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 | string | undefined; -+ filter?: ReadonlyArray | 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 | string | undefined; -+ filter?: ReadonlyArray | 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 @@ - * 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)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 @@ - UIColor *_titleColor; - CGFloat _progressViewOffset; - BOOL _hasMovedToWindow; -+ UIColor *_customTintColor; - } - - - (instancetype)init -@@ -58,6 +59,12 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder) - _isInitialRender = false; - } - -+- (void)didMoveToSuperview -+{ -+ [super didMoveToSuperview]; -+ [self setTintColor:_customTintColor]; -+} -+ - - (void)didMoveToWindow - { - [super didMoveToWindow]; -@@ -221,4 +228,50 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder) - } - } - -+// 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 @@ RCT_EXPORT_MODULE() - - 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/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m -index 6f41b5c..9b4f77f 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 @@ - 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")); -@@ -433,6 +415,11 @@ static inline void RCTApplyTransformationAccordingLayoutDirection( - // Does nothing - } - -+- (void)setFrame:(CGRect)frame { -+ [super setFrame:frame]; -+ [self centerContentIfNeeded]; -+} -+ - - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex - { - [super insertReactSubview:view atIndex:atIndex]; -@@ -449,6 +436,8 @@ static inline void RCTApplyTransformationAccordingLayoutDirection( - _contentView = view; - RCTApplyTransformationAccordingLayoutDirection(_contentView, self.reactLayoutDirection); - [_scrollView addSubview:view]; -+ -+ [self centerContentIfNeeded]; - } - } - -@@ -658,9 +647,46 @@ static inline void RCTApplyTransformationAccordingLayoutDirection( - } - - 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 *)scrollListener - { - [_scrollListeners addObject:scrollListener]; -@@ -939,6 +965,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop) - CGSize contentSize = self.contentSize; - if (!CGSizeEqualToSize(_scrollView.contentSize, contentSize)) { - _scrollView.contentSize = contentSize; -+ [self centerContentIfNeeded]; - } - } - -@@ -1061,6 +1088,22 @@ RCT_SET_AND_PRESERVE_OFFSET(setShowsHorizontalScrollIndicator, showsHorizontalSc - RCT_SET_AND_PRESERVE_OFFSET(setShowsVerticalScrollIndicator, showsVerticalScrollIndicator, BOOL) - RCT_SET_AND_PRESERVE_OFFSET(setZoomScale, zoomScale, CGFloat); - -+- (void)setScrollIndicatorInsets:(UIEdgeInsets)value -+{ -+ [_scrollView setScrollIndicatorInsets:value]; -+} -+ -+- (UIEdgeInsets)scrollIndicatorInsets -+{ -+ UIEdgeInsets verticalScrollIndicatorInsets = [_scrollView verticalScrollIndicatorInsets]; -+ UIEdgeInsets horizontalScrollIndicatorInsets = [_scrollView horizontalScrollIndicatorInsets]; -+ return UIEdgeInsetsMake( -+ verticalScrollIndicatorInsets.top, -+ horizontalScrollIndicatorInsets.left, -+ verticalScrollIndicatorInsets.bottom, -+ horizontalScrollIndicatorInsets.right); -+} -+ - - (void)setAutomaticallyAdjustsScrollIndicatorInsets:(BOOL)automaticallyAdjusts API_AVAILABLE(ios(13.0)) - { - // `automaticallyAdjustsScrollIndicatorInsets` is available since iOS 13. -diff --git a/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m -index cd1e7eb..c1d0172 100644 ---- a/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m -+++ b/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m -@@ -83,6 +83,7 @@ RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL) - RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval) - RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat) - RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets) -+RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets) - RCT_EXPORT_VIEW_PROPERTY(verticalScrollIndicatorInsets, UIEdgeInsets) - RCT_EXPORT_VIEW_PROPERTY(scrollToOverflowEnabled, BOOL) - RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int) diff --git a/patches/react-native+0.79.2.patch b/patches/react-native+0.79.2.patch new file mode 100644 index 0000000000..609ae66178 --- /dev/null +++ b/patches/react-native+0.79.2.patch @@ -0,0 +1,119 @@ +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 @@ + UIColor *_titleColor; + CGFloat _progressViewOffset; + BOOL _hasMovedToWindow; ++ UIColor *_customTintColor; + } + + - (instancetype)init +@@ -58,6 +59,12 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder) + _isInitialRender = false; + } + ++- (void)didMoveToSuperview ++{ ++ [super didMoveToSuperview]; ++ [self setTintColor:_customTintColor]; ++} ++ + - (void)didMoveToWindow + { + [super didMoveToWindow]; +@@ -221,4 +228,50 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder) + } + } + ++// 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 @@ RCT_EXPORT_MODULE() + + 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/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m +index cd1e7eb..c1d0172 100644 +--- a/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m ++++ b/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m +@@ -83,6 +83,7 @@ RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL) + RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval) + RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat) + RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets) ++RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets) + RCT_EXPORT_VIEW_PROPERTY(verticalScrollIndicatorInsets, UIEdgeInsets) + RCT_EXPORT_VIEW_PROPERTY(scrollToOverflowEnabled, BOOL) + RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int) diff --git a/patches/react-native+0.76.6.patch.md b/patches/react-native+0.79.2.patch.md similarity index 84% rename from patches/react-native+0.76.6.patch.md rename to patches/react-native+0.79.2.patch.md index eacb9f267c..9c93aee5cb 100644 --- a/patches/react-native+0.76.6.patch.md +++ b/patches/react-native+0.79.2.patch.md @@ -11,7 +11,3 @@ in the RN repo: https://github.com/facebook/react-native/issues/43388 Patching `RCTRefreshControl.m` and `RCTRefreshControl.h` to add a new `forwarderBeginRefreshing` method to the class. This method is used by `ExpoScrollForwarder` to initiate a refresh of the underlying `UIScrollView` from inside that module. - -## ScrollView centerContent fix - -Includes https://github.com/facebook/react-native/pull/47591 early. Delete when it's in a release. diff --git a/patches/react-native-gesture-handler+2.20.2.patch b/patches/react-native-gesture-handler+2.25.0.patch similarity index 100% rename from patches/react-native-gesture-handler+2.20.2.patch rename to patches/react-native-gesture-handler+2.25.0.patch diff --git a/patches/react-native-image-crop-picker+0.41.6.patch b/patches/react-native-image-crop-picker+0.42.0.patch similarity index 83% rename from patches/react-native-image-crop-picker+0.41.6.patch rename to patches/react-native-image-crop-picker+0.42.0.patch index 57589931a3..c6124ad1e1 100644 --- a/patches/react-native-image-crop-picker+0.41.6.patch +++ b/patches/react-native-image-crop-picker+0.42.0.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/react-native-image-crop-picker/android/src/main/AndroidManifest.xml b/node_modules/react-native-image-crop-picker/android/src/main/AndroidManifest.xml -index 391f303..8e2c3db 100644 +index a08629b..fab6299 100644 --- a/node_modules/react-native-image-crop-picker/android/src/main/AndroidManifest.xml +++ b/node_modules/react-native-image-crop-picker/android/src/main/AndroidManifest.xml @@ -24,7 +24,7 @@ @@ -8,12 +8,12 @@ index 391f303..8e2c3db 100644 android:name="com.yalantis.ucrop.UCropActivity" - android:theme="@style/Theme.AppCompat.Light.NoActionBar" /> + android:theme="@style/Theme.UCropNoEdgeToEdge" /> - - + + diff --git a/node_modules/react-native-image-crop-picker/android/src/main/res/values-v35/styles.xml b/node_modules/react-native-image-crop-picker/android/src/main/res/values-v35/styles.xml new file mode 100644 -index 0000000..396d5a8 +index 0000000..5301f74 --- /dev/null +++ b/node_modules/react-native-image-crop-picker/android/src/main/res/values-v35/styles.xml @@ -0,0 +1,5 @@ @@ -22,26 +22,27 @@ index 0000000..396d5a8 + true + + +\ No newline at end of file diff --git a/node_modules/react-native-image-crop-picker/android/src/main/res/values/styles.xml b/node_modules/react-native-image-crop-picker/android/src/main/res/values/styles.xml new file mode 100644 -index 0000000..50129b6 +index 0000000..55569aa --- /dev/null +++ b/node_modules/react-native-image-crop-picker/android/src/main/res/values/styles.xml @@ -0,0 +1,3 @@ + +