From cf201b1d9525d965cf0ce2d2cb983b14184cd90f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Mon, 2 Jun 2025 12:38:21 +0200 Subject: [PATCH 1/5] NavigationTabBar: add dummy component --- .../NavigationTabBarDummy.tsx | 297 ++++++++++++++++++ src/components/Navigation/SearchSidebar.tsx | 4 +- src/pages/Search/SearchPageNarrow.tsx | 4 +- src/pages/home/sidebar/BaseSidebarScreen.tsx | 6 +- src/pages/settings/InitialSettingsPage.tsx | 6 +- src/pages/workspace/WorkspaceInitialPage.tsx | 6 +- src/pages/workspace/WorkspacesListPage.tsx | 14 +- 7 files changed, 317 insertions(+), 20 deletions(-) create mode 100644 src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx diff --git a/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx b/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx new file mode 100644 index 000000000000..962eca698a3e --- /dev/null +++ b/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx @@ -0,0 +1,297 @@ +import React, {memo} from 'react'; +import {View} from 'react-native'; +import type {ValueOf} from 'type-fest'; +import FloatingActionButton from '@components/FloatingActionButton'; +import HeaderGap from '@components/HeaderGap'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import ImageSVG from '@components/ImageSVG'; +import {PressableWithFeedback} from '@components/Pressable'; +import Text from '@components/Text'; +import EducationalTooltip from '@components/Tooltip/EducationalTooltip'; +import useLocalize from '@hooks/useLocalize'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import getPlatform from '@libs/getPlatform'; +import NavigationTabBarAvatar from '@pages/home/sidebar/NavigationTabBarAvatar'; +import variables from '@styles/variables'; +import CONST from '@src/CONST'; +import NAVIGATION_TABS from './NAVIGATION_TABS'; + +const noop = () => undefined; + +function NavigationTabBarWideDummy({selectedTab}: {selectedTab: ValueOf}) { + const theme = useTheme(); + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const StyleUtils = useStyleUtils(); + const platform = getPlatform(); + const isWebOrDesktop = platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP; + + return ( + + + + + + + + + + + + + {translate('common.inbox')} + + + + + + + + + {translate('common.reports')} + + + + + + + + {translate('common.workspacesTabTitle')} + + + + + + + + + ); +} + +function NavigationTabBarNarrowDummy({selectedTab}: {selectedTab: ValueOf}) { + const theme = useTheme(); + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const platform = getPlatform(); + const isWebOrDesktop = platform === CONST.PLATFORM.WEB || platform === CONST.PLATFORM.DESKTOP; + + return ( + + + + + + + + {translate('common.inbox')} + + + + + + + + + {translate('common.reports')} + + + + + + + + + + + {translate('common.workspacesTabTitle')} + + + + + ); +} + +/** + * This is a dummy component for the NavigationTabBar created for performance reasons. + * It is used to render the dummy NavigationTabBar in a wide or narrow layout. + * @param selectedTab - The selected tab. + * @returns The NavigationTabBarDummy component. + */ +function NavigationTabBarDummy({selectedTab}: {selectedTab: ValueOf}) { + const {shouldUseNarrowLayout} = useResponsiveLayout(); + + if (shouldUseNarrowLayout) { + return ; + } + + return ; +} + +export default NavigationTabBarDummy; diff --git a/src/components/Navigation/SearchSidebar.tsx b/src/components/Navigation/SearchSidebar.tsx index 6da1ec0b91e0..ae6e42796201 100644 --- a/src/components/Navigation/SearchSidebar.tsx +++ b/src/components/Navigation/SearchSidebar.tsx @@ -17,8 +17,8 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import type {SearchResults} from '@src/types/onyx'; -import NavigationTabBar from './NavigationTabBar'; import NAVIGATION_TABS from './NavigationTabBar/NAVIGATION_TABS'; +import NavigationTabBarDummy from './NavigationTabBar/NavigationTabBarDummy'; import TopBar from './TopBar'; type SearchSidebarProps = { @@ -76,7 +76,7 @@ function SearchSidebar({state}: SearchSidebarProps) { /> - + ); } diff --git a/src/pages/Search/SearchPageNarrow.tsx b/src/pages/Search/SearchPageNarrow.tsx index 63ca9abb5ba2..23b6b55b25ea 100644 --- a/src/pages/Search/SearchPageNarrow.tsx +++ b/src/pages/Search/SearchPageNarrow.tsx @@ -5,8 +5,8 @@ import Animated, {clamp, runOnJS, useAnimatedScrollHandler, useAnimatedStyle, us import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import NavigationTabBar from '@components/Navigation/NavigationTabBar'; import NAVIGATION_TABS from '@components/Navigation/NavigationTabBar/NAVIGATION_TABS'; +import NavigationTabBarDummy from '@components/Navigation/NavigationTabBar/NavigationTabBarDummy'; import TopBar from '@components/Navigation/TopBar'; import ScreenWrapper from '@components/ScreenWrapper'; import Search from '@components/Search'; @@ -141,7 +141,7 @@ function SearchPageNarrow({queryJSON, headerButtonsOptions, currentSearchResults testID={SearchPageNarrow.displayName} shouldEnableMaxHeight offlineIndicatorStyle={styles.mtAuto} - bottomContent={} + bottomContent={} headerGapStyles={styles.searchHeaderGap} shouldShowOfflineIndicator={!!searchResults} > diff --git a/src/pages/home/sidebar/BaseSidebarScreen.tsx b/src/pages/home/sidebar/BaseSidebarScreen.tsx index b713f09429d7..c71fd8cc7c3b 100644 --- a/src/pages/home/sidebar/BaseSidebarScreen.tsx +++ b/src/pages/home/sidebar/BaseSidebarScreen.tsx @@ -1,7 +1,7 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; -import NavigationTabBar from '@components/Navigation/NavigationTabBar'; import NAVIGATION_TABS from '@components/Navigation/NavigationTabBar/NAVIGATION_TABS'; +import NavigationTabBarDummy from '@components/Navigation/NavigationTabBar/NavigationTabBarDummy'; import TopBar from '@components/Navigation/TopBar'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; @@ -27,7 +27,7 @@ function BaseSidebarScreen() { shouldEnableKeyboardAvoidingView={false} style={[styles.sidebar, isMobile() ? styles.userSelectNone : {}]} testID={BaseSidebarScreen.displayName} - bottomContent={!shouldDisplayLHB && } + bottomContent={!shouldDisplayLHB && } > {({insets}) => ( <> @@ -39,7 +39,7 @@ function BaseSidebarScreen() { - {shouldDisplayLHB && } + {shouldDisplayLHB && } )} diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 56c4f2d3a18e..ceb13a8044fb 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -13,8 +13,8 @@ import CustomStatusBarAndBackgroundContext from '@components/CustomStatusBarAndB import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; -import NavigationTabBar from '@components/Navigation/NavigationTabBar'; import NAVIGATION_TABS from '@components/Navigation/NavigationTabBar/NAVIGATION_TABS'; +import NavigationTabBarDummy from '@components/Navigation/NavigationTabBar/NavigationTabBarDummy'; import {PressableWithFeedback} from '@components/Pressable'; import ScreenWrapper from '@components/ScreenWrapper'; import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider'; @@ -419,7 +419,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr } + bottomContent={!shouldDisplayLHB && } shouldEnableKeyboardAvoidingView={false} > {headerContent} @@ -443,7 +443,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr onCancel={() => toggleSignoutConfirmModal(false)} /> - {shouldDisplayLHB && } + {shouldDisplayLHB && } ); } diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 91deef0f5b42..b5015126d92e 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -25,8 +25,8 @@ import { Workflows, } from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; -import NavigationTabBar from '@components/Navigation/NavigationTabBar'; import NAVIGATION_TABS from '@components/Navigation/NavigationTabBar/NAVIGATION_TABS'; +import NavigationTabBarDummy from '@components/Navigation/NavigationTabBar/NavigationTabBarDummy'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; @@ -437,7 +437,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, route}: Workspac } + bottomContent={shouldShowNavigationTabBar && !shouldDisplayLHB && } > )} - {shouldShowNavigationTabBar && shouldDisplayLHB && } + {shouldShowNavigationTabBar && shouldDisplayLHB && } ); diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index e09a780f7ec0..1b6b02c478db 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -12,8 +12,8 @@ import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import LottieAnimations from '@components/LottieAnimations'; import type {MenuItemProps} from '@components/MenuItem'; -import NavigationTabBar from '@components/Navigation/NavigationTabBar'; import NAVIGATION_TABS from '@components/Navigation/NavigationTabBar/NAVIGATION_TABS'; +import NavigationTabBarDummy from '@components/Navigation/NavigationTabBar/NavigationTabBarDummy'; import TopBar from '@components/Navigation/TopBar'; import type {OfflineWithFeedbackProps} from '@components/OfflineWithFeedback'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -434,11 +434,11 @@ function WorkspacesListPage() { action: () => navigateToWorkspace(policy.id), brickRoadIndicator: !isPolicyAdmin(policy) ? undefined - : (reimbursementAccountBrickRoadIndicator ?? + : reimbursementAccountBrickRoadIndicator ?? getPolicyBrickRoadIndicatorStatus( policy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy.id}`], policy), - )), + ), pendingAction: policy.pendingAction, errors: policy.errors, dismissError: () => dismissWorkspaceError(policy.id, policy.pendingAction), @@ -482,7 +482,7 @@ function WorkspacesListPage() { shouldEnableMaxHeight testID={WorkspacesListPage.displayName} shouldShowOfflineIndicatorInWideScreen - bottomContent={shouldUseNarrowLayout && } + bottomContent={shouldUseNarrowLayout && } enableEdgeToEdgeBottomSafeAreaPadding={false} > @@ -509,7 +509,7 @@ function WorkspacesListPage() { )} - {shouldDisplayLHB && } + {shouldDisplayLHB && } ); } @@ -520,7 +520,7 @@ function WorkspacesListPage() { shouldShowOfflineIndicatorInWideScreen testID={WorkspacesListPage.displayName} enableEdgeToEdgeBottomSafeAreaPadding={false} - bottomContent={shouldUseNarrowLayout && } + bottomContent={shouldUseNarrowLayout && } > {!shouldUseNarrowLayout && {getHeaderButton()}} @@ -546,7 +546,7 @@ function WorkspacesListPage() { isModalOpen={isSupportalActionRestrictedModalOpen} hideSupportalModal={hideSupportalModal} /> - {shouldDisplayLHB && } + {shouldDisplayLHB && } ); } From a0346fabed4371b7d546cd7649d3e28cb9721c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Mon, 2 Jun 2025 14:07:19 +0200 Subject: [PATCH 2/5] NavigationTabBar: use memo for dummy --- .../Navigation/NavigationTabBar/NavigationTabBarDummy.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx b/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx index 962eca698a3e..d80ef756a1f2 100644 --- a/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx +++ b/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx @@ -294,4 +294,4 @@ function NavigationTabBarDummy({selectedTab}: {selectedTab: ValueOf; } -export default NavigationTabBarDummy; +export default memo(NavigationTabBarDummy); From 13a055a5d442c79f3fe0a53b0114888f7022ef7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Mon, 2 Jun 2025 15:39:40 +0200 Subject: [PATCH 3/5] NavigationTabBarDummy: remove tooltips --- .../NavigationTabBarDummy.tsx | 130 +++++++----------- 1 file changed, 49 insertions(+), 81 deletions(-) diff --git a/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx b/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx index d80ef756a1f2..36c9b8286292 100644 --- a/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx +++ b/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx @@ -8,13 +8,11 @@ import * as Expensicons from '@components/Icon/Expensicons'; import ImageSVG from '@components/ImageSVG'; import {PressableWithFeedback} from '@components/Pressable'; import Text from '@components/Text'; -import EducationalTooltip from '@components/Tooltip/EducationalTooltip'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import getPlatform from '@libs/getPlatform'; import NavigationTabBarAvatar from '@pages/home/sidebar/NavigationTabBarAvatar'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -27,8 +25,6 @@ function NavigationTabBarWideDummy({selectedTab}: {selectedTab: ValueOf @@ -47,45 +43,32 @@ function NavigationTabBarWideDummy({selectedTab}: {selectedTab: ValueOf - - + + + - - - - - {translate('common.inbox')} - - - + {translate('common.inbox')} + + - - + + + - - - - - {translate('common.inbox')} - - - + {translate('common.inbox')} + + Date: Tue, 3 Jun 2025 23:03:45 +0200 Subject: [PATCH 4/5] NavigationTabBarDummy: add RBR indicator support --- .../NavigationTabBarDummy.tsx | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx b/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx index 36c9b8286292..2eeebcaebcc7 100644 --- a/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx +++ b/src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx @@ -1,4 +1,4 @@ -import React, {memo} from 'react'; +import React, {memo, useEffect, useState} from 'react'; import {View} from 'react-native'; import type {ValueOf} from 'type-fest'; import FloatingActionButton from '@components/FloatingActionButton'; @@ -9,13 +9,19 @@ import ImageSVG from '@components/ImageSVG'; import {PressableWithFeedback} from '@components/Pressable'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; +import useOnyx from '@hooks/useOnyx'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import {useSidebarOrderedReports} from '@hooks/useSidebarOrderedReports'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWorkspacesTabIndicatorStatus from '@hooks/useWorkspacesTabIndicatorStatus'; +import type {BrickRoad} from '@libs/WorkspacesSettingsUtils'; +import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils'; import NavigationTabBarAvatar from '@pages/home/sidebar/NavigationTabBarAvatar'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import NAVIGATION_TABS from './NAVIGATION_TABS'; const noop = () => undefined; @@ -23,8 +29,18 @@ const noop = () => undefined; function NavigationTabBarWideDummy({selectedTab}: {selectedTab: ValueOf}) { const theme = useTheme(); const styles = useThemeStyles(); - const {translate} = useLocalize(); const StyleUtils = useStyleUtils(); + const {translate} = useLocalize(); + const {indicatorColor: workspacesTabIndicatorColor, status: workspacesTabIndicatorStatus} = useWorkspacesTabIndicatorStatus(); + const [chatTabBrickRoad, setChatTabBrickRoad] = useState(undefined); + const {orderedReports} = useSidebarOrderedReports(); + const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (value) => value?.reports, canBeMissing: true}); + + useEffect(() => { + setChatTabBrickRoad(getChatTabBrickRoad(orderedReports)); + // We need to get a new brick road state when report attributes are updated, otherwise we'll be showing an outdated brick road. + // That's why reportAttributes is added as a dependency here + }, [orderedReports, reportAttributes]); return ( @@ -56,6 +72,9 @@ function NavigationTabBarWideDummy({selectedTab}: {selectedTab: ValueOf + {!!chatTabBrickRoad && ( + + )} + {!!workspacesTabIndicatorStatus && } (undefined); + const {orderedReports} = useSidebarOrderedReports(); + const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (value) => value?.reports, canBeMissing: true}); + + useEffect(() => { + setChatTabBrickRoad(getChatTabBrickRoad(orderedReports)); + // We need to get a new brick road state when report attributes are updated, otherwise we'll be showing an outdated brick road. + // That's why reportAttributes is added as a dependency here + }, [orderedReports, reportAttributes]); return ( @@ -161,6 +191,9 @@ function NavigationTabBarNarrowDummy({selectedTab}: {selectedTab: ValueOf + {!!chatTabBrickRoad && ( + + )} + {!!workspacesTabIndicatorStatus && } Date: Thu, 5 Jun 2025 10:44:02 +0200 Subject: [PATCH 5/5] WorkspacesListPage: fix prettier --- src/pages/workspace/WorkspacesListPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 1b6b02c478db..d0df3fb83c3d 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -434,11 +434,11 @@ function WorkspacesListPage() { action: () => navigateToWorkspace(policy.id), brickRoadIndicator: !isPolicyAdmin(policy) ? undefined - : reimbursementAccountBrickRoadIndicator ?? + : (reimbursementAccountBrickRoadIndicator ?? getPolicyBrickRoadIndicatorStatus( policy, isConnectionInProgress(allConnectionSyncProgresses?.[`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy.id}`], policy), - ), + )), pendingAction: policy.pendingAction, errors: policy.errors, dismissError: () => dismissWorkspaceError(policy.id, policy.pendingAction),