diff --git a/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/index.tsx index 00b009efc089..9ac2ffd6c8f9 100644 --- a/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/index.tsx @@ -1,6 +1,6 @@ import type {ParamListBase} from '@react-navigation/native'; import {createNavigatorFactory} from '@react-navigation/native'; -import useNavigationResetOnLayoutChange from '@libs/Navigation/AppNavigator/useNavigationResetOnLayoutChange'; +import useNavigationResetRootOnLayoutChange from '@libs/Navigation/AppNavigator/useNavigationResetRootOnLayoutChange'; import createPlatformStackNavigatorComponent from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigatorComponent'; import defaultPlatformStackScreenOptions from '@libs/Navigation/PlatformStackNavigation/defaultPlatformStackScreenOptions'; import type {PlatformStackNavigationEventMap, PlatformStackNavigationOptions, PlatformStackNavigationState} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -12,7 +12,7 @@ const ResponsiveStackNavigatorComponent = createPlatformStackNavigatorComponent( createRouter: CustomRouter, defaultScreenOptions: defaultPlatformStackScreenOptions, useCustomState: useStateWithSearch, - useCustomEffects: useNavigationResetOnLayoutChange, + useCustomEffects: useNavigationResetRootOnLayoutChange, ExtraContent: RenderSearchRoute, }); diff --git a/src/libs/Navigation/AppNavigator/useNavigationResetOnLayoutChange.ts b/src/libs/Navigation/AppNavigator/useNavigationResetOnLayoutChange.ts index d86d38711f8c..0a671529ee7d 100644 --- a/src/libs/Navigation/AppNavigator/useNavigationResetOnLayoutChange.ts +++ b/src/libs/Navigation/AppNavigator/useNavigationResetOnLayoutChange.ts @@ -1,15 +1,23 @@ +import type {ParamListBase} from '@react-navigation/native'; import {useEffect} from 'react'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import navigationRef from '@libs/Navigation/navigationRef'; +import type {CustomEffectsHookProps} from '@libs/Navigation/PlatformStackNavigation/types'; -function useNavigationResetOnLayoutChange() { +/** + * This hook resets the navigation root state when changing the layout size, resetting the state calls the getRehydredState method in CustomFullScreenRouter.tsx. + * When the screen size is changed, it is necessary to check whether the application displays the content correctly. + * When the app is opened on a small layout and the user resizes it to wide, a second screen has to be present in the navigation state to fill the space. + */ +function useNavigationResetOnLayoutChange({navigation}: CustomEffectsHookProps) { const {shouldUseNarrowLayout} = useResponsiveLayout(); useEffect(() => { if (!navigationRef.isReady()) { return; } - navigationRef.resetRoot(navigationRef.getRootState()); + navigation.reset(navigation.getState()); + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [shouldUseNarrowLayout]); } diff --git a/src/libs/Navigation/AppNavigator/useNavigationResetRootOnLayoutChange.ts b/src/libs/Navigation/AppNavigator/useNavigationResetRootOnLayoutChange.ts new file mode 100644 index 000000000000..03caac57410f --- /dev/null +++ b/src/libs/Navigation/AppNavigator/useNavigationResetRootOnLayoutChange.ts @@ -0,0 +1,21 @@ +import {useEffect} from 'react'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import navigationRef from '@libs/Navigation/navigationRef'; + +/** + * This hook resets the navigation root state when changing the layout size, resetting the state calls the getRehydredState method in CustomRouter.ts. + * When the screen size is changed, it is necessary to check whether the application displays the content correctly. + * When the app is opened on a small layout and the user resizes it to wide, a second screen has to be present in the navigation state to fill the space. + */ +function useNavigationResetRootOnLayoutChange() { + const {shouldUseNarrowLayout} = useResponsiveLayout(); + + useEffect(() => { + if (!navigationRef.isReady()) { + return; + } + navigationRef.resetRoot(navigationRef.getRootState()); + }, [shouldUseNarrowLayout]); +} + +export default useNavigationResetRootOnLayoutChange; diff --git a/tests/ui/ResizeScreenTests.tsx b/tests/ui/ResizeScreenTests.tsx new file mode 100644 index 000000000000..5bd86ad152b2 --- /dev/null +++ b/tests/ui/ResizeScreenTests.tsx @@ -0,0 +1,95 @@ +import {NavigationContainer} from '@react-navigation/native'; +import {render, renderHook} from '@testing-library/react-native'; +import React from 'react'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import type ResponsiveLayoutResult from '@hooks/useResponsiveLayout/types'; +import getIsNarrowLayout from '@libs/getIsNarrowLayout'; +import createResponsiveStackNavigator from '@libs/Navigation/AppNavigator/createResponsiveStackNavigator'; +import BottomTabNavigator from '@libs/Navigation/AppNavigator/Navigators/BottomTabNavigator'; +import useNavigationResetRootOnLayoutChange from '@libs/Navigation/AppNavigator/useNavigationResetRootOnLayoutChange'; +import navigationRef from '@libs/Navigation/navigationRef'; +import type {AuthScreensParamList} from '@libs/Navigation/types'; +import ProfilePage from '@pages/settings/Profile/ProfilePage'; +import NAVIGATORS from '@src/NAVIGATORS'; +import SCREENS from '@src/SCREENS'; + +const RootStack = createResponsiveStackNavigator(); + +jest.mock('@hooks/useResponsiveLayout', () => jest.fn()); +jest.mock('@libs/getIsNarrowLayout', () => jest.fn()); + +jest.mock('@pages/settings/InitialSettingsPage'); +jest.mock('@pages/settings/Profile/ProfilePage'); +jest.mock('@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar'); + +const DEFAULT_USE_RESPONSIVE_LAYOUT_VALUE: ResponsiveLayoutResult = { + shouldUseNarrowLayout: true, + isSmallScreenWidth: true, + isInNarrowPaneModal: false, + isExtraSmallScreenHeight: false, + isMediumScreenWidth: false, + isLargeScreenWidth: false, + isExtraSmallScreenWidth: false, + isSmallScreen: false, + onboardingIsMediumOrLargerScreenWidth: false, +}; + +const INITIAL_STATE = { + routes: [ + { + name: NAVIGATORS.BOTTOM_TAB_NAVIGATOR, + state: { + index: 1, + routes: [{name: SCREENS.HOME}, {name: SCREENS.SETTINGS.ROOT}], + }, + }, + ], +}; + +const mockedGetIsNarrowLayout = getIsNarrowLayout as jest.MockedFunction; +const mockedUseResponsiveLayout = useResponsiveLayout as jest.MockedFunction; + +describe('Resize screen', () => { + it('Should display the settings profile after resizing the screen with the settings page opened to the wide layout', () => { + // Given the initialized navigation on the narrow layout with the settings screen + mockedGetIsNarrowLayout.mockReturnValue(true); + mockedUseResponsiveLayout.mockReturnValue({...DEFAULT_USE_RESPONSIVE_LAYOUT_VALUE, shouldUseNarrowLayout: true}); + + const {rerender} = renderHook(() => useNavigationResetRootOnLayoutChange()); + + render( + + + + + + + , + ); + + const rootStateBeforeResize = navigationRef.current?.getRootState(); + + expect(rootStateBeforeResize?.routes.at(0)?.name).toBe(NAVIGATORS.BOTTOM_TAB_NAVIGATOR); + expect(rootStateBeforeResize?.routes.at(1)).toBeUndefined(); + + // When resizing the screen to the wide layout + mockedGetIsNarrowLayout.mockReturnValue(false); + mockedUseResponsiveLayout.mockReturnValue({...DEFAULT_USE_RESPONSIVE_LAYOUT_VALUE, shouldUseNarrowLayout: false}); + rerender({}); + + const rootStateAfterResize = navigationRef.current?.getRootState(); + + // Then the settings profile page should be displayed on the screen + expect(rootStateAfterResize?.routes.at(0)?.name).toBe(NAVIGATORS.BOTTOM_TAB_NAVIGATOR); + expect(rootStateAfterResize?.routes.at(1)?.name).toBe(SCREENS.SETTINGS.PROFILE.ROOT); + }); +});