Skip to content

Commit

Permalink
Merge pull request Expensify#43074 from Expensify/Rory-UseResponsiveL…
Browse files Browse the repository at this point in the history
…ayoutCurrentNavigator

Search bottom-up if a component is in a narrow modal navigator
  • Loading branch information
roryabraham authored Jun 5, 2024
2 parents 4780624 + 3d37f26 commit 3c04b02
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 53 deletions.
24 changes: 18 additions & 6 deletions src/hooks/useResponsiveLayout.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {useContext} from 'react';
import {NavigationContainerRefContext, NavigationContext} from '@react-navigation/native';
import {useContext, useMemo} from 'react';
import ModalContext from '@components/Modal/ModalContext';
import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
import useRootNavigationState from './useRootNavigationState';
import NAVIGATORS from '@src/NAVIGATORS';
import useWindowDimensions from './useWindowDimensions';

type ResponsiveLayoutResult = {
Expand Down Expand Up @@ -38,16 +38,28 @@ export default function useResponsiveLayout(): ResponsiveLayoutResult {
// This means it will only be defined if the component calling this hook is a child of a modal component. See BaseModal for the provider.
const {activeModalType} = useContext(ModalContext);

// This refers to the state of the root navigator, and is true if and only if the topmost navigator is the "left modal navigator" or the "right modal navigator"
const isDisplayedInModalNavigator = !!useRootNavigationState(Navigation.isModalNavigatorActive);
// We are using these contexts directly instead of useNavigation/useNavigationState, because those will throw an error if used outside a navigator.
// This hook can be used within or outside a navigator, so using useNavigationState does not work.
// Furthermore, wrapping useNavigationState in a try/catch does not work either, because that breaks the rules of hooks.
// Note that these three lines are copied closely from the internal implementation of useNavigation: https://github.com/react-navigation/react-navigation/blob/52a3234b7aaf4d4fcc9c0155f44f3ea2233f0f40/packages/core/src/useNavigation.tsx#L18-L28
const navigationContainerRef = useContext(NavigationContainerRefContext);
const navigator = useContext(NavigationContext);
const currentNavigator = navigator ?? navigationContainerRef;

const isDisplayedInNarrowModalNavigator = useMemo(
() =>
!!currentNavigator?.getParent?.(NAVIGATORS.RIGHT_MODAL_NAVIGATOR as unknown as undefined) ||
!!currentNavigator?.getParent?.(NAVIGATORS.LEFT_MODAL_NAVIGATOR as unknown as undefined),
[currentNavigator],
);

// The component calling this hook is in a "narrow pane modal" if:
const isInNarrowPaneModal =
// it's a child of the right-docked modal
activeModalType === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED ||
// or there's a "right modal navigator" or "left modal navigator" on the top of the root navigation stack
// and the component calling this hook is not the child of another modal type, such as a confirm modal
(isDisplayedInModalNavigator && !activeModalType);
(isDisplayedInNarrowModalNavigator && !activeModalType);

const shouldUseNarrowLayout = isSmallScreenWidth || isInNarrowPaneModal;

Expand Down
30 changes: 0 additions & 30 deletions src/hooks/useRootNavigationState.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import ModalNavigatorScreenOptions from '@libs/Navigation/AppNavigator/ModalNavigatorScreenOptions';
import type {AuthScreensParamList, LeftModalNavigatorParamList} from '@libs/Navigation/types';
import type NAVIGATORS from '@src/NAVIGATORS';
import NAVIGATORS from '@src/NAVIGATORS';
import SCREENS from '@src/SCREENS';
import Overlay from './Overlay';

Expand All @@ -32,7 +32,10 @@ function LeftModalNavigator({navigation}: LeftModalNavigatorProps) {
/>
)}
<View style={styles.LHPNavigatorContainer(isSmallScreenWidth)}>
<Stack.Navigator screenOptions={screenOptions}>
<Stack.Navigator
screenOptions={screenOptions}
id={NAVIGATORS.LEFT_MODAL_NAVIGATOR}
>
<Stack.Screen
name={SCREENS.LEFT_MODAL.CHAT_FINDER}
getComponent={loadChatFinder}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
import ModalNavigatorScreenOptions from '@libs/Navigation/AppNavigator/ModalNavigatorScreenOptions';
import * as ModalStackNavigators from '@libs/Navigation/AppNavigator/ModalStackNavigators';
import type {AuthScreensParamList, RightModalNavigatorParamList} from '@navigation/types';
import type NAVIGATORS from '@src/NAVIGATORS';
import NAVIGATORS from '@src/NAVIGATORS';
import SCREENS from '@src/SCREENS';
import Overlay from './Overlay';

Expand Down Expand Up @@ -36,7 +36,10 @@ function RightModalNavigator({navigation}: RightModalNavigatorProps) {
/>
)}
<View style={styles.RHPNavigatorContainer(isSmallScreenWidth)}>
<Stack.Navigator screenOptions={screenOptions}>
<Stack.Navigator
screenOptions={screenOptions}
id={NAVIGATORS.RIGHT_MODAL_NAVIGATOR}
>
<Stack.Screen
name={SCREENS.RIGHT_MODAL.SETTINGS}
component={ModalStackNavigators.SettingsModalStackNavigator}
Expand Down
14 changes: 1 addition & 13 deletions src/libs/Navigation/Navigation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {findFocusedRoute} from '@react-navigation/core';
import type {EventArg, NavigationContainerEventMap, NavigationState} from '@react-navigation/native';
import type {EventArg, NavigationContainerEventMap} from '@react-navigation/native';
import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native';
import Log from '@libs/Log';
import * as ReportUtils from '@libs/ReportUtils';
Expand Down Expand Up @@ -356,17 +356,6 @@ function navigateWithSwitchPolicyID(params: SwitchPolicyIDParams) {
return switchPolicyID(navigationRef.current, params);
}

/**
* Check if the modal is being displayed.
*
* @param state - MUST be the state of the root navigator for this to work. Do not use a child navigator state.
*/
function isModalNavigatorActive(state: NavigationState) {
const lastRoute = state?.routes?.at(-1);
const lastRouteName = lastRoute?.name;
return lastRouteName === NAVIGATORS.LEFT_MODAL_NAVIGATOR || lastRouteName === NAVIGATORS.RIGHT_MODAL_NAVIGATOR;
}

export default {
setShouldPopAllStateOnUP,
navigate,
Expand All @@ -386,7 +375,6 @@ export default {
parseHybridAppUrl,
navigateWithSwitchPolicyID,
resetToHome,
isModalNavigatorActive,
closeRHPFlow,
setNavigationActionToMicrotaskQueue,
};
Expand Down

0 comments on commit 3c04b02

Please sign in to comment.