diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 74ef76c511a4..f103504cbd86 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -21,6 +21,7 @@ import Log from '@libs/Log'; import getCurrentUrl from '@libs/Navigation/currentUrl'; import getOnboardingModalScreenOptions from '@libs/Navigation/getOnboardingModalScreenOptions'; import Navigation from '@libs/Navigation/Navigation'; +import shouldOpenOnAdminRoom from '@libs/Navigation/shouldOpenOnAdminRoom'; import type {AuthScreensParamList, CentralPaneName, CentralPaneScreensParamList} from '@libs/Navigation/types'; import NetworkConnection from '@libs/NetworkConnection'; import onyxSubscribe from '@libs/onyxSubscribe'; @@ -89,11 +90,6 @@ const loadReportAvatar = () => require('../../../pages/Rep const loadReceiptView = () => require('../../../pages/TransactionReceiptPage').default; const loadWorkspaceJoinUser = () => require('@pages/workspace/WorkspaceJoinUserPage').default; -function shouldOpenOnAdminRoom() { - const url = getCurrentUrl(); - return url ? new URL(url).searchParams.get('openOnAdminRoom') === 'true' : false; -} - function getCentralPaneScreenInitialParams(screenName: CentralPaneName, initialReportID?: string): Partial> { if (screenName === SCREENS.SEARCH.CENTRAL_PANE) { // Generate default query string with buildSearchQueryString without argument. @@ -589,6 +585,10 @@ AuthScreens.displayName = 'AuthScreens'; const AuthScreensMemoized = memo(AuthScreens, () => true); +// Migration to useOnyx cause re-login if logout from deeplinked report in desktop app +// Further analysis required and more details can be seen here: +// https://github.com/Expensify/App/issues/50560 +// eslint-disable-next-line export default withOnyx({ session: { key: ONYXKEYS.SESSION, diff --git a/src/libs/Navigation/shouldOpenOnAdminRoom.ts b/src/libs/Navigation/shouldOpenOnAdminRoom.ts new file mode 100644 index 000000000000..a593e8c22768 --- /dev/null +++ b/src/libs/Navigation/shouldOpenOnAdminRoom.ts @@ -0,0 +1,6 @@ +import getCurrentUrl from './currentUrl'; + +export default function shouldOpenOnAdminRoom() { + const url = getCurrentUrl(); + return url ? new URL(url).searchParams.get('openOnAdminRoom') === 'true' : false; +} diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index eee1fa52e851..d7ff4538748c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -65,7 +65,6 @@ import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import {hasValidDraftComment} from './DraftCommentUtils'; import getAttachmentDetails from './fileDownload/getAttachmentDetails'; -import getIsSmallScreenWidth from './getIsSmallScreenWidth'; import isReportMessageAttachment from './isReportMessageAttachment'; import localeCompare from './LocaleCompare'; import * as LocalePhoneNumber from './LocalePhoneNumber'; @@ -1369,12 +1368,6 @@ function findLastAccessedReport(ignoreDomainRooms: boolean, openOnAdminRoom = fa }); } - // if the user hasn't completed the onboarding flow, whether the user should be in the concierge chat or system chat - // should be consistent with what chat the user will land after onboarding flow - if (!getIsSmallScreenWidth() && !Array.isArray(onboarding) && !onboarding?.hasCompletedGuidedSetupFlow) { - return reportsValues.find(isChatUsedForOnboarding); - } - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldFilter = excludeReportID || ignoreDomainRooms; if (shouldFilter) { diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts new file mode 100644 index 000000000000..d84927988b5c --- /dev/null +++ b/src/libs/navigateAfterOnboarding.ts @@ -0,0 +1,40 @@ +import ROUTES from '@src/ROUTES'; +import * as Report from './actions/Report'; +import Navigation from './Navigation/Navigation'; +import shouldOpenOnAdminRoom from './Navigation/shouldOpenOnAdminRoom'; +import * as ReportUtils from './ReportUtils'; + +const navigateAfterOnboarding = ( + isSmallScreenWidth: boolean, + shouldUseNarrowLayout: boolean, + canUseDefaultRooms: boolean | undefined, + onboardingPolicyID?: string, + activeWorkspaceID?: string, + backTo?: string, +) => { + Navigation.dismissModal(); + + // When hasCompletedGuidedSetupFlow is true, OnboardingModalNavigator in AuthScreen is removed from the navigation stack. + // On small screens, this removal redirects navigation to HOME. Dismissing the modal doesn't work properly, + // so we need to specifically navigate to the last accessed report. + if (!isSmallScreenWidth) { + return; + } + + const lastAccessedReport = ReportUtils.findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom(), activeWorkspaceID); + const lastAccessedReportID = lastAccessedReport?.reportID; + // we don't want to navigate to newly creaded workspace after onboarding completed. + if (!lastAccessedReportID || lastAccessedReport.policyID === onboardingPolicyID) { + // Only navigate to concierge chat when central pane is visible + // Otherwise stay on the chats screen. + if (!shouldUseNarrowLayout && !backTo) { + Report.navigateToConciergeChat(); + } + return; + } + + const lastAccessedReportRoute = ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID ?? '-1'); + Navigation.navigate(lastAccessedReportRoute); +}; + +export default navigateAfterOnboarding; diff --git a/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx b/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx index 0530d618d661..ad7e5d38698f 100644 --- a/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx +++ b/src/pages/OnboardingAccounting/BaseOnboardingAccounting.tsx @@ -10,12 +10,14 @@ import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/RadioListItem'; import type {ListItem} from '@components/SelectionList/types'; import Text from '@components/Text'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useLocalize from '@hooks/useLocalize'; +import usePermissions from '@hooks/usePermissions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@libs/Navigation/Navigation'; +import navigateAfterOnboarding from '@libs/navigateAfterOnboarding'; import variables from '@styles/variables'; import * as Report from '@userActions/Report'; import * as Welcome from '@userActions/Welcome'; @@ -35,11 +37,13 @@ function BaseOnboardingAccounting({shouldUseNativeStyles, route}: BaseOnboarding const theme = useTheme(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); - const {onboardingIsMediumOrLargerScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); + const {onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const [onboardingPurposeSelected] = useOnyx(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED); const [onboardingPolicyID] = useOnyx(ONYXKEYS.ONBOARDING_POLICY_ID); const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID); const [onboardingCompanySize] = useOnyx(ONYXKEYS.ONBOARDING_COMPANY_SIZE); + const {canUseDefaultRooms} = usePermissions(); + const {activeWorkspaceID} = useActiveWorkspace(); const [userReportedIntegration, setUserReportedIntegration] = useState(undefined); const [error, setError] = useState(''); @@ -144,13 +148,7 @@ function BaseOnboardingAccounting({shouldUseNativeStyles, route}: BaseOnboarding Welcome.setOnboardingAdminsChatReportID(); Welcome.setOnboardingPolicyID(); - Navigation.dismissModal(); - - // Only navigate to concierge chat when central pane is visible - // Otherwise stay on the chats screen. - if (!shouldUseNarrowLayout && !route.params?.backTo) { - Report.navigateToConciergeChat(); - } + navigateAfterOnboarding(isSmallScreenWidth, shouldUseNarrowLayout, canUseDefaultRooms, onboardingPolicyID, activeWorkspaceID, route.params?.backTo); }} pressOnEnter /> diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index eb6d61770be0..f1c79d7aa76b 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -10,13 +10,15 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; +import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; +import usePermissions from '@hooks/usePermissions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ErrorUtils from '@libs/ErrorUtils'; -import Navigation from '@libs/Navigation/Navigation'; +import navigateAfterOnboarding from '@libs/navigateAfterOnboarding'; import * as ValidationUtils from '@libs/ValidationUtils'; import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Report from '@userActions/Report'; @@ -33,10 +35,12 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const [onboardingPurposeSelected] = useOnyx(ONYXKEYS.ONBOARDING_PURPOSE_SELECTED); const [onboardingPolicyID] = useOnyx(ONYXKEYS.ONBOARDING_POLICY_ID); const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID); - const {shouldUseNarrowLayout, isSmallScreenWidth, onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout(); + const {onboardingIsMediumOrLargerScreenWidth, isSmallScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const {inputCallbackRef} = useAutoFocusInput(); const [shouldValidateOnChange, setShouldValidateOnChange] = useState(false); const {isOffline} = useNetwork(); + const {canUseDefaultRooms} = usePermissions(); + const {activeWorkspaceID} = useActiveWorkspace(); useEffect(() => { Welcome.setOnboardingErrorMessage(''); @@ -65,15 +69,9 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat Welcome.setOnboardingAdminsChatReportID(); Welcome.setOnboardingPolicyID(); - Navigation.dismissModal(); - - // Only navigate to concierge chat when central pane is visible - // Otherwise stay on the chats screen. - if (!shouldUseNarrowLayout && !route.params?.backTo) { - Report.navigateToConciergeChat(); - } + navigateAfterOnboarding(isSmallScreenWidth, shouldUseNarrowLayout, canUseDefaultRooms, onboardingPolicyID, activeWorkspaceID, route.params?.backTo); }, - [onboardingPurposeSelected, onboardingAdminsChatReportID, onboardingPolicyID, shouldUseNarrowLayout, route.params?.backTo], + [onboardingPurposeSelected, onboardingAdminsChatReportID, onboardingPolicyID, route.params?.backTo, activeWorkspaceID, canUseDefaultRooms, isSmallScreenWidth, shouldUseNarrowLayout], ); const validate = (values: FormOnyxValues<'onboardingPersonalDetailsForm'>) => { diff --git a/src/pages/signin/SignInModal.tsx b/src/pages/signin/SignInModal.tsx index bad93b29f8af..8cfa3ba3dcc8 100644 --- a/src/pages/signin/SignInModal.tsx +++ b/src/pages/signin/SignInModal.tsx @@ -1,36 +1,37 @@ import React, {useEffect, useRef} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {useSession} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import Navigation from '@libs/Navigation/Navigation'; +import {waitForIdle} from '@libs/Network/SequentialQueue'; import * as App from '@userActions/App'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; -import type {Session} from '@src/types/onyx'; import SignInPage from './SignInPage'; import type {SignInPageRef} from './SignInPage'; -type SignInModalOnyxProps = { - session: OnyxEntry; -}; - -type SignInModalProps = SignInModalOnyxProps; - -function SignInModal({session}: SignInModalProps) { +function SignInModal() { const theme = useTheme(); const StyleUtils = useStyleUtils(); const siginPageRef = useRef(null); + const session = useSession(); useEffect(() => { const isAnonymousUser = session?.authTokenType === CONST.AUTH_TOKEN_TYPES.ANONYMOUS; if (!isAnonymousUser) { // Signing in RHP is only for anonymous users - Navigation.isNavigationReady().then(() => Navigation.dismissModal()); - App.openApp(); + Navigation.isNavigationReady().then(() => { + Navigation.dismissModal(); + }); + + // To prevent deadlock when OpenReport and OpenApp overlap, wait for the queue to be idle before calling openApp. + // This ensures that any communication gaps between the client and server during OpenReport processing do not cause the queue to pause, + // which would prevent us from processing or clearing the queue. + waitForIdle().then(() => { + App.openApp(); + }); } }, [session?.authTokenType]); @@ -61,6 +62,4 @@ function SignInModal({session}: SignInModalProps) { SignInModal.displayName = 'SignInModal'; -export default withOnyx({ - session: {key: ONYXKEYS.SESSION}, -})(SignInModal); +export default SignInModal;