diff --git a/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch b/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch
index 877521094cd4..c65ebbb98007 100644
--- a/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch
+++ b/patches/@react-navigation+stack+6.3.16+002+dontDetachScreen.patch
@@ -43,7 +43,7 @@ index 7558eb3..b7bb75e 100644
}) : STATE_TRANSITIONING_OR_BELOW_TOP;
}
+
-+ const isHomeScreenAndNotOnTop = (route.name === 'BottomTabNavigator' || route.name === 'Settings_Root') && isScreenActive !== STATE_ON_TOP;
++ const isHomeScreenAndNotOnTop = (route.name === 'BottomTabNavigator' || route.name === 'Workspace_Initial') && isScreenActive !== STATE_ON_TOP;
+
const {
headerShown = true,
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 8de1757fc1b4..f89ba19736ed 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -450,128 +450,128 @@ const ROUTES = {
WORKSPACE_NEW: 'workspace/new',
WORKSPACE_NEW_ROOM: 'workspace/new-room',
WORKSPACE_INITIAL: {
- route: 'workspace/:policyID',
- getRoute: (policyID: string) => `workspace/${policyID}` as const,
+ route: 'settings/workspaces/:policyID',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}` as const,
},
WORKSPACE_INVITE: {
- route: 'workspace/:policyID/invite',
- getRoute: (policyID: string) => `workspace/${policyID}/invite` as const,
+ route: 'settings/workspaces/:policyID/invite',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/invite` as const,
},
WORKSPACE_INVITE_MESSAGE: {
- route: 'workspace/:policyID/invite-message',
- getRoute: (policyID: string) => `workspace/${policyID}/invite-message` as const,
+ route: 'settings/workspaces/:policyID/invite-message',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/invite-message` as const,
},
WORKSPACE_PROFILE: {
- route: 'workspace/:policyID/profile',
- getRoute: (policyID: string) => `workspace/${policyID}/profile` as const,
+ route: 'settings/workspaces/:policyID/profile',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/profile` as const,
},
WORKSPACE_PROFILE_CURRENCY: {
- route: 'workspace/:policyID/profile/currency',
- getRoute: (policyID: string) => `workspace/${policyID}/profile/currency` as const,
+ route: 'settings/workspaces/:policyID/profile/currency',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/profile/currency` as const,
},
WORKSPACE_PROFILE_NAME: {
- route: 'workspace/:policyID/profile/name',
- getRoute: (policyID: string) => `workspace/${policyID}/profile/name` as const,
+ route: 'settings/workspaces/:policyID/profile/name',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/profile/name` as const,
},
WORKSPACE_PROFILE_DESCRIPTION: {
- route: 'workspace/:policyID/profile/description',
- getRoute: (policyID: string) => `workspace/${policyID}/profile/description` as const,
+ route: 'settings/workspaces/:policyID/profile/description',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/profile/description` as const,
},
WORKSPACE_PROFILE_SHARE: {
- route: 'workspace/:policyID/profile/share',
- getRoute: (policyID: string) => `workspace/${policyID}/profile/share` as const,
+ route: 'settings/workspaces/:policyID/profile/share',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/profile/share` as const,
},
WORKSPACE_AVATAR: {
- route: 'workspace/:policyID/avatar',
- getRoute: (policyID: string) => `workspace/${policyID}/avatar` as const,
+ route: 'settings/workspaces/:policyID/avatar',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/avatar` as const,
},
WORKSPACE_JOIN_USER: {
- route: 'workspace/:policyID/join',
- getRoute: (policyID: string, inviterEmail: string) => `workspace/${policyID}/join?email=${inviterEmail}` as const,
+ route: 'settings/workspaces/:policyID/join',
+ getRoute: (policyID: string, inviterEmail: string) => `settings/workspaces/${policyID}/join?email=${inviterEmail}` as const,
},
WORKSPACE_SETTINGS_CURRENCY: {
- route: 'workspace/:policyID/settings/currency',
- getRoute: (policyID: string) => `workspace/${policyID}/settings/currency` as const,
+ route: 'settings/workspaces/:policyID/settings/currency',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/settings/currency` as const,
},
WORKSPACE_WORKFLOWS: {
- route: 'workspace/:policyID/workflows',
- getRoute: (policyID: string) => `workspace/${policyID}/workflows` as const,
+ route: 'settings/workspaces/:policyID/workflows',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/workflows` as const,
},
WORKSPACE_WORKFLOWS_PAYER: {
route: 'workspace/:policyID/settings/workflows/payer',
getRoute: (policyId: string) => `workspace/${policyId}/settings/workflows/payer` as const,
},
WORKSPACE_WORKFLOWS_APPROVER: {
- route: 'workspace/:policyID/settings/workflows/approver',
- getRoute: (policyId: string) => `workspace/${policyId}/settings/workflows/approver` as const,
+ route: 'settings/workspaces/:policyID/settings/workflows/approver',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/settings/workflows/approver` as const,
},
WORKSPACE_WORKFLOWS_AUTOREPORTING_FREQUENCY: {
- route: 'workspace/:policyID/settings/workflows/auto-reporting-frequency',
- getRoute: (policyID: string) => `workspace/${policyID}/settings/workflows/auto-reporting-frequency` as const,
+ route: 'settings/workspaces/:policyID/settings/workflows/auto-reporting-frequency',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/settings/workflows/auto-reporting-frequency` as const,
},
WORKSPACE_WORKFLOWS_AUTOREPORTING_MONTHLY_OFFSET: {
- route: 'workspace/:policyID/settings/workflows/auto-reporting-frequency/monthly-offset',
- getRoute: (policyID: string) => `workspace/${policyID}/settings/workflows/auto-reporting-frequency/monthly-offset` as const,
+ route: 'settings/workspaces/:policyID/settings/workflows/auto-reporting-frequency/monthly-offset',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/settings/workflows/auto-reporting-frequency/monthly-offset` as const,
},
WORKSPACE_CARD: {
- route: 'workspace/:policyID/card',
- getRoute: (policyID: string) => `workspace/${policyID}/card` as const,
+ route: 'settings/workspaces/:policyID/card',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/card` as const,
},
WORKSPACE_REIMBURSE: {
- route: 'workspace/:policyID/reimburse',
- getRoute: (policyID: string) => `workspace/${policyID}/reimburse` as const,
+ route: 'settings/workspaces/:policyID/reimburse',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/reimburse` as const,
},
WORKSPACE_RATE_AND_UNIT: {
- route: 'workspace/:policyID/rateandunit',
- getRoute: (policyID: string) => `workspace/${policyID}/rateandunit` as const,
+ route: 'settings/workspaces/:policyID/rateandunit',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/rateandunit` as const,
},
WORKSPACE_RATE_AND_UNIT_RATE: {
- route: 'workspace/:policyID/rateandunit/rate',
- getRoute: (policyID: string) => `workspace/${policyID}/rateandunit/rate` as const,
+ route: 'settings/workspaces/:policyID/rateandunit/rate',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/rateandunit/rate` as const,
},
WORKSPACE_RATE_AND_UNIT_UNIT: {
- route: 'workspace/:policyID/rateandunit/unit',
- getRoute: (policyID: string) => `workspace/${policyID}/rateandunit/unit` as const,
+ route: 'settings/workspaces/:policyID/rateandunit/unit',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/rateandunit/unit` as const,
},
WORKSPACE_BILLS: {
- route: 'workspace/:policyID/bills',
- getRoute: (policyID: string) => `workspace/${policyID}/bills` as const,
+ route: 'settings/workspaces/:policyID/bills',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/bills` as const,
},
WORKSPACE_INVOICES: {
- route: 'workspace/:policyID/invoices',
- getRoute: (policyID: string) => `workspace/${policyID}/invoices` as const,
+ route: 'settings/workspaces/:policyID/invoices',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/invoices` as const,
},
WORKSPACE_TRAVEL: {
- route: 'workspace/:policyID/travel',
- getRoute: (policyID: string) => `workspace/${policyID}/travel` as const,
+ route: 'settings/workspaces/:policyID/travel',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/travel` as const,
},
WORKSPACE_MEMBERS: {
- route: 'workspace/:policyID/members',
- getRoute: (policyID: string) => `workspace/${policyID}/members` as const,
+ route: 'settings/workspaces/:policyID/members',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/members` as const,
},
WORKSPACE_CATEGORIES: {
- route: 'workspace/:policyID/categories',
- getRoute: (policyID: string) => `workspace/${policyID}/categories` as const,
+ route: 'settings/workspaces/:policyID/categories',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories` as const,
},
WORKSPACE_CATEGORY_SETTINGS: {
- route: 'workspace/:policyID/categories/:categoryName',
- getRoute: (policyID: string, categoryName: string) => `workspace/${policyID}/categories/${encodeURI(categoryName)}` as const,
+ route: 'settings/workspaces/:policyID/categories/:categoryName',
+ getRoute: (policyID: string, categoryName: string) => `settings/workspaces/${policyID}/categories/${encodeURI(categoryName)}` as const,
},
WORKSPACE_CATEGORIES_SETTINGS: {
- route: 'workspace/:policyID/categories/settings',
- getRoute: (policyID: string) => `workspace/${policyID}/categories/settings` as const,
+ route: 'settings/workspaces/:policyID/categories/settings',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories/settings` as const,
},
WORKSPACE_MORE_FEATURES: {
route: 'workspace/:policyID/more-features',
getRoute: (policyID: string) => `workspace/${policyID}/more-features` as const,
},
WORKSPACE_CATEGORY_CREATE: {
- route: 'workspace/:policyID/categories/new',
- getRoute: (policyID: string) => `workspace/${policyID}/categories/new` as const,
+ route: 'settings/workspaces/:policyID/categories/new',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories/new` as const,
},
WORKSPACE_TAGS: {
- route: 'workspace/:policyID/tags',
- getRoute: (policyID: string) => `workspace/${policyID}/tags` as const,
+ route: 'settings/workspaces/:policyID/tags',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags` as const,
},
WORKSPACE_TAGS_SETTINGS: {
route: 'workspace/:policyID/tags/settings',
@@ -582,12 +582,12 @@ const ROUTES = {
getRoute: (policyID: string) => `workspace/${policyID}/tags/edit` as const,
},
WORKSPACE_MEMBER_DETAILS: {
- route: 'workspace/:policyID/members/:accountID',
- getRoute: (policyID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`workspace/${policyID}/members/${accountID}`, backTo),
+ route: 'settings/workspaces/:policyID/members/:accountID',
+ getRoute: (policyID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/members/${accountID}`, backTo),
},
WORKSPACE_MEMBER_ROLE_SELECTION: {
- route: 'workspace/:policyID/members/:accountID/role-selection',
- getRoute: (policyID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`workspace/${policyID}/members/${accountID}/role-selection`, backTo),
+ route: 'settings/workspaces/:policyID/members/:accountID/role-selection',
+ getRoute: (policyID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/members/${accountID}/role-selection`, backTo),
},
WORKSPACE_DISTANCE_RATES: {
route: 'workspace/:policyID/distance-rates',
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index e5cf783b553f..dedb022415ca 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -22,6 +22,7 @@ const SCREENS = {
VALIDATE_LOGIN: 'ValidateLogin',
UNLINK_LOGIN: 'UnlinkLogin',
SETTINGS_CENTRAL_PANE: 'SettingsCentralPane',
+ WORKSPACES_CENTRAL_PANE: 'WorkspacesCentralPane',
SETTINGS: {
ROOT: 'Settings_Root',
SHARE_CODE: 'Settings_Share_Code',
diff --git a/src/components/AvatarSkeleton.tsx b/src/components/AvatarSkeleton.tsx
index 0887830aa07a..a6781448c3ba 100644
--- a/src/components/AvatarSkeleton.tsx
+++ b/src/components/AvatarSkeleton.tsx
@@ -1,21 +1,24 @@
import React from 'react';
import {Circle} from 'react-native-svg';
import useTheme from '@hooks/useTheme';
+import variables from '@styles/variables';
import SkeletonViewContentLoader from './SkeletonViewContentLoader';
function AvatarSkeleton() {
const theme = useTheme();
+ const skeletonCircleRadius = variables.componentSizeSmall / 2;
+
return (
);
diff --git a/src/components/AvatarWithIndicator.tsx b/src/components/AvatarWithIndicator.tsx
index 2fd733d4b072..42b91b3d2d71 100644
--- a/src/components/AvatarWithIndicator.tsx
+++ b/src/components/AvatarWithIndicator.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import * as UserUtils from '@libs/UserUtils';
+import CONST from '@src/CONST';
import Avatar from './Avatar';
import AvatarSkeleton from './AvatarSkeleton';
import * as Expensicons from './Icon/Expensicons';
@@ -33,6 +34,7 @@ function AvatarWithIndicator({source, tooltipText = '', fallbackIcon = Expensico
) : (
<>
diff --git a/src/components/FloatingActionButton.tsx b/src/components/FloatingActionButton.tsx
index 88938f31cd79..b9c52ad397ec 100644
--- a/src/components/FloatingActionButton.tsx
+++ b/src/components/FloatingActionButton.tsx
@@ -104,13 +104,13 @@ function FloatingActionButton({onPress, isActive, accessibilityLabel, role}: Flo
};
return (
-
-
-
+
+
+
{
fabPressable.current = el ?? null;
@@ -136,9 +136,9 @@ function FloatingActionButton({onPress, isActive, accessibilityLabel, role}: Flo
/>
-
-
-
+
+
+
);
}
diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx
index 3a1c35d46c94..21f3e9a3b605 100755
--- a/src/components/HeaderWithBackButton/index.tsx
+++ b/src/components/HeaderWithBackButton/index.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import {Keyboard, StyleSheet, View} from 'react-native';
+import Avatar from '@components/Avatar';
import AvatarWithDisplayName from '@components/AvatarWithDisplayName';
import Header from '@components/Header';
import Icon from '@components/Icon';
@@ -32,7 +33,8 @@ function HeaderWithBackButton({
onThreeDotsButtonPress = () => {},
report = null,
policy,
- shouldShowAvatarWithDisplay = false,
+ policyAvatar,
+ shouldShowReportAvatarWithDisplay = false,
shouldShowBackButton = true,
shouldShowBorderBottom = false,
shouldShowCloseButton = false,
@@ -58,6 +60,7 @@ function HeaderWithBackButton({
shouldOverlay = false,
singleExecution = (func) => func,
shouldNavigateToTopMostReport = false,
+ style,
}: HeaderWithBackButtonProps) {
const theme = useTheme();
const styles = useThemeStyles();
@@ -81,6 +84,7 @@ function HeaderWithBackButton({
shouldShowBorderBottom && styles.borderBottom,
shouldShowBackButton && styles.pl2,
shouldOverlay && StyleSheet.absoluteFillObject,
+ style,
]}
>
@@ -118,7 +122,15 @@ function HeaderWithBackButton({
additionalStyles={[styles.mr2]}
/>
)}
- {shouldShowAvatarWithDisplay ? (
+ {policyAvatar && (
+
+ )}
+ {shouldShowReportAvatarWithDisplay ? (
& {
/** Data to display a step counter in the header */
stepCounter?: StepCounterParams;
- /** Whether we should show an avatar */
- shouldShowAvatarWithDisplay?: boolean;
+ /** Whether we should show a report avatar */
+ shouldShowReportAvatarWithDisplay?: boolean;
/** Parent report, if provided it will override props.report for AvatarWithDisplay */
parentReport?: OnyxEntry;
@@ -121,6 +123,12 @@ type HeaderWithBackButtonProps = Partial & {
/** Whether we should overlay the 3 dots menu */
shouldOverlayDots?: boolean;
+
+ /** Policy avatar to display in the header */
+ policyAvatar?: Icon;
+
+ /** Additional styles to add to the component */
+ style?: StyleProp;
};
export type {ThreeDotsMenuItem};
diff --git a/src/components/Indicator.tsx b/src/components/Indicator.tsx
index 1420a6abe189..e3d226a17999 100644
--- a/src/components/Indicator.tsx
+++ b/src/components/Indicator.tsx
@@ -1,17 +1,24 @@
import React from 'react';
import {StyleSheet, View} from 'react-native';
-import type {OnyxEntry} from 'react-native-onyx';
+import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
+import * as PolicyUtils from '@libs/PolicyUtils';
import * as UserUtils from '@libs/UserUtils';
import * as PaymentMethods from '@userActions/PaymentMethods';
import ONYXKEYS from '@src/ONYXKEYS';
-import type {BankAccountList, FundList, LoginList, UserWallet, WalletTerms} from '@src/types/onyx';
+import type {BankAccountList, FundList, LoginList, Policy, PolicyMembers, ReimbursementAccount, UserWallet, WalletTerms} from '@src/types/onyx';
type CheckingMethod = () => boolean;
type IndicatorOnyxProps = {
+ /** The employee list of all policies (coming from Onyx) */
+ allPolicyMembers: OnyxCollection;
+
+ /** All the user's policies (from Onyx via withFullPolicy) */
+ policies: OnyxCollection;
+
/** List of bank accounts */
bankAccountList: OnyxEntry;
@@ -21,6 +28,9 @@ type IndicatorOnyxProps = {
/** The user's wallet (coming from Onyx) */
userWallet: OnyxEntry;
+ /** Bank account attached to free plan */
+ reimbursementAccount: OnyxEntry;
+
/** Information about the user accepting the terms for payments */
walletTerms: OnyxEntry;
@@ -30,16 +40,25 @@ type IndicatorOnyxProps = {
type IndicatorProps = IndicatorOnyxProps;
-function Indicator({bankAccountList, fundList, userWallet, walletTerms, loginList}: IndicatorOnyxProps) {
+function Indicator({reimbursementAccount, allPolicyMembers, policies, bankAccountList, fundList, userWallet, walletTerms, loginList}: IndicatorOnyxProps) {
const theme = useTheme();
const styles = useThemeStyles();
+ // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and
+ // those should be cleaned out before doing any error checking
+ const cleanPolicies = Object.fromEntries(Object.entries(policies ?? {}).filter(([, policy]) => policy?.id));
+ const cleanAllPolicyMembers = Object.fromEntries(Object.entries(allPolicyMembers ?? {}).filter(([, policyMembers]) => !!policyMembers));
+
// All of the error & info-checking methods are put into an array. This is so that using _.some() will return
// early as soon as the first error / info condition is returned. This makes the checks very efficient since
// we only care if a single error / info condition exists anywhere.
const errorCheckingMethods: CheckingMethod[] = [
() => Object.keys(userWallet?.errors ?? {}).length > 0,
() => PaymentMethods.hasPaymentMethodError(bankAccountList, fundList),
+ () => Object.values(cleanPolicies).some(PolicyUtils.hasPolicyError),
+ () => Object.values(cleanPolicies).some(PolicyUtils.hasCustomUnitsError),
+ () => Object.values(cleanAllPolicyMembers).some(PolicyUtils.hasPolicyMemberError),
+ () => Object.keys(reimbursementAccount?.errors ?? {}).length > 0,
() => !!loginList && UserUtils.hasLoginListError(loginList),
// Wallet term errors that are not caused by an IOU (we show the red brick indicator for those in the LHN instead)
@@ -58,9 +77,19 @@ function Indicator({bankAccountList, fundList, userWallet, walletTerms, loginLis
Indicator.displayName = 'Indicator';
export default withOnyx({
+ allPolicyMembers: {
+ key: ONYXKEYS.COLLECTION.POLICY_MEMBERS,
+ },
+ policies: {
+ key: ONYXKEYS.COLLECTION.POLICY,
+ },
bankAccountList: {
key: ONYXKEYS.BANK_ACCOUNT_LIST,
},
+ // @ts-expect-error: ONYXKEYS.REIMBURSEMENT_ACCOUNT is conflicting with ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM
+ reimbursementAccount: {
+ key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
+ },
fundList: {
key: ONYXKEYS.FUND_LIST,
},
diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx
index e0d6c39623ed..74fec2c606af 100644
--- a/src/components/MoneyReportHeader.tsx
+++ b/src/components/MoneyReportHeader.tsx
@@ -97,7 +97,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money
return (
;
};
-type WorkspaceSwitcherButtonProps = {activeWorkspaceID?: string} & WorkspaceSwitcherButtonOnyxProps;
+type WorkspaceSwitcherButtonProps = WorkspaceSwitcherButtonOnyxProps;
-function WorkspaceSwitcherButton({activeWorkspaceID, policy}: WorkspaceSwitcherButtonProps) {
+function WorkspaceSwitcherButton({policy}: WorkspaceSwitcherButtonProps) {
const {translate} = useLocalize();
const theme = useTheme();
const {source, name, type} = useMemo(() => {
- if (!activeWorkspaceID) {
+ if (!policy) {
return {source: Expensicons.ExpensifyAppIcon, name: CONST.WORKSPACE_SWITCHER.NAME, type: CONST.ICON_TYPE_AVATAR};
}
@@ -36,7 +34,7 @@ function WorkspaceSwitcherButton({activeWorkspaceID, policy}: WorkspaceSwitcherB
name: policy?.name ?? '',
type: CONST.ICON_TYPE_WORKSPACE,
};
- }, [policy, activeWorkspaceID]);
+ }, [policy]);
return (
@@ -71,8 +69,4 @@ function WorkspaceSwitcherButton({activeWorkspaceID, policy}: WorkspaceSwitcherB
WorkspaceSwitcherButton.displayName = 'WorkspaceSwitcherButton';
-export default withOnyx({
- policy: {
- key: ({activeWorkspaceID}) => `${ONYXKEYS.COLLECTION.POLICY}${activeWorkspaceID}`,
- },
-})(WorkspaceSwitcherButton);
+export default WorkspaceSwitcherButton;
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx
index 3f18e0c3c30c..897ac9c71a84 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx
@@ -186,13 +186,20 @@ const NewTeachersUniteNavigator = createModalStackNavigator require('../../../pages/TeachersUnite/ImTeacherPage').default as React.ComponentType,
});
-const AccountSettingsModalStackNavigator = createModalStackNavigator(
+const WorkspaceSettingsModalStackNavigator = createModalStackNavigator(
{
- [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,
- [SCREENS.SETTINGS.WALLET.ROOT]: () => require('../../../pages/settings/Wallet/WalletPage').default as React.ComponentType,
- [SCREENS.SETTINGS.ABOUT]: () => require('../../../pages/settings/AboutPage/AboutPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.PROFILE]: () => require('../../../pages/workspace/WorkspaceProfilePage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.CARD]: () => require('../../../pages/workspace/card/WorkspaceCardPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.WORKFLOWS]: () => require('../../../pages/workspace/workflows/WorkspaceWorkflowsPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.REIMBURSE]: () => require('../../../pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.BILLS]: () => require('../../../pages/workspace/bills/WorkspaceBillsPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.INVOICES]: () => require('../../../pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.TRAVEL]: () => require('../../../pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.MEMBERS]: () => require('../../../pages/workspace/WorkspaceMembersPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.CATEGORIES]: () => require('../../../pages/workspace/categories/WorkspaceCategoriesPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.MORE_FEATURES]: () => require('../../../pages/workspace/WorkspaceMoreFeaturesPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.TAGS]: () => require('../../../pages/workspace/tags/WorkspaceTagsPage').default as React.ComponentType,
+ [SCREENS.WORKSPACE.DISTANCE_RATES]: () => require('../../../pages/workspace/distanceRates/PolicyDistanceRatesPage').default as React.ComponentType,
},
(styles) => ({cardStyle: styles.navigationScreenCardStyle, headerShown: false}),
);
@@ -313,7 +320,6 @@ const ProcessMoneyRequestHoldStackNavigator = createModalStackNavigator({
});
export {
- AccountSettingsModalStackNavigator,
AddPersonalBankAccountModalStackNavigator,
DetailsModalStackNavigator,
OnboardEngagementModalStackNavigator,
@@ -342,4 +348,5 @@ export {
TaskModalStackNavigator,
WalletStatementStackNavigator,
ProcessMoneyRequestHoldStackNavigator,
+ WorkspaceSettingsModalStackNavigator,
};
diff --git a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx
index ce03a8d5bcba..87a441f16ddb 100644
--- a/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx
+++ b/src/libs/Navigation/AppNavigator/Navigators/BottomTabNavigator.tsx
@@ -4,12 +4,11 @@ import React from 'react';
import createCustomBottomTabNavigator from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator';
import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute';
import type {BottomTabNavigatorParamList} from '@libs/Navigation/types';
-import AllSettingsScreen from '@pages/home/sidebar/AllSettingsScreen';
import SidebarScreen from '@pages/home/sidebar/SidebarScreen';
import SCREENS from '@src/SCREENS';
import ActiveRouteContext from './ActiveRouteContext';
-const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType;
+const loadInitialSettingsPage = () => require('../../../../pages/settings/InitialSettingsPage').default as React.ComponentType;
const Tab = createCustomBottomTabNavigator();
@@ -20,6 +19,7 @@ const screenOptions: StackNavigationOptions = {
function BottomTabNavigator() {
const activeRoute = useNavigationState(getTopmostCentralPaneRoute);
+
return (
@@ -28,12 +28,8 @@ function BottomTabNavigator() {
component={SidebarScreen}
/>
-
diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx
index 5a3af07a3d5a..16f403342a58 100644
--- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx
+++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx
@@ -13,20 +13,13 @@ const openOnAdminRoom = url ? new URL(url).searchParams.get('openOnAdminRoom') :
type Screens = Partial React.ComponentType>>;
-const workspaceSettingsScreens = {
+const settingsScreens = {
[SCREENS.SETTINGS.WORKSPACES]: () => require('../../../../../pages/workspace/WorkspacesListPage').default as React.ComponentType,
- [SCREENS.WORKSPACE.PROFILE]: () => require('../../../../../pages/workspace/WorkspaceProfilePage').default as React.ComponentType,
- [SCREENS.WORKSPACE.CARD]: () => require('../../../../../pages/workspace/card/WorkspaceCardPage').default as React.ComponentType,
- [SCREENS.WORKSPACE.WORKFLOWS]: () => require('../../../../../pages/workspace/workflows/WorkspaceWorkflowsPage').default as React.ComponentType,
- [SCREENS.WORKSPACE.REIMBURSE]: () => require('../../../../../pages/workspace/reimburse/WorkspaceReimbursePage').default as React.ComponentType,
- [SCREENS.WORKSPACE.BILLS]: () => require('../../../../../pages/workspace/bills/WorkspaceBillsPage').default as React.ComponentType,
- [SCREENS.WORKSPACE.INVOICES]: () => require('../../../../../pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType,
- [SCREENS.WORKSPACE.TRAVEL]: () => require('../../../../../pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType,
- [SCREENS.WORKSPACE.MEMBERS]: () => require('../../../../../pages/workspace/WorkspaceMembersPage').default as React.ComponentType,
- [SCREENS.WORKSPACE.CATEGORIES]: () => require('../../../../../pages/workspace/categories/WorkspaceCategoriesPage').default as React.ComponentType,
- [SCREENS.WORKSPACE.MORE_FEATURES]: () => require('../../../../../pages/workspace/WorkspaceMoreFeaturesPage').default as React.ComponentType,
- [SCREENS.WORKSPACE.TAGS]: () => require('../../../../../pages/workspace/tags/WorkspaceTagsPage').default as React.ComponentType,
- [SCREENS.WORKSPACE.DISTANCE_RATES]: () => require('../../../../../pages/workspace/distanceRates/PolicyDistanceRatesPage').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,
+ [SCREENS.SETTINGS.WALLET.ROOT]: () => require('../../../../../pages/settings/Wallet/WalletPage').default as React.ComponentType,
+ [SCREENS.SETTINGS.ABOUT]: () => require('../../../../../pages/settings/AboutPage/AboutPage').default as React.ComponentType,
} satisfies Screens;
function BaseCentralPaneNavigator() {
@@ -46,8 +39,7 @@ function BaseCentralPaneNavigator() {
initialParams={{openOnAdminRoom: openOnAdminRoom === 'true' || undefined}}
component={ReportScreenWrapper}
/>
-
- {Object.entries(workspaceSettingsScreens).map(([screenName, componentGetter]) => (
+ {Object.entries(settingsScreens).map(([screenName, componentGetter]) => (
require('../../../../pages/settings/InitialSettingsPage').default as React.ComponentType;
+const loadWorkspaceInitialPage = () => require('../../../../pages/workspace/WorkspaceInitialPage').default as React.ComponentType;
const RootStack = createCustomFullScreenNavigator();
@@ -22,14 +22,14 @@ function FullScreenNavigator() {
diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
index 58d9efb43df5..3a59e42bcca1 100644
--- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
+++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/BottomTabBar.tsx
@@ -12,11 +12,11 @@ import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import * as Session from '@libs/actions/Session';
-import interceptAnonymousUser from '@libs/interceptAnonymousUser';
import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute';
import Navigation from '@libs/Navigation/Navigation';
import type {RootStackParamList, State} from '@libs/Navigation/types';
-import {checkIfWorkspaceSettingsTabHasRBR, getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils';
+import {getChatTabBrickRoad} from '@libs/WorkspacesSettingsUtils';
+import BottomTabAvatar from '@pages/home/sidebar/BottomTabAvatar';
import BottomTabBarFloatingActionButton from '@pages/home/sidebar/BottomTabBarFloatingActionButton';
import variables from '@styles/variables';
import * as Welcome from '@userActions/Welcome';
@@ -43,14 +43,7 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps
const navigationState = navigation.getState() as State | undefined;
const routes = navigationState?.routes;
const currentRoute = routes?.[navigationState?.index ?? 0];
- const bottomTabRoute = getTopmostBottomTabRoute(navigationState);
- if (
- // When we are redirected to the Settings tab from the OldDot, we don't want to call the Welcome.show() method.
- // To prevent this, the value of the bottomTabRoute?.name is checked here
- bottomTabRoute?.name === SCREENS.WORKSPACE.INITIAL ||
- Boolean(currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && currentRoute.name !== NAVIGATORS.CENTRAL_PANE_NAVIGATOR) ||
- Session.isAnonymousUser()
- ) {
+ if (Boolean(currentRoute && currentRoute.name !== NAVIGATORS.BOTTOM_TAB_NAVIGATOR && currentRoute.name !== NAVIGATORS.CENTRAL_PANE_NAVIGATOR) || Session.isAnonymousUser()) {
return;
}
@@ -64,22 +57,20 @@ function BottomTabBar({isLoadingApp = false}: PurposeForUsingExpensifyModalProps
return topmostBottomTabRoute?.name ?? SCREENS.HOME;
});
- const shouldShowWorkspaceRedBrickRoad = checkIfWorkspaceSettingsTabHasRBR(activeWorkspaceID) && currentTabName === SCREENS.HOME;
-
- const chatTabBrickRoad = currentTabName !== SCREENS.HOME ? getChatTabBrickRoad(activeWorkspaceID) : undefined;
+ const chatTabBrickRoad = getChatTabBrickRoad(activeWorkspaceID);
return (
-
- {
- Navigation.navigate(ROUTES.HOME);
- }}
- role={CONST.ROLE.BUTTON}
- accessibilityLabel={translate('common.chats')}
- wrapperStyle={styles.flexGrow1}
- style={styles.bottomTabBarItem}
- >
+ {
+ Navigation.navigate(ROUTES.HOME);
+ }}
+ role={CONST.ROLE.BUTTON}
+ accessibilityLabel={translate('common.chats')}
+ wrapperStyle={styles.flex1}
+ style={styles.bottomTabBarItem}
+ >
+
)}
-
-
+
+
+
-
-
- interceptAnonymousUser(() =>
- activeWorkspaceID ? Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(activeWorkspaceID)) : Navigation.navigate(ROUTES.ALL_SETTINGS),
- )
- }
- role={CONST.ROLE.BUTTON}
- accessibilityLabel={translate('common.settings')}
- wrapperStyle={styles.flexGrow1}
- style={styles.bottomTabBarItem}
- >
-
-
- {shouldShowWorkspaceRedBrickRoad && }
-
-
-
+
+
+
);
}
diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx
index 4ed8869c1eaa..38bfe4af9ab6 100644
--- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx
+++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar.tsx
@@ -1,36 +1,87 @@
import React from 'react';
import {View} from 'react-native';
-import Search from '@components/Search';
+import type {OnyxEntry} from 'react-native-onyx';
+import {withOnyx} from 'react-native-onyx';
+import Breadcrumbs from '@components/Breadcrumbs';
+import Icon from '@components/Icon';
+import * as Expensicons from '@components/Icon/Expensicons';
+import {PressableWithoutFeedback} from '@components/Pressable';
+import Tooltip from '@components/Tooltip';
import WorkspaceSwitcherButton from '@components/WorkspaceSwitcherButton';
-import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
-import SignInOrAvatarWithOptionalStatus from '@pages/home/sidebar/SignInOrAvatarWithOptionalStatus';
+import SignInButton from '@pages/home/sidebar/SignInButton';
import * as Session from '@userActions/Session';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
+import type {Policy} from '@src/types/onyx';
-function TopBar() {
+type TopBarOnyxProps = {
+ policy: OnyxEntry;
+};
+
+// eslint-disable-next-line react/no-unused-prop-types
+type TopBarProps = {activeWorkspaceID?: string} & TopBarOnyxProps;
+
+function TopBar({policy}: TopBarProps) {
const styles = useThemeStyles();
+ const theme = useTheme();
const {translate} = useLocalize();
- const {activeWorkspaceID} = useActiveWorkspace();
+
+ const headerBreadcrumb = policy?.name
+ ? {type: CONST.BREADCRUMB_TYPE.STRONG, text: policy.name}
+ : {
+ type: CONST.BREADCRUMB_TYPE.ROOT,
+ };
return (
-
-
- Navigation.navigate(ROUTES.SEARCH))}
- containerStyle={[styles.flex1]}
- />
-
+
+
+
+
+
+
+
+
+
+ {Session.isAnonymousUser() ? (
+
+ ) : (
+
+ Navigation.navigate(ROUTES.SEARCH))}
+ >
+
+
+
+ )}
+
);
}
TopBar.displayName = 'TopBar';
-export default TopBar;
+export default withOnyx({
+ policy: {
+ key: ({activeWorkspaceID}) => `${ONYXKEYS.COLLECTION.POLICY}${activeWorkspaceID}`,
+ },
+})(TopBar);
diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx
index bd32c6cab73c..8c53027cf713 100644
--- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx
+++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx
@@ -9,7 +9,6 @@ import useThemeStyles from '@hooks/useThemeStyles';
import type {NavigationStateRoute} from '@libs/Navigation/types';
import SCREENS from '@src/SCREENS';
import BottomTabBar from './BottomTabBar';
-import TopBar from './TopBar';
type CustomNavigatorProps = DefaultNavigatorOptions, StackNavigationOptions, StackNavigationEventMap> & {
initialRouteName: string;
@@ -52,7 +51,6 @@ function CustomBottomTabNavigator({initialRouteName, children, screenOptions, ..
shouldShowOfflineIndicator={false}
>
-
| PartialState>;
-const isAtLeastOneInState = (state: StackState, screenName: string): boolean => !!state.routes.find((route) => route.name === screenName);
+const isAtLeastOneInState = (state: StackState, screenName: string): boolean => state.routes.some((route) => route.name === screenName);
function adaptStateIfNecessary(state: StackState) {
const isNarrowLayout = getIsNarrowLayout();
+ const workspaceCentralPane = state.routes.at(-1);
+ const topmostWorkspaceCentralPaneRoute = workspaceCentralPane?.state?.routes[0];
- // There should always be SETTINGS.ROOT screen in the state to make sure go back works properly if we deeplinkg to a subpage of settings.
- if (!isAtLeastOneInState(state, SCREENS.SETTINGS.ROOT)) {
+ // When a screen from the FullScreenNavigator is opened from the deeplink then params should be passed to SCREENS.WORKSPACE.INITIAL from the variable defined below.
+ const workspacesCentralPaneParams =
+ workspaceCentralPane?.params && 'params' in workspaceCentralPane.params ? (workspaceCentralPane.params.params as Record) : undefined;
+
+ // There should always be WORKSPACE.INITIAL screen in the state to make sure go back works properly if we deeplinkg to a subpage of settings.
+ if (!isAtLeastOneInState(state, SCREENS.WORKSPACE.INITIAL)) {
// @ts-expect-error Updating read only property
// noinspection JSConstantReassignment
state.stale = true; // eslint-disable-line
@@ -20,27 +26,30 @@ function adaptStateIfNecessary(state: StackState) {
// This is necessary for ts to narrow type down to PartialState.
if (state.stale === true) {
// Unshift the root screen to fill left pane.
- state.routes.unshift({name: SCREENS.SETTINGS.ROOT});
+ state.routes.unshift({
+ name: SCREENS.WORKSPACE.INITIAL,
+ params: topmostWorkspaceCentralPaneRoute?.params ?? workspacesCentralPaneParams,
+ });
}
}
// If the screen is wide, there should be at least two screens inside:
- // - SETINGS.ROOT to cover left pane.
- // - SETTINGS_CENTRAL_PANE to cover central pane.
+ // - WORKSPACE.INITIAL to cover left pane.
+ // - WORKSPACES_CENTRAL_PANE to cover central pane.
if (!isNarrowLayout) {
- if (!isAtLeastOneInState(state, SCREENS.SETTINGS_CENTRAL_PANE)) {
+ if (!isAtLeastOneInState(state, SCREENS.WORKSPACES_CENTRAL_PANE)) {
// @ts-expect-error Updating read only property
// noinspection JSConstantReassignment
state.stale = true; // eslint-disable-line
-
// Push the default settings central pane screen.
if (state.stale === true) {
state.routes.push({
- name: SCREENS.SETTINGS_CENTRAL_PANE,
+ name: SCREENS.WORKSPACES_CENTRAL_PANE,
state: {
routes: [
{
- name: SCREENS.SETTINGS.PROFILE.ROOT,
+ name: SCREENS.WORKSPACE.PROFILE,
+ params: state.routes[0]?.params,
},
],
},
diff --git a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/index.tsx
index fb7ae24947c2..f35c609402b0 100644
--- a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/index.tsx
+++ b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/index.tsx
@@ -2,34 +2,12 @@ import type {ParamListBase, StackActionHelpers, StackNavigationState} from '@rea
import {createNavigatorFactory, useNavigationBuilder} from '@react-navigation/native';
import type {StackNavigationEventMap, StackNavigationOptions} from '@react-navigation/stack';
import {StackView} from '@react-navigation/stack';
-import React, {useEffect, useMemo} from 'react';
+import React, {useEffect} from 'react';
import useWindowDimensions from '@hooks/useWindowDimensions';
import navigationRef from '@libs/Navigation/navigationRef';
-import SCREENS from '@src/SCREENS';
import CustomFullScreenRouter from './CustomFullScreenRouter';
import type {FullScreenNavigatorProps, FullScreenNavigatorRouterOptions} from './types';
-type Routes = StackNavigationState['routes'];
-function reduceReportRoutes(routes: Routes): Routes {
- const result: Routes = [];
- let count = 0;
- const reverseRoutes = [...routes].reverse();
-
- reverseRoutes.forEach((route) => {
- if (route.name === SCREENS.SETTINGS_CENTRAL_PANE) {
- // Remove all report routes except the last 3. This will improve performance.
- if (count < 3) {
- result.push(route);
- count++;
- }
- } else {
- result.push(route);
- }
- });
-
- return result.reverse();
-}
-
function CustomFullScreenNavigator(props: FullScreenNavigatorProps) {
const {navigation, state, descriptors, NavigationContent} = useNavigationBuilder<
StackNavigationState,
@@ -45,16 +23,6 @@ function CustomFullScreenNavigator(props: FullScreenNavigatorProps) {
const {isSmallScreenWidth} = useWindowDimensions();
- const stateToRender = useMemo(() => {
- const result = reduceReportRoutes(state.routes);
-
- return {
- ...state,
- index: result.length - 1,
- routes: [...result],
- };
- }, [state]);
-
useEffect(() => {
if (!navigationRef.isReady()) {
return;
@@ -69,7 +37,7 @@ function CustomFullScreenNavigator(props: FullScreenNavigatorProps) {
diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts
index 4cd6a141bd3b..c55145a5d580 100644
--- a/src/libs/Navigation/Navigation.ts
+++ b/src/libs/Navigation/Navigation.ts
@@ -19,7 +19,7 @@ import linkingConfig from './linkingConfig';
import linkTo from './linkTo';
import navigationRef from './navigationRef';
import switchPolicyID from './switchPolicyID';
-import type {State, StateOrRoute, SwitchPolicyIDParams} from './types';
+import type {NavigationStateRoute, State, StateOrRoute, SwitchPolicyIDParams} from './types';
let resolveNavigationIsReadyPromise: () => void;
const navigationIsReadyPromise = new Promise((resolve) => {
@@ -234,6 +234,18 @@ function goBack(fallbackRoute?: Route, shouldEnforceFallback = false, shouldPopT
navigationRef.current.goBack();
}
+/**
+ * Reset the navigation state to Home page
+ */
+function resetToHome() {
+ const rootState = navigationRef.getRootState();
+ const bottomTabKey = rootState.routes.find((item: NavigationStateRoute) => item.name === NAVIGATORS.BOTTOM_TAB_NAVIGATOR)?.state?.key;
+ if (bottomTabKey) {
+ navigationRef.dispatch({...StackActions.popToTop(), target: bottomTabKey});
+ }
+ navigationRef.dispatch({...StackActions.popToTop(), target: rootState.key});
+}
+
/**
* Close the full screen modal.
*/
@@ -366,6 +378,7 @@ export default {
parseHybridAppUrl,
closeFullScreen,
navigateWithSwitchPolicyID,
+ resetToHome,
};
export {navigationRef};
diff --git a/src/libs/Navigation/getTopmostSettingsCentralPaneName.ts b/src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts
similarity index 76%
rename from src/libs/Navigation/getTopmostSettingsCentralPaneName.ts
rename to src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts
index 0ddea6588ef6..db11368c1345 100644
--- a/src/libs/Navigation/getTopmostSettingsCentralPaneName.ts
+++ b/src/libs/Navigation/getTopmostWorkspacesCentralPaneName.ts
@@ -2,12 +2,12 @@ import type {NavigationState, PartialState} from '@react-navigation/native';
import SCREENS from '@src/SCREENS';
// Get the name of topmost report in the navigation stack.
-function getTopmostSettingsCentralPaneName(state: NavigationState | PartialState): string | undefined {
+function getTopmostWorkspacesCentralPaneName(state: NavigationState | PartialState): string | undefined {
if (!state) {
return;
}
- const topmostCentralPane = state.routes.filter((route) => typeof route !== 'number' && 'name' in route && route.name === SCREENS.SETTINGS_CENTRAL_PANE).at(-1);
+ const topmostCentralPane = state.routes.filter((route) => typeof route !== 'number' && 'name' in route && route.name === SCREENS.WORKSPACES_CENTRAL_PANE).at(-1);
if (!topmostCentralPane) {
return;
@@ -24,4 +24,4 @@ function getTopmostSettingsCentralPaneName(state: NavigationState | PartialState
return topmostCentralPane.state?.routes.at(-1)?.name;
}
-export default getTopmostSettingsCentralPaneName;
+export default getTopmostWorkspacesCentralPaneName;
diff --git a/src/libs/Navigation/linkTo.ts b/src/libs/Navigation/linkTo.ts
index 371ea89df2e2..2a00895f0492 100644
--- a/src/libs/Navigation/linkTo.ts
+++ b/src/libs/Navigation/linkTo.ts
@@ -6,7 +6,6 @@ import {extractPolicyIDFromPath, getPathWithoutPolicyID} from '@libs/PolicyUtils
import CONST from '@src/CONST';
import NAVIGATORS from '@src/NAVIGATORS';
import type {Route} from '@src/ROUTES';
-import SCREENS from '@src/SCREENS';
import getActionsFromPartialDiff from './AppNavigator/getActionsFromPartialDiff';
import getPartialStateDiff from './AppNavigator/getPartialStateDiff';
import dismissModal from './dismissModal';
@@ -119,7 +118,6 @@ export default function linkTo(navigation: NavigationContainerRef)?.name === SCREENS.WORKSPACE.INITIAL && path.includes('workspace');
+ const isFullScreenOnTop = rootState.routes?.at(-1)?.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR;
- if (policyID && !isWorkspaceSettingsOpened) {
+ if (policyID && !isFullScreenOnTop) {
// The stateFromPath doesn't include proper path if there is a policy passed with /w/id.
// We need to replace the path in the state with the proper one.
// To avoid this hacky solution we may want to create custom getActionFromState function in the future.
diff --git a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts
index 69c23b2ea07a..95233bfed079 100755
--- a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts
+++ b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts
@@ -2,17 +2,40 @@ import type {CentralPaneName} from '@libs/Navigation/types';
import SCREENS from '@src/SCREENS';
const CENTRAL_PANE_TO_RHP_MAPPING: Partial> = {
- [SCREENS.WORKSPACE.PROFILE]: [SCREENS.WORKSPACE.NAME, SCREENS.WORKSPACE.CURRENCY, SCREENS.WORKSPACE.DESCRIPTION, SCREENS.WORKSPACE.SHARE],
- [SCREENS.WORKSPACE.REIMBURSE]: [SCREENS.WORKSPACE.RATE_AND_UNIT, SCREENS.WORKSPACE.RATE_AND_UNIT_RATE, SCREENS.WORKSPACE.RATE_AND_UNIT_UNIT],
- [SCREENS.WORKSPACE.MEMBERS]: [SCREENS.WORKSPACE.INVITE, SCREENS.WORKSPACE.INVITE_MESSAGE, SCREENS.WORKSPACE.MEMBER_DETAILS, SCREENS.WORKSPACE.MEMBER_DETAILS_ROLE_SELECTION],
- [SCREENS.WORKSPACE.WORKFLOWS]: [
- SCREENS.WORKSPACE.WORKFLOWS_APPROVER,
- SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY,
- SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET,
- SCREENS.WORKSPACE.WORKFLOWS_PAYER,
+ [SCREENS.SETTINGS.PROFILE.ROOT]: [
+ SCREENS.SETTINGS.PROFILE.DISPLAY_NAME,
+ SCREENS.SETTINGS.PROFILE.CONTACT_METHODS,
+ SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_DETAILS,
+ SCREENS.SETTINGS.PROFILE.NEW_CONTACT_METHOD,
+ SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER,
+ SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_DATE,
+ SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_TIME,
+ SCREENS.SETTINGS.PROFILE.STATUS,
+ SCREENS.SETTINGS.PROFILE.PRONOUNS,
+ SCREENS.SETTINGS.PROFILE.TIMEZONE,
+ SCREENS.SETTINGS.PROFILE.TIMEZONE_SELECT,
+ SCREENS.SETTINGS.PROFILE.LEGAL_NAME,
+ SCREENS.SETTINGS.PROFILE.DATE_OF_BIRTH,
+ SCREENS.SETTINGS.PROFILE.ADDRESS,
+ SCREENS.SETTINGS.PROFILE.ADDRESS_COUNTRY,
+ SCREENS.SETTINGS.SHARE_CODE,
],
- [SCREENS.WORKSPACE.TAGS]: [SCREENS.WORKSPACE.TAGS_SETTINGS, SCREENS.WORKSPACE.TAGS_EDIT],
- [SCREENS.WORKSPACE.CATEGORIES]: [SCREENS.WORKSPACE.CATEGORY_CREATE, SCREENS.WORKSPACE.CATEGORY_SETTINGS, SCREENS.WORKSPACE.CATEGORIES_SETTINGS],
+ [SCREENS.SETTINGS.PREFERENCES.ROOT]: [SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE, SCREENS.SETTINGS.PREFERENCES.LANGUAGE, SCREENS.SETTINGS.PREFERENCES.THEME],
+ [SCREENS.SETTINGS.WALLET.ROOT]: [
+ SCREENS.SETTINGS.WALLET.DOMAIN_CARD,
+ SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.NAME,
+ SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.PHONE,
+ SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.ADDRESS,
+ SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.CONFIRM,
+ SCREENS.SETTINGS.WALLET.TRANSFER_BALANCE,
+ SCREENS.SETTINGS.WALLET.CHOOSE_TRANSFER_ACCOUNT,
+ SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS,
+ SCREENS.SETTINGS.WALLET.CARD_ACTIVATE,
+ SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD,
+ SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS,
+ ],
+ [SCREENS.SETTINGS.SECURITY]: [SCREENS.SETTINGS.TWO_FACTOR_AUTH, SCREENS.SETTINGS.CLOSE],
+ [SCREENS.SETTINGS.ABOUT]: [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS, SCREENS.SETTINGS.TROUBLESHOOT],
};
export default CENTRAL_PANE_TO_RHP_MAPPING;
diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
index f79e275007d7..99f6159549c5 100755
--- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
+++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
@@ -2,39 +2,17 @@ import type {FullScreenName} from '@libs/Navigation/types';
import SCREENS from '@src/SCREENS';
const FULL_SCREEN_TO_RHP_MAPPING: Partial> = {
- [SCREENS.SETTINGS.PROFILE.ROOT]: [
- SCREENS.SETTINGS.PROFILE.DISPLAY_NAME,
- SCREENS.SETTINGS.PROFILE.CONTACT_METHODS,
- SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_DETAILS,
- SCREENS.SETTINGS.PROFILE.NEW_CONTACT_METHOD,
- SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER,
- SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_DATE,
- SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_TIME,
- SCREENS.SETTINGS.PROFILE.STATUS,
- SCREENS.SETTINGS.PROFILE.PRONOUNS,
- SCREENS.SETTINGS.PROFILE.TIMEZONE,
- SCREENS.SETTINGS.PROFILE.TIMEZONE_SELECT,
- SCREENS.SETTINGS.PROFILE.LEGAL_NAME,
- SCREENS.SETTINGS.PROFILE.DATE_OF_BIRTH,
- SCREENS.SETTINGS.PROFILE.ADDRESS,
- SCREENS.SETTINGS.PROFILE.ADDRESS_COUNTRY,
+ [SCREENS.WORKSPACE.PROFILE]: [SCREENS.WORKSPACE.NAME, SCREENS.WORKSPACE.CURRENCY, SCREENS.WORKSPACE.DESCRIPTION, SCREENS.WORKSPACE.SHARE],
+ [SCREENS.WORKSPACE.REIMBURSE]: [SCREENS.WORKSPACE.RATE_AND_UNIT, SCREENS.WORKSPACE.RATE_AND_UNIT_RATE, SCREENS.WORKSPACE.RATE_AND_UNIT_UNIT],
+ [SCREENS.WORKSPACE.MEMBERS]: [SCREENS.WORKSPACE.INVITE, SCREENS.WORKSPACE.INVITE_MESSAGE, SCREENS.WORKSPACE.MEMBER_DETAILS, SCREENS.WORKSPACE.MEMBER_DETAILS_ROLE_SELECTION],
+ [SCREENS.WORKSPACE.WORKFLOWS]: [
+ SCREENS.WORKSPACE.WORKFLOWS_APPROVER,
+ SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY,
+ SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET,
+ SCREENS.WORKSPACE.WORKFLOWS_PAYER,
],
- [SCREENS.SETTINGS.PREFERENCES.ROOT]: [SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE, SCREENS.SETTINGS.PREFERENCES.LANGUAGE, SCREENS.SETTINGS.PREFERENCES.THEME],
- [SCREENS.SETTINGS.WALLET.ROOT]: [
- SCREENS.SETTINGS.WALLET.DOMAIN_CARD,
- SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.NAME,
- SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.PHONE,
- SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.ADDRESS,
- SCREENS.SETTINGS.WALLET.CARD_GET_PHYSICAL.CONFIRM,
- SCREENS.SETTINGS.WALLET.TRANSFER_BALANCE,
- SCREENS.SETTINGS.WALLET.CHOOSE_TRANSFER_ACCOUNT,
- SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS,
- SCREENS.SETTINGS.WALLET.CARD_ACTIVATE,
- SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD,
- SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS,
- ],
- [SCREENS.SETTINGS.SECURITY]: [SCREENS.SETTINGS.TWO_FACTOR_AUTH, SCREENS.SETTINGS.CLOSE],
- [SCREENS.SETTINGS.ABOUT]: [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS, SCREENS.SETTINGS.TROUBLESHOOT],
+ [SCREENS.WORKSPACE.TAGS]: [SCREENS.WORKSPACE.TAGS_SETTINGS, SCREENS.WORKSPACE.TAGS_EDIT],
+ [SCREENS.WORKSPACE.CATEGORIES]: [SCREENS.WORKSPACE.CATEGORY_CREATE, SCREENS.WORKSPACE.CATEGORY_SETTINGS, SCREENS.WORKSPACE.CATEGORIES_SETTINGS],
};
export default FULL_SCREEN_TO_RHP_MAPPING;
diff --git a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts
index b2939cf38d9f..78a644ab4aee 100755
--- a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts
+++ b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts
@@ -3,20 +3,13 @@ import SCREENS from '@src/SCREENS';
const TAB_TO_CENTRAL_PANE_MAPPING: Record = {
[SCREENS.HOME]: [SCREENS.REPORT],
- [SCREENS.ALL_SETTINGS]: [SCREENS.SETTINGS.WORKSPACES],
- [SCREENS.WORKSPACE.INITIAL]: [
- SCREENS.WORKSPACE.PROFILE,
- SCREENS.WORKSPACE.CARD,
- SCREENS.WORKSPACE.WORKFLOWS,
- SCREENS.WORKSPACE.REIMBURSE,
- SCREENS.WORKSPACE.BILLS,
- SCREENS.WORKSPACE.INVOICES,
- SCREENS.WORKSPACE.TRAVEL,
- SCREENS.WORKSPACE.MEMBERS,
- SCREENS.WORKSPACE.CATEGORIES,
- SCREENS.WORKSPACE.TAGS,
- SCREENS.WORKSPACE.MORE_FEATURES,
- SCREENS.WORKSPACE.DISTANCE_RATES,
+ [SCREENS.SETTINGS.ROOT]: [
+ SCREENS.SETTINGS.PROFILE.ROOT,
+ SCREENS.SETTINGS.PREFERENCES.ROOT,
+ SCREENS.SETTINGS.SECURITY,
+ SCREENS.SETTINGS.WALLET.ROOT,
+ SCREENS.SETTINGS.ABOUT,
+ SCREENS.SETTINGS.WORKSPACES,
],
};
diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts
index 98a32dcd62fc..a9042cb2bfac 100644
--- a/src/libs/Navigation/linkingConfig/config.ts
+++ b/src/libs/Navigation/linkingConfig/config.ts
@@ -31,10 +31,8 @@ const config: LinkingOptions['config'] = {
initialRouteName: SCREENS.HOME,
screens: {
[SCREENS.HOME]: ROUTES.HOME,
- [SCREENS.ALL_SETTINGS]: ROUTES.ALL_SETTINGS,
- [SCREENS.WORKSPACE.INITIAL]: {
- path: ROUTES.WORKSPACE_INITIAL.route,
- exact: true,
+ [SCREENS.SETTINGS.ROOT]: {
+ path: ROUTES.SETTINGS,
},
},
},
@@ -42,42 +40,27 @@ const config: LinkingOptions['config'] = {
[NAVIGATORS.CENTRAL_PANE_NAVIGATOR]: {
screens: {
[SCREENS.REPORT]: ROUTES.REPORT_WITH_ID.route,
-
- [SCREENS.SETTINGS.WORKSPACES]: ROUTES.SETTINGS_WORKSPACES,
- [SCREENS.WORKSPACE.PROFILE]: ROUTES.WORKSPACE_PROFILE.route,
- [SCREENS.WORKSPACE.CARD]: {
- path: ROUTES.WORKSPACE_CARD.route,
- },
- [SCREENS.WORKSPACE.WORKFLOWS]: {
- path: ROUTES.WORKSPACE_WORKFLOWS.route,
- },
- [SCREENS.WORKSPACE.REIMBURSE]: {
- path: ROUTES.WORKSPACE_REIMBURSE.route,
- },
- [SCREENS.WORKSPACE.BILLS]: {
- path: ROUTES.WORKSPACE_BILLS.route,
- },
- [SCREENS.WORKSPACE.INVOICES]: {
- path: ROUTES.WORKSPACE_INVOICES.route,
- },
- [SCREENS.WORKSPACE.TRAVEL]: {
- path: ROUTES.WORKSPACE_TRAVEL.route,
- },
- [SCREENS.WORKSPACE.MEMBERS]: {
- path: ROUTES.WORKSPACE_MEMBERS.route,
+ [SCREENS.SETTINGS.PROFILE.ROOT]: {
+ path: ROUTES.SETTINGS_PROFILE,
+ exact: true,
},
- [SCREENS.WORKSPACE.CATEGORIES]: {
- path: ROUTES.WORKSPACE_CATEGORIES.route,
+ [SCREENS.SETTINGS.PREFERENCES.ROOT]: {
+ path: ROUTES.SETTINGS_PREFERENCES,
+ exact: true,
},
- [SCREENS.WORKSPACE.MORE_FEATURES]: {
- path: ROUTES.WORKSPACE_MORE_FEATURES.route,
+ [SCREENS.SETTINGS.SECURITY]: {
+ path: ROUTES.SETTINGS_SECURITY,
+ exact: true,
},
- [SCREENS.WORKSPACE.TAGS]: {
- path: ROUTES.WORKSPACE_TAGS.route,
+ [SCREENS.SETTINGS.WALLET.ROOT]: {
+ path: ROUTES.SETTINGS_WALLET,
+ exact: true,
},
- [SCREENS.WORKSPACE.DISTANCE_RATES]: {
- path: ROUTES.WORKSPACE_DISTANCE_RATES.route,
+ [SCREENS.SETTINGS.ABOUT]: {
+ path: ROUTES.SETTINGS_ABOUT,
+ exact: true,
},
+ [SCREENS.SETTINGS.WORKSPACES]: ROUTES.SETTINGS_WORKSPACES,
},
},
[SCREENS.NOT_FOUND]: '*',
@@ -560,30 +543,44 @@ const config: LinkingOptions['config'] = {
[NAVIGATORS.FULL_SCREEN_NAVIGATOR]: {
screens: {
- [SCREENS.SETTINGS.ROOT]: {
- path: ROUTES.SETTINGS,
+ [SCREENS.WORKSPACE.INITIAL]: {
+ path: ROUTES.WORKSPACE_INITIAL.route,
},
- [SCREENS.SETTINGS_CENTRAL_PANE]: {
+ [SCREENS.WORKSPACES_CENTRAL_PANE]: {
screens: {
- [SCREENS.SETTINGS.PROFILE.ROOT]: {
- path: ROUTES.SETTINGS_PROFILE,
- exact: true,
+ [SCREENS.WORKSPACE.PROFILE]: ROUTES.WORKSPACE_PROFILE.route,
+ [SCREENS.WORKSPACE.CARD]: {
+ path: ROUTES.WORKSPACE_CARD.route,
},
- [SCREENS.SETTINGS.PREFERENCES.ROOT]: {
- path: ROUTES.SETTINGS_PREFERENCES,
- exact: true,
+ [SCREENS.WORKSPACE.WORKFLOWS]: {
+ path: ROUTES.WORKSPACE_WORKFLOWS.route,
},
- [SCREENS.SETTINGS.SECURITY]: {
- path: ROUTES.SETTINGS_SECURITY,
- exact: true,
+ [SCREENS.WORKSPACE.REIMBURSE]: {
+ path: ROUTES.WORKSPACE_REIMBURSE.route,
},
- [SCREENS.SETTINGS.WALLET.ROOT]: {
- path: ROUTES.SETTINGS_WALLET,
- exact: true,
+ [SCREENS.WORKSPACE.BILLS]: {
+ path: ROUTES.WORKSPACE_BILLS.route,
},
- [SCREENS.SETTINGS.ABOUT]: {
- path: ROUTES.SETTINGS_ABOUT,
- exact: true,
+ [SCREENS.WORKSPACE.INVOICES]: {
+ path: ROUTES.WORKSPACE_INVOICES.route,
+ },
+ [SCREENS.WORKSPACE.TRAVEL]: {
+ path: ROUTES.WORKSPACE_TRAVEL.route,
+ },
+ [SCREENS.WORKSPACE.MEMBERS]: {
+ path: ROUTES.WORKSPACE_MEMBERS.route,
+ },
+ [SCREENS.WORKSPACE.CATEGORIES]: {
+ path: ROUTES.WORKSPACE_CATEGORIES.route,
+ },
+ [SCREENS.WORKSPACE.MORE_FEATURES]: {
+ path: ROUTES.WORKSPACE_MORE_FEATURES.route,
+ },
+ [SCREENS.WORKSPACE.TAGS]: {
+ path: ROUTES.WORKSPACE_TAGS.route,
+ },
+ [SCREENS.WORKSPACE.DISTANCE_RATES]: {
+ path: ROUTES.WORKSPACE_DISTANCE_RATES.route,
},
},
},
diff --git a/src/libs/Navigation/linkingConfig/customGetPathFromState.ts b/src/libs/Navigation/linkingConfig/customGetPathFromState.ts
index 4f7023d14db4..4017b1b2b17c 100644
--- a/src/libs/Navigation/linkingConfig/customGetPathFromState.ts
+++ b/src/libs/Navigation/linkingConfig/customGetPathFromState.ts
@@ -8,20 +8,19 @@ import SCREENS from '@src/SCREENS';
const removePolicyIDParamFromState = (state: State) => {
const stateCopy = _.cloneDeep(state);
const bottomTabRoute = getTopmostBottomTabRoute(stateCopy);
- if (bottomTabRoute?.name === SCREENS.HOME && bottomTabRoute?.params && 'policyID' in bottomTabRoute.params) {
+ if (bottomTabRoute?.params && 'policyID' in bottomTabRoute.params) {
delete bottomTabRoute.params.policyID;
}
return stateCopy;
};
const customGetPathFromState: typeof getPathFromState = (state, options) => {
+ // For the Home and Settings pages we should remove policyID from the params, because on small screens it's displayed twice in the URL
const stateWithoutPolicyID = removePolicyIDParamFromState(state as State);
-
- // For the Home page we should remove policyID from the params,
const path = getPathFromState(stateWithoutPolicyID, options);
const policyIDFromState = getPolicyIDFromState(state as State);
- const isWorkspaceSettingsOpened = getTopmostBottomTabRoute(state as State)?.name === SCREENS.WORKSPACE.INITIAL && path.includes('workspace');
- return `${policyIDFromState && !isWorkspaceSettingsOpened ? `/w/${policyIDFromState}` : ''}${path}`;
+ const isHomeOpened = getTopmostBottomTabRoute(state as State)?.name === SCREENS.HOME;
+ return `${policyIDFromState && isHomeOpened ? `/w/${policyIDFromState}` : ''}${path}`;
};
export default customGetPathFromState;
diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts
index 5f89b2cd4630..e4a8464d7ddd 100644
--- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts
+++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts
@@ -14,6 +14,10 @@ import getMatchingBottomTabRouteForState from './getMatchingBottomTabRouteForSta
import getMatchingCentralPaneRouteForState from './getMatchingCentralPaneRouteForState';
import replacePathInNestedState from './replacePathInNestedState';
+const RHP_SCREENS_OPENED_FROM_LHN = [SCREENS.SETTINGS.SHARE_CODE, SCREENS.SETTINGS.PROFILE.STATUS] as const;
+
+type RHPScreenOpenedFromLHN = (typeof RHP_SCREENS_OPENED_FROM_LHN)[number];
+
type Metainfo = {
// Sometimes modal screens don't have information about what should be visible under the overlay.
// That means such screen can have different screens under the overlay depending on what was already in the state.
@@ -73,14 +77,21 @@ function createCentralPaneNavigator(route: NavigationPartialRoute): NavigationPartialRoute {
const routes = [];
- routes.push({name: SCREENS.SETTINGS.ROOT});
+ const policyID = route?.params && 'policyID' in route.params ? route.params.policyID : undefined;
+
+ // Both routes in FullScreenNavigator should store a policyID in params, so here this param is also passed to the screen displayed in LHN in FullScreenNavigator
+ routes.push({
+ name: SCREENS.WORKSPACE.INITIAL,
+ params: {
+ policyID,
+ },
+ });
if (route) {
routes.push({
- name: SCREENS.SETTINGS_CENTRAL_PANE,
+ name: SCREENS.WORKSPACES_CENTRAL_PANE,
state: getRoutesWithIndex([route]),
});
}
-
return {
name: NAVIGATORS.FULL_SCREEN_NAVIGATOR,
state: getRoutesWithIndex(routes),
@@ -131,11 +142,6 @@ function getMatchingRootRouteForRHPRoute(
return createFullScreenNavigator({name: fullScreenName as FullScreenName, params: route.params});
}
}
-
- // This screen is opened from the LHN of the FullStackNavigator, so in this case we shouldn't push any CentralPane screen
- if (route.name === SCREENS.SETTINGS.SHARE_CODE) {
- return createFullScreenNavigator();
- }
}
function getAdaptedState(state: PartialState>, policyID?: string): GetAdaptedStateReturnType {
@@ -165,18 +171,25 @@ function getAdaptedState(state: PartialState
if (topmostNestedRHPRoute) {
let matchingRootRoute = getMatchingRootRouteForRHPRoute(topmostNestedRHPRoute);
-
+ const isRHPScreenOpenedFromLHN = topmostNestedRHPRoute?.name && RHP_SCREENS_OPENED_FROM_LHN.includes(topmostNestedRHPRoute?.name as RHPScreenOpenedFromLHN);
// This may happen if this RHP doens't have a route that should be under the overlay defined.
- if (!matchingRootRoute) {
+ if (!matchingRootRoute || isRHPScreenOpenedFromLHN) {
metainfo.isCentralPaneAndBottomTabMandatory = false;
metainfo.isFullScreenNavigatorMandatory = false;
- matchingRootRoute = createCentralPaneNavigator({name: SCREENS.REPORT});
+ matchingRootRoute = matchingRootRoute ?? createCentralPaneNavigator({name: SCREENS.REPORT});
}
// If the root route is type of FullScreenNavigator, the default bottom tab will be added.
const matchingBottomTabRoute = getMatchingBottomTabRouteForState({routes: [matchingRootRoute]});
routes.push(createBottomTabNavigator(matchingBottomTabRoute, policyID));
- routes.push(matchingRootRoute);
+ // When we open a screen in RHP from FullScreenNavigator, we need to add the appropriate screen in CentralPane.
+ // Then, when we close FullScreenNavigator, we will be redirected to the correct page in CentralPane.
+ if (matchingRootRoute.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR) {
+ routes.push(createCentralPaneNavigator({name: SCREENS.SETTINGS.WORKSPACES}));
+ }
+ if (!isNarrowLayout || !isRHPScreenOpenedFromLHN) {
+ routes.push(matchingRootRoute);
+ }
}
routes.push(rhpNavigator);
@@ -230,14 +243,18 @@ function getAdaptedState(state: PartialState
routes.push(
createBottomTabNavigator(
{
- name: SCREENS.HOME,
+ name: SCREENS.SETTINGS.ROOT,
},
policyID,
),
);
- if (!isNarrowLayout) {
- routes.push(createCentralPaneNavigator({name: SCREENS.REPORT}));
- }
+
+ routes.push(
+ createCentralPaneNavigator({
+ name: SCREENS.SETTINGS.WORKSPACES,
+ }),
+ );
+
routes.push(fullScreenNavigator);
return {
@@ -318,7 +335,6 @@ const getAdaptedStateFromPath: GetAdaptedStateFromPath = (path, options) => {
const state = getStateFromPath(pathWithoutPolicyID, options) as PartialState>;
replacePathInNestedState(state, path);
-
if (state === undefined) {
throw new Error('Unable to parse path');
}
diff --git a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts
index ef4cd65942b0..fd45685acf23 100644
--- a/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts
+++ b/src/libs/Navigation/linkingConfig/getMatchingBottomTabRouteForState.ts
@@ -1,5 +1,6 @@
import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute';
import type {BottomTabName, NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types';
+import NAVIGATORS from '@src/NAVIGATORS';
import SCREENS from '@src/SCREENS';
import {CENTRAL_PANE_TO_TAB_MAPPING} from './TAB_TO_CENTRAL_PANE_MAPPING';
@@ -7,6 +8,12 @@ import {CENTRAL_PANE_TO_TAB_MAPPING} from './TAB_TO_CENTRAL_PANE_MAPPING';
function getMatchingBottomTabRouteForState(state: State, policyID?: string): NavigationPartialRoute {
const paramsWithPolicyID = policyID ? {policyID} : undefined;
const defaultRoute = {name: SCREENS.HOME, params: paramsWithPolicyID};
+ const isFullScreenNavigatorOpened = state.routes.some((route) => route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR);
+
+ if (isFullScreenNavigatorOpened) {
+ return {name: SCREENS.SETTINGS.ROOT, params: paramsWithPolicyID};
+ }
+
const topmostCentralPaneRoute = getTopmostCentralPaneRoute(state);
if (topmostCentralPaneRoute === undefined) {
@@ -14,9 +21,6 @@ function getMatchingBottomTabRouteForState(state: State, pol
}
const tabName = CENTRAL_PANE_TO_TAB_MAPPING[topmostCentralPaneRoute.name];
- if (tabName === SCREENS.WORKSPACE.INITIAL) {
- return {name: tabName, params: topmostCentralPaneRoute.params};
- }
return {name: tabName, params: paramsWithPolicyID};
}
diff --git a/src/libs/Navigation/linkingConfig/getMatchingCentralPaneRouteForState.ts b/src/libs/Navigation/linkingConfig/getMatchingCentralPaneRouteForState.ts
index d4d558708b63..51cb9e3aa9a5 100644
--- a/src/libs/Navigation/linkingConfig/getMatchingCentralPaneRouteForState.ts
+++ b/src/libs/Navigation/linkingConfig/getMatchingCentralPaneRouteForState.ts
@@ -4,8 +4,6 @@ import NAVIGATORS from '@src/NAVIGATORS';
import SCREENS from '@src/SCREENS';
import TAB_TO_CENTRAL_PANE_MAPPING from './TAB_TO_CENTRAL_PANE_MAPPING';
-const WORKSPACES_SCREENS = TAB_TO_CENTRAL_PANE_MAPPING[SCREENS.WORKSPACE.INITIAL].concat(TAB_TO_CENTRAL_PANE_MAPPING[SCREENS.ALL_SETTINGS]);
-
/**
* @param state - react-navigation state
*/
@@ -55,11 +53,11 @@ function getAlreadyOpenedSettingsScreen(rootState?: State, policyID?: string): k
return undefined;
}
- // If one of the screen from WORKSPACES_SCREENS is now in the navigation state, we can decide which screen we should display.
+ // If one of the screen from TAB_TO_CENTRAL_PANE_MAPPING[SCREENS.SETTINGS.ROOT] is now in the navigation state, we can decide which screen we should display.
// A screen from the navigation state can be pushed to the navigation state again only if it has a matching policyID with the currently selected workspace.
// Otherwise, when we switch the workspace, we want to display the initial screen in the settings tab.
const alreadyOpenedSettingsTab = rootState.routes
- .filter((item) => item.params && 'screen' in item.params && WORKSPACES_SCREENS.includes(item.params.screen as keyof CentralPaneNavigatorParamList))
+ .filter((item) => item.params && 'screen' in item.params && TAB_TO_CENTRAL_PANE_MAPPING[SCREENS.SETTINGS.ROOT].includes(item.params.screen as keyof CentralPaneNavigatorParamList))
.at(-1);
if (!hasRouteMatchingPolicyID(alreadyOpenedSettingsTab as NavigationPartialRoute, policyID)) {
@@ -82,7 +80,7 @@ function getMatchingCentralPaneRouteForState(state: State, r
const centralPaneName = TAB_TO_CENTRAL_PANE_MAPPING[topmostBottomTabRoute.name][0];
- if (topmostBottomTabRoute.name === SCREENS.WORKSPACE.INITIAL) {
+ if (topmostBottomTabRoute.name === SCREENS.SETTINGS.ROOT) {
// When we go back to the settings tab without switching the workspace id, we want to return to the previously opened screen
const policyID = topmostBottomTabRoute?.params && 'policyID' in topmostBottomTabRoute.params ? (topmostBottomTabRoute.params.policyID as string) : undefined;
const screen = getAlreadyOpenedSettingsScreen(rootState, policyID) ?? centralPaneName;
diff --git a/src/libs/Navigation/switchPolicyID.ts b/src/libs/Navigation/switchPolicyID.ts
index c425beca73fd..685c21d88e79 100644
--- a/src/libs/Navigation/switchPolicyID.ts
+++ b/src/libs/Navigation/switchPolicyID.ts
@@ -10,7 +10,6 @@ import SCREENS from '@src/SCREENS';
import getStateFromPath from './getStateFromPath';
import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute';
import linkingConfig from './linkingConfig';
-import TAB_TO_CENTRAL_PANE_MAPPING from './linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING';
import type {NavigationRoot, RootStackParamList, StackNavigationAction, State, SwitchPolicyIDParams} from './types';
type ActionPayloadParams = {
@@ -62,7 +61,7 @@ function getActionForBottomTabNavigator(action: StackNavigationAction, state: Na
};
}
-export default function switchPolicyID(navigation: NavigationContainerRef | null, {policyID, route, isPolicyAdmin = false}: SwitchPolicyIDParams) {
+export default function switchPolicyID(navigation: NavigationContainerRef | null, {policyID, route}: SwitchPolicyIDParams) {
if (!navigation) {
throw new Error("Couldn't find a navigation object. Is your component inside a screen in a navigator?");
}
@@ -110,7 +109,7 @@ export default function switchPolicyID(navigation: NavigationContainerRef);
@@ -122,23 +121,12 @@ export default function switchPolicyID(navigation: NavigationContainerRef;
};
-type SettingsCentralPaneNavigatorParamList = {
- [SCREENS.SETTINGS.PROFILE.ROOT]: undefined;
- [SCREENS.SETTINGS.PREFERENCES.ROOT]: undefined;
- [SCREENS.SETTINGS.SECURITY]: undefined;
- [SCREENS.SETTINGS.WALLET.ROOT]: undefined;
- [SCREENS.SETTINGS.ABOUT]: undefined;
+type WorkspacesCentralPaneNavigatorParamList = {
+ [SCREENS.WORKSPACE.PROFILE]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.CARD]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.WORKFLOWS]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.WORKFLOWS_APPROVER]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.REIMBURSE]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.BILLS]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.INVOICES]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.TRAVEL]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.MEMBERS]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.CATEGORIES]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.MORE_FEATURES]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.TAGS]: {
+ policyID: string;
+ categoryName: string;
+ };
+ [SCREENS.WORKSPACE.DISTANCE_RATES]: {
+ policyID: string;
+ };
};
type FullScreenNavigatorParamList = {
- [SCREENS.SETTINGS.ROOT]: undefined;
- [SCREENS.SETTINGS_CENTRAL_PANE]: NavigatorScreenParams;
+ [SCREENS.WORKSPACE.INITIAL]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACES_CENTRAL_PANE]: NavigatorScreenParams;
};
type BottomTabNavigatorParamList = {
[SCREENS.HOME]: undefined;
- [SCREENS.ALL_SETTINGS]: undefined;
- [SCREENS.WORKSPACE.INITIAL]: undefined;
+ [SCREENS.SETTINGS.ROOT]: undefined;
};
type SharedScreensParamList = {
@@ -621,7 +621,7 @@ type BottomTabName = keyof BottomTabNavigatorParamList;
type CentralPaneName = keyof CentralPaneNavigatorParamList;
-type FullScreenName = keyof SettingsCentralPaneNavigatorParamList;
+type FullScreenName = keyof WorkspacesCentralPaneNavigatorParamList;
type SwitchPolicyIDParams = {
policyID?: string;
@@ -675,5 +675,7 @@ export type {
WorkspaceSwitcherNavigatorParamList,
OnboardEngagementNavigatorParamList,
SwitchPolicyIDParams,
+ FullScreenNavigatorParamList,
+ WorkspacesCentralPaneNavigatorParamList,
BackToParams,
};
diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts
index 47d5419893f5..2c2baee9b96e 100644
--- a/src/libs/actions/Session/index.ts
+++ b/src/libs/actions/Session/index.ts
@@ -191,7 +191,7 @@ function hasAuthToken(): boolean {
return !!session.authToken;
}
-function signOutAndRedirectToSignIn(shouldReplaceCurrentScreen?: boolean, shouldStashSession?: boolean) {
+function signOutAndRedirectToSignIn(shouldResetToHome?: boolean, shouldStashSession?: boolean) {
Log.info('Redirecting to Sign In because signOut() was called');
hideContextMenu(false);
if (!isAnonymousUser()) {
@@ -240,11 +240,10 @@ function signOutAndRedirectToSignIn(shouldReplaceCurrentScreen?: boolean, should
if (Navigation.isActiveRoute(ROUTES.SIGN_IN_MODAL)) {
return;
}
- if (shouldReplaceCurrentScreen) {
- Navigation.navigate(ROUTES.SIGN_IN_MODAL, CONST.NAVIGATION.TYPE.UP);
- } else {
- Navigation.navigate(ROUTES.SIGN_IN_MODAL);
+ if (shouldResetToHome) {
+ Navigation.resetToHome();
}
+ Navigation.navigate(ROUTES.SIGN_IN_MODAL);
Linking.getInitialURL().then((url) => {
const reportID = ReportUtils.getReportIDFromLink(url);
if (reportID) {
diff --git a/src/pages/WorkspaceSwitcherPage.tsx b/src/pages/WorkspaceSwitcherPage.tsx
index 3d46ede8b31e..2eb5ecaf373f 100644
--- a/src/pages/WorkspaceSwitcherPage.tsx
+++ b/src/pages/WorkspaceSwitcherPage.tsx
@@ -18,6 +18,7 @@ import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
+import interceptAnonymousUser from '@libs/interceptAnonymousUser';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
@@ -102,7 +103,7 @@ function WorkspaceSwitcherPage({policies}: WorkspaceSwitcherPageProps) {
return;
}
- const {policyID, isPolicyAdmin} = option;
+ const {policyID} = option;
if (policyID) {
setSelectedOption(option);
@@ -112,7 +113,7 @@ function WorkspaceSwitcherPage({policies}: WorkspaceSwitcherPageProps) {
setActiveWorkspaceID(policyID);
Navigation.goBack();
if (policyID !== activeWorkspaceID) {
- Navigation.navigateWithSwitchPolicyID({policyID, isPolicyAdmin});
+ Navigation.navigateWithSwitchPolicyID({policyID});
}
},
[activeWorkspaceID, setActiveWorkspaceID],
@@ -219,7 +220,7 @@ function WorkspaceSwitcherPage({policies}: WorkspaceSwitcherPageProps) {
role={CONST.ROLE.BUTTON}
onPress={() => {
Navigation.goBack();
- App.createWorkspaceWithPolicyDraftAndNavigateToIt();
+ interceptAnonymousUser(() => App.createWorkspaceWithPolicyDraftAndNavigateToIt());
}}
>
{({hovered}) => (
diff --git a/src/pages/home/sidebar/AvatarWithOptionalStatus.js b/src/pages/home/sidebar/AvatarWithOptionalStatus.js
index e1ff3982a0cc..942d5c1da1f2 100644
--- a/src/pages/home/sidebar/AvatarWithOptionalStatus.js
+++ b/src/pages/home/sidebar/AvatarWithOptionalStatus.js
@@ -1,61 +1,53 @@
/* eslint-disable rulesdir/onyx-props-must-have-default */
import PropTypes from 'prop-types';
-import React, {useCallback} from 'react';
+import React from 'react';
import {View} from 'react-native';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import Text from '@components/Text';
-import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
-import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
-import ROUTES from '@src/ROUTES';
import PressableAvatarWithIndicator from './PressableAvatarWithIndicator';
const propTypes = {
- /** Whether the create menu is open or not */
- isCreateMenuOpen: PropTypes.bool,
-
/** Emoji status */
emojiStatus: PropTypes.string,
+
+ /** Whether the avatar is selected */
+ isSelected: PropTypes.bool,
+
+ /** Callback called when the avatar or status icon is pressed */
+ onPress: PropTypes.func,
};
const defaultProps = {
- isCreateMenuOpen: false,
emojiStatus: '',
+ isSelected: false,
+ onPress: () => {},
};
-function AvatarWithOptionalStatus({emojiStatus, isCreateMenuOpen}) {
+function AvatarWithOptionalStatus({emojiStatus, isSelected, onPress}) {
const styles = useThemeStyles();
const {translate} = useLocalize();
- const showStatusPage = useCallback(() => {
- if (isCreateMenuOpen) {
- // Prevent opening Settings page when click profile avatar quickly after clicking FAB icon
- return;
- }
-
- Navigation.setShouldPopAllStateOnUP();
- Navigation.navigate(ROUTES.SETTINGS_STATUS);
- }, [isCreateMenuOpen]);
-
return (
-
+
-
-
- {emojiStatus}
-
-
+
+ {emojiStatus}
+
);
diff --git a/src/pages/home/sidebar/BottomTabAvatar.tsx b/src/pages/home/sidebar/BottomTabAvatar.tsx
new file mode 100644
index 000000000000..15134b762161
--- /dev/null
+++ b/src/pages/home/sidebar/BottomTabAvatar.tsx
@@ -0,0 +1,49 @@
+/* eslint-disable rulesdir/onyx-props-must-have-default */
+import React, {useCallback} from 'react';
+import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
+import interceptAnonymousUser from '@libs/interceptAnonymousUser';
+import Navigation from '@libs/Navigation/Navigation';
+import ROUTES from '@src/ROUTES';
+import AvatarWithOptionalStatus from './AvatarWithOptionalStatus';
+import PressableAvatarWithIndicator from './PressableAvatarWithIndicator';
+
+type BottomTabAvatarProps = {
+ /** Whether the create menu is open or not */
+ isCreateMenuOpen?: boolean;
+
+ /** Whether the avatar is selected */
+ isSelected?: boolean;
+};
+
+function BottomTabAvatar({isCreateMenuOpen = false, isSelected = false}: BottomTabAvatarProps) {
+ const currentUserPersonalDetails = useCurrentUserPersonalDetails();
+ const emojiStatus = currentUserPersonalDetails?.status?.emojiCode ?? '';
+
+ const showSettingsPage = useCallback(() => {
+ if (isCreateMenuOpen) {
+ // Prevent opening Settings page when click profile avatar quickly after clicking FAB icon
+ return;
+ }
+
+ interceptAnonymousUser(() => Navigation.navigate(ROUTES.SETTINGS));
+ }, [isCreateMenuOpen]);
+
+ if (emojiStatus) {
+ return (
+
+ );
+ }
+ return (
+
+ );
+}
+
+BottomTabAvatar.displayName = 'BottomTabAvatar';
+export default BottomTabAvatar;
diff --git a/src/pages/home/sidebar/PressableAvatarWithIndicator.js b/src/pages/home/sidebar/PressableAvatarWithIndicator.js
index 63c5936e957b..a7345ff6c14a 100644
--- a/src/pages/home/sidebar/PressableAvatarWithIndicator.js
+++ b/src/pages/home/sidebar/PressableAvatarWithIndicator.js
@@ -1,67 +1,65 @@
/* eslint-disable rulesdir/onyx-props-must-have-default */
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
-import React, {useCallback} from 'react';
+import React from 'react';
+import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import AvatarWithIndicator from '@components/AvatarWithIndicator';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
-import Navigation from '@libs/Navigation/Navigation';
import * as UserUtils from '@libs/UserUtils';
import personalDetailsPropType from '@pages/personalDetailsPropType';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
-import ROUTES from '@src/ROUTES';
const propTypes = {
- /** Whether the create menu is open or not */
- isCreateMenuOpen: PropTypes.bool,
-
/** The personal details of the person who is logged in */
currentUserPersonalDetails: personalDetailsPropType,
/** Indicates whether the app is loading initial data */
isLoading: PropTypes.bool,
+
+ /** Whether the avatar is selected */
+ isSelected: PropTypes.bool,
+
+ /** Callback called when the avatar is pressed */
+ onPress: PropTypes.func,
};
const defaultProps = {
- isCreateMenuOpen: false,
currentUserPersonalDetails: {
pendingFields: {avatar: ''},
accountID: '',
avatar: '',
},
isLoading: true,
+ isSelected: false,
+ onPress: () => {},
};
-function PressableAvatarWithIndicator({isCreateMenuOpen, currentUserPersonalDetails, isLoading}) {
+function PressableAvatarWithIndicator({currentUserPersonalDetails, isLoading, isSelected, onPress}) {
const {translate} = useLocalize();
-
- const showSettingsPage = useCallback(() => {
- if (isCreateMenuOpen) {
- // Prevent opening Settings page when click profile avatar quickly after clicking FAB icon
- return;
- }
-
- Navigation.navigate(ROUTES.SETTINGS);
- }, [isCreateMenuOpen]);
+ const styles = useThemeStyles();
return (
-
+
+
+
);
diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js
index e872bbad008a..4d1585cd424a 100644
--- a/src/pages/home/sidebar/SidebarLinks.js
+++ b/src/pages/home/sidebar/SidebarLinks.js
@@ -1,11 +1,8 @@
/* eslint-disable rulesdir/onyx-props-must-have-default */
-import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import {InteractionManager, StyleSheet, View} from 'react-native';
-import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
-import Breadcrumbs from '@components/Breadcrumbs';
import LHNOptionsList from '@components/LHNOptionsList/LHNOptionsList';
import OptionsListSkeletonView from '@components/OptionsListSkeletonView';
import useLocalize from '@hooks/useLocalize';
@@ -43,11 +40,11 @@ const propTypes = {
isActiveReport: PropTypes.func.isRequired,
};
-function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priorityMode = CONST.PRIORITY_MODE.DEFAULT, isActiveReport, isCreateMenuOpen, activePolicy}) {
+function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priorityMode = CONST.PRIORITY_MODE.DEFAULT, isActiveReport, isCreateMenuOpen}) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const modal = useRef({});
- const {translate, updateLocale} = useLocalize();
+ const {updateLocale} = useLocalize();
const {isSmallScreenWidth} = useWindowDimensions();
useEffect(() => {
@@ -129,22 +126,6 @@ function SidebarLinks({onLinkClick, insets, optionListItems, isLoading, priority
return (
-
`${ONYXKEYS.COLLECTION.POLICY}${activeWorkspaceID}`,
- },
-})(SidebarLinks);
+export default SidebarLinks;
export {basePropTypes};
diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
index 9188a859d175..2c2d28a0edbc 100644
--- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
+++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
@@ -1,8 +1,10 @@
import React, {useEffect} from 'react';
import {View} from 'react-native';
import ScreenWrapper from '@components/ScreenWrapper';
+import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useThemeStyles from '@hooks/useThemeStyles';
import * as Browser from '@libs/Browser';
+import TopBar from '@libs/Navigation/AppNavigator/createCustomBottomTabNavigator/TopBar';
import Performance from '@libs/Performance';
import SidebarLinksData from '@pages/home/sidebar/SidebarLinksData';
import Timing from '@userActions/Timing';
@@ -19,6 +21,7 @@ const startTimer = () => {
function BaseSidebarScreen(props) {
const styles = useThemeStyles();
+ const {activeWorkspaceID} = useActiveWorkspace();
useEffect(() => {
Performance.markStart(CONST.TIMING.SIDEBAR_LOADED);
Timing.start(CONST.TIMING.SIDEBAR_LOADED, true);
@@ -33,13 +36,16 @@ function BaseSidebarScreen(props) {
includePaddingTop={false}
>
{({insets}) => (
-
-
-
+ <>
+
+
+
+
+ >
)}
);
diff --git a/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.js b/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.js
deleted file mode 100644
index 0ea6195cd713..000000000000
--- a/src/pages/home/sidebar/SignInOrAvatarWithOptionalStatus.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/* eslint-disable rulesdir/onyx-props-must-have-default */
-import lodashGet from 'lodash/get';
-import PropTypes from 'prop-types';
-import React from 'react';
-import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
-import * as Session from '@userActions/Session';
-import AvatarWithOptionalStatus from './AvatarWithOptionalStatus';
-import PressableAvatarWithIndicator from './PressableAvatarWithIndicator';
-import SignInButton from './SignInButton';
-
-const propTypes = {
- /** Whether the create menu is open or not */
- isCreateMenuOpen: PropTypes.bool,
-};
-
-const defaultProps = {
- isCreateMenuOpen: false,
-};
-
-function SignInOrAvatarWithOptionalStatus({isCreateMenuOpen}) {
- const currentUserPersonalDetails = useCurrentUserPersonalDetails();
- const emojiStatus = lodashGet(currentUserPersonalDetails, 'status.emojiCode', '');
-
- if (Session.isAnonymousUser()) {
- return ;
- }
- if (emojiStatus) {
- return (
-
- );
- }
- return ;
-}
-
-SignInOrAvatarWithOptionalStatus.propTypes = propTypes;
-SignInOrAvatarWithOptionalStatus.defaultProps = defaultProps;
-SignInOrAvatarWithOptionalStatus.displayName = 'SignInOrAvatarWithOptionalStatus';
-export default SignInOrAvatarWithOptionalStatus;
diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx
index 2f2343027cf0..a57f308b5623 100755
--- a/src/pages/settings/InitialSettingsPage.tsx
+++ b/src/pages/settings/InitialSettingsPage.tsx
@@ -1,23 +1,24 @@
-import {useNavigationState} from '@react-navigation/native';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
-import type {OnyxEntry} from 'react-native-onyx';
+import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import AvatarWithImagePicker from '@components/AvatarWithImagePicker';
import ConfirmModal from '@components/ConfirmModal';
import CurrentUserPersonalDetailsSkeletonView from '@components/CurrentUserPersonalDetailsSkeletonView';
-import HeaderPageLayout from '@components/HeaderPageLayout';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import {PressableWithFeedback} from '@components/Pressable';
+import ScreenWrapper from '@components/ScreenWrapper';
+import ScrollView from '@components/ScrollView';
import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails';
import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails';
+import useActiveRoute from '@hooks/useActiveRoute';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useSingleExecution from '@hooks/useSingleExecution';
@@ -25,9 +26,10 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWaitForNavigation from '@hooks/useWaitForNavigation';
import * as CurrencyUtils from '@libs/CurrencyUtils';
-import getTopmostSettingsCentralPaneName from '@libs/Navigation/getTopmostSettingsCentralPaneName';
import Navigation from '@libs/Navigation/Navigation';
+import shouldShowSubscriptionsMenu from '@libs/shouldShowSubscriptionsMenu';
import * as UserUtils from '@libs/UserUtils';
+import {hasGlobalWorkspaceSettingsRBR} from '@libs/WorkspacesSettingsUtils';
import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
import variables from '@styles/variables';
import * as Link from '@userActions/Link';
@@ -40,7 +42,6 @@ import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
-import SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
import type {Icon as TIcon} from '@src/types/onyx/OnyxCommon';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
@@ -64,6 +65,12 @@ type InitialSettingsPageOnyxProps = {
/** Login list for the user that is signed in */
loginList: OnyxEntry;
+
+ /** The policies which the user has access to */
+ policies: OnyxCollection;
+
+ /** Members of all the workspaces the user is member of */
+ policyMembers: OnyxCollection;
};
type InitialSettingsPageProps = InitialSettingsPageOnyxProps & WithCurrentUserPersonalDetailsProps;
@@ -88,7 +95,7 @@ type MenuData = {
type Menu = {sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[]};
-function InitialSettingsPage({session, userWallet, bankAccountList, fundList, walletTerms, loginList, currentUserPersonalDetails}: InitialSettingsPageProps) {
+function InitialSettingsPage({session, userWallet, bankAccountList, fundList, walletTerms, loginList, currentUserPersonalDetails, policies, policyMembers}: InitialSettingsPageProps) {
const network = useNetwork();
const theme = useTheme();
const styles = useThemeStyles();
@@ -96,7 +103,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa
const waitForNavigate = useWaitForNavigation();
const popoverAnchor = useRef(null);
const {translate, formatPhoneNumber} = useLocalize();
- const activeRoute = useNavigationState(getTopmostSettingsCentralPaneName);
+ const activeRoute = useActiveRoute();
const emojiCode = currentUserPersonalDetails?.status?.emojiCode ?? '';
const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false);
@@ -129,7 +136,6 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa
const accountMenuItemsData: Menu = useMemo(() => {
const profileBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(loginList);
const paymentCardList = fundList;
- const signOutTranslationKey = Session.isSupportAuthToken() && Session.hasStashedSession() ? 'initialSettingsPage.restoreStashed' : 'initialSettingsPage.signOut';
const defaultMenu: Menu = {
sectionStyle: styles.accountSettingsSectionContainer,
sectionTranslationKey: 'initialSettingsPage.account',
@@ -164,32 +170,70 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa
icon: Expensicons.Lock,
routeName: ROUTES.SETTINGS_SECURITY,
},
- {
- translationKey: signOutTranslationKey,
- icon: Expensicons.Exit,
- action: () => {
- signOut(false);
- },
- },
],
};
return defaultMenu;
- }, [loginList, fundList, styles.accountSettingsSectionContainer, bankAccountList, userWallet?.errors, walletTerms?.errors, signOut]);
+ }, [loginList, fundList, styles.accountSettingsSectionContainer, bankAccountList, userWallet?.errors, walletTerms?.errors]);
+
+ /**
+ * Retuns a list of menu items data for workspace section
+ * @returns object with translationKey, style and items for the workspace section
+ */
+ const workspaceMenuItemsData: Menu = useMemo(() => {
+ const items: MenuData[] = [
+ {
+ translationKey: 'common.workspaces',
+ icon: Expensicons.Building,
+ routeName: ROUTES.SETTINGS_WORKSPACES,
+ brickRoadIndicator: hasGlobalWorkspaceSettingsRBR(policies, policyMembers) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined,
+ },
+ {
+ translationKey: 'allSettingsScreen.cardsAndDomains',
+ icon: Expensicons.CardsAndDomains,
+ action: () => {
+ Link.openOldDotLink(CONST.OLDDOT_URLS.ADMIN_DOMAINS_URL);
+ },
+ shouldShowRightIcon: true,
+ iconRight: Expensicons.NewWindow,
+ link: () => Link.buildOldDotURL(CONST.OLDDOT_URLS.ADMIN_DOMAINS_URL),
+ },
+ ];
+
+ if (shouldShowSubscriptionsMenu) {
+ items.splice(1, 0, {
+ translationKey: 'allSettingsScreen.subscriptions',
+ icon: Expensicons.MoneyBag,
+ action: () => {
+ Link.openOldDotLink(CONST.OLDDOT_URLS.ADMIN_POLICIES_URL);
+ },
+ shouldShowRightIcon: true,
+ iconRight: Expensicons.NewWindow,
+ link: () => Link.buildOldDotURL(CONST.OLDDOT_URLS.ADMIN_POLICIES_URL),
+ });
+ }
+
+ return {
+ sectionStyle: styles.workspaceSettingsSectionContainer,
+ sectionTranslationKey: 'common.workspaces',
+ items,
+ };
+ }, [policies, policyMembers, styles.workspaceSettingsSectionContainer]);
/**
* Retuns a list of menu items data for general section
* @returns object with translationKey, style and items for the general section
*/
- const generalMenuItemsData: Menu = useMemo(
- () => ({
+ const generalMenuItemsData: Menu = useMemo(() => {
+ const signOutTranslationKey = Session.isSupportAuthToken() && Session.hasStashedSession() ? 'initialSettingsPage.restoreStashed' : 'initialSettingsPage.signOut';
+ const defaultMenu: Menu = {
sectionStyle: {
...styles.pt4,
},
- sectionTranslationKey: 'initialSettingsPage.general' as const,
+ sectionTranslationKey: 'initialSettingsPage.general',
items: [
{
- translationKey: 'initialSettingsPage.help' as const,
+ translationKey: 'initialSettingsPage.help',
icon: Expensicons.QuestionMark,
action: () => {
Link.openExternalLink(CONST.NEWHELP_URL);
@@ -199,14 +243,22 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa
link: CONST.NEWHELP_URL,
},
{
- translationKey: 'initialSettingsPage.about' as const,
+ translationKey: 'initialSettingsPage.about',
icon: Expensicons.Info,
routeName: ROUTES.SETTINGS_ABOUT,
},
+ {
+ translationKey: signOutTranslationKey,
+ icon: Expensicons.Exit,
+ action: () => {
+ signOut(false);
+ },
+ },
],
- }),
- [styles.pt4],
- );
+ };
+
+ return defaultMenu;
+ }, [styles.pt4, signOut]);
/**
* Retuns JSX.Element with menu items
@@ -291,13 +343,14 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa
const accountMenuItems = useMemo(() => getMenuItemsSection(accountMenuItemsData), [accountMenuItemsData, getMenuItemsSection]);
const generalMenuItems = useMemo(() => getMenuItemsSection(generalMenuItemsData), [generalMenuItemsData, getMenuItemsSection]);
+ const workspaceMenuItems = useMemo(() => getMenuItemsSection(workspaceMenuItemsData), [workspaceMenuItemsData, getMenuItemsSection]);
const currentUserDetails = currentUserPersonalDetails;
const avatarURL = currentUserDetails?.avatar ?? '';
const accountID = currentUserDetails?.accountID ?? '';
const headerContent = (
-
+
{isEmptyObject(currentUserPersonalDetails) || currentUserPersonalDetails.displayName === undefined ? (
) : (
@@ -384,17 +437,15 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa
);
return (
- Navigation.closeFullScreen()}
- backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.ROOT].backgroundColor}
- childrenContainerStyles={[styles.m0, styles.p0]}
+
-
+
+ {headerContent}
{accountMenuItems}
+ {workspaceMenuItems}
{generalMenuItems}
signOut(true)}
onCancel={() => toggleSignoutConfirmModal(false)}
/>
-
-
+
+
);
}
@@ -433,5 +484,11 @@ export default withCurrentUserPersonalDetails(
session: {
key: ONYXKEYS.SESSION,
},
+ policies: {
+ key: ONYXKEYS.COLLECTION.POLICY,
+ },
+ policyMembers: {
+ key: ONYXKEYS.COLLECTION.POLICY_MEMBERS,
+ },
})(InitialSettingsPage),
);
diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx
index 58738a5c8f1c..38828a0406ef 100644
--- a/src/pages/workspace/WorkspaceInitialPage.tsx
+++ b/src/pages/workspace/WorkspaceInitialPage.tsx
@@ -1,26 +1,28 @@
+import {useNavigationState} from '@react-navigation/native';
import type {StackScreenProps} from '@react-navigation/stack';
-import React, {useCallback, useEffect, useState} from 'react';
+import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {View} from 'react-native';
-import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
+import {withOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
-import Breadcrumbs from '@components/Breadcrumbs';
import ConfirmModal from '@components/ConfirmModal';
+import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
-import useActiveRoute from '@hooks/useActiveRoute';
import useLocalize from '@hooks/useLocalize';
import usePrevious from '@hooks/usePrevious';
import useSingleExecution from '@hooks/useSingleExecution';
import useThemeStyles from '@hooks/useThemeStyles';
import useWaitForNavigation from '@hooks/useWaitForNavigation';
+import getTopmostWorkspacesCentralPaneName from '@libs/Navigation/getTopmostWorkspacesCentralPaneName';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
-import type {BottomTabNavigatorParamList} from '@navigation/types';
+import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils';
+import type {FullScreenNavigatorParamList} from '@navigation/types';
import * as App from '@userActions/App';
import * as Policy from '@userActions/Policy';
import * as ReimbursementAccount from '@userActions/ReimbursementAccount';
@@ -48,7 +50,7 @@ type WorkspaceInitialPageOnyxProps = {
reimbursementAccount: OnyxEntry;
};
-type WorkspaceInitialPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceInitialPageOnyxProps & StackScreenProps;
+type WorkspaceInitialPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceInitialPageOnyxProps & StackScreenProps;
function dismissError(policyID: string) {
PolicyUtils.goBackFromInvalidPolicy();
@@ -62,8 +64,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r
const hasPolicyCreationError = !!(policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD && policy.errors);
const waitForNavigate = useWaitForNavigation();
const {singleExecution, isExecuting} = useSingleExecution();
- const activeRoute = useActiveRoute();
-
+ const activeRoute = useNavigationState(getTopmostWorkspacesCentralPaneName);
const {translate} = useLocalize();
const policyID = policy?.id ?? '';
@@ -216,31 +217,38 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r
// We check isPendingDelete for both policy and prevPolicy to prevent the NotFound view from showing right after we delete the workspace
(PolicyUtils.isPendingDeletePolicy(policy) && PolicyUtils.isPendingDeletePolicy(prevPolicy));
+ const policyAvatar = useMemo(() => {
+ if (!policy) {
+ return {source: Expensicons.ExpensifyAppIcon, name: CONST.WORKSPACE_SWITCHER.NAME, type: CONST.ICON_TYPE_AVATAR};
+ }
+
+ const avatar = policy?.avatar ? policy.avatar : getDefaultWorkspaceAvatar(policy?.name);
+ return {
+ source: avatar,
+ name: policy?.name ?? '',
+ type: CONST.ICON_TYPE_WORKSPACE,
+ };
+ }, [policy]);
+
return (
-
+
-
+
{/*
Ideally we should use MenuList component for MenuItems with singleExecution/Navigation actions.
In this case where user can click on workspace avatar or menu items, we need to have a check for `isExecuting`. So, we are directly mapping menuItems.
diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx
index 1aae3294be0d..818edc9c389e 100644
--- a/src/pages/workspace/WorkspaceMembersPage.tsx
+++ b/src/pages/workspace/WorkspaceMembersPage.tsx
@@ -31,7 +31,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
-import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types';
+import type {WorkspacesCentralPaneNavigatorParamList} from '@libs/Navigation/types';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
@@ -57,7 +57,7 @@ type WorkspaceMembersPageOnyxProps = {
type WorkspaceMembersPageProps = WithPolicyAndFullscreenLoadingProps &
WithCurrentUserPersonalDetailsProps &
WorkspaceMembersPageOnyxProps &
- StackScreenProps;
+ StackScreenProps;
/**
* Inverts an object, equivalent of _.invert
diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx
index 45a950e0fafb..000ba0db7bc7 100644
--- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx
+++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx
@@ -9,7 +9,7 @@ import Section from '@components/Section';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types';
+import type {WorkspacesCentralPaneNavigatorParamList} from '@libs/Navigation/types';
import * as Policy from '@userActions/Policy';
import type {TranslationPaths} from '@src/languages/types';
import type SCREENS from '@src/SCREENS';
@@ -21,7 +21,7 @@ import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscree
import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading';
import ToggleSettingOptionRow from './workflows/ToggleSettingsOptionRow';
-type WorkspaceMoreFeaturesPageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps;
+type WorkspaceMoreFeaturesPageProps = WithPolicyAndFullscreenLoadingProps & StackScreenProps;
type Item = {
icon: IconAsset;
diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx
index fb01a858642c..4904a4f35193 100644
--- a/src/pages/workspace/WorkspacePageWithSections.tsx
+++ b/src/pages/workspace/WorkspacePageWithSections.tsx
@@ -19,7 +19,6 @@ import * as ReimbursementAccountProps from '@pages/ReimbursementAccount/reimburs
import * as BankAccounts from '@userActions/BankAccounts';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
-import ROUTES from '@src/ROUTES';
import type {Policy, ReimbursementAccount, User} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type IconAsset from '@src/types/utils/IconAsset';
@@ -157,8 +156,8 @@ function WorkspacePageWithSections({
shouldShowOfflineIndicatorInWideScreen={shouldShowOfflineIndicatorInWideScreen && !shouldShow}
>
Navigation.goBack(backButtonRoute ?? ROUTES.WORKSPACE_INITIAL.getRoute(policyID))}
+ onBackButtonPress={() => Navigation.goBack(backButtonRoute)}
icon={icon ?? undefined}
+ style={styles.headerBarDesktopHeight}
/>
{(isLoading || firstRender.current) && shouldShowLoading && isFocused ? (
diff --git a/src/pages/workspace/WorkspaceProfilePage.tsx b/src/pages/workspace/WorkspaceProfilePage.tsx
index 9d90557b1d37..ddebc9d4b368 100644
--- a/src/pages/workspace/WorkspaceProfilePage.tsx
+++ b/src/pages/workspace/WorkspaceProfilePage.tsx
@@ -102,7 +102,7 @@ function WorkspaceProfilePage({policy, currencyList = {}, route}: WorkSpaceProfi
>
{(hasVBA?: boolean) => (
-
+
App.createWorkspaceWithPolicyDraftAndNavigateToIt()}
+ onPress={() => interceptAnonymousUser(() => App.createWorkspaceWithPolicyDraftAndNavigateToIt())}
/>
@@ -366,7 +367,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r
subtitle={translate('workspace.emptyWorkspace.subtitle')}
ctaText={translate('workspace.new.newWorkspace')}
ctaAccessibilityLabel={translate('workspace.new.newWorkspace')}
- onCtaPress={() => App.createWorkspaceWithPolicyDraftAndNavigateToIt()}
+ onCtaPress={() => interceptAnonymousUser(() => App.createWorkspaceWithPolicyDraftAndNavigateToIt())}
illustration={LottieAnimations.WorkspacePlanet}
// We use this style to vertically center the illustration, as the original illustration is not centered
illustrationStyle={styles.emptyWorkspaceIllustrationStyle}
@@ -394,7 +395,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r
success
medium
text={translate('workspace.new.newWorkspace')}
- onPress={() => App.createWorkspaceWithPolicyDraftAndNavigateToIt()}
+ onPress={() => interceptAnonymousUser(() => App.createWorkspaceWithPolicyDraftAndNavigateToIt())}
/>
;
+type WorkspaceBillsPageProps = StackScreenProps;
function WorkspaceBillsPage({route}: WorkspaceBillsPageProps) {
const {translate} = useLocalize();
diff --git a/src/pages/workspace/card/WorkspaceCardPage.tsx b/src/pages/workspace/card/WorkspaceCardPage.tsx
index 710ef3735026..079c715bffd6 100644
--- a/src/pages/workspace/card/WorkspaceCardPage.tsx
+++ b/src/pages/workspace/card/WorkspaceCardPage.tsx
@@ -4,7 +4,7 @@ import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types';
+import type {WorkspacesCentralPaneNavigatorParamList} from '@libs/Navigation/types';
import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections';
import CONST from '@src/CONST';
import type SCREENS from '@src/SCREENS';
@@ -12,7 +12,7 @@ import WorkspaceCardNoVBAView from './WorkspaceCardNoVBAView';
import WorkspaceCardVBANoECardView from './WorkspaceCardVBANoECardView';
import WorkspaceCardVBAWithECardView from './WorkspaceCardVBAWithECardView';
-type WorkspaceCardPageProps = StackScreenProps;
+type WorkspaceCardPageProps = StackScreenProps;
function WorkspaceCardPage({route}: WorkspaceCardPageProps) {
const {translate} = useLocalize();
diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
index 17afefd1cea4..3f2ef8ce6aa6 100644
--- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
+++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
@@ -21,7 +21,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
import localeCompare from '@libs/LocaleCompare';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
-import type {CentralPaneNavigatorParamList} from '@navigation/types';
+import type {WorkspacesCentralPaneNavigatorParamList} from '@navigation/types';
import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper';
import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper';
import * as Policy from '@userActions/Policy';
@@ -47,7 +47,7 @@ type WorkspaceCategoriesOnyxProps = {
policyCategories: OnyxEntry;
};
-type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & StackScreenProps;
+type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & StackScreenProps;
function WorkspaceCategoriesPage({policy, policyCategories, route}: WorkspaceCategoriesPageProps) {
const {isSmallScreenWidth} = useWindowDimensions();
diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
index fd6466da1758..ca508791c028 100644
--- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
+++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
@@ -20,7 +20,7 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as CurrencyUtils from '@libs/CurrencyUtils';
-import type {CentralPaneNavigatorParamList} from '@navigation/types';
+import type {WorkspacesCentralPaneNavigatorParamList} from '@navigation/types';
import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper';
import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper';
import * as Policy from '@userActions/Policy';
@@ -44,7 +44,7 @@ type PolicyDistanceRatesPageOnyxProps = {
policy: OnyxEntry;
};
-type PolicyDistanceRatesPageProps = PolicyDistanceRatesPageOnyxProps & StackScreenProps;
+type PolicyDistanceRatesPageProps = PolicyDistanceRatesPageOnyxProps & StackScreenProps;
function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) {
const {isSmallScreenWidth} = useWindowDimensions();
diff --git a/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx b/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx
index 96aa350496b5..a00c4959cedb 100644
--- a/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx
+++ b/src/pages/workspace/invoices/WorkspaceInvoicesPage.tsx
@@ -4,14 +4,14 @@ import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import type {CentralPaneNavigatorParamList} from '@navigation/types';
+import type {WorkspacesCentralPaneNavigatorParamList} from '@navigation/types';
import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections';
import CONST from '@src/CONST';
import type SCREENS from '@src/SCREENS';
import WorkspaceInvoicesNoVBAView from './WorkspaceInvoicesNoVBAView';
import WorkspaceInvoicesVBAView from './WorkspaceInvoicesVBAView';
-type WorkspaceInvoicesPageProps = StackScreenProps;
+type WorkspaceInvoicesPageProps = StackScreenProps;
function WorkspaceInvoicesPage({route}: WorkspaceInvoicesPageProps) {
const {translate} = useLocalize();
diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
index cc809892d45e..baf3e8adb460 100644
--- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
@@ -20,7 +20,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
-import type {CentralPaneNavigatorParamList} from '@navigation/types';
+import type {WorkspacesCentralPaneNavigatorParamList} from '@navigation/types';
import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper';
import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper';
import * as Policy from '@userActions/Policy';
@@ -43,7 +43,7 @@ type WorkspaceTagsOnyxProps = {
policyTags: OnyxEntry;
};
-type WorkspaceTagsPageProps = WorkspaceTagsOnyxProps & StackScreenProps;
+type WorkspaceTagsPageProps = WorkspaceTagsOnyxProps & StackScreenProps;
function WorkspaceTagsPage({policyTags, route}: WorkspaceTagsPageProps) {
const {isSmallScreenWidth} = useWindowDimensions();
diff --git a/src/pages/workspace/travel/WorkspaceTravelPage.tsx b/src/pages/workspace/travel/WorkspaceTravelPage.tsx
index 88dfe5254fcf..c03bcc7cfb9b 100644
--- a/src/pages/workspace/travel/WorkspaceTravelPage.tsx
+++ b/src/pages/workspace/travel/WorkspaceTravelPage.tsx
@@ -4,14 +4,14 @@ import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types';
+import type {WorkspacesCentralPaneNavigatorParamList} from '@libs/Navigation/types';
import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections';
import CONST from '@src/CONST';
import type SCREENS from '@src/SCREENS';
import WorkspaceTravelNoVBAView from './WorkspaceTravelNoVBAView';
import WorkspaceTravelVBAView from './WorkspaceTravelVBAView';
-type WorkspaceTravelPageProps = StackScreenProps;
+type WorkspaceTravelPageProps = StackScreenProps;
function WorkspaceTravelPage({route}: WorkspaceTravelPageProps) {
const {translate} = useLocalize();
diff --git a/src/pages/workspace/withPolicy.tsx b/src/pages/workspace/withPolicy.tsx
index 76126040652b..16af069fa0a2 100644
--- a/src/pages/workspace/withPolicy.tsx
+++ b/src/pages/workspace/withPolicy.tsx
@@ -8,7 +8,7 @@ import {withOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import taxPropTypes from '@components/taxPropTypes';
import {translatableTextPropTypes} from '@libs/Localize';
-import type {BottomTabNavigatorParamList, CentralPaneNavigatorParamList, SettingsNavigatorParamList} from '@navigation/types';
+import type {CentralPaneNavigatorParamList, FullScreenNavigatorParamList, SettingsNavigatorParamList, WorkspacesCentralPaneNavigatorParamList} from '@navigation/types';
import policyMemberPropType from '@pages/policyMemberPropType';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
@@ -16,7 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
-type WorkspaceParamList = BottomTabNavigatorParamList & CentralPaneNavigatorParamList & SettingsNavigatorParamList;
+type WorkspaceParamList = WorkspacesCentralPaneNavigatorParamList & FullScreenNavigatorParamList & CentralPaneNavigatorParamList & SettingsNavigatorParamList;
type PolicyRoute = RouteProp>;
function getPolicyIDFromRoute(route: PolicyRoute): string {
@@ -148,5 +148,5 @@ export default function (WrappedComponent:
})(forwardRef(WithPolicy));
}
-export {policyPropTypes, policyDefaultProps};
-export type {WithPolicyOnyxProps, WithPolicyProps, PolicyRoute};
+export {policyDefaultProps, policyPropTypes};
+export type {PolicyRoute, WithPolicyOnyxProps, WithPolicyProps};
diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
index 34446fecdaa2..c6ace2b0856e 100644
--- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
+++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
@@ -18,7 +18,7 @@ import Navigation from '@libs/Navigation/Navigation';
import Permissions from '@libs/Permissions';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
-import type {CentralPaneNavigatorParamList} from '@navigation/types';
+import type {WorkspacesCentralPaneNavigatorParamList} from '@navigation/types';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
import withPolicy from '@pages/workspace/withPolicy';
import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections';
@@ -42,7 +42,7 @@ type WorkspaceWorkflowsPageOnyxProps = {
/** Policy details */
session: OnyxEntry;
};
-type WorkspaceWorkflowsPageProps = WithPolicyProps & WorkspaceWorkflowsPageOnyxProps & StackScreenProps;
+type WorkspaceWorkflowsPageProps = WithPolicyProps & WorkspaceWorkflowsPageOnyxProps & StackScreenProps;
function WorkspaceWorkflowsPage({policy, betas, route, reimbursementAccount, session}: WorkspaceWorkflowsPageProps) {
const {translate, preferredLocale} = useLocalize();
diff --git a/src/styles/index.ts b/src/styles/index.ts
index 0f129d898758..3cfb38ef4bab 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -1442,10 +1442,16 @@ const styles = (theme: ThemeColors) =>
},
sidebarAvatar: {
- backgroundColor: theme.icon,
+ borderRadius: 28,
+ height: variables.componentSizeSmall,
+ width: variables.componentSizeSmall,
+ },
+
+ selectedAvatarBorder: {
+ padding: 2,
+ borderWidth: 2,
borderRadius: 20,
- height: variables.componentSizeNormal,
- width: variables.componentSizeNormal,
+ borderColor: theme.success,
},
statusIndicator: (backgroundColor = theme.danger) =>
@@ -1455,10 +1461,10 @@ const styles = (theme: ThemeColors) =>
borderRadius: 8,
borderWidth: 2,
position: 'absolute',
- right: -2,
- top: -1,
- height: 16,
- width: 16,
+ right: -4,
+ top: -3,
+ height: 12,
+ width: 12,
zIndex: 10,
} satisfies ViewStyle),
@@ -2648,6 +2654,12 @@ const styles = (theme: ThemeColors) =>
...spacing.pt0,
},
+ workspaceSettingsSectionContainer: {
+ borderBottomWidth: 1,
+ borderBottomColor: theme.border,
+ ...spacing.pt4,
+ },
+
centralPaneAnimation: {
height: CONST.CENTRAL_PANE_ANIMATION_HEIGHT,
},
@@ -3086,7 +3098,7 @@ const styles = (theme: ThemeColors) =>
smallEditIcon: {
alignItems: 'center',
backgroundColor: theme.buttonDefaultBG,
- borderColor: theme.cardBG,
+ borderColor: theme.appBG,
borderRadius: 20,
borderWidth: 3,
color: theme.textReversed,
diff --git a/src/styles/variables.ts b/src/styles/variables.ts
index d63fb5e9f339..61c16a8c2fd7 100644
--- a/src/styles/variables.ts
+++ b/src/styles/variables.ts
@@ -12,7 +12,7 @@ function getValueUsingPixelRatio(defaultValue: number, maxValue: number): number
}
export default {
- bottomTabHeight: 80,
+ bottomTabHeight: 72,
contentHeaderHeight: getValueUsingPixelRatio(72, 100),
contentHeaderDesktopHeight: getValueUsingPixelRatio(80, 100),
componentSizeSmall: getValueUsingPixelRatio(28, 32),