From 18e58a3b12565ff46976072502ccd3cd538da925 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 6 Mar 2025 11:23:36 +0100 Subject: [PATCH 01/22] Fix Help RHP does not close when clicking outside RHP or with ESC key --- src/components/SidePane/SidePaneOverlay.tsx | 59 +++++++++++++++++++ src/components/SidePane/index.tsx | 19 +++--- .../Navigation/AppNavigator/AuthScreens.tsx | 4 +- .../useRootNavigatorScreenOptions.ts | 2 - src/styles/index.ts | 9 ++- 5 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 src/components/SidePane/SidePaneOverlay.tsx diff --git a/src/components/SidePane/SidePaneOverlay.tsx b/src/components/SidePane/SidePaneOverlay.tsx new file mode 100644 index 000000000000..a561bd226a9d --- /dev/null +++ b/src/components/SidePane/SidePaneOverlay.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import Animated, {Easing, Keyframe} from 'react-native-reanimated'; +import {PressableWithoutFeedback} from '@components/Pressable'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; + +type SidePaneOverlayProps = { + /** Whether the side pane is displayed inside of RHP */ + isInNarrowPaneModal: boolean; + + /** Callback fired when pressing the backdrop */ + onBackdropPress: () => void; +}; + +const easing = Easing.bezier(0.76, 0.0, 0.24, 1.0); + +const CustomFadeIn = new Keyframe({ + from: {opacity: 0}, + to: { + opacity: 0.72, + // @ts-expect-error Types mismatch in reanimated, should to be fixed in 3.17 + easing, + }, +}).duration(CONST.MODAL.ANIMATION_TIMING.DEFAULT_IN); + +const CustomFadeOut = new Keyframe({ + from: {opacity: 0.72}, + to: { + opacity: 0, + // @ts-expect-error Types mismatch in reanimated, should to be fixed in 3.17 + easing, + }, +}).duration(CONST.MODAL.ANIMATION_TIMING.DEFAULT_OUT); + +/* eslint-disable @typescript-eslint/no-unsafe-call */ +function SidePaneOverlay({isInNarrowPaneModal, onBackdropPress}: SidePaneOverlayProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + return ( + + + + ); +} + +SidePaneOverlay.displayName = 'SidePaneOverlay'; + +export default SidePaneOverlay; diff --git a/src/components/SidePane/index.tsx b/src/components/SidePane/index.tsx index 5caa4925dd54..cc281a331750 100644 --- a/src/components/SidePane/index.tsx +++ b/src/components/SidePane/index.tsx @@ -3,8 +3,8 @@ import React, {useCallback, useEffect, useRef} from 'react'; // eslint-disable-next-line no-restricted-imports import {Animated, View} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import Backdrop from '@components/Modal/BottomDockedModal/Backdrop'; import ScreenWrapper from '@components/ScreenWrapper'; +import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSidePane from '@hooks/useSidePane'; @@ -12,10 +12,12 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {triggerSidePane} from '@libs/actions/SidePane'; import Navigation from '@libs/Navigation/Navigation'; import {substituteRouteParameters} from '@libs/SidePaneUtils'; +import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import getHelpContent from './getHelpContent'; +import SidePaneOverlay from './SidePaneOverlay'; -function SidePane({shouldShowOverlay = false}: {shouldShowOverlay?: boolean}) { +function SidePane() { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -49,6 +51,8 @@ function SidePane({shouldShowOverlay = false}: {shouldShowOverlay?: boolean}) { } }, [isExtraLargeScreenWidth, onClose]); + useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ESCAPE, () => onClose(), {shouldBubble: shouldHideSidePane, isActive: !isExtraLargeScreenWidth}); + if (shouldHideSidePane) { return null; } @@ -56,10 +60,10 @@ function SidePane({shouldShowOverlay = false}: {shouldShowOverlay?: boolean}) { return ( <> - {shouldShowOverlay && !shouldHideSidePaneBackdrop && !isInNarrowPaneModal && ( - )} @@ -81,11 +85,6 @@ function SidePane({shouldShowOverlay = false}: {shouldShowOverlay?: boolean}) { ); } -function SidePaneWithOverlay() { - return ; -} - SidePane.displayName = 'SidePane'; export default SidePane; -export {SidePaneWithOverlay, useSidePane as useAnimatedPaddingRight}; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index e33ef15cfdcd..a7777cf51fc5 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -11,7 +11,7 @@ import OptionsListContextProvider from '@components/OptionListContextProvider'; import {SearchContextProvider} from '@components/Search/SearchContext'; import {useSearchRouterContext} from '@components/Search/SearchRouter/SearchRouterContext'; import SearchRouterModal from '@components/Search/SearchRouter/SearchRouterModal'; -import {SidePaneWithOverlay} from '@components/SidePane'; +import SidePane from '@components/SidePane'; import TestToolsModal from '@components/TestToolsModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnboardingFlowRouter from '@hooks/useOnboardingFlow'; @@ -457,7 +457,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie {/* This has to be the first navigator in auth screens. */} { presentation: Presentation.TRANSPARENT_MODAL, cardStyleInterpolator: (props: StackCardInterpolationProps) => modalCardStyleInterpolator({props, shouldAnimateSidePane: true}), }, - // @ts-expect-error SidePane is a custom screen option that was added in a patch (when we migrate to react-navigation v7 we can use screenLayout instead) - sidePane: SidePane, }, basicModalNavigator: { presentation: Presentation.TRANSPARENT_MODAL, diff --git a/src/styles/index.ts b/src/styles/index.ts index 5f21a6e4324d..c1dfabdf4d6f 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5458,12 +5458,15 @@ const styles = (theme: ThemeColors) => alignSelf: 'center', }, - sidePaneOverlay: { + sidePaneOverlay: (isOverlayVisible: boolean) => ({ ...positioning.pFixed, + top: 0, + bottom: 0, + left: 0, right: -variables.sideBarWidth, backgroundColor: theme.overlay, - opacity: variables.overlayOpacity, - }, + opacity: isOverlayVisible ? 0 : variables.overlayOpacity, + }), sidePaneContainer: (shouldUseNarrowLayout: boolean, isExtraLargeScreenWidth: boolean): ViewStyle => ({ position: Platform.OS === 'web' ? 'fixed' : 'absolute', right: 0, From d4841c129782ce7aac1e6c60e1a5b6418e7a92af Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 6 Mar 2025 11:43:37 +0100 Subject: [PATCH 02/22] Fix eslint --- .../Navigation/AppNavigator/useRootNavigatorScreenOptions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/useRootNavigatorScreenOptions.ts b/src/libs/Navigation/AppNavigator/useRootNavigatorScreenOptions.ts index c152c09eb9cb..6b92532542f0 100644 --- a/src/libs/Navigation/AppNavigator/useRootNavigatorScreenOptions.ts +++ b/src/libs/Navigation/AppNavigator/useRootNavigatorScreenOptions.ts @@ -1,5 +1,4 @@ import type {StackCardInterpolationProps} from '@react-navigation/stack'; -import SidePane from '@components/SidePane'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import Animations from '@libs/Navigation/PlatformStackNavigation/navigationOptions/animation'; From a2ab1f5d0e35253cb424a3eaa74ca05c81908596 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 6 Mar 2025 13:25:40 +0100 Subject: [PATCH 03/22] Fix tests --- src/components/SidePane/SidePaneOverlay.tsx | 37 ++++++++++----------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/components/SidePane/SidePaneOverlay.tsx b/src/components/SidePane/SidePaneOverlay.tsx index a561bd226a9d..382f71dee14c 100644 --- a/src/components/SidePane/SidePaneOverlay.tsx +++ b/src/components/SidePane/SidePaneOverlay.tsx @@ -15,29 +15,28 @@ type SidePaneOverlayProps = { const easing = Easing.bezier(0.76, 0.0, 0.24, 1.0); -const CustomFadeIn = new Keyframe({ - from: {opacity: 0}, - to: { - opacity: 0.72, - // @ts-expect-error Types mismatch in reanimated, should to be fixed in 3.17 - easing, - }, -}).duration(CONST.MODAL.ANIMATION_TIMING.DEFAULT_IN); - -const CustomFadeOut = new Keyframe({ - from: {opacity: 0.72}, - to: { - opacity: 0, - // @ts-expect-error Types mismatch in reanimated, should to be fixed in 3.17 - easing, - }, -}).duration(CONST.MODAL.ANIMATION_TIMING.DEFAULT_OUT); - -/* eslint-disable @typescript-eslint/no-unsafe-call */ function SidePaneOverlay({isInNarrowPaneModal, onBackdropPress}: SidePaneOverlayProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); + const CustomFadeIn = new Keyframe({ + from: {opacity: 0}, + to: { + opacity: 0.72, + // @ts-expect-error Types mismatch in reanimated, should to be fixed in 3.17 + easing, + }, + }).duration(CONST.MODAL.ANIMATION_TIMING.DEFAULT_IN); + + const CustomFadeOut = new Keyframe({ + from: {opacity: 0.72}, + to: { + opacity: 0, + // @ts-expect-error Types mismatch in reanimated, should to be fixed in 3.17 + easing, + }, + }).duration(CONST.MODAL.ANIMATION_TIMING.DEFAULT_OUT); + return ( Date: Thu, 6 Mar 2025 12:48:05 +0100 Subject: [PATCH 04/22] SidePane navigation fixes --- .../Navigation/TopLevelBottomTabBar/index.tsx | 24 ++++++++-------- src/components/SidePane/index.tsx | 28 +++++++++---------- src/hooks/useSidePane.ts | 2 -- .../Navigation/AppNavigator/AuthScreens.tsx | 7 +---- 4 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/components/Navigation/TopLevelBottomTabBar/index.tsx b/src/components/Navigation/TopLevelBottomTabBar/index.tsx index 2f99c4936116..e34883f90549 100644 --- a/src/components/Navigation/TopLevelBottomTabBar/index.tsx +++ b/src/components/Navigation/TopLevelBottomTabBar/index.tsx @@ -3,8 +3,8 @@ import React, {useContext, useEffect, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import {FullScreenBlockingViewContext} from '@components/FullScreenBlockingViewContextProvider'; import BottomTabBar from '@components/Navigation/BottomTabBar'; +import SidePane from '@components/SidePane'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useSidePane from '@hooks/useSidePane'; import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets'; import useThemeStyles from '@hooks/useThemeStyles'; import type {PlatformStackNavigationState} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -31,11 +31,10 @@ function TopLevelBottomTabBar({state}: TopLevelBottomTabBarProps) { const [isAfterClosingTransition, setIsAfterClosingTransition] = useState(false); const cancelAfterInteractions = useRef | undefined>(); const {isBlockingViewVisible} = useContext(FullScreenBlockingViewContext); - const {shouldHideTopLevelBottomBar} = useSidePane(); // That means it's visible and it's not covered by the overlay. - const isBottomTabVisibleDirectly = getIsBottomTabVisibleDirectly(state) && !shouldHideTopLevelBottomBar; - const isScreenWithBottomTabFocused = getIsScreenWithBottomTabFocused(state) && !shouldHideTopLevelBottomBar; + const isBottomTabVisibleDirectly = getIsBottomTabVisibleDirectly(state); + const isScreenWithBottomTabFocused = getIsScreenWithBottomTabFocused(state); const selectedTab = getSelectedTab(state); const shouldDisplayBottomBar = shouldUseNarrowLayout ? isScreenWithBottomTabFocused : isBottomTabVisibleDirectly; @@ -56,15 +55,18 @@ function TopLevelBottomTabBar({state}: TopLevelBottomTabBarProps) { }, [shouldDisplayBottomBar]); return ( - - {/* We are not rendering BottomTabBar conditionally for two reasons + <> + + + {/* We are not rendering BottomTabBar conditionally for two reasons 1. It's faster to hide/show it than mount a new when needed. 2. We need to hide tooltips as well if they were displayed. */} - - + + + ); } diff --git a/src/components/SidePane/index.tsx b/src/components/SidePane/index.tsx index 5caa4925dd54..bee7fa3a5952 100644 --- a/src/components/SidePane/index.tsx +++ b/src/components/SidePane/index.tsx @@ -1,5 +1,6 @@ -import {findFocusedRoute, useNavigationState} from '@react-navigation/native'; -import React, {useCallback, useEffect, useRef} from 'react'; +import type {ParamListBase} from '@react-navigation/native'; +import {findFocusedRoute} from '@react-navigation/native'; +import React, {useCallback, useEffect, useMemo, useRef} from 'react'; // eslint-disable-next-line no-restricted-imports import {Animated, View} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -11,22 +12,26 @@ import useSidePane from '@hooks/useSidePane'; import useThemeStyles from '@hooks/useThemeStyles'; import {triggerSidePane} from '@libs/actions/SidePane'; import Navigation from '@libs/Navigation/Navigation'; +import type {PlatformStackNavigationState} from '@libs/Navigation/PlatformStackNavigation/types'; import {substituteRouteParameters} from '@libs/SidePaneUtils'; -import NAVIGATORS from '@src/NAVIGATORS'; import getHelpContent from './getHelpContent'; -function SidePane({shouldShowOverlay = false}: {shouldShowOverlay?: boolean}) { +type SidePaneProps = { + state: PlatformStackNavigationState; +}; + +function SidePane({state}: SidePaneProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const {isExtraLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); + const {isExtraLargeScreenWidth, shouldUseNarrowLayout, isInNarrowPaneModal} = useResponsiveLayout(); const {sidePaneTranslateX, shouldHideSidePane, shouldHideSidePaneBackdrop} = useSidePane(); - const {route, isInNarrowPaneModal} = useNavigationState((state) => { + const route = useMemo(() => { const params = (findFocusedRoute(state)?.params as Record) ?? {}; const activeRoute = Navigation.getActiveRouteWithoutParams(); - return {route: substituteRouteParameters(activeRoute, params), isInNarrowPaneModal: state.routes.some((r) => r.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR)}; - }); + return substituteRouteParameters(activeRoute, params); + }, [state]); const onClose = useCallback( (shouldUpdateNarrow = false) => { @@ -56,7 +61,7 @@ function SidePane({shouldShowOverlay = false}: {shouldShowOverlay?: boolean}) { return ( <> - {shouldShowOverlay && !shouldHideSidePaneBackdrop && !isInNarrowPaneModal && ( + {!shouldHideSidePaneBackdrop && !isInNarrowPaneModal && ( ; -} - SidePane.displayName = 'SidePane'; export default SidePane; -export {SidePaneWithOverlay, useSidePane as useAnimatedPaddingRight}; diff --git a/src/hooks/useSidePane.ts b/src/hooks/useSidePane.ts index a2d7fa73b14c..0a70e8f2f5a2 100644 --- a/src/hooks/useSidePane.ts +++ b/src/hooks/useSidePane.ts @@ -35,7 +35,6 @@ function useSidePane() { const [shouldHideSidePane, setShouldHideSidePane] = useState(true); const shouldHideSidePaneBackdrop = isPaneHidden || isExtraLargeScreenWidth || shouldUseNarrowLayout; - const shouldHideTopLevelBottomBar = !shouldHideSidePaneBackdrop || (!isPaneHidden && shouldUseNarrowLayout); const sidePaneOffset = useRef(new Animated.Value(shouldApplySidePaneOffset ? variables.sideBarWidth : 0)); const sidePaneTranslateX = useRef(new Animated.Value(isPaneHidden ? sidePaneWidth : 0)); @@ -65,7 +64,6 @@ function useSidePane() { sidePane, shouldHideSidePane, shouldHideSidePaneBackdrop, - shouldHideTopLevelBottomBar, sidePaneOffset, sidePaneTranslateX, }; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 1225219be0c6..7b52887f3905 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -11,7 +11,6 @@ import OptionsListContextProvider from '@components/OptionListContextProvider'; import {SearchContextProvider} from '@components/Search/SearchContext'; import {useSearchRouterContext} from '@components/Search/SearchRouter/SearchRouterContext'; import SearchRouterModal from '@components/Search/SearchRouter/SearchRouterModal'; -import {SidePaneWithOverlay} from '@components/SidePane'; import TestToolsModal from '@components/TestToolsModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnboardingFlowRouter from '@hooks/useOnboardingFlow'; @@ -453,11 +452,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie return ( - + {/* This has to be the first navigator in auth screens. */} Date: Thu, 6 Mar 2025 13:39:37 +0100 Subject: [PATCH 05/22] Move SidePane over BottomBar --- src/components/Navigation/TopLevelBottomTabBar/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Navigation/TopLevelBottomTabBar/index.tsx b/src/components/Navigation/TopLevelBottomTabBar/index.tsx index e34883f90549..fc9ad86fe328 100644 --- a/src/components/Navigation/TopLevelBottomTabBar/index.tsx +++ b/src/components/Navigation/TopLevelBottomTabBar/index.tsx @@ -56,7 +56,6 @@ function TopLevelBottomTabBar({state}: TopLevelBottomTabBarProps) { return ( <> - {/* We are not rendering BottomTabBar conditionally for two reasons 1. It's faster to hide/show it than mount a new when needed. @@ -66,6 +65,7 @@ function TopLevelBottomTabBar({state}: TopLevelBottomTabBarProps) { isTooltipAllowed={isReadyToDisplayBottomBar} /> + ); } From 3c0f398d7c738c5ffff393fb415d95b001251759 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 6 Mar 2025 13:39:51 +0100 Subject: [PATCH 06/22] Remove ScreenWrapper from the sidepane --- src/components/SidePane/index.tsx | 38 +++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/components/SidePane/index.tsx b/src/components/SidePane/index.tsx index bee7fa3a5952..07c501994ef0 100644 --- a/src/components/SidePane/index.tsx +++ b/src/components/SidePane/index.tsx @@ -5,15 +5,16 @@ import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import {Animated, View} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Backdrop from '@components/Modal/BottomDockedModal/Backdrop'; -import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSidePane from '@hooks/useSidePane'; +import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets'; import useThemeStyles from '@hooks/useThemeStyles'; import {triggerSidePane} from '@libs/actions/SidePane'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackNavigationState} from '@libs/Navigation/PlatformStackNavigation/types'; import {substituteRouteParameters} from '@libs/SidePaneUtils'; +import NAVIGATORS from '@src/NAVIGATORS'; import getHelpContent from './getHelpContent'; type SidePaneProps = { @@ -24,13 +25,18 @@ function SidePane({state}: SidePaneProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const {isExtraLargeScreenWidth, shouldUseNarrowLayout, isInNarrowPaneModal} = useResponsiveLayout(); + const {isExtraLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const {sidePaneTranslateX, shouldHideSidePane, shouldHideSidePaneBackdrop} = useSidePane(); + const {paddingTop} = useStyledSafeAreaInsets(); - const route = useMemo(() => { + const {route, isInNarrowPaneModal} = useMemo(() => { const params = (findFocusedRoute(state)?.params as Record) ?? {}; const activeRoute = Navigation.getActiveRouteWithoutParams(); - return substituteRouteParameters(activeRoute, params); + + return { + route: substituteRouteParameters(activeRoute, params), + isInNarrowPaneModal: state?.routes?.some((r) => r.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR), + }; }, [state]); const onClose = useCallback( @@ -68,19 +74,17 @@ function SidePane({state}: SidePaneProps) { /> )} - - - onClose(false)} - onCloseButtonPress={() => onClose(false)} - shouldShowBackButton={!isExtraLargeScreenWidth} - shouldShowCloseButton={isExtraLargeScreenWidth} - shouldDisplayHelpButton={false} - /> - {getHelpContent(styles, route)} - + + onClose(false)} + onCloseButtonPress={() => onClose(false)} + shouldShowBackButton={!isExtraLargeScreenWidth} + shouldShowCloseButton={isExtraLargeScreenWidth} + shouldDisplayHelpButton={false} + /> + {getHelpContent(styles, route)} ); From 20e024c91f1de01e80dc754bc5229a6c29722e30 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 6 Mar 2025 13:49:03 +0100 Subject: [PATCH 07/22] Remove sidePane patch --- ...navigation+core+6.4.11+004+side-pane.patch | 92 ------------------- 1 file changed, 92 deletions(-) delete mode 100644 patches/@react-navigation+core+6.4.11+004+side-pane.patch diff --git a/patches/@react-navigation+core+6.4.11+004+side-pane.patch b/patches/@react-navigation+core+6.4.11+004+side-pane.patch deleted file mode 100644 index 995c37eedd04..000000000000 --- a/patches/@react-navigation+core+6.4.11+004+side-pane.patch +++ /dev/null @@ -1,92 +0,0 @@ -diff --git a/node_modules/@react-navigation/core/lib/module/useDescriptors.js b/node_modules/@react-navigation/core/lib/module/useDescriptors.js -index 76fdab1..75f315c 100644 ---- a/node_modules/@react-navigation/core/lib/module/useDescriptors.js -+++ b/node_modules/@react-navigation/core/lib/module/useDescriptors.js -@@ -112,6 +112,19 @@ export default function useDescriptors(_ref, convertCustomScreenOptions) { - } - return o; - }); -+ const SidePane = customOptions.sidePane; -+ let element = /*#__PURE__*/React.createElement(React.Fragment, { -+ children: [/*#__PURE__*/React.createElement(SceneView, { -+ navigation: navigation, -+ route: route, -+ screen: screen, -+ routeState: state.routes[i].state, -+ getState: getState, -+ setState: setState, -+ options: customOptions, -+ clearOptions: clearOptions -+ }), SidePane && /*#__PURE__*/React.createElement(SidePane, {})] -+ }); - acc[route.key] = { - route, - // @ts-expect-error: it's missing action helpers, fix later -@@ -123,17 +136,10 @@ export default function useDescriptors(_ref, convertCustomScreenOptions) { - }, /*#__PURE__*/React.createElement(NavigationContext.Provider, { - value: navigation - }, /*#__PURE__*/React.createElement(NavigationRouteContext.Provider, { -- value: route -- }, /*#__PURE__*/React.createElement(SceneView, { -- navigation: navigation, -- route: route, -- screen: screen, -- routeState: state.routes[i].state, -- getState: getState, -- setState: setState, -- options: mergedOptions, -- clearOptions: clearOptions -- })))); -+ value: route, -+ children: element -+ }, -+ ))); - }, - options: mergedOptions - }; -diff --git a/node_modules/@react-navigation/core/src/useDescriptors.tsx b/node_modules/@react-navigation/core/src/useDescriptors.tsx -index 2e4ee0f..11ece43 100644 ---- a/node_modules/@react-navigation/core/src/useDescriptors.tsx -+++ b/node_modules/@react-navigation/core/src/useDescriptors.tsx -@@ -238,6 +238,23 @@ export default function useDescriptors< - return o; - }); - -+ const SidePane = (customOptions as any).sidePane; -+ let element = ( -+ <> -+ -+ {SidePane && } -+ -+ ); -+ - acc[route.key] = { - route, - // @ts-expect-error: it's missing action helpers, fix later -@@ -247,16 +264,7 @@ export default function useDescriptors< - - - -- -+ {element} - - - From 23edfed7a884860847b94e9cc07c7394f7c233a9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 7 Mar 2025 09:25:50 +0100 Subject: [PATCH 08/22] Add documentation for substituteRouteParameters function in SidePaneUtils --- src/libs/SidePaneUtils.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libs/SidePaneUtils.ts b/src/libs/SidePaneUtils.ts index d9b84edae1a7..3c59fc3940d8 100644 --- a/src/libs/SidePaneUtils.ts +++ b/src/libs/SidePaneUtils.ts @@ -1,3 +1,11 @@ +/** + * This function is used to substitute the route parameters in the route string with the actual parameter names + * + * Example: + * route: /workspaces/123/rules/456 + * params: {workspaceID: '123', ruleID: '456'} + * result: /workspaces/:workspaceID/rules/:ruleID + */ function substituteRouteParameters(route: string, params: Record) { let updatedRoute = route; From ebc5cbbacfe175a69e9deca910dee7c07c0fcec1 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 7 Mar 2025 09:26:00 +0100 Subject: [PATCH 09/22] Change onyx name for nvp --- src/ONYXKEYS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 72cbcee20e34..2572883b1849 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -480,7 +480,7 @@ const ONYXKEYS = { TRAVEL_PROVISIONING: 'travelProvisioning', /** Stores the information about the state of side panel */ - NVP_SIDE_PANE: 'nvp_sidePaneExpanded', + NVP_SIDE_PANE: 'nvp_sidePane', /** Collection Keys */ COLLECTION: { From e892e91eb9eb64043f5862abc3963e846dc53159 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 7 Mar 2025 09:26:34 +0100 Subject: [PATCH 10/22] Show HelpButton on staging by default --- src/components/SidePane/HelpButton.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/SidePane/HelpButton.tsx b/src/components/SidePane/HelpButton.tsx index e945d0d1fe33..1003b9a3b26d 100644 --- a/src/components/SidePane/HelpButton.tsx +++ b/src/components/SidePane/HelpButton.tsx @@ -5,6 +5,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {PressableWithoutFeedback} from '@components/Pressable'; import Tooltip from '@components/Tooltip'; +import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; @@ -24,8 +25,10 @@ function HelpButton({style}: HelpButtonProps) { const [sidePane] = useOnyx(ONYXKEYS.NVP_SIDE_PANE); const [language] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE); const {isExtraLargeScreenWidth} = useResponsiveLayout(); + const {isProduction} = useEnvironment(); - if (!sidePane || language !== CONST.LOCALES.EN) { + const shouldHideHelpButton = !sidePane || language !== CONST.LOCALES.EN; + if (shouldHideHelpButton && isProduction) { return null; } @@ -33,7 +36,7 @@ function HelpButton({style}: HelpButtonProps) { triggerSidePane(isExtraLargeScreenWidth ? !sidePane?.open : !sidePane?.openNarrowScreen, {shouldUpdateNarrowLayout: !isExtraLargeScreenWidth})} > Date: Fri, 7 Mar 2025 10:58:44 +0100 Subject: [PATCH 11/22] Centralize helpButton logic in useSidePane --- src/components/SidePane/HelpButton.tsx | 42 +++++++++++++------------- src/hooks/useSidePane.ts | 15 +++++++-- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/components/SidePane/HelpButton.tsx b/src/components/SidePane/HelpButton.tsx index 1003b9a3b26d..37a82bc80ef9 100644 --- a/src/components/SidePane/HelpButton.tsx +++ b/src/components/SidePane/HelpButton.tsx @@ -1,18 +1,16 @@ import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; -import {useOnyx} from 'react-native-onyx'; +import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {PressableWithoutFeedback} from '@components/Pressable'; import Tooltip from '@components/Tooltip'; -import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useSidePane from '@hooks/useSidePane'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {triggerSidePane} from '@libs/actions/SidePane'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; type HelpButtonProps = { style?: StyleProp; @@ -22,29 +20,31 @@ function HelpButton({style}: HelpButtonProps) { const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); - const [sidePane] = useOnyx(ONYXKEYS.NVP_SIDE_PANE); - const [language] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE); const {isExtraLargeScreenWidth} = useResponsiveLayout(); - const {isProduction} = useEnvironment(); + const {sidePane, shouldHideHelpButton} = useSidePane(); - const shouldHideHelpButton = !sidePane || language !== CONST.LOCALES.EN; - if (shouldHideHelpButton && isProduction) { + if (shouldHideHelpButton) { return null; } return ( - - triggerSidePane(isExtraLargeScreenWidth ? !sidePane?.open : !sidePane?.openNarrowScreen, {shouldUpdateNarrowLayout: !isExtraLargeScreenWidth})} - > - - - + + + triggerSidePane(isExtraLargeScreenWidth ? !sidePane?.open : !sidePane?.openNarrowScreen, {shouldUpdateNarrowLayout: !isExtraLargeScreenWidth})} + > + + + + ); } diff --git a/src/hooks/useSidePane.ts b/src/hooks/useSidePane.ts index 0a70e8f2f5a2..cf196532213f 100644 --- a/src/hooks/useSidePane.ts +++ b/src/hooks/useSidePane.ts @@ -8,6 +8,7 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; +import useEnvironment from './useEnvironment'; import useResponsiveLayout from './useResponsiveLayout'; import useWindowDimensions from './useWindowDimensions'; @@ -25,10 +26,12 @@ function isSidePaneHidden(sidePane: OnyxEntry, isExtraLargeS function useSidePane() { const {isExtraLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const {windowWidth} = useWindowDimensions(); + const {isProduction} = useEnvironment(); - const [sidePane] = useOnyx(ONYXKEYS.NVP_SIDE_PANE); + const [sidePaneNVP] = useOnyx(ONYXKEYS.NVP_SIDE_PANE); const [language] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE); - const isPaneHidden = isSidePaneHidden(sidePane, isExtraLargeScreenWidth) || language !== CONST.LOCALES.EN; + const isLanguageUnsupported = language !== CONST.LOCALES.EN; + const isPaneHidden = isSidePaneHidden(sidePaneNVP, isExtraLargeScreenWidth) || isLanguageUnsupported; const sidePaneWidth = shouldUseNarrowLayout ? windowWidth : variables.sideBarWidth; const shouldApplySidePaneOffset = isExtraLargeScreenWidth && !isPaneHidden; @@ -36,6 +39,11 @@ function useSidePane() { const [shouldHideSidePane, setShouldHideSidePane] = useState(true); const shouldHideSidePaneBackdrop = isPaneHidden || isExtraLargeScreenWidth || shouldUseNarrowLayout; + // The help button is hidden in production if the side pane nvp is not present or the language is unsupported. + const shouldHideOnProduction = isProduction && (!sidePaneNVP || isLanguageUnsupported); + // The help button is also hidden if the side pane is displayed currently. + const shouldHideHelpButton = shouldHideOnProduction || !shouldHideSidePane; + const sidePaneOffset = useRef(new Animated.Value(shouldApplySidePaneOffset ? variables.sideBarWidth : 0)); const sidePaneTranslateX = useRef(new Animated.Value(isPaneHidden ? sidePaneWidth : 0)); @@ -61,9 +69,10 @@ function useSidePane() { }, [isPaneHidden, shouldApplySidePaneOffset, shouldUseNarrowLayout, sidePaneWidth]); return { - sidePane, + sidePane: sidePaneNVP, shouldHideSidePane, shouldHideSidePaneBackdrop, + shouldHideHelpButton, sidePaneOffset, sidePaneTranslateX, }; From 55b0f069d44f23735cf0cff2fc00fae7093d6f0c Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 7 Mar 2025 11:03:10 +0100 Subject: [PATCH 12/22] Remove unused FadeOut animation from HelpButton component --- src/components/SidePane/HelpButton.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/SidePane/HelpButton.tsx b/src/components/SidePane/HelpButton.tsx index 37a82bc80ef9..ff5cdaa0af90 100644 --- a/src/components/SidePane/HelpButton.tsx +++ b/src/components/SidePane/HelpButton.tsx @@ -1,6 +1,6 @@ import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; -import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'; +import Animated, {FadeIn} from 'react-native-reanimated'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {PressableWithoutFeedback} from '@components/Pressable'; @@ -28,10 +28,7 @@ function HelpButton({style}: HelpButtonProps) { } return ( - + Date: Fri, 7 Mar 2025 11:05:28 +0100 Subject: [PATCH 13/22] Create content mapping and logic to display appropriate content based on the current route. --- src/components/SidePane/getHelpContent.tsx | 77 +++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/components/SidePane/getHelpContent.tsx b/src/components/SidePane/getHelpContent.tsx index f72d5ebe9cc8..36c0efc3f55c 100644 --- a/src/components/SidePane/getHelpContent.tsx +++ b/src/components/SidePane/getHelpContent.tsx @@ -1,9 +1,84 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import type {ReactNode} from 'react'; import React from 'react'; import {View} from 'react-native'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; -const getHelpContent = (styles: ThemeStyles, route: string) => { +type HelpContentMap = Record< + string, + { + /** The content to display for this route */ + content?: (styles: ThemeStyles) => ReactNode; + + /** Any children routes that this route has */ + children?: HelpContentMap; + } +>; + +const helpContentMap: HelpContentMap = { + r: { + content: (styles: ThemeStyles) => ( + <> + Chat Reports + ... general chat reports help ... + + ), + }, + search: { + content: (styles: ThemeStyles) => ( + <> + Searching Reports + ... general search help ... + + ), + }, + settings: { + content: (styles: ThemeStyles) => ( + <> + Settings + ... general settings help ... + + ), + children: { + workspaces: { + content: (styles: ThemeStyles) => ( + <> + Workspaces + ... general workspaces help ... + + ), + children: { + ':policyID': { + content: (styles: ThemeStyles) => ( + <> + Workspace Settings + ... general workspace settings help ... + + ), + }, + }, + }, + }, + }, +}; + +const getHelpContent = (styles: ThemeStyles, route: string): ReactNode => { + const [firstPart, ...routeParts] = route.substring(1).split('/'); + let currentNode: HelpContentMap[string] = helpContentMap[firstPart]; + + for (const part of routeParts) { + if (currentNode?.children?.[part]) { + currentNode = currentNode.children[part]; + } else { + break; + } + } + + if (currentNode?.content) { + return {currentNode.content(styles)}; + } + return ( Missing page for route From d1e32aa6c0781546fd143439c9115b8eefdc4ac6 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 7 Mar 2025 12:15:02 +0100 Subject: [PATCH 14/22] Add diagnostic data on dev and staging --- src/components/SidePane/getHelpContent.tsx | 79 +++++++++++++++++----- src/components/SidePane/index.tsx | 4 +- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/components/SidePane/getHelpContent.tsx b/src/components/SidePane/getHelpContent.tsx index 36c0efc3f55c..955973e56ea5 100644 --- a/src/components/SidePane/getHelpContent.tsx +++ b/src/components/SidePane/getHelpContent.tsx @@ -5,18 +5,18 @@ import {View} from 'react-native'; import Text from '@components/Text'; import type {ThemeStyles} from '@styles/index'; -type HelpContentMap = Record< - string, - { - /** The content to display for this route */ - content?: (styles: ThemeStyles) => ReactNode; +type HelpContent = { + /** The content to display for this route */ + content?: (styles: ThemeStyles) => ReactNode; - /** Any children routes that this route has */ - children?: HelpContentMap; - } ->; + /** Any children routes that this route has */ + children?: Record; + + /** Whether this route is an exact match or displays parent content */ + isExact?: boolean; +}; -const helpContentMap: HelpContentMap = { +const helpContentMap: Record = { r: { content: (styles: ThemeStyles) => ( <> @@ -24,6 +24,16 @@ const helpContentMap: HelpContentMap = { ... general chat reports help ... ), + children: { + ':reportID': { + content: (styles: ThemeStyles) => ( + <> + Chat Report + ... general chat report help ... + + ), + }, + }, }, search: { content: (styles: ThemeStyles) => ( @@ -63,28 +73,63 @@ const helpContentMap: HelpContentMap = { }, }; -const getHelpContent = (styles: ThemeStyles, route: string): ReactNode => { +type DiagnosticDataProps = { + styles: ThemeStyles; + route: string; + children?: ReactNode; +}; + +function DiagnosticData({styles, route, children}: DiagnosticDataProps) { + return ( + + Missing help content for route: + {route} + {!!children && ( + <> + + {children} + + )} + + ); +} + +function getHelpContent(styles: ThemeStyles, route: string, isProduction: boolean): ReactNode { const [firstPart, ...routeParts] = route.substring(1).split('/'); - let currentNode: HelpContentMap[string] = helpContentMap[firstPart]; + let currentNode: HelpContent = helpContentMap[firstPart]; + let isExactMatch = true; for (const part of routeParts) { if (currentNode?.children?.[part]) { currentNode = currentNode.children[part]; + isExactMatch = true; } else { + isExactMatch = false; break; } } if (currentNode?.content) { + if (!isExactMatch && !isProduction) { + return ( + + {currentNode.content(styles)} + + ); + } + return {currentNode.content(styles)}; } return ( - - Missing page for route - {route} - + ); -}; +} export default getHelpContent; diff --git a/src/components/SidePane/index.tsx b/src/components/SidePane/index.tsx index 6206d7c473e3..62a2b62e94bf 100644 --- a/src/components/SidePane/index.tsx +++ b/src/components/SidePane/index.tsx @@ -4,6 +4,7 @@ import React, {useCallback, useEffect, useMemo, useRef} from 'react'; // eslint-disable-next-line no-restricted-imports import {Animated, View} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import useEnvironment from '@hooks/useEnvironment'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -26,6 +27,7 @@ type SidePaneProps = { function SidePane({state}: SidePaneProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); + const {isProduction} = useEnvironment(); const {isExtraLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const {sidePaneTranslateX, shouldHideSidePane, shouldHideSidePaneBackdrop} = useSidePane(); @@ -88,7 +90,7 @@ function SidePane({state}: SidePaneProps) { shouldShowCloseButton={isExtraLargeScreenWidth} shouldDisplayHelpButton={false} /> - {getHelpContent(styles, route)} + {getHelpContent(styles, route, isProduction)} ); From e97ff0fbaae61e0674e9a290e54fc46b2052d5c6 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 7 Mar 2025 13:46:21 +0100 Subject: [PATCH 15/22] Remove unnecessary styles for stack navigator --- .../AppNavigator/Navigators/SearchFullscreenNavigator.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/SearchFullscreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/SearchFullscreenNavigator.tsx index b5f26b1f611c..019955409ddd 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SearchFullscreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/SearchFullscreenNavigator.tsx @@ -3,7 +3,6 @@ import type {SearchFullscreenNavigatorParamList} from '@libs/Navigation/types'; import * as SearchQueryUtils from '@libs/SearchQueryUtils'; import createSearchFullscreenNavigator from '@navigation/AppNavigator/createSearchFullscreenNavigator'; import FreezeWrapper from '@navigation/AppNavigator/FreezeWrapper'; -import useRootNavigatorScreenOptions from '@navigation/AppNavigator/useRootNavigatorScreenOptions'; import SCREENS from '@src/SCREENS'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; @@ -13,14 +12,9 @@ const loadSearchMoneyReportPage = () => require('@pages/Se const Stack = createSearchFullscreenNavigator(); function SearchFullscreenNavigator() { - const rootNavigatorScreenOptions = useRootNavigatorScreenOptions(); - return ( - + Date: Fri, 7 Mar 2025 13:46:35 +0100 Subject: [PATCH 16/22] Get state from useRootNavigationState --- .../Navigation/TopLevelBottomTabBar/index.tsx | 2 +- src/components/SidePane/index.tsx | 24 +++++++------------ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/components/Navigation/TopLevelBottomTabBar/index.tsx b/src/components/Navigation/TopLevelBottomTabBar/index.tsx index fc9ad86fe328..c75664051697 100644 --- a/src/components/Navigation/TopLevelBottomTabBar/index.tsx +++ b/src/components/Navigation/TopLevelBottomTabBar/index.tsx @@ -65,7 +65,7 @@ function TopLevelBottomTabBar({state}: TopLevelBottomTabBarProps) { isTooltipAllowed={isReadyToDisplayBottomBar} /> - + ); } diff --git a/src/components/SidePane/index.tsx b/src/components/SidePane/index.tsx index 62a2b62e94bf..eafd0f9ca69f 100644 --- a/src/components/SidePane/index.tsx +++ b/src/components/SidePane/index.tsx @@ -1,6 +1,5 @@ -import type {ParamListBase} from '@react-navigation/native'; import {findFocusedRoute} from '@react-navigation/native'; -import React, {useCallback, useEffect, useMemo, useRef} from 'react'; +import React, {useCallback, useEffect, useRef} from 'react'; // eslint-disable-next-line no-restricted-imports import {Animated, View} from 'react-native'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -8,32 +7,23 @@ import useEnvironment from '@hooks/useEnvironment'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useRootNavigationState from '@hooks/useRootNavigationState'; import useSidePane from '@hooks/useSidePane'; import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets'; import useThemeStyles from '@hooks/useThemeStyles'; import {triggerSidePane} from '@libs/actions/SidePane'; import Navigation from '@libs/Navigation/Navigation'; -import type {PlatformStackNavigationState} from '@libs/Navigation/PlatformStackNavigation/types'; import {substituteRouteParameters} from '@libs/SidePaneUtils'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import getHelpContent from './getHelpContent'; import SidePaneOverlay from './SidePaneOverlay'; -type SidePaneProps = { - state: PlatformStackNavigationState; -}; - -function SidePane({state}: SidePaneProps) { +function SidePane() { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isProduction} = useEnvironment(); - - const {isExtraLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); - const {sidePaneTranslateX, shouldHideSidePane, shouldHideSidePaneBackdrop} = useSidePane(); - const {paddingTop} = useStyledSafeAreaInsets(); - - const {route, isInNarrowPaneModal} = useMemo(() => { + const {route, isInNarrowPaneModal} = useRootNavigationState((state) => { const params = (findFocusedRoute(state)?.params as Record) ?? {}; const activeRoute = Navigation.getActiveRouteWithoutParams(); @@ -41,7 +31,11 @@ function SidePane({state}: SidePaneProps) { route: substituteRouteParameters(activeRoute, params), isInNarrowPaneModal: state?.routes?.some((r) => r.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR), }; - }, [state]); + }); + + const {isExtraLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); + const {sidePaneTranslateX, shouldHideSidePane, shouldHideSidePaneBackdrop} = useSidePane(); + const {paddingTop} = useStyledSafeAreaInsets(); const onClose = useCallback( (shouldUpdateNarrow = false) => { From 0890ba3d3918ce42ddecb0bbb950f2241d9bb30d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 7 Mar 2025 14:18:42 +0100 Subject: [PATCH 17/22] Always display diagnostic data on dev/staging --- src/components/SidePane/getHelpContent.tsx | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/components/SidePane/getHelpContent.tsx b/src/components/SidePane/getHelpContent.tsx index 955973e56ea5..06db2503dcc1 100644 --- a/src/components/SidePane/getHelpContent.tsx +++ b/src/components/SidePane/getHelpContent.tsx @@ -76,13 +76,16 @@ const helpContentMap: Record = { type DiagnosticDataProps = { styles: ThemeStyles; route: string; + isExactMatch?: boolean; children?: ReactNode; }; -function DiagnosticData({styles, route, children}: DiagnosticDataProps) { +function DiagnosticData({styles, route, children, isExactMatch}: DiagnosticDataProps) { + const diagnosticTitle = isExactMatch ? 'Help content found for route:' : 'Missing help content for route:'; + return ( - Missing help content for route: + {diagnosticTitle} {route} {!!children && ( <> @@ -110,18 +113,19 @@ function getHelpContent(styles: ThemeStyles, route: string, isProduction: boolea } if (currentNode?.content) { - if (!isExactMatch && !isProduction) { - return ( - - {currentNode.content(styles)} - - ); + if (isProduction) { + return {currentNode.content(styles)}; } - return {currentNode.content(styles)}; + return ( + + {currentNode.content(styles)} + + ); } return ( From f031128ae6226d46871308f2e2ef0b4902779f0a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 7 Mar 2025 14:23:15 +0100 Subject: [PATCH 18/22] Fix missing bottomTab on search screen --- .../Navigators/SearchFullscreenNavigator.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/Navigators/SearchFullscreenNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/SearchFullscreenNavigator.tsx index 019955409ddd..4f8e4478e189 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/SearchFullscreenNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/SearchFullscreenNavigator.tsx @@ -1,8 +1,10 @@ import React from 'react'; +import type {PlatformStackNavigationOptions} from '@libs/Navigation/PlatformStackNavigation/types'; import type {SearchFullscreenNavigatorParamList} from '@libs/Navigation/types'; import * as SearchQueryUtils from '@libs/SearchQueryUtils'; import createSearchFullscreenNavigator from '@navigation/AppNavigator/createSearchFullscreenNavigator'; import FreezeWrapper from '@navigation/AppNavigator/FreezeWrapper'; +import useRootNavigatorScreenOptions from '@navigation/AppNavigator/useRootNavigatorScreenOptions'; import SCREENS from '@src/SCREENS'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; @@ -12,9 +14,22 @@ const loadSearchMoneyReportPage = () => require('@pages/Se const Stack = createSearchFullscreenNavigator(); function SearchFullscreenNavigator() { + const rootNavigatorScreenOptions = useRootNavigatorScreenOptions(); + + const searchRootScreenOptions: PlatformStackNavigationOptions = { + ...rootNavigatorScreenOptions.fullScreen, + web: { + ...rootNavigatorScreenOptions.fullScreen.web, + cardStyleInterpolator: undefined, + }, + }; + return ( - + Date: Fri, 7 Mar 2025 14:34:40 +0100 Subject: [PATCH 19/22] Fix desktop header gap --- src/components/SidePane/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/SidePane/index.tsx b/src/components/SidePane/index.tsx index eafd0f9ca69f..24e9f19e689b 100644 --- a/src/components/SidePane/index.tsx +++ b/src/components/SidePane/index.tsx @@ -2,6 +2,7 @@ import {findFocusedRoute} from '@react-navigation/native'; import React, {useCallback, useEffect, useRef} from 'react'; // eslint-disable-next-line no-restricted-imports import {Animated, View} from 'react-native'; +import HeaderGap from '@components/HeaderGap'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import useEnvironment from '@hooks/useEnvironment'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; @@ -75,6 +76,7 @@ function SidePane() { )} + Date: Fri, 7 Mar 2025 14:41:23 +0100 Subject: [PATCH 20/22] Create RootNavigatorExtraContent --- .../Navigation/RootNavigatorExtraContent.tsx | 22 +++++++++++++++++++ .../Navigation/TopLevelBottomTabBar/index.tsx | 18 ++++++--------- .../createRootStackNavigator/index.tsx | 4 ++-- 3 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 src/components/Navigation/RootNavigatorExtraContent.tsx diff --git a/src/components/Navigation/RootNavigatorExtraContent.tsx b/src/components/Navigation/RootNavigatorExtraContent.tsx new file mode 100644 index 000000000000..198f1aa0d6c2 --- /dev/null +++ b/src/components/Navigation/RootNavigatorExtraContent.tsx @@ -0,0 +1,22 @@ +import type {ParamListBase} from '@react-navigation/native'; +import React from 'react'; +import SidePane from '@components/SidePane'; +import type {PlatformStackNavigationState} from '@libs/Navigation/PlatformStackNavigation/types'; +import TopLevelBottomTabBar from './TopLevelBottomTabBar'; + +type RootNavigatorExtraContentProps = { + state: PlatformStackNavigationState; +}; + +function RootNavigatorExtraContent({state}: RootNavigatorExtraContentProps) { + return ( + <> + + + + ); +} + +RootNavigatorExtraContent.displayName = 'RootNavigatorExtraContent'; + +export default RootNavigatorExtraContent; diff --git a/src/components/Navigation/TopLevelBottomTabBar/index.tsx b/src/components/Navigation/TopLevelBottomTabBar/index.tsx index c75664051697..ed0e65e23dbb 100644 --- a/src/components/Navigation/TopLevelBottomTabBar/index.tsx +++ b/src/components/Navigation/TopLevelBottomTabBar/index.tsx @@ -3,7 +3,6 @@ import React, {useContext, useEffect, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import {FullScreenBlockingViewContext} from '@components/FullScreenBlockingViewContextProvider'; import BottomTabBar from '@components/Navigation/BottomTabBar'; -import SidePane from '@components/SidePane'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -55,18 +54,15 @@ function TopLevelBottomTabBar({state}: TopLevelBottomTabBarProps) { }, [shouldDisplayBottomBar]); return ( - <> - - {/* We are not rendering BottomTabBar conditionally for two reasons + + {/* We are not rendering BottomTabBar conditionally for two reasons 1. It's faster to hide/show it than mount a new when needed. 2. We need to hide tooltips as well if they were displayed. */} - - - - + + ); } diff --git a/src/libs/Navigation/AppNavigator/createRootStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createRootStackNavigator/index.tsx index 3c4fe0471ef5..0b0ad520d02d 100644 --- a/src/libs/Navigation/AppNavigator/createRootStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createRootStackNavigator/index.tsx @@ -1,6 +1,6 @@ import {createNavigatorFactory} from '@react-navigation/native'; import type {ParamListBase} from '@react-navigation/native'; -import TopLevelBottomTabBar from '@components/Navigation/TopLevelBottomTabBar'; +import RootNavigatorExtraContent from '@components/Navigation/RootNavigatorExtraContent'; import useNavigationResetOnLayoutChange from '@libs/Navigation/AppNavigator/useNavigationResetOnLayoutChange'; import {isFullScreenName} from '@libs/Navigation/helpers/isNavigatorName'; import createPlatformStackNavigatorComponent from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigatorComponent'; @@ -21,7 +21,7 @@ const RootStackNavigatorComponent = createPlatformStackNavigatorComponent('RootS defaultScreenOptions: defaultPlatformStackScreenOptions, useCustomEffects: useNavigationResetOnLayoutChange, useCustomState: useCustomRootStackNavigatorState, - ExtraContent: TopLevelBottomTabBar, + ExtraContent: RootNavigatorExtraContent, }); function createRootStackNavigator() { From 181d6c72895f447657dd69a41f6cd65db587d5df Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 7 Mar 2025 14:51:12 +0100 Subject: [PATCH 21/22] Remove animation, change useSidePane button logic --- src/components/SidePane/HelpButton.tsx | 27 ++++++++++++-------------- src/hooks/useSidePane.ts | 2 +- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/components/SidePane/HelpButton.tsx b/src/components/SidePane/HelpButton.tsx index ff5cdaa0af90..3ad251797378 100644 --- a/src/components/SidePane/HelpButton.tsx +++ b/src/components/SidePane/HelpButton.tsx @@ -1,6 +1,5 @@ import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; -import Animated, {FadeIn} from 'react-native-reanimated'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {PressableWithoutFeedback} from '@components/Pressable'; @@ -28,20 +27,18 @@ function HelpButton({style}: HelpButtonProps) { } return ( - - - triggerSidePane(isExtraLargeScreenWidth ? !sidePane?.open : !sidePane?.openNarrowScreen, {shouldUpdateNarrowLayout: !isExtraLargeScreenWidth})} - > - - - - + + triggerSidePane(isExtraLargeScreenWidth ? !sidePane?.open : !sidePane?.openNarrowScreen, {shouldUpdateNarrowLayout: !isExtraLargeScreenWidth})} + > + + + ); } diff --git a/src/hooks/useSidePane.ts b/src/hooks/useSidePane.ts index cf196532213f..7199315f733d 100644 --- a/src/hooks/useSidePane.ts +++ b/src/hooks/useSidePane.ts @@ -42,7 +42,7 @@ function useSidePane() { // The help button is hidden in production if the side pane nvp is not present or the language is unsupported. const shouldHideOnProduction = isProduction && (!sidePaneNVP || isLanguageUnsupported); // The help button is also hidden if the side pane is displayed currently. - const shouldHideHelpButton = shouldHideOnProduction || !shouldHideSidePane; + const shouldHideHelpButton = shouldHideOnProduction || !isPaneHidden; const sidePaneOffset = useRef(new Animated.Value(shouldApplySidePaneOffset ? variables.sideBarWidth : 0)); const sidePaneTranslateX = useRef(new Animated.Value(isPaneHidden ? sidePaneWidth : 0)); From 9063a67f3d25fc5dd666da54404e7faec03dfa42 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 7 Mar 2025 15:14:13 +0100 Subject: [PATCH 22/22] Fix navigation in very very big edgecase --- src/components/SidePane/index.tsx | 2 +- src/hooks/useSidePane.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/SidePane/index.tsx b/src/components/SidePane/index.tsx index 24e9f19e689b..1d7db1cbadbb 100644 --- a/src/components/SidePane/index.tsx +++ b/src/components/SidePane/index.tsx @@ -30,7 +30,7 @@ function SidePane() { return { route: substituteRouteParameters(activeRoute, params), - isInNarrowPaneModal: state?.routes?.some((r) => r.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR), + isInNarrowPaneModal: state?.routes.at(-1)?.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR, }; }); diff --git a/src/hooks/useSidePane.ts b/src/hooks/useSidePane.ts index 7199315f733d..73e1f24a16ca 100644 --- a/src/hooks/useSidePane.ts +++ b/src/hooks/useSidePane.ts @@ -41,6 +41,7 @@ function useSidePane() { // The help button is hidden in production if the side pane nvp is not present or the language is unsupported. const shouldHideOnProduction = isProduction && (!sidePaneNVP || isLanguageUnsupported); + // The help button is also hidden if the side pane is displayed currently. const shouldHideHelpButton = shouldHideOnProduction || !isPaneHidden;