diff --git a/.yarn/patches/react-native-screens-npm-4.18.0-fa7de65975.patch b/.yarn/patches/react-native-screens-npm-4.18.0-fa7de65975.patch index e6f99e6f..64ea2389 100644 --- a/.yarn/patches/react-native-screens-npm-4.18.0-fa7de65975.patch +++ b/.yarn/patches/react-native-screens-npm-4.18.0-fa7de65975.patch @@ -1,60 +1,3 @@ -diff --git a/ios/RNSScreen.mm b/ios/RNSScreen.mm -index 65c18f1ddccc64b3169e050e577eab77fd8c183b..8ffd9d4b48f85e53c6fcf0a76963cfcf946dad97 100644 ---- a/ios/RNSScreen.mm -+++ b/ios/RNSScreen.mm -@@ -38,6 +38,7 @@ - #import "RNSTabBarController.h" - - #import "RNSDefines.h" -+#import "integrations/RNSLifecycleListenerProtocol.h" - #import "UIView+RNSUtility.h" - - #ifdef RCT_NEW_ARCH_ENABLED -@@ -74,6 +75,7 @@ struct ContentWrapperBox { - ContentWrapperBox _contentWrapperBox; - bool _sheetHasInitialDetentSet; - BOOL _shouldUpdateScrollEdgeEffects; -+ RNSScreen *_controllerBeforeInvalidate; - #ifdef RCT_NEW_ARCH_ENABLED - RCTSurfaceTouchHandler *_touchHandler; - react::RNSScreenShadowNode::ConcreteState::Shared _state; -@@ -608,6 +610,26 @@ RNS_IGNORE_SUPER_CALL_END - if (_hideKeyboardOnSwipe) { - [self endEditing:YES]; - } -+ -+ // Notify any presented view controllers that conform to RNSLifecycleListenerProtocol -+ RNSScreen *controller = _controller ?: _controllerBeforeInvalidate; -+ if (controller) { -+ UIViewController *presented = controller.presentedViewController; -+ while (presented) { -+ UIViewController *next = presented.presentedViewController; -+ if ([presented conformsToProtocol:@protocol(RNSLifecycleListenerProtocol)]) { -+ BOOL isPresenterUnmounting = NO; -+ RNSScreen *presenter = (RNSScreen *)presented.presentingViewController; -+ if ([presenter isKindOfClass:[RNSScreen class]]) { -+ isPresenterUnmounting = presenter.screenView.isMarkedForUnmountInCurrentTransaction; -+ } -+ [(id)presented screenWillDisappear:controller -+ isPresenterUnmounting:isPresenterUnmounting]; -+ } -+ presented = next; -+ } -+ } -+ - #ifdef RCT_NEW_ARCH_ENABLED - // If screen is already unmounted then there will be no event emitter - if (_eventEmitter != nullptr) { -@@ -927,6 +949,9 @@ RNS_IGNORE_SUPER_CALL_END - - - (void)invalidate - { -+ if (_controller && !_controllerBeforeInvalidate) { -+ _controllerBeforeInvalidate = _controller; -+ } - _controller = nil; - [_sheetsScrollView removeObserver:self forKeyPath:@"bounds" context:nil]; - } diff --git a/ios/RNSScreenStack.mm b/ios/RNSScreenStack.mm index 51f021831aed26a4eed3c85014020423b7b3108b..268fa69dfee2b20d8b5a66c77c1b4cbd8c831573 100644 --- a/ios/RNSScreenStack.mm @@ -130,23 +73,3 @@ index 006f809d104c1d4fbdf6eccca89d6c6e190cca71..89e297f1b7a9582fee3e19237dfba8d4 @end NS_ASSUME_NONNULL_END -diff --git a/ios/integrations/RNSLifecycleListenerProtocol.h b/ios/integrations/RNSLifecycleListenerProtocol.h -new file mode 100644 -index 0000000000000000000000000000000000000000..025b4231c0b45f9f10034280037617b9b6d6fec4 ---- /dev/null -+++ b/ios/integrations/RNSLifecycleListenerProtocol.h -@@ -0,0 +1,14 @@ -+#import -+ -+NS_ASSUME_NONNULL_BEGIN -+ -+@protocol RNSLifecycleListenerProtocol -+ -+// Called when a screen in the presenting hierarchy is about to disappear. -+// @param screen The screen controller that is disappearing -+// @param isPresenterUnmounting YES if the presenter (modal) itself is being unmounted -+- (void)screenWillDisappear:(UIViewController *)screen isPresenterUnmounting:(BOOL)isPresenterUnmounting; -+ -+@end -+ -+NS_ASSUME_NONNULL_END diff --git a/CHANGELOG.md b/CHANGELOG.md index 23c93b7a..29437ac5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### 💡 Others + +- **iOS**: Refactored screen unmount detection to use C++ EventDispatcher instead of RNSLifecycleListenerProtocol. ([#410](https://github.com/lodev09/react-native-true-sheet/pull/410) by [@lodev09](https://github.com/lodev09)) + ## 3.7.3 ### 🐛 Bug fixes diff --git a/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h b/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h index 065f95a1..d6a44373 100644 --- a/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +++ b/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h @@ -18,6 +18,10 @@ class TrueSheetViewComponentDescriptor final concreteShadowNode.adjustLayoutWithState(); ConcreteComponentDescriptor::adopt(shadowNode); + +#if !defined(ANDROID) + concreteShadowNode.setEventDispatcher(eventDispatcher_); +#endif } }; diff --git a/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp b/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp index eeda2447..c6f27f50 100644 --- a/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +++ b/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp @@ -45,4 +45,17 @@ void TrueSheetViewShadowNode::adjustLayoutWithState() { } } +#if !defined(ANDROID) +void TrueSheetViewShadowNode::setEventDispatcher( + std::weak_ptr dispatcher) { + getStateDataMutable().setEventDispatcher(dispatcher); +} + +TrueSheetViewShadowNode::StateData & +TrueSheetViewShadowNode::getStateDataMutable() { + ensureUnsealed(); + return const_cast(getStateData()); +} +#endif + } // namespace facebook::react diff --git a/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h b/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h index 01ab9a60..d0efc832 100644 --- a/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +++ b/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h @@ -8,6 +8,8 @@ namespace facebook::react { +class EventDispatcher; + JSI_EXPORT extern const char TrueSheetViewComponentName[]; /* @@ -22,6 +24,8 @@ class JSI_EXPORT TrueSheetViewShadowNode final using ConcreteViewShadowNode::ConcreteViewShadowNode; public: + using StateData = ConcreteViewShadowNode::ConcreteStateData; + static ShadowNodeTraits BaseTraits() { auto traits = ConcreteViewShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::RootNodeKind); @@ -29,6 +33,13 @@ class JSI_EXPORT TrueSheetViewShadowNode final } void adjustLayoutWithState(); + +#if !defined(ANDROID) + void setEventDispatcher(std::weak_ptr dispatcher); + + private: + StateData &getStateDataMutable(); +#endif }; } // namespace facebook::react diff --git a/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp b/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp index 8b621130..a716c352 100644 --- a/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +++ b/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp @@ -8,4 +8,16 @@ folly::dynamic TrueSheetViewState::getDynamic() const { } #endif +#if !defined(ANDROID) +void TrueSheetViewState::setEventDispatcher( + std::weak_ptr dispatcher) { + eventDispatcher_ = dispatcher; +} + +std::weak_ptr TrueSheetViewState::getEventDispatcher() + const noexcept { + return eventDispatcher_; +} +#endif + } // namespace facebook::react diff --git a/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h b/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h index d96b6923..c87f13a5 100644 --- a/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +++ b/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h @@ -10,6 +10,8 @@ namespace facebook::react { +class EventDispatcher; + /* * State for component. * Contains the container dimensions from native. @@ -37,6 +39,14 @@ class TrueSheetViewState final { return MapBufferBuilder::EMPTY(); } #endif + +#if !defined(ANDROID) + void setEventDispatcher(std::weak_ptr dispatcher); + std::weak_ptr getEventDispatcher() const noexcept; + + private: + std::weak_ptr eventDispatcher_; +#endif }; } // namespace facebook::react diff --git a/docs/docs/guides/navigation.mdx b/docs/docs/guides/navigation.mdx index a9891770..8f4d4e5d 100644 --- a/docs/docs/guides/navigation.mdx +++ b/docs/docs/guides/navigation.mdx @@ -236,5 +236,5 @@ navigation.navigate('SomeScreen') ``` :::note -Requires a [patch to react-native-screens](https://github.com/lodev09/react-native-true-sheet/blob/main/.yarn/patches/react-native-screens-npm-4.18.0-fa7de65975.patch). See [PR #3415](https://github.com/software-mansion/react-native-screens/pull/3415), [PR #3527](https://github.com/software-mansion/react-native-screens/pull/3527). +Requires a [patch to react-native-screens](https://github.com/lodev09/react-native-true-sheet/blob/main/.yarn/patches/react-native-screens-npm-4.18.0-fa7de65975.patch). See [PR #3415](https://github.com/software-mansion/react-native-screens/pull/3415). ::: diff --git a/example/bare/ios/Podfile.lock b/example/bare/ios/Podfile.lock index a6a8f72e..97dfbc24 100644 --- a/example/bare/ios/Podfile.lock +++ b/example/bare/ios/Podfile.lock @@ -2646,7 +2646,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNTrueSheet (3.7.2): + - RNTrueSheet (3.7.3): - boost - DoubleConversion - fast_float @@ -3095,7 +3095,7 @@ SPEC CHECKSUMS: RNGestureHandler: e1cf8ef3f11045536eed6bd4f132b003ef5f9a5f RNReanimated: f1868b36f4b2b52a0ed00062cfda69506f75eaee RNScreens: d821082c6dd1cb397cc0c98b026eeafaa68be479 - RNTrueSheet: fdf1146eb62282d33c4982c2903593466913afdd + RNTrueSheet: ccd12867de774263d4c4f9dd4ea621900d69780c RNWorklets: d9c050940f140af5d8b611d937eab1cbfce5e9a5 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: 689c8e04277f3ad631e60fe2a08e41d411daf8eb diff --git a/example/bare/src/navigators/RootNavigator.tsx b/example/bare/src/navigators/RootNavigator.tsx index 0d35f781..02514e1b 100644 --- a/example/bare/src/navigators/RootNavigator.tsx +++ b/example/bare/src/navigators/RootNavigator.tsx @@ -1,6 +1,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import { MapScreen, StandardScreen } from '@example/shared/screens'; +import { MapScreen, StandardScreen, TestScreen } from '@example/shared/screens'; +import { DARK_BLUE } from '@example/shared/utils'; import { Map } from '@example/shared/components'; import { ModalStackNavigator } from './ModalStackNavigator'; import { SheetNavigator } from './SheetNavigator'; @@ -18,6 +19,7 @@ const MapScreenWrapper = () => { MapComponent={Map} onNavigateToModal={() => navigation.navigate('ModalStack')} onNavigateToSheetStack={() => navigation.navigate('SheetStack')} + onNavigateToTest={() => navigation.navigate('Test')} /> ); }; @@ -33,10 +35,15 @@ const StandardScreenWrapper = () => { ); }; +const TestScreenWrapper = () => { + const navigation = useAppNavigation(); + return navigation.goBack()} />; +}; + export const RootNavigator = () => { return ( { component={ModalStackNavigator} options={{ presentation: 'fullScreenModal', headerShown: false }} /> + ); }; diff --git a/example/expo/app/_layout.tsx b/example/expo/app/_layout.tsx index fbe2025c..3f7115ce 100644 --- a/example/expo/app/_layout.tsx +++ b/example/expo/app/_layout.tsx @@ -6,6 +6,7 @@ import * as SplashScreen from 'expo-splash-screen'; import { useEffect } from 'react'; import { useColorScheme } from 'react-native'; import { TrueSheetProvider } from '@lodev09/react-native-true-sheet'; +import { DARK_BLUE } from '@example/shared/utils'; import 'react-native-reanimated'; export { @@ -50,9 +51,12 @@ function RootLayoutNav() { return ( - + + router.push('/modal')} onNavigateToSheetStack={() => router.push('/sheet')} + onNavigateToTest={() => router.push('/test')} /> ); } diff --git a/example/expo/app/test.tsx b/example/expo/app/test.tsx new file mode 100644 index 00000000..9aec4658 --- /dev/null +++ b/example/expo/app/test.tsx @@ -0,0 +1,7 @@ +import { TestScreen } from '@example/shared/screens'; +import { useRouter } from 'expo-router'; + +export default function Test() { + const router = useRouter(); + return router.back()} />; +} diff --git a/example/shared/src/screens/MapScreen.tsx b/example/shared/src/screens/MapScreen.tsx index bcc58ba9..7b3da77c 100644 --- a/example/shared/src/screens/MapScreen.tsx +++ b/example/shared/src/screens/MapScreen.tsx @@ -41,12 +41,14 @@ export interface MapScreenProps { MapComponent: ComponentType<{ style?: StyleProp }>; onNavigateToModal?: () => void; onNavigateToSheetStack?: () => void; + onNavigateToTest?: () => void; } const MapScreenInner = ({ MapComponent, onNavigateToModal, onNavigateToSheetStack, + onNavigateToTest, }: MapScreenProps) => { const { height } = useWindowDimensions(); const { animatedPosition } = useReanimatedTrueSheet(); @@ -149,23 +151,35 @@ const MapScreenInner = ({ The true native bottom sheet experience.