diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 998a439a6fdce..9464c1c268bdc 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -6496,6 +6496,8 @@ const CONST = { ONBOARDING_RHP_VARIANT: { RHP_CONCIERGE_DM: 'rhpConciergeDm', RHP_ADMINS_ROOM: 'rhpAdminsRoom', + RHP_HOME_PAGE: 'rhpHomePage', + TRACK_EXPENSES_WITH_CONCIERGE: 'trackExpensesWithConcierge', CONTROL: 'control', }, ACTIONABLE_TRACK_EXPENSE_WHISPER_MESSAGE: 'What would you like to do with this expense?', diff --git a/src/components/SidePanel/RHPVariantTest/index.ts b/src/components/SidePanel/RHPVariantTest/index.ts index 43cfe8e7d11d2..2a2a6fc831b1c 100644 --- a/src/components/SidePanel/RHPVariantTest/index.ts +++ b/src/components/SidePanel/RHPVariantTest/index.ts @@ -28,12 +28,23 @@ Onyx.connectWithoutView({ /** * Determines if the user should be navigated to the RHP variant side panel after onboarding. - * The RHP variant is only shown to micro companies that are part of the RHP experiment. + * The RHP variant is only shown to micro companies that are part of the RHP experiment, + * except for the Track expenses variant which applies regardless of company size. + * + * Accepts an optional variantOverride to bypass the module-level Onyx variable, avoiding a race + * condition where the Onyx callback hasn't fired yet when this is called immediately after the + * CompleteGuidedSetup API response. */ -const shouldOpenRHPVariant: ShouldOpenRHPVariant = () => { +const shouldOpenRHPVariant: ShouldOpenRHPVariant = (variantOverride) => { + const variant = variantOverride ?? onboardingRHPVariant; + + if (variant === CONST.ONBOARDING_RHP_VARIANT.TRACK_EXPENSES_WITH_CONCIERGE) { + return true; + } + const isMicroCompany = onboardingCompanySize === CONST.ONBOARDING_COMPANY_SIZE.MICRO; - const isRHPConciergeDM = onboardingRHPVariant === CONST.ONBOARDING_RHP_VARIANT.RHP_CONCIERGE_DM; - const isRHPAdminsRoom = onboardingRHPVariant === CONST.ONBOARDING_RHP_VARIANT.RHP_ADMINS_ROOM; + const isRHPConciergeDM = variant === CONST.ONBOARDING_RHP_VARIANT.RHP_CONCIERGE_DM; + const isRHPAdminsRoom = variant === CONST.ONBOARDING_RHP_VARIANT.RHP_ADMINS_ROOM; return isMicroCompany && (isRHPConciergeDM || isRHPAdminsRoom); }; @@ -44,7 +55,14 @@ const shouldOpenRHPVariant: ShouldOpenRHPVariant = () => { * - RHP Concierge DM: navigate to the workspace overview and open the side panel with the Concierge DM * - RHP Admins Room: navigate to the workspace overview and open the side panel with the Admins Room */ -const handleRHPVariantNavigation: HandleRHPVariantNavigation = (onboardingPolicyID) => { +const handleRHPVariantNavigation: HandleRHPVariantNavigation = (onboardingPolicyID, variantOverride) => { + const variant = variantOverride ?? onboardingRHPVariant; + if (variant === CONST.ONBOARDING_RHP_VARIANT.RHP_HOME_PAGE || variant === CONST.ONBOARDING_RHP_VARIANT.TRACK_EXPENSES_WITH_CONCIERGE) { + Navigation.navigate(ROUTES.HOME); + SidePanelActions.openSidePanel(true); + return; + } + Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW.getRoute(onboardingPolicyID)); SidePanelActions.openSidePanel(true); }; diff --git a/src/components/SidePanel/RHPVariantTest/types.ts b/src/components/SidePanel/RHPVariantTest/types.ts index 7efd441c05264..15dff41a1af68 100644 --- a/src/components/SidePanel/RHPVariantTest/types.ts +++ b/src/components/SidePanel/RHPVariantTest/types.ts @@ -1,4 +1,6 @@ -type ShouldOpenRHPVariant = () => boolean; -type HandleRHPVariantNavigation = (onboardingPolicyID?: string) => void; +import type {OnboardingRHPVariant} from '@src/types/onyx'; + +type ShouldOpenRHPVariant = (variantOverride?: OnboardingRHPVariant | null) => boolean; +type HandleRHPVariantNavigation = (onboardingPolicyID?: string, variantOverride?: OnboardingRHPVariant | null) => void; export type {ShouldOpenRHPVariant, HandleRHPVariantNavigation}; diff --git a/src/hooks/useAutoCreateTrackWorkspace.ts b/src/hooks/useAutoCreateTrackWorkspace.ts index 6e9bd3e10eeff..1c36fff5ab8af 100644 --- a/src/hooks/useAutoCreateTrackWorkspace.ts +++ b/src/hooks/useAutoCreateTrackWorkspace.ts @@ -1,6 +1,7 @@ import {hasSeenTourSelector} from '@selectors/Onboarding'; import {useCallback, useMemo} from 'react'; import type {OnyxCollection} from 'react-native-onyx'; +import isSidePanelReportSupported from '@components/SidePanel/isSidePanelReportSupported'; import {navigateAfterOnboardingWithMicrotaskQueue} from '@libs/navigateAfterOnboarding'; import {createDisplayName} from '@libs/PersonalDetailsUtils'; import {isPaidGroupPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; @@ -9,7 +10,7 @@ import {completeOnboarding} from '@userActions/Report'; import {setOnboardingAdminsChatReportID, setOnboardingPolicyID} from '@userActions/Welcome'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {OnboardingPurpose, Policy} from '@src/types/onyx'; +import type {OnboardingPurpose, OnboardingRHPVariant, Policy} from '@src/types/onyx'; import useArchivedReportsIdSet from './useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useHasActiveAdminPolicies from './useHasActiveAdminPolicies'; @@ -59,7 +60,7 @@ function useAutoCreateTrackWorkspace() { const mergedAccountConciergeReportID = !onboardingValues?.shouldRedirectToClassicAfterMerge && onboardingValues?.shouldValidate ? conciergeChatReportID : undefined; const autoCreateTrackWorkspace = useCallback( - (firstName: string, lastName: string, onboardingPurposeSelected: OnboardingPurpose) => { + async (firstName: string, lastName: string, onboardingPurposeSelected: OnboardingPurpose) => { const shouldCreateWorkspace = !isRestrictedPolicyCreation && !onboardingPolicyID && !hasPaidGroupAdminPolicy; const displayName = createDisplayName(session?.email ?? '', {firstName, lastName}, formatPhoneNumber); @@ -85,18 +86,23 @@ function useAutoCreateTrackWorkspace() { }) : {adminsChatReportID: onboardingAdminsChatReportID, policyID: onboardingPolicyID}; - completeOnboarding({ + const response = await completeOnboarding({ engagementChoice: CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE, onboardingMessage: onboardingMessages[CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE], firstName, lastName, adminsChatReportID: newAdminsChatReportID, onboardingPolicyID: newPolicyID, + shouldWaitForRHPVariantInitialization: isSidePanelReportSupported, introSelected, isSelfTourViewed, betas, }); + // Extract the RHP variant directly from the API response to avoid a race condition + // where the Onyx callback hasn't fired yet when navigateAfterOnboarding is called. + const rhpVariant = response?.onyxData?.find((update) => (update.key as string) === ONYXKEYS.NVP_ONBOARDING_RHP_VARIANT)?.value as OnboardingRHPVariant | undefined; + setOnboardingAdminsChatReportID(); setOnboardingPolicyID(); @@ -108,6 +114,7 @@ function useAutoCreateTrackWorkspace() { newPolicyID, mergedAccountConciergeReportID, false, + rhpVariant, ); }, [ diff --git a/src/languages/en.ts b/src/languages/en.ts index 710871e37334b..954e5c8eae5ba 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3121,7 +3121,7 @@ const translations = { 馃憢 Hey there, I'm your Expensify setup specialist. Now that you've created a workspace, make the most of your 30-day free trial by following the steps below! `), onboardingTrackWorkspaceMessage: - "# Let鈥檚 get you set up\n馃憢 Hey there, I'm your Expensify setup specialist. I've already created a workspace to help manage your receipts and expenses. To make the most of your 30-day free trial, just follow the remaining setup steps below!", + '# Welcome to Expensify\nHi! I鈥檓 Concierge, your personal financial assistant. To make the most of your 30-day free trial, just follow the remaining setup steps below!', onboardingChatSplitMessage: 'Splitting bills with friends is as easy as sending a message. Here鈥檚 how.', onboardingAdminMessage: "Learn how to manage your team's workspace as an admin and submit your own expenses.", onboardingTestDriveReceiverMessage: "*You've got 3 months free! Get started below.*", diff --git a/src/languages/es.ts b/src/languages/es.ts index 600cf35d8d250..f9632aaa59eb3 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2976,7 +2976,7 @@ ${amount} para ${merchant} - ${date}`, 馃憢 Hola, soy tu **especialista asignado** de configuraci贸n de Expensify. Ahora que ya has creado un espacio de trabajo, aprovecha al m谩ximo tu prueba gratuita de 30 d铆as siguiendo los pasos que aparecen a continuaci贸n. `), onboardingTrackWorkspaceMessage: - '# Vamos a configurarte\n馃憢 Hola, soy tu **especialista asignado** de configuraci贸n de Expensify. Ya he creado un espacio de trabajo para ayudarte a gestionar tus recibos y gastos. Para aprovechar al m谩ximo tu prueba gratuita de 30 d铆as, 隆solo sigue los pasos de configuraci贸n restantes que aparecen a continuaci贸n!', + '# Bienvenido a Expensify\n隆Hola! Soy Concierge, tu asistente financiero personal. Para aprovechar al m谩ximo tu prueba gratuita de 30 d铆as, 隆solo sigue los pasos de configuraci贸n restantes que aparecen a continuaci贸n!', onboardingChatSplitMessage: 'Dividir cuentas con amigos es tan f谩cil como enviar un mensaje. As铆 se hace.', onboardingAdminMessage: 'Aprende a gestionar el espacio de tu equipo como administrador y enviar tus propios gastos.', onboardingTestDriveReceiverMessage: '*隆Tienes 3 meses gratis! Empieza abajo.*', diff --git a/src/libs/actions/Welcome/OnboardingFlow.ts b/src/libs/actions/Welcome/OnboardingFlow.ts index cfc514c62ec89..6ef14b7192eee 100644 --- a/src/libs/actions/Welcome/OnboardingFlow.ts +++ b/src/libs/actions/Welcome/OnboardingFlow.ts @@ -298,13 +298,6 @@ const getOnboardingMessages = (locale?: Locale) => { description: ({workspaceSettingsLink}) => translate(resolvedLocale, 'onboarding.tasks.reviewWorkspaceSettingsTask.description', {workspaceSettingsLink}), }; - const inviteAccountantTask: OnboardingTask = { - type: CONST.ONBOARDING_TASK_TYPE.INVITE_ACCOUNTANT, - autoCompleted: false, - title: ({workspaceMembersLink}) => translate(resolvedLocale, 'onboarding.tasks.inviteAccountantTask.title', {workspaceMembersLink}), - description: ({workspaceMembersLink}) => translate(resolvedLocale, 'onboarding.tasks.inviteAccountantTask.description', {workspaceMembersLink}), - }; - const onboardingEmployerOrSubmitMessage: OnboardingMessage = { message: translate(resolvedLocale, 'onboarding.messages.onboardingEmployerOrSubmitMessage'), tasks: [testDriveEmployeeTask, adminSubmitExpenseTask], @@ -349,7 +342,7 @@ const getOnboardingMessages = (locale?: Locale) => { width: 1280, height: 960, }, - tasks: [createWorkspaceTask, testDriveAdminTask, createReportTask, setupCategoriesTask, inviteAccountantTask, reviewWorkspaceSettingsTask], + tasks: [createWorkspaceTask, testDriveAdminTask, createReportTask, setupCategoriesTask], }; const onboardingChatSplitMessage: OnboardingMessage = { diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts index cfb8de469374a..b02c18d112a5c 100644 --- a/src/libs/navigateAfterOnboarding.ts +++ b/src/libs/navigateAfterOnboarding.ts @@ -1,11 +1,24 @@ +import Onyx from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import {handleRHPVariantNavigation, shouldOpenRHPVariant} from '@components/SidePanel/RHPVariantTest'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {OnboardingRHPVariant} from '@src/types/onyx'; import {setDisableDismissOnEscape} from './actions/Modal'; import shouldOpenOnAdminRoom from './Navigation/helpers/shouldOpenOnAdminRoom'; import Navigation from './Navigation/Navigation'; import {findLastAccessedReport, isConciergeChatReport, isSelfDM} from './ReportUtils'; import type {ArchivedReportsIDSet} from './SearchUIUtils'; +let onboardingRHPVariant: OnyxEntry; +Onyx.connectWithoutView({ + key: ONYXKEYS.NVP_ONBOARDING_RHP_VARIANT, + callback: (value) => { + onboardingRHPVariant = value; + }, +}); + /** * Determines the report ID to navigate to after onboarding for control variant or ineligible users. * On large screens, navigates to the admins chat if available. On small screens, finds the last @@ -50,11 +63,21 @@ function navigateAfterOnboarding( onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, + variantOverride?: OnboardingRHPVariant | null, ) { setDisableDismissOnEscape(false); - if (shouldOpenRHPVariant()) { - handleRHPVariantNavigation(onboardingPolicyID); + if (shouldOpenRHPVariant(variantOverride)) { + handleRHPVariantNavigation(onboardingPolicyID, variantOverride); + return; + } + + // On mobile (small screen), Track workspace admins with the trackExpensesWithConcierge variant + // should navigate directly to the Concierge DM (which contains onboarding tasks). + // The native shouldOpenRHPVariant() returns false (no side panel on mobile), so we handle it here. + const variant = variantOverride ?? onboardingRHPVariant; + if (isSmallScreenWidth && variant === CONST.ONBOARDING_RHP_VARIANT.TRACK_EXPENSES_WITH_CONCIERGE) { + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(conciergeReportID)); return; } @@ -83,6 +106,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, + variantOverride?: OnboardingRHPVariant | null, ) { Navigation.dismissModal(); Navigation.setNavigationActionToMicrotaskQueue(() => { @@ -94,6 +118,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, + variantOverride, ); }); } diff --git a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx index 062d0d91664be..bbcf474a97a79 100644 --- a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx +++ b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx @@ -36,6 +36,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; +import type {OnboardingRHPVariant} from '@src/types/onyx'; import type {BaseOnboardingInterestedFeaturesProps, Feature, SectionObject} from './types'; function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardingInterestedFeaturesProps) { @@ -219,7 +220,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin setOnboardingPolicyID(policyID); } - await completeOnboarding({ + const response = await completeOnboarding({ engagementChoice: onboardingPurposeSelected, onboardingMessage: onboardingMessages[onboardingPurposeSelected], adminsChatReportID, @@ -235,6 +236,10 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin betas, }); + // Extract the RHP variant directly from the API response to avoid a race condition + // where the Onyx callback hasn't fired yet when navigateAfterOnboarding is called. + const rhpVariant = response?.onyxData?.find((update) => (update.key as string) === ONYXKEYS.NVP_ONBOARDING_RHP_VARIANT)?.value as OnboardingRHPVariant | undefined; + // Avoid creating new WS because onboardingPolicyID is cleared before unmounting // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => { @@ -253,6 +258,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin // Onboarding tasks would show in Concierge instead of admins room for testing accounts, we should open where onboarding tasks are located // See https://github.com/Expensify/App/issues/57167 for more details (session?.email ?? '').includes('+'), + rhpVariant, ); } catch (error) { Log.warn('[BaseOnboardingInterestedFeatures] Error completing onboarding', {error}); diff --git a/src/types/onyx/OnboardingRHPVariant.ts b/src/types/onyx/OnboardingRHPVariant.ts index 271e1977e7370..891c86d4eb4b2 100644 --- a/src/types/onyx/OnboardingRHPVariant.ts +++ b/src/types/onyx/OnboardingRHPVariant.ts @@ -3,7 +3,9 @@ * @description 'control' - The variant with the Concierge DM * @description 'rhpConciergeDm' - Admin of workspace with Concierge DM * @description 'rhpAdminsRoom' - Admin of workspace with the admins room + * @description 'rhpHomePage' - Navigate to Home page with Concierge Anywhere accessible in #admins room + * @description 'trackExpensesWithConcierge' - Track workspace admins land on Home page with Concierge side panel (desktop) or Concierge chat (mobile) */ -type OnboardingRHPVariant = 'rhpConciergeDm' | 'rhpAdminsRoom' | 'control'; +type OnboardingRHPVariant = 'rhpConciergeDm' | 'rhpAdminsRoom' | 'rhpHomePage' | 'trackExpensesWithConcierge' | 'control'; export default OnboardingRHPVariant;