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
299 changes: 299 additions & 0 deletions src/components/Navigation/NavigationTabBar/NavigationTabBarDummy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
import React, {memo, useEffect, useState} 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 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;

function NavigationTabBarWideDummy({selectedTab}: {selectedTab: ValueOf<typeof NAVIGATION_TABS>}) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();
const {indicatorColor: workspacesTabIndicatorColor, status: workspacesTabIndicatorStatus} = useWorkspacesTabIndicatorStatus();
const [chatTabBrickRoad, setChatTabBrickRoad] = useState<BrickRoad>(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 (
<View style={styles.leftNavigationTabBarContainer}>
<HeaderGap />
<View style={styles.flex1}>
<PressableWithFeedback
accessibilityRole={CONST.ROLE.BUTTON}
accessibilityLabel="Home"
accessible={false}
testID="ExpensifyLogoButton"
onPress={noop}
wrapperStyle={styles.leftNavigationTabBarItem}
>
<ImageSVG
style={StyleUtils.getAvatarStyle(CONST.AVATAR_SIZE.DEFAULT)}
src={Expensicons.ExpensifyAppIcon}
/>
</PressableWithFeedback>
<PressableWithFeedback
onPress={noop}
role={CONST.ROLE.BUTTON}
accessibilityLabel={translate('common.inbox')}
style={styles.leftNavigationTabBarItem}
>
<View>
<Icon
src={Expensicons.Inbox}
fill={selectedTab === NAVIGATION_TABS.HOME ? theme.iconMenu : theme.icon}
width={variables.iconBottomBar}
height={variables.iconBottomBar}
/>
{!!chatTabBrickRoad && (
<View style={styles.navigationTabBarStatusIndicator(chatTabBrickRoad === CONST.BRICK_ROAD_INDICATOR_STATUS.INFO ? theme.iconSuccessFill : theme.danger)} />
)}
</View>
<Text
style={[
styles.textSmall,
styles.textAlignCenter,
styles.mt1Half,
selectedTab === NAVIGATION_TABS.HOME ? styles.textBold : styles.textSupporting,
styles.navigationTabBarLabel,
]}
>
{translate('common.inbox')}
</Text>
</PressableWithFeedback>
<PressableWithFeedback
onPress={noop}
role={CONST.ROLE.BUTTON}
accessibilityLabel={translate('common.reports')}
style={styles.leftNavigationTabBarItem}
>
<View>
<Icon
src={Expensicons.MoneySearch}
fill={selectedTab === NAVIGATION_TABS.SEARCH ? theme.iconMenu : theme.icon}
width={variables.iconBottomBar}
height={variables.iconBottomBar}
/>
</View>
<Text
style={[
styles.textSmall,
styles.textAlignCenter,
styles.mt1Half,
selectedTab === NAVIGATION_TABS.SEARCH ? styles.textBold : styles.textSupporting,
styles.navigationTabBarLabel,
]}
>
{translate('common.reports')}
</Text>
</PressableWithFeedback>
<PressableWithFeedback
onPress={noop}
role={CONST.ROLE.BUTTON}
accessibilityLabel={translate('common.workspacesTabTitle')}
style={styles.leftNavigationTabBarItem}
>
<View>
<Icon
src={Expensicons.Buildings}
fill={selectedTab === NAVIGATION_TABS.WORKSPACES ? theme.iconMenu : theme.icon}
width={variables.iconBottomBar}
height={variables.iconBottomBar}
/>
{!!workspacesTabIndicatorStatus && <View style={styles.navigationTabBarStatusIndicator(workspacesTabIndicatorColor)} />}
</View>
<Text
style={[
styles.textSmall,
styles.textAlignCenter,
styles.mt1Half,
selectedTab === NAVIGATION_TABS.WORKSPACES ? styles.textBold : styles.textSupporting,
styles.navigationTabBarLabel,
]}
>
{translate('common.workspacesTabTitle')}
</Text>
</PressableWithFeedback>
<NavigationTabBarAvatar
style={styles.leftNavigationTabBarItem}
isSelected={selectedTab === NAVIGATION_TABS.SETTINGS}
onPress={noop}
/>
</View>
<View style={styles.leftNavigationTabBarItem}>
<FloatingActionButton
onPress={noop}
isActive={false}
accessibilityLabel={translate('sidebarScreen.fabNewChatExplained')}
role={CONST.ROLE.BUTTON}
isTooltipAllowed={false}
/>
</View>
</View>
);
}

function NavigationTabBarNarrowDummy({selectedTab}: {selectedTab: ValueOf<typeof NAVIGATION_TABS>}) {
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
const {indicatorColor: workspacesTabIndicatorColor, status: workspacesTabIndicatorStatus} = useWorkspacesTabIndicatorStatus();
const [chatTabBrickRoad, setChatTabBrickRoad] = useState<BrickRoad>(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 (
<View style={styles.navigationTabBarContainer}>
<PressableWithFeedback
onPress={noop}
role={CONST.ROLE.BUTTON}
accessibilityLabel={translate('common.inbox')}
wrapperStyle={styles.flex1}
style={styles.navigationTabBarItem}
>
<View>
<Icon
src={Expensicons.Inbox}
fill={selectedTab === NAVIGATION_TABS.HOME ? theme.iconMenu : theme.icon}
width={variables.iconBottomBar}
height={variables.iconBottomBar}
/>
{!!chatTabBrickRoad && (
<View style={styles.navigationTabBarStatusIndicator(chatTabBrickRoad === CONST.BRICK_ROAD_INDICATOR_STATUS.INFO ? theme.iconSuccessFill : theme.danger)} />
)}
</View>
<Text
style={[
styles.textSmall,
styles.textAlignCenter,
styles.mt1Half,
selectedTab === NAVIGATION_TABS.HOME ? styles.textBold : styles.textSupporting,
styles.navigationTabBarLabel,
]}
>
{translate('common.inbox')}
</Text>
</PressableWithFeedback>
<PressableWithFeedback
onPress={noop}
role={CONST.ROLE.BUTTON}
accessibilityLabel={translate('common.reports')}
wrapperStyle={styles.flex1}
style={styles.navigationTabBarItem}
>
<View>
<Icon
src={Expensicons.MoneySearch}
fill={selectedTab === NAVIGATION_TABS.SEARCH ? theme.iconMenu : theme.icon}
width={variables.iconBottomBar}
height={variables.iconBottomBar}
/>
</View>
<Text
style={[
styles.textSmall,
styles.textAlignCenter,
styles.mt1Half,
selectedTab === NAVIGATION_TABS.SEARCH ? styles.textBold : styles.textSupporting,
styles.navigationTabBarLabel,
]}
>
{translate('common.reports')}
</Text>
</PressableWithFeedback>
<View style={[styles.flex1, styles.navigationTabBarItem]}>
<FloatingActionButton
onPress={noop}
isActive={false}
accessibilityLabel={translate('sidebarScreen.fabNewChatExplained')}
role={CONST.ROLE.BUTTON}
isTooltipAllowed={false}
/>
</View>
<PressableWithFeedback
onPress={noop}
role={CONST.ROLE.BUTTON}
accessibilityLabel={translate('common.workspacesTabTitle')}
wrapperStyle={styles.flex1}
style={styles.navigationTabBarItem}
>
<View>
<Icon
src={Expensicons.Buildings}
fill={selectedTab === NAVIGATION_TABS.WORKSPACES ? theme.iconMenu : theme.icon}
width={variables.iconBottomBar}
height={variables.iconBottomBar}
/>
{!!workspacesTabIndicatorStatus && <View style={styles.navigationTabBarStatusIndicator(workspacesTabIndicatorColor)} />}
</View>
<Text
style={[
styles.textSmall,
styles.textAlignCenter,
styles.mt1Half,
selectedTab === NAVIGATION_TABS.WORKSPACES ? styles.textBold : styles.textSupporting,
styles.navigationTabBarLabel,
]}
>
{translate('common.workspacesTabTitle')}
</Text>
</PressableWithFeedback>
<NavigationTabBarAvatar
style={styles.navigationTabBarItem}
isSelected={selectedTab === NAVIGATION_TABS.SETTINGS}
onPress={noop}
/>
</View>
);
}

/**
* 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<typeof NAVIGATION_TABS>}) {
const {shouldUseNarrowLayout} = useResponsiveLayout();

if (shouldUseNarrowLayout) {
return <NavigationTabBarNarrowDummy selectedTab={selectedTab} />;
}

return <NavigationTabBarWideDummy selectedTab={selectedTab} />;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which case do we need this in the Wide layout? Could we not render this at all in wide?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question! It is there to make dummy counterpart 1:1 match with regular component, but we can investigate it further to see how it behaves on wider screens 👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to see the navigation bar under the overlay (the one from RHP), we need to render it per screen on the wide layout. The top level is displayed over everything else. It wouldn't be covered by the overlay.

}

export default memo(NavigationTabBarDummy);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Display name

4 changes: 2 additions & 2 deletions src/components/Navigation/SearchSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -76,7 +76,7 @@ function SearchSidebar({state}: SearchSidebarProps) {
/>
<SearchTypeMenu queryJSON={queryJSON} />
</View>
<NavigationTabBar selectedTab={NAVIGATION_TABS.SEARCH} />
<NavigationTabBarDummy selectedTab={NAVIGATION_TABS.SEARCH} />
</View>
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Search/SearchPageNarrow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -141,7 +141,7 @@ function SearchPageNarrow({queryJSON, headerButtonsOptions, currentSearchResults
testID={SearchPageNarrow.displayName}
shouldEnableMaxHeight
offlineIndicatorStyle={styles.mtAuto}
bottomContent={<NavigationTabBar selectedTab={NAVIGATION_TABS.SEARCH} />}
bottomContent={<NavigationTabBarDummy selectedTab={NAVIGATION_TABS.SEARCH} />}
headerGapStyles={styles.searchHeaderGap}
shouldShowOfflineIndicator={!!searchResults}
>
Expand Down
6 changes: 3 additions & 3 deletions src/pages/home/sidebar/BaseSidebarScreen.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -27,7 +27,7 @@ function BaseSidebarScreen() {
shouldEnableKeyboardAvoidingView={false}
style={[styles.sidebar, isMobile() ? styles.userSelectNone : {}]}
testID={BaseSidebarScreen.displayName}
bottomContent={!shouldDisplayLHB && <NavigationTabBar selectedTab={NAVIGATION_TABS.HOME} />}
bottomContent={!shouldDisplayLHB && <NavigationTabBarDummy selectedTab={NAVIGATION_TABS.HOME} />}
>
{({insets}) => (
<>
Expand All @@ -39,7 +39,7 @@ function BaseSidebarScreen() {
<View style={[styles.flex1]}>
<SidebarLinksData insets={insets} />
</View>
{shouldDisplayLHB && <NavigationTabBar selectedTab={NAVIGATION_TABS.HOME} />}
{shouldDisplayLHB && <NavigationTabBarDummy selectedTab={NAVIGATION_TABS.HOME} />}
</>
)}
</ScreenWrapper>
Expand Down
6 changes: 3 additions & 3 deletions src/pages/settings/InitialSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -419,7 +419,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
<ScreenWrapper
includeSafeAreaPaddingBottom
testID={InitialSettingsPage.displayName}
bottomContent={!shouldDisplayLHB && <NavigationTabBar selectedTab={NAVIGATION_TABS.SETTINGS} />}
bottomContent={!shouldDisplayLHB && <NavigationTabBarDummy selectedTab={NAVIGATION_TABS.SETTINGS} />}
shouldEnableKeyboardAvoidingView={false}
>
{headerContent}
Expand All @@ -443,7 +443,7 @@ function InitialSettingsPage({currentUserPersonalDetails}: InitialSettingsPagePr
onCancel={() => toggleSignoutConfirmModal(false)}
/>
</ScrollView>
{shouldDisplayLHB && <NavigationTabBar selectedTab={NAVIGATION_TABS.SETTINGS} />}
{shouldDisplayLHB && <NavigationTabBarDummy selectedTab={NAVIGATION_TABS.SETTINGS} />}
</ScreenWrapper>
);
}
Expand Down
Loading