Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address ideal nav review v3 #24

Merged
merged 15 commits into from
Jan 15, 2024
2 changes: 1 addition & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,7 @@ const CONST = {
GUIDES_CALL_TASK_IDS: {
CONCIERGE_DM: 'NewExpensifyConciergeDM',
WORKSPACE_INITIAL: 'WorkspaceHome',
WORKSPACE_OVERVIEW: 'WorkspaceGeneralSettings',
WORKSPACE_OVERVIEW: 'WorkspaceOverview',
WORKSPACE_CARD: 'WorkspaceCorporateCards',
WORKSPACE_REIMBURSE: 'WorkspaceReimburseReceipts',
WORKSPACE_BILLS: 'WorkspacePayBills',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type ForceFullScreenViewProps from './types';

function ForceFullScreenView({children}: ForceFullScreenViewProps) {
return children;
}

ForceFullScreenView.displayName = 'ForceFullScreenView';

export default ForceFullScreenView;
18 changes: 18 additions & 0 deletions src/components/BlockingViews/ForceFullScreenView/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import type ForceFullScreenViewProps from './types';

function ForceFullScreenView({children, forceFullScreen = false}: ForceFullScreenViewProps) {
const styles = useThemeStyles();

if (forceFullScreen) {
return <View style={forceFullScreen && styles.forcedBlockingViewContainer}>{children}</View>;
}

return children;
}

ForceFullScreenView.displayName = 'ForceFullScreenView';

export default ForceFullScreenView;
7 changes: 7 additions & 0 deletions src/components/BlockingViews/ForceFullScreenView/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type ChildrenProps from '@src/types/utils/ChildrenProps';

type ForceFullScreenViewProps = ChildrenProps & {
forceFullScreen?: boolean;
};

export default ForceFullScreenViewProps;
9 changes: 7 additions & 2 deletions src/components/BlockingViews/FullPageNotFoundView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import variables from '@styles/variables';
import type {TranslationPaths} from '@src/languages/types';
import ROUTES from '@src/ROUTES';
import BlockingView from './BlockingView';
import ForceFullScreenView from './ForceFullScreenView';

type FullPageNotFoundViewProps = {
/** Child elements */
Expand Down Expand Up @@ -37,6 +38,9 @@ type FullPageNotFoundViewProps = {

/** Function to call when pressing the navigation link */
onLinkPress: () => void;

/** Whether we should force the full page view */
forceFullScreen?: boolean;
};

// eslint-disable-next-line rulesdir/no-negated-variables
Expand All @@ -50,13 +54,14 @@ function FullPageNotFoundView({
shouldShowLink = true,
shouldShowBackButton = true,
onLinkPress = () => Navigation.dismissModal(),
forceFullScreen = false,
}: FullPageNotFoundViewProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

if (shouldShow) {
return (
<>
<ForceFullScreenView forceFullScreen={forceFullScreen}>
<HeaderWithBackButton
onBackButtonPress={onBackButtonPress}
shouldShowBackButton={shouldShowBackButton}
Expand All @@ -73,7 +78,7 @@ function FullPageNotFoundView({
onLinkPress={onLinkPress}
/>
</View>
</>
</ForceFullScreenView>
);
}

Expand Down
6 changes: 5 additions & 1 deletion src/components/ScreenWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ type ScreenWrapperProps = {
/** Whether to include padding bottom */
includeSafeAreaPaddingBottom?: boolean;

/** This overrides the above prop. It force disable the safe area bottom padding. */
shouldDisableSafeAreaPaddingBottom?: boolean;

/** Whether to include padding top */
includePaddingTop?: boolean;

Expand Down Expand Up @@ -103,6 +106,7 @@ function ScreenWrapper(
headerGapStyles,
children,
shouldShowOfflineIndicator = true,
shouldDisableSafeAreaPaddingBottom = false,
offlineIndicatorStyle,
style,
shouldDismissKeyboardBeforeClose = true,
Expand Down Expand Up @@ -202,7 +206,7 @@ function ScreenWrapper(
}

// We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked.
if (includeSafeAreaPaddingBottom || (isOffline && shouldShowOfflineIndicator)) {
if (!shouldDisableSafeAreaPaddingBottom && (includeSafeAreaPaddingBottom || (isOffline && shouldShowOfflineIndicator))) {
paddingStyle.paddingBottom = paddingBottom;
}

Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,7 @@ export default {
},
emptyWorkspace: {
title: 'Crea un espacio de trabajo',
subtitle: 'Administra gastos de empresa, emite tarjetas, envía facturas y mucho más.',
subtitle: 'En los espacios de trabajo podrás chatear con tu equipo, reembolsar gastos, emitir tarjetas, enviar y pagar facturas, y mucho más - todo en un mismo lugar.',
createAWorkspaceCTA: 'Comenzar',
features: {
trackAndCollect: 'Organiza recibos',
Expand Down
12 changes: 1 addition & 11 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type Screens = Partial<Record<Screen, () => React.ComponentType>>;
* Create a modal stack navigator with an array of sub-screens.
*
* @param screens key/value pairs where the key is the name of the screen and the value is a functon that returns the lazy-loaded component
* @param getScreenOptions optional function that returns the screen options, override the default options
*/
function createModalStackNavigator<TStackParams extends ParamListBase>(screens: Screens, getScreenOptions?: (styles: ThemeStyles) => StackNavigationOptions): React.ComponentType {
const ModalStackNavigator = createStackNavigator<TStackParams>();
Expand Down Expand Up @@ -184,8 +185,6 @@ const NewTeachersUniteNavigator = createModalStackNavigator<TeachersUniteNavigat

const AccountSettingsModalStackNavigator = createModalStackNavigator(
{
[SCREENS.SETTINGS.ROOT]: () => require('../../../pages/settings/InitialSettingsPage').default as React.ComponentType,
[SCREENS.SETTINGS.WORKSPACES]: () => require('../../../pages/workspace/WorkspacesListPage').default as React.ComponentType,
[SCREENS.SETTINGS.PREFERENCES.ROOT]: () => require('../../../pages/settings/Preferences/PreferencesPage').default as React.ComponentType,
[SCREENS.SETTINGS.SECURITY]: () => require('../../../pages/settings/Security/SecuritySettingsPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.ROOT]: () => require('../../../pages/settings/Profile/ProfilePage').default as React.ComponentType,
Expand All @@ -197,8 +196,6 @@ const AccountSettingsModalStackNavigator = createModalStackNavigator(
);

const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorParamList>({
[SCREENS.SETTINGS.SHARE_CODE]: () => require('../../../pages/ShareCodePage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.ROOT]: () => require('../../../pages/settings/Profile/ProfilePage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.PRONOUNS]: () => require('../../../pages/settings/Profile/PronounsPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.DISPLAY_NAME]: () => require('../../../pages/settings/Profile/DisplayNamePage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.TIMEZONE]: () => require('../../../pages/settings/Profile/TimezoneInitialPage').default as React.ComponentType,
Expand All @@ -211,16 +208,12 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.SETTINGS.PROFILE.CONTACT_METHODS]: () => require('../../../pages/settings/Profile/Contacts/ContactMethodsPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_DETAILS]: () => require('../../../pages/settings/Profile/Contacts/ContactMethodDetailsPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.NEW_CONTACT_METHOD]: () => require('../../../pages/settings/Profile/Contacts/NewContactMethodPage').default as React.ComponentType,
[SCREENS.SETTINGS.PREFERENCES.ROOT]: () => require('../../../pages/settings/Preferences/PreferencesPage').default as React.ComponentType,
[SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE]: () => require('../../../pages/settings/Preferences/PriorityModePage').default as React.ComponentType,
[SCREENS.SETTINGS.PREFERENCES.LANGUAGE]: () => require('../../../pages/settings/Preferences/LanguagePage').default as React.ComponentType,
[SCREENS.SETTINGS.PREFERENCES.THEME]: () => require('../../../pages/settings/Preferences/ThemePage').default as React.ComponentType,
[SCREENS.SETTINGS.CLOSE]: () => require('../../../pages/settings/Security/CloseAccountPage').default as React.ComponentType,
[SCREENS.SETTINGS.SECURITY]: () => require('../../../pages/settings/Security/SecuritySettingsPage').default as React.ComponentType,
[SCREENS.SETTINGS.ABOUT]: () => require('../../../pages/settings/AboutPage/AboutPage').default as React.ComponentType,
[SCREENS.SETTINGS.APP_DOWNLOAD_LINKS]: () => require('../../../pages/settings/AppDownloadLinks').default as React.ComponentType,
[SCREENS.SETTINGS.LOUNGE_ACCESS]: () => require('../../../pages/settings/Profile/LoungeAccessPage').default as React.ComponentType,
[SCREENS.SETTINGS.WALLET.ROOT]: () => require('../../../pages/settings/Wallet/WalletPage').default as React.ComponentType,
[SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: () => require('../../../pages/settings/Profile/PersonalDetails/AddressPage').default as React.ComponentType,
[SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: () => require('../../../pages/settings/Wallet/ExpensifyCardPage').default as React.ComponentType,
[SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: () => require('../../../pages/settings/Wallet/ReportVirtualCardFraudPage').default as React.ComponentType,
Expand All @@ -238,9 +231,6 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER]: () => require('../../../pages/settings/Profile/CustomStatus/StatusClearAfterPage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_DATE]: () => require('../../../pages/settings/Profile/CustomStatus/SetDatePage').default as React.ComponentType,
[SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_TIME]: () => require('../../../pages/settings/Profile/CustomStatus/SetTimePage').default as React.ComponentType,
[SCREENS.WORKSPACE.INITIAL]: () => require('../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType,
[SCREENS.WORKSPACE.CARD]: () => require('../../../pages/workspace/card/WorkspaceCardPage').default as React.ComponentType,
[SCREENS.WORKSPACE.REIMBURSE]: () => require('../../../pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType,
[SCREENS.WORKSPACE.RATE_AND_UNIT]: () => require('../../../pages/workspace/reimburse/WorkspaceRateAndUnitPage').default as React.ComponentType,
[SCREENS.WORKSPACE.INVITE]: () => require('../../../pages/workspace/WorkspaceInvitePage').default as React.ComponentType,
[SCREENS.WORKSPACE.INVITE_MESSAGE]: () => require('../../../pages/workspace/WorkspaceInviteMessagePage').default as React.ComponentType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const Tab = createCustomBottomTabNavigator<BottomTabNavigatorParamList>();

const screenOptions: StackNavigationOptions = {
headerShown: false,
animationEnabled: false,
};

function BottomTabNavigator() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function FullScreenNavigator() {

return (
<View style={styles.rootNavigatorContainerStyles(isSmallScreenWidth)}>
<RootStack.Navigator isSmallScreenWidth={isSmallScreenWidth}>
<RootStack.Navigator>
<RootStack.Screen
name={SCREENS.SETTINGS.ROOT}
options={screenOptions.homeScreen}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function TopBar() {
<Search
placeholder={translate('sidebarScreen.buttonSearch')}
onPress={Session.checkIfActionIsAllowed(() => Navigation.navigate(ROUTES.SEARCH))}
containerStyle={styles.flexGrow1}
containerStyle={styles.flexShrink1}
/>
<SignInOrAvatarWithOptionalStatus />
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type {DefaultNavigatorOptions, ParamListBase, StackActionHelpers, StackNa
import {createNavigatorFactory, StackRouter, useNavigationBuilder} from '@react-navigation/native';
import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack';
import {StackView} from '@react-navigation/stack';
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import ScreenWrapper from '@components/ScreenWrapper';
Expand All @@ -16,23 +15,6 @@ type CustomNavigatorProps = DefaultNavigatorOptions<ParamListBase, StackNavigati
initialRouteName: string;
};

const propTypes = {
/* Children for the useNavigationBuilder hook */
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,

/* initialRouteName for this navigator */
initialRouteName: PropTypes.oneOf([PropTypes.string, undefined]),

/* Screen options defined for this navigator */
// eslint-disable-next-line react/forbid-prop-types
screenOptions: PropTypes.object,
};

const defaultProps = {
initialRouteName: undefined,
screenOptions: undefined,
};

function getStateToRender(state: StackNavigationState<ParamListBase>): StackNavigationState<ParamListBase> {
const routesToRender = [state.routes.at(-1)] as NavigationStateRoute[];
// We need to render at least one HOME screen to make sure everything load properly.
Expand Down Expand Up @@ -84,8 +66,6 @@ function CustomBottomTabNavigator({initialRouteName, children, screenOptions, ..
);
}

CustomBottomTabNavigator.defaultProps = defaultProps;
CustomBottomTabNavigator.propTypes = propTypes;
CustomBottomTabNavigator.displayName = 'CustomBottomTabNavigator';

export default createNavigatorFactory(CustomBottomTabNavigator);
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import type {DefaultNavigatorOptions, ParamListBase, StackNavigationState, StackRouterOptions} from '@react-navigation/native';
import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack';

type FullScreenNavigatorConfig = {
isSmallScreenWidth: boolean;
};

type FullScreenNavigatorRouterOptions = StackRouterOptions;

type FullScreenNavigatorProps = DefaultNavigatorOptions<ParamListBase, StackNavigationState<ParamListBase>, StackNavigationOptions, StackNavigationEventMap> & FullScreenNavigatorConfig;
type FullScreenNavigatorProps = DefaultNavigatorOptions<ParamListBase, StackNavigationState<ParamListBase>, StackNavigationOptions, StackNavigationEventMap>;

export type {FullScreenNavigatorConfig, FullScreenNavigatorProps, FullScreenNavigatorRouterOptions};
export type {FullScreenNavigatorProps, FullScreenNavigatorRouterOptions};
2 changes: 1 addition & 1 deletion src/libs/Navigation/Navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ function goBack(fallbackRoute: Route = '', shouldEnforceFallback = false, should
*/
function closeFullScreen() {
const rootState = navigationRef.getRootState();
navigationRef.dispatch({...StackActions.pop(), target: rootState.key});
navigationRef.dispatch({...StackActions.popToTop(), target: rootState.key});
}

/**
Expand Down
13 changes: 13 additions & 0 deletions src/libs/Navigation/TAB_TO_CENTRAL_PANE_MAPPING.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,17 @@ const TAB_TO_CENTRAL_PANE_MAPPING: Record<BottomTabName, CentralPaneName[]> = {
],
};

const generateCentralPaneToTabMapping = (): Record<CentralPaneName, BottomTabName> => {
const mapping: Record<CentralPaneName, BottomTabName> = {} as Record<CentralPaneName, BottomTabName>;
for (const [tabName, centralPaneNames] of Object.entries(TAB_TO_CENTRAL_PANE_MAPPING)) {
for (const centralPaneName of centralPaneNames) {
mapping[centralPaneName] = tabName as BottomTabName;
}
}
return mapping;
};

const CENTRAL_PANE_TO_TAB_MAPPING: Record<CentralPaneName, BottomTabName> = generateCentralPaneToTabMapping();

export {CENTRAL_PANE_TO_TAB_MAPPING};
export default TAB_TO_CENTRAL_PANE_MAPPING;
15 changes: 5 additions & 10 deletions src/libs/Navigation/getMatchingBottomTabRouteForState.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// import CONST from '@src/CONST';
import SCREENS from '@src/SCREENS';
import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute';
import TAB_TO_CENTRAL_PANE_MAPPING from './TAB_TO_CENTRAL_PANE_MAPPING';
import {CENTRAL_PANE_TO_TAB_MAPPING} from './TAB_TO_CENTRAL_PANE_MAPPING';
import type {BottomTabName, NavigationPartialRoute, RootStackParamList, State} from './types';

// Get the route that matches the topmost central pane route in the navigation stack. e.g REPORT -> HOME
Expand All @@ -13,16 +13,11 @@ function getMatchingBottomTabRouteForState(state: State<RootStackParamList>): Na
return defaultRoute;
}

for (const [tabName, centralPaneNames] of Object.entries(TAB_TO_CENTRAL_PANE_MAPPING)) {
if (centralPaneNames.includes(topmostCentralPaneRoute.name)) {
if (tabName === SCREENS.WORKSPACE.INITIAL) {
return {name: tabName, params: topmostCentralPaneRoute.params};
}
return {name: tabName as BottomTabName};
}
const tabName = CENTRAL_PANE_TO_TAB_MAPPING[topmostCentralPaneRoute.name];
if (tabName === SCREENS.WORKSPACE.INITIAL) {
return {name: tabName, params: topmostCentralPaneRoute.params};
}

return defaultRoute;
return {name: tabName as BottomTabName} || defaultRoute;
}

export default getMatchingBottomTabRouteForState;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {NavigationState, PartialState} from '@react-navigation/native';
import SCREENS from '@src/SCREENS';

// Get the name of topmost report in the navigation stack.
function getTopmostCentralPaneName(state: NavigationState | PartialState<NavigationState>): string | undefined {
function getTopmostSettingsCentralPaneName(state: NavigationState | PartialState<NavigationState>): string | undefined {
if (!state) {
return;
}
Expand All @@ -16,4 +16,4 @@ function getTopmostCentralPaneName(state: NavigationState | PartialState<Navigat
return topmostCentralPane.state?.routes.at(-1)?.name;
}

export default getTopmostCentralPaneName;
export default getTopmostSettingsCentralPaneName;
8 changes: 7 additions & 1 deletion src/libs/Navigation/linkTo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ function getActionForBottomTabNavigator(action: StackNavigationAction, state: Na
const params = action.payload.params as ActionPayloadParams;
const screen = params.screen;

// Check if the current bottom tab is the same as the one we want to navigate to. If it is, we don't need to do anything.
const bottomTabCurrentTab = getTopmostBottomTabRoute(state);
if (bottomTabCurrentTab?.name === screen) {
return;
}

return {
type: CONST.NAVIGATION.ACTION_TYPE.PUSH,
payload: {
Expand Down Expand Up @@ -149,7 +155,7 @@ export default function linkTo(navigation: NavigationContainerRef<RootStackParam
const actionForBottomTabNavigator = getActionForBottomTabNavigator(action, rootState);

if (!actionForBottomTabNavigator) {
throw new Error('Could not get action for bottom tab navigator');
return;
}

root.dispatch(actionForBottomTabNavigator);
Expand Down
3 changes: 2 additions & 1 deletion src/pages/home/sidebar/AllSettingsScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ function AllSettingsScreen() {
<ScreenWrapper
testID={AllSettingsScreen.displayName}
includePaddingTop={false}
offlineIndicatorStyle={styles.offlineIndicatorBottomTabBar}
includeSafeAreaPaddingBottom={false}
shouldDisableSafeAreaPaddingBottom
>
<Breadcrumbs
breadcrumbs={[
Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function BaseSidebarScreen(props) {
style={[styles.sidebar, Browser.isMobile() ? styles.userSelectNone : {}]}
testID={BaseSidebarScreen.displayName}
includePaddingTop={false}
offlineIndicatorStyle={styles.offlineIndicatorBottomTabBar}
shouldDisableSafeAreaPaddingBottom
>
{({insets}) => (
<View style={[styles.flex1]}>
Expand Down
Loading
Loading