Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions apps/src/tests/TestModalNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import * as React from 'react';
import { View, Text, Button } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { NativeStackScreenProps, createNativeStackNavigator } from '@react-navigation/native-stack';
import {
type NativeStackScreenProps,
createNativeStackNavigator,
} from '@react-navigation/native-stack';

type StackParamList = {
Home: undefined;
Expand All @@ -13,7 +16,6 @@ type StackParamList = {
Screen3: undefined;
};


function HomeScreen({
navigation,
}: NativeStackScreenProps<StackParamList, 'Home'>) {
Expand All @@ -32,7 +34,9 @@ function HomeScreen({
);
}

function MainStackScreen({ navigation }: NativeStackScreenProps<StackParamList, 'MainStackScreen'>) {
function MainStackScreen({
navigation,
}: NativeStackScreenProps<StackParamList, 'MainStackScreen'>) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Main stack screen</Text>
Expand All @@ -41,21 +45,21 @@ function MainStackScreen({ navigation }: NativeStackScreenProps<StackParamList,
onPress={() => navigation.navigate('MainStackScreen2')}
/>
<Button
title="goBack"
onPress={() => navigation.goBack()}
title="Replace with the second modal"
onPress={() => navigation.replace('MainStackScreen2')}
/>
<Button title="goBack" onPress={() => navigation.goBack()} />
</View>
);
}

function MainStackScreen2({ navigation }: NativeStackScreenProps<StackParamList, 'MainStackScreen2'>) {
function MainStackScreen2({
navigation,
}: NativeStackScreenProps<StackParamList, 'MainStackScreen2'>) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Main stack screen 2</Text>
<Button
title="goBack"
onPress={() => navigation.goBack()}
/>
<Button title="goBack" onPress={() => navigation.goBack()} />
</View>
);
}
Expand Down Expand Up @@ -117,9 +121,21 @@ function RootStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="NestedStack" component={NestedStackScreen} options={{ headerShown: true, presentation: 'modal' }} />
<Stack.Screen name="MainStackScreen" component={MainStackScreen} options={{ headerShown: true, presentation: 'modal' }} />
<Stack.Screen name="MainStackScreen2" component={MainStackScreen2} options={{ headerShown: true, presentation: 'modal' }} />
<Stack.Screen
name="NestedStack"
component={NestedStackScreen}
options={{ headerShown: true, presentation: 'modal' }}
/>
<Stack.Screen
name="MainStackScreen"
component={MainStackScreen}
options={{ headerShown: true, presentation: 'modal' }}
/>
<Stack.Screen
name="MainStackScreen2"
component={MainStackScreen2}
options={{ headerShown: true, presentation: 'modal' }}
/>
</Stack.Navigator>
);
}
Expand Down
20 changes: 13 additions & 7 deletions ios/RNSScreenStack.mm
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,10 @@ - (void)setModalViewControllers:(NSArray<UIViewController *> *)controllers
// (2) there are modals presented by other RNSNavigationControllers (nested/outer),
// (3) there are modals presented by other controllers (e.g. React Native's Modal view).

// Last controller that is common for both _presentedModals & controllers
// Last controller that is common for both _presentedModals & controllers or this RNSNavigationController in case
// there is no common part.
__block UIViewController *changeRootController = _controller;

// Last common controller index + 1
NSUInteger changeRootIndex = 0;
for (NSUInteger i = 0; i < MIN(_presentedModals.count, controllers.count); i++) {
Expand Down Expand Up @@ -535,12 +537,11 @@ - (void)setModalViewControllers:(NSArray<UIViewController *> *)controllers
UIViewController *firstModalToBeDismissed = changeRootController.presentedViewController;

if (firstModalToBeDismissed != nil) {
BOOL shouldAnimate = changeRootIndex == controllers.count &&
[firstModalToBeDismissed isKindOfClass:[RNSScreen class]] &&
((RNSScreen *)firstModalToBeDismissed).screenView.stackAnimation != RNSScreenStackAnimationNone;
const BOOL firstModalToBeDismissedIsOwned = [firstModalToBeDismissed isKindOfClass:RNSScreen.class];
const BOOL firstModalToBeDismissedIsOwnedByThisStack =
firstModalToBeDismissedIsOwned && [_presentedModals containsObject:firstModalToBeDismissed];

if ([_presentedModals containsObject:firstModalToBeDismissed] ||
![firstModalToBeDismissed isKindOfClass:RNSScreen.class]) {
if (firstModalToBeDismissedIsOwnedByThisStack || !firstModalToBeDismissedIsOwned) {
// We dismiss every VC that was presented by changeRootController VC or its descendant.
// After the series of dismissals is completed we run completion block in which
// we present modals on top of changeRootController (which may be the this stack VC)
Expand All @@ -550,7 +551,12 @@ - (void)setModalViewControllers:(NSArray<UIViewController *> *)controllers
// For now, to mitigate the issue, we also decide to trigger its dismissal before
// starting the presentation chain down below in finish() callback.
if (!firstModalToBeDismissed.isBeingDismissed) {
[changeRootController dismissViewControllerAnimated:shouldAnimate completion:finish];
// If the modal is owned we let it control whether the dismissal is animated or not. For foreign controllers
// we just assume animation.
const BOOL firstModalToBeDismissedPrefersAnimation = firstModalToBeDismissedIsOwned
? static_cast<RNSScreen *>(firstModalToBeDismissed).screenView.stackAnimation != RNSScreenStackAnimationNone
: YES;
[changeRootController dismissViewControllerAnimated:firstModalToBeDismissedPrefersAnimation completion:finish];
} else {
// We need to wait for its dismissal and then run our presentation code.
// This happens, e.g. when we have foreign modal presented on top of owned one & we dismiss foreign one and
Expand Down
Loading