diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 9c84bf9b382e..4d624d9b8dc1 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -130,6 +130,7 @@ const onboardingInviteTypes = { IOU: 'iou', INVOICE: 'invoice', CHAT: 'chat', + WORKSPACE: 'workspace', } as const; const onboardingCompanySize = { @@ -965,18 +966,15 @@ const CONST = { TEST_RECEIPT_URL: `${CLOUDFRONT_URL}/images/fake-receipt__tacotodds.png`, // Use Environment.getEnvironmentURL to get the complete URL with port number DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:', - NAVATTIC: { - ADMIN_TOUR_PRODUCTION: 'https://expensify.navattic.com/kh204a7', - ADMIN_TOUR_STAGING: 'https://expensify.navattic.com/3i300k18', - EMPLOYEE_TOUR_PRODUCTION: 'https://expensify.navattic.com/35609gb', - EMPLOYEE_TOUR_STAGING: 'https://expensify.navattic.com/cf15002s', - COMPLETED: 'completed', - }, STORYLANE: { ADMIN_TOUR: 'https://app.storylane.io/demo/bbcreg8vccag?embed=inline', ADMIN_TOUR_MOBILE: 'https://app.storylane.io/demo/b6faqcdsxgww?embed=inline', TRACK_WORKSPACE_TOUR: 'https://app.storylane.io/share/agmsfwgasaed?embed=inline', TRACK_WORKSPACE_TOUR_MOBILE: 'https://app.storylane.io/share/wq4hiwsqvoho?embed=inline', + + // At the moment we are using Navattic links, but it will be changed to Storylane in the future. + EMPLOYEE_TOUR: 'https://expensify.navattic.com/35609gb', + EMPLOYEE_TOUR_MOBILE: 'https://expensify.navattic.com/35609gb', }, OLD_DOT_PUBLIC_URLS: { TERMS_URL: `${EXPENSIFY_URL}/terms`, diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx index 3a36ebf2dd38..6434920b77be 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx @@ -1,31 +1,20 @@ import {Str} from 'expensify-common'; -import React, {useContext, useMemo} from 'react'; +import React, {useMemo} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; -import {TNodeChildrenRenderer} from 'react-native-render-html'; import type {CustomRendererProps, TBlock} from 'react-native-render-html'; +import {TNodeChildrenRenderer} from 'react-native-render-html'; import AnchorForAttachmentsOnly from '@components/AnchorForAttachmentsOnly'; import AnchorForCommentsOnly from '@components/AnchorForCommentsOnly'; import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils'; -import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useEnvironment from '@hooks/useEnvironment'; import useHover from '@hooks/useHover'; -import useOnyx from '@hooks/useOnyx'; -import useParentReport from '@hooks/useParentReport'; -import useReportIsArchived from '@hooks/useReportIsArchived'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getInternalExpensifyPath, getInternalNewExpensifyPath, openExternalLink, openLink} from '@libs/actions/Link'; -import {isAnonymousUser} from '@libs/actions/Session'; -import {canActionTask, canModifyTask, completeTask} from '@libs/actions/Task'; -import {setSelfTourViewed} from '@libs/actions/Welcome'; -import {hasSeenTourSelector} from '@libs/onboardingSelectors'; -import {getNavatticURL} from '@libs/TourUtils'; +import {getInternalExpensifyPath, getInternalNewExpensifyPath, openLink} from '@libs/actions/Link'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; type AnchorRendererProps = CustomRendererProps & { /** Key of the element */ @@ -33,24 +22,11 @@ type AnchorRendererProps = CustomRendererProps & { }; function AnchorRenderer({tnode, style, key}: AnchorRendererProps) { - const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const {report, action} = useContext(ShowContextMenuContext); - const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); - const [viewTourTaskReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${introSelected?.viewTour}`, {canBeMissing: true}); - const [hasSeenTour = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, { - selector: hasSeenTourSelector, - canBeMissing: true, - }); - const parentReport = useParentReport(report?.reportID); - const isParentReportArchived = useReportIsArchived(parentReport?.reportID); - const canModifyViewTourTask = canModifyTask(viewTourTaskReport, currentUserPersonalDetails.accountID, isParentReportArchived); - const canActionViewTourTask = canActionTask(viewTourTaskReport, currentUserPersonalDetails.accountID, parentReport, isParentReportArchived); - const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const htmlAttribs = tnode.attributes; - const {environment, environmentURL} = useEnvironment(); + const {environmentURL} = useEnvironment(); const {hovered, bind} = useHover(); // An auth token is needed to download Expensify chat attachments const isAttachment = !!htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]; @@ -68,26 +44,13 @@ function AnchorRenderer({tnode, style, key}: AnchorRendererProps) { const textDecorationLineStyle = isDeleted ? styles.lineThrough : {}; - const isInConciergeTaskView = action?.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && report?.type === CONST.REPORT.TYPE.TASK && report.ownerAccountID === CONST.ACCOUNT_ID.CONCIERGE; - const isTourTask = attrHref === getNavatticURL(environment, introSelected?.choice) && (action?.actorAccountID === CONST.ACCOUNT_ID.CONCIERGE || isInConciergeTaskView); - const onLinkPress = useMemo(() => { if (internalNewExpensifyPath || internalExpensifyPath) { return () => openLink(attrHref, environmentURL, isAttachment); } - if (isTourTask && !hasSeenTour) { - return () => { - openExternalLink(attrHref); - setSelfTourViewed(isAnonymousUser()); - if (viewTourTaskReport && canModifyViewTourTask && canActionViewTourTask) { - completeTask(viewTourTaskReport); - } - }; - } - return undefined; - }, [internalNewExpensifyPath, internalExpensifyPath, attrHref, environmentURL, isAttachment, isTourTask, hasSeenTour, viewTourTaskReport, canModifyViewTourTask, canActionViewTourTask]); + }, [internalNewExpensifyPath, internalExpensifyPath, attrHref, environmentURL, isAttachment]); if (!HTMLEngineUtils.isChildOfComment(tnode) && !isChildOfTaskTitle) { // This is not a comment from a chat, the AnchorForCommentsOnly uses a Pressable to create a context menu on right click. diff --git a/src/components/TestDrive/TestDriveDemo.tsx b/src/components/TestDrive/TestDriveDemo.tsx index 1ea27b4fd1c9..45171c8413c2 100644 --- a/src/components/TestDrive/TestDriveDemo.tsx +++ b/src/components/TestDrive/TestDriveDemo.tsx @@ -64,7 +64,7 @@ function TestDriveDemo() { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index fd3786c4c663..2b9225e4d230 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -211,7 +211,6 @@ import { } from './ReportActionsUtils'; import type {LastVisibleMessage} from './ReportActionsUtils'; import {shouldRestrictUserBillableActions} from './SubscriptionUtils'; -import {getNavatticURL} from './TourUtils'; import { getAttendees, getBillable, @@ -10160,6 +10159,16 @@ function prepareOnboardingOnyxData( const firstAdminPolicy = getActivePolicies(allPolicies, currentUserEmail).find( (policy) => policy.type !== CONST.POLICY.TYPE.PERSONAL && getPolicyRole(policy, currentUserEmail) === CONST.POLICY.ROLE.ADMIN, ); + + let testDriveURL: string; + if (([CONST.ONBOARDING_CHOICES.MANAGE_TEAM, CONST.ONBOARDING_CHOICES.TEST_DRIVE_RECEIVER, CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE] as OnboardingPurpose[]).includes(engagementChoice)) { + testDriveURL = ROUTES.TEST_DRIVE_DEMO_ROOT; + } else if (introSelected?.choice === CONST.ONBOARDING_CHOICES.SUBMIT && introSelected.inviteType === CONST.ONBOARDING_INVITE_TYPES.WORKSPACE) { + testDriveURL = ROUTES.TEST_DRIVE_DEMO_ROOT; + } else { + testDriveURL = ROUTES.TEST_DRIVE_MODAL_ROOT.route; + } + const onboardingTaskParams: OnboardingTaskLinks = { integrationName, onboardingCompanySize: companySize ?? onboardingCompanySize, @@ -10168,12 +10177,7 @@ function prepareOnboardingOnyxData( workspaceMembersLink: `${environmentURL}/${ROUTES.WORKSPACE_MEMBERS.getRoute(onboardingPolicyID)}`, workspaceMoreFeaturesLink: `${environmentURL}/${ROUTES.WORKSPACE_MORE_FEATURES.getRoute(onboardingPolicyID)}`, workspaceConfirmationLink: `${environmentURL}/${ROUTES.WORKSPACE_CONFIRMATION.getRoute(ROUTES.WORKSPACES_LIST.route)}`, - navatticURL: getNavatticURL(environment, engagementChoice), - testDriveURL: `${environmentURL}/${ - ([CONST.ONBOARDING_CHOICES.MANAGE_TEAM, CONST.ONBOARDING_CHOICES.TEST_DRIVE_RECEIVER, CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE] as OnboardingPurpose[]).includes(engagementChoice) - ? ROUTES.TEST_DRIVE_DEMO_ROOT - : ROUTES.TEST_DRIVE_MODAL_ROOT.route - }`, + testDriveURL: `${environmentURL}/${testDriveURL}`, workspaceAccountingLink: `${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(onboardingPolicyID)}`, corporateCardLink: `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(onboardingPolicyID)}`, }; diff --git a/src/libs/TourUtils.ts b/src/libs/TourUtils.ts index 5a873e2e5fc7..1fcf3a6683ee 100644 --- a/src/libs/TourUtils.ts +++ b/src/libs/TourUtils.ts @@ -1,19 +1,17 @@ -import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; -import type {OnboardingPurpose} from './actions/Welcome/OnboardingFlow'; +import type {IntroSelected} from './actions/Report'; -function getNavatticURL(environment: ValueOf, introSelected?: OnboardingPurpose) { - const adminTourURL = environment === CONST.ENVIRONMENT.PRODUCTION ? CONST.NAVATTIC.ADMIN_TOUR_PRODUCTION : CONST.NAVATTIC.ADMIN_TOUR_STAGING; - const employeeTourURL = environment === CONST.ENVIRONMENT.PRODUCTION ? CONST.NAVATTIC.EMPLOYEE_TOUR_PRODUCTION : CONST.NAVATTIC.EMPLOYEE_TOUR_STAGING; - return introSelected === CONST.SELECTABLE_ONBOARDING_CHOICES.MANAGE_TEAM ? adminTourURL : employeeTourURL; -} +function getTestDriveURL(shouldUseNarrowLayout: boolean, introSelected?: IntroSelected) { + if (introSelected?.choice === CONST.ONBOARDING_CHOICES.SUBMIT && introSelected.inviteType === CONST.ONBOARDING_INVITE_TYPES.WORKSPACE) { + return shouldUseNarrowLayout ? CONST.STORYLANE.EMPLOYEE_TOUR_MOBILE : CONST.STORYLANE.EMPLOYEE_TOUR; + } -function getTestDriveURL(shouldUseNarrowLayout: boolean, introSelected?: OnboardingPurpose) { - if (shouldUseNarrowLayout) { - return introSelected === CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE ? CONST.STORYLANE.TRACK_WORKSPACE_TOUR_MOBILE : CONST.STORYLANE.ADMIN_TOUR_MOBILE; + if (introSelected?.choice === CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE) { + return shouldUseNarrowLayout ? CONST.STORYLANE.TRACK_WORKSPACE_TOUR_MOBILE : CONST.STORYLANE.TRACK_WORKSPACE_TOUR; } - return introSelected === CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE ? CONST.STORYLANE.TRACK_WORKSPACE_TOUR : CONST.STORYLANE.ADMIN_TOUR; + return shouldUseNarrowLayout ? CONST.STORYLANE.ADMIN_TOUR_MOBILE : CONST.STORYLANE.ADMIN_TOUR; } -export {getNavatticURL, getTestDriveURL}; +// eslint-disable-next-line import/prefer-default-export +export {getTestDriveURL}; diff --git a/src/libs/actions/Welcome/OnboardingFlow.ts b/src/libs/actions/Welcome/OnboardingFlow.ts index 0ce301d7cba0..e4916f9d43bb 100644 --- a/src/libs/actions/Welcome/OnboardingFlow.ts +++ b/src/libs/actions/Welcome/OnboardingFlow.ts @@ -56,7 +56,6 @@ type OnboardingTaskLinks = Partial<{ workspaceMembersLink: string; workspaceAccountingLink: string; workspaceConfirmationLink: string; - navatticURL: string; testDriveURL: string; corporateCardLink: string; }>; diff --git a/src/pages/ConciergePage.tsx b/src/pages/ConciergePage.tsx index 45dc8d71a22d..7233d3fc6d66 100644 --- a/src/pages/ConciergePage.tsx +++ b/src/pages/ConciergePage.tsx @@ -1,4 +1,4 @@ -import {useFocusEffect, useRoute} from '@react-navigation/native'; +import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useEffect, useRef} from 'react'; import {View} from 'react-native'; import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView'; @@ -8,9 +8,7 @@ import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {confirmReadyToOpenApp} from '@libs/actions/App'; import {navigateToConciergeChat} from '@libs/actions/Report'; -import {completeTask} from '@libs/actions/Task'; import Navigation from '@libs/Navigation/Navigation'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -24,11 +22,6 @@ function ConciergePage() { const isUnmounted = useRef(false); const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false}); const [isLoadingReportData = true] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {canBeMissing: true}); - const route = useRoute(); - - const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); - const viewTourTaskReportID = introSelected?.viewTour; - const [viewTourTaskReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${viewTourTaskReportID}`, {canBeMissing: true}); useFocusEffect( useCallback(() => { @@ -39,21 +32,12 @@ function ConciergePage() { return; } - // Mark the viewTourTask as complete if we are redirected to Concierge after finishing the Navattic tour - const {navattic} = (route.params as {navattic?: string}) ?? {}; - if (navattic === CONST.NAVATTIC.COMPLETED) { - if (viewTourTaskReport) { - if (viewTourTaskReport.stateNum !== CONST.REPORT.STATE_NUM.APPROVED || viewTourTaskReport.statusNum !== CONST.REPORT.STATUS_NUM.APPROVED) { - completeTask(viewTourTaskReport); - } - } - } navigateToConciergeChat(true, () => !isUnmounted.current); }); } else { Navigation.navigate(ROUTES.HOME); } - }, [session, isLoadingReportData, route.params, viewTourTaskReport]), + }, [session, isLoadingReportData]), ); useEffect(() => { diff --git a/src/pages/Search/EmptySearchView.tsx b/src/pages/Search/EmptySearchView.tsx index da735dba6e7b..8bbd613ed8ff 100644 --- a/src/pages/Search/EmptySearchView.tsx +++ b/src/pages/Search/EmptySearchView.tsx @@ -209,7 +209,8 @@ function EmptySearchView({hash, type, groupBy, hasResults}: EmptySearchViewProps if ( introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM || introSelected?.choice === CONST.ONBOARDING_CHOICES.TEST_DRIVE_RECEIVER || - introSelected?.choice === CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE + introSelected?.choice === CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE || + (introSelected?.choice === CONST.ONBOARDING_CHOICES.SUBMIT && introSelected.inviteType === CONST.ONBOARDING_INVITE_TYPES.WORKSPACE) ) { completeTestDriveTask(viewTourReport, viewTourReportID); Navigation.navigate(ROUTES.TEST_DRIVE_DEMO_ROOT); @@ -379,6 +380,7 @@ function EmptySearchView({hash, type, groupBy, hasResults}: EmptySearchViewProps styles.textAlignLeft, styles.tripEmptyStateLottieWebView, introSelected?.choice, + introSelected?.inviteType, hasResults, defaultViewItemHeader, hasSeenTour, diff --git a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx index 77466c605893..37a557b77219 100644 --- a/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/FloatingActionButtonAndPopover.tsx @@ -512,7 +512,8 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, isT if ( introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM || introSelected?.choice === CONST.ONBOARDING_CHOICES.TEST_DRIVE_RECEIVER || - introSelected?.choice === CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE + introSelected?.choice === CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE || + (introSelected?.choice === CONST.ONBOARDING_CHOICES.SUBMIT && introSelected.inviteType === CONST.ONBOARDING_INVITE_TYPES.WORKSPACE) ) { completeTestDriveTask(viewTourReport, viewTourReportID, isAnonymousUser()); Navigation.navigate(ROUTES.TEST_DRIVE_DEMO_ROOT); diff --git a/src/types/onyx/Onboarding.ts b/src/types/onyx/Onboarding.ts index ef8f748797cf..5faed3345f57 100644 --- a/src/types/onyx/Onboarding.ts +++ b/src/types/onyx/Onboarding.ts @@ -12,7 +12,7 @@ type Onboarding = { /** A string that informs which qualifier the user selected during sign up */ signupQualifier: ValueOf; - /** A Boolean that tells whether the user has seen navattic tour */ + /** A Boolean that tells whether the user has seen Storylane tour */ selfTourViewed?: boolean; /** A Boolean that tells whether the user should be redirected to OD after merging work email */ diff --git a/tests/unit/TourUtilsTest.ts b/tests/unit/TourUtilsTest.ts index 77a5cc549c3f..47739ecc41d7 100644 --- a/tests/unit/TourUtilsTest.ts +++ b/tests/unit/TourUtilsTest.ts @@ -3,21 +3,35 @@ import CONST from '@src/CONST'; describe('TourUtils', () => { describe('getTestDriveURL', () => { + describe('Invited employee', () => { + it('returns proper URL when screen is narrow', () => { + const url = getTestDriveURL(true, {choice: CONST.ONBOARDING_CHOICES.SUBMIT, inviteType: CONST.ONBOARDING_INVITE_TYPES.WORKSPACE}); + + expect(url).toBe(CONST.STORYLANE.EMPLOYEE_TOUR_MOBILE); + }); + + it('returns proper URL when screen is not narrow', () => { + const url = getTestDriveURL(false, {choice: CONST.ONBOARDING_CHOICES.SUBMIT, inviteType: CONST.ONBOARDING_INVITE_TYPES.WORKSPACE}); + + expect(url).toBe(CONST.STORYLANE.EMPLOYEE_TOUR); + }); + }); + describe('Intro selected is Track Workspace', () => { it('returns proper URL when screen is narrow', () => { - const url = getTestDriveURL(true, CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE); + const url = getTestDriveURL(true, {choice: CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE}); expect(url).toBe(CONST.STORYLANE.TRACK_WORKSPACE_TOUR_MOBILE); }); it('returns proper URL when screen is not narrow', () => { - const url = getTestDriveURL(false, CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE); + const url = getTestDriveURL(false, {choice: CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE}); expect(url).toBe(CONST.STORYLANE.TRACK_WORKSPACE_TOUR); }); }); - describe('Intro selected is Track Workspace', () => { + describe('Default case - Admin tour', () => { it('returns proper URL when screen is narrow', () => { const url = getTestDriveURL(true);