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 290e6943..e6f99e6f 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,19 +1,62 @@ diff --git a/ios/RNSScreen.mm b/ios/RNSScreen.mm +index 65c18f1ddccc64b3169e050e577eab77fd8c183b..8ffd9d4b48f85e53c6fcf0a76963cfcf946dad97 100644 --- a/ios/RNSScreen.mm +++ b/ios/RNSScreen.mm -@@ -605,6 +605,11 @@ - - (void)notifyWillDisappear - { +@@ -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]; } + -+ [[NSNotificationCenter defaultCenter] postNotificationName:@"RNSScreenViewWillDisappear" -+ object:self -+ userInfo:nil]; ++ // 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 +++ b/ios/RNSScreenStack.mm @@ -640,8 +640,10 @@ RNS_IGNORE_SUPER_CALL_END @@ -64,6 +107,7 @@ diff --git a/ios/RNSScreenStack.mm b/ios/RNSScreenStack.mm // We didn't detect any controllers for dismissal, thus we start presenting new VCs diff --git a/ios/integrations/RNSDismissibleModalProtocol.h b/ios/integrations/RNSDismissibleModalProtocol.h +index 006f809d104c1d4fbdf6eccca89d6c6e190cca71..89e297f1b7a9582fee3e19237dfba8d4c87a352f 100644 --- a/ios/integrations/RNSDismissibleModalProtocol.h +++ b/ios/integrations/RNSDismissibleModalProtocol.h @@ -1,3 +1,5 @@ @@ -86,3 +130,23 @@ diff --git a/ios/integrations/RNSDismissibleModalProtocol.h b/ios/integrations/R @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 66e6ed86..41e9aaa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ ### 🐛 Bug fixes -- Fixed sheet not dismissing when presenter screen is popped from navigation. ([#400](https://github.com/lodev09/react-native-true-sheet/pull/400) by [@lodev09](https://github.com/lodev09)) +- Fixed sheet not dismissing when presenter screen is popped from navigation. ([#400](https://github.com/lodev09/react-native-true-sheet/pull/400), [#402](https://github.com/lodev09/react-native-true-sheet/pull/402) by [@lodev09](https://github.com/lodev09)) - **iOS**: Fixed position tracking for pending detent changes. ([#394](https://github.com/lodev09/react-native-true-sheet/pull/394) by [@lodev09](https://github.com/lodev09)) - **Android**: Fixed keyboard and focus handling inside RN Modal. ([#387](https://github.com/lodev09/react-native-true-sheet/pull/387) by [@lodev09](https://github.com/lodev09)) diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt index 81748ef4..8245d702 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt @@ -129,9 +129,9 @@ class TrueSheetView(private val reactContext: ThemedReactContext) : if (child is TrueSheetContainerView) { child.delegate = null - // Dismiss the sheet when container is removed + // Dismiss when container is removed if (viewController.isPresented) { - viewController.dismiss(animated = false) + dismissAll(false) {} } } viewController.removeView(child) @@ -476,7 +476,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) : eventDispatcher?.dispatchEvent(BackPressEvent(surfaceId, id)) } - override fun viewControllerDidDetectPresenterDismiss() { + override fun viewControllerDidDetectScreenDismiss() { dismissAll(animated = true) {} } diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt index 6fc4b523..10c568e8 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt @@ -62,7 +62,7 @@ interface TrueSheetViewControllerDelegate { fun viewControllerWillBlur() fun viewControllerDidBlur() fun viewControllerDidBackPress() - fun viewControllerDidDetectPresenterDismiss() + fun viewControllerDidDetectScreenDismiss() } // ============================================================================= @@ -540,7 +540,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) : onNonModalScreenPushed = { // Only handle on root sheet (no parent) to trigger dismissAll if (isPresented && isSheetVisible && parentSheetView == null) { - delegate?.viewControllerDidDetectPresenterDismiss() + delegate?.viewControllerDidDetectScreenDismiss() } } ) diff --git a/docs/docs/guides/navigation.mdx b/docs/docs/guides/navigation.mdx index 2bb483d4..05aa6ffd 100644 --- a/docs/docs/guides/navigation.mdx +++ b/docs/docs/guides/navigation.mdx @@ -204,7 +204,7 @@ export default function SheetLayout() { ## Navigating from Sheets :::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) and [PR #3525](https://github.com/software-mansion/react-native-screens/pull/3525). +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 #3526](https://github.com/software-mansion/react-native-screens/pull/3526). ::: Navigate directly from sheets - they remain visible when presenting modals on top. diff --git a/example/bare/ios/Podfile.lock b/example/bare/ios/Podfile.lock index 526b8952..6cd13aa5 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.0-beta.3): + - RNTrueSheet (3.7.0): - boost - DoubleConversion - fast_float @@ -3095,7 +3095,7 @@ SPEC CHECKSUMS: RNGestureHandler: e1cf8ef3f11045536eed6bd4f132b003ef5f9a5f RNReanimated: f1868b36f4b2b52a0ed00062cfda69506f75eaee RNScreens: d821082c6dd1cb397cc0c98b026eeafaa68be479 - RNTrueSheet: 192e3d4a0e32be2f16a06d1f15e939b6e045af2d + RNTrueSheet: 98de027f434047a746fdf797927f2cfeaf84fc84 RNWorklets: d9c050940f140af5d8b611d937eab1cbfce5e9a5 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: 689c8e04277f3ad631e60fe2a08e41d411daf8eb diff --git a/example/bare/src/navigators/ModalStackNavigator.tsx b/example/bare/src/navigators/ModalStackNavigator.tsx index 050a0e43..896f4f4a 100644 --- a/example/bare/src/navigators/ModalStackNavigator.tsx +++ b/example/bare/src/navigators/ModalStackNavigator.tsx @@ -18,6 +18,11 @@ const ModalScreenWrapper = () => { ); }; +const TestScreenWrapper = () => { + const navigation = useNavigation>(); + return navigation.goBack()} />; +}; + export const ModalStackNavigator = () => { return ( { }} > - + ); }; diff --git a/example/bare/src/navigators/RootNavigator.tsx b/example/bare/src/navigators/RootNavigator.tsx index 60704944..0d35f781 100644 --- a/example/bare/src/navigators/RootNavigator.tsx +++ b/example/bare/src/navigators/RootNavigator.tsx @@ -1,6 +1,6 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import { MapScreen, StandardScreen, TestScreen } from '@example/shared/screens'; +import { MapScreen, StandardScreen } from '@example/shared/screens'; import { Map } from '@example/shared/components'; import { ModalStackNavigator } from './ModalStackNavigator'; import { SheetNavigator } from './SheetNavigator'; @@ -50,7 +50,6 @@ export const RootNavigator = () => { name="Standard" component={StandardScreenWrapper} /> - ; + const router = useRouter(); + return router.back()} />; } diff --git a/example/shared/src/screens/TestScreen.tsx b/example/shared/src/screens/TestScreen.tsx index 27eb4d7f..2bfeac20 100644 --- a/example/shared/src/screens/TestScreen.tsx +++ b/example/shared/src/screens/TestScreen.tsx @@ -6,7 +6,11 @@ import { BLUE, GAP, SPACING } from '../utils'; import { Button } from '../components'; import { BasicSheet, PromptSheet, FlatListSheet } from '../components/sheets'; -export const TestScreen = () => { +interface TestScreenProps { + onGoBack: () => void; +} + +export const TestScreen = ({ onGoBack }: TestScreenProps) => { const basicSheet = useRef(null); const promptSheet = useRef(null); const flatListSheet = useRef(null); @@ -14,6 +18,7 @@ export const TestScreen = () => { return ( +