App icon indicating copy to clipboard operation
App copied to clipboard

[$500] iOS - The concierge task is not auto-completed after completing Test drive via Reports tab

Open nlemma opened this issue 1 month ago β€’ 32 comments

If you haven’t already, check out our contributing guidelines for onboarding and email [email protected] to request to join our Slack channel!


Version Number: 9.2.71-5 Reproducible in staging?: Yes Reproducible in production?: Yes If this was caught during regression testing, add the test name, ID and link from BrowserStack: https://github.com/Expensify/Expensify/issues/536068 Email or phone of affected tester (no customers): N/A Issue reported by: Applause Internal Team Device used: Apple iPhone 13 - iOS 18.6.2 App Component: Other

Action Performed:

Prerequisite: Invite a brand new account to a workspace

  1. Open the app
  2. Sign-in with the invited user
  3. Go to the Reports tab > Expenses.
  4. Tap the "Take a test drive" button
  5. Finish Test Drive
  6. Tap Get Started
  7. Navigate to the Concierge chat

Expected Result:

The Test Drive task in the Concierge chat should be completed

Actual Result:

The concierge task is not auto-complete after the invited employee completes the Test drive via Reports tab

Workaround:

Unknown

Platforms:

  • [ ] Android: App
  • [ ] Android: mWeb Chrome
  • [x] iOS: App
  • [ ] iOS: mWeb Safari
  • [ ] iOS: mWeb Chrome
  • [ ] Windows: Chrome
  • [ ] MacOS: Chrome / Safari
  • [ ] MacOS: Desktop

Screenshots/Videos

https://github.com/user-attachments/assets/a38d8c66-7088-4dcc-b26f-35376542187d

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021996584871222053906
  • Upwork Job ID: 1996584871222053906
  • Last Price Increase: 2025-12-25
Issue OwnerCurrent Issue Owner: @shubham1206agra

nlemma avatar Dec 04 '25 12:12 nlemma

Triggered auto assignment to @trjExpensify (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

melvin-bot[bot] avatar Dec 04 '25 12:12 melvin-bot[bot]

Job added to Upwork: https://www.upwork.com/jobs/~021996584871222053906

melvin-bot[bot] avatar Dec 04 '25 14:12 melvin-bot[bot]

Triggered auto assignment to Contributor-plus team member for initial proposal review - @shubham1206agra (External)

melvin-bot[bot] avatar Dec 04 '25 14:12 melvin-bot[bot]

🚨 Edited by proposal-police: This proposal was edited at 2025-12-18 13:31:55 UTC.

Proposal

Please re-state the problem that we are trying to solve in this issue.

The concierge task is not completed after completing test drive via Reports tab.

What is the root cause of that problem?

The Onyx optimistic onboarding data is coupled with the openReport function. This means, if openReport never executes, or it executes before it's supposed to (before OpenApp), the optimistic onboarding data, and therefore, its tasks (which are fully optimistic), are never created.

Currently, there are two scenarios where this happens: Scenario 1 (this issue):

  1. Invite a brand new account (A) through the workspace.
  2. Log into Account A manually and open the app in a narrow screen (no report is opened).
  3. Go straight to Reports > Expenses > "Take a test drive".
  4. Finish the task and go to Concierge.
  5. Notice the task in Concierge is not crossed off.

Scenario 2:

  1. Invite a brand new account through the workspace.
  2. Click on the "Get started" link in the invite email (either narrow or wide screen is fine).
  3. Go to Reports > Expenses > "Take a test drive".
  4. Finish the task and go to Concierge.
  5. Notice the task in Concierge is not crossed off.

Scenario 1 happens because openReport is never executed. Scenario 2 happens because OpenReport executes before OpenApp which makes the introSelected variable undefined, which means the onboarding Onyx data is also never created.

All of this means this doesn't ever get executed, which is why we have to decouple it from openReport: https://github.com/Expensify/App/blob/4625fac8c6f6e0f1109fc4b103ed28791551da9a/src/libs/actions/Report.ts#L1190-L1194

What changes do you think we should make in order to solve the problem?

Since this is a very specific edge case, I think it's a good complexity<->correctness tradeoff to simply open the Concierge report if we do end up in this situation.

  1. Remove these lines from openReport: https://github.com/Expensify/App/blob/549b140c93d50ab1cfaaa7b8f20db36dd74e088b/src/libs/actions/Report.ts#L1160-L1204

  2. Create and export this function in src/libs/actions/Report.ts:

function openOnboardingDataOnce(conciergeReportID: string, introSelected: IntroSelected) {
    const optimisticData: OnyxUpdate[] = [];
    const successData: OnyxUpdate[] = [];
    const failureData: OnyxUpdate[] = [];
    const finallyData: OnyxUpdate[] = [];
    const parameters: OpenReportParams = {
        reportID: conciergeReportID
    };

    const isInviteOnboardingComplete = introSelected?.isInviteOnboardingComplete ?? false;
    const isOnboardingCompleted = onboarding?.hasCompletedGuidedSetupFlow ?? false;

    // Some cases we can have two open report requests with guide setup data because isInviteOnboardingComplete is not updated completely.
    // Then we need to check the list request and prevent the guided setup data from being duplicated.
    const allPersistedRequests = getAll();
    const hasOpenReportWithGuidedSetupData = allPersistedRequests.some((request) => request.command === WRITE_COMMANDS.OPEN_REPORT && request.data?.guidedSetupData);

    // Prepare guided setup data only when nvp_introSelected is set and onboarding is not completed
    // OldDot users will never have nvp_introSelected set, so they will not see guided setup messages
    if (!isOnboardingCompleted && !isInviteOnboardingComplete && !hasOpenReportWithGuidedSetupData) {
        const {choice, inviteType} = introSelected;
        const isInviteIOUorInvoice = inviteType === CONST.ONBOARDING_INVITE_TYPES.IOU || inviteType === CONST.ONBOARDING_INVITE_TYPES.INVOICE;
        const isInviteChoiceCorrect = choice === CONST.ONBOARDING_CHOICES.ADMIN || choice === CONST.ONBOARDING_CHOICES.SUBMIT || choice === CONST.ONBOARDING_CHOICES.CHAT_SPLIT;

        if (isInviteChoiceCorrect && !isInviteIOUorInvoice) {
            const onboardingMessage = getOnboardingMessages().onboardingMessages[choice];
            if (choice === CONST.ONBOARDING_CHOICES.CHAT_SPLIT) {
                const updatedTasks = onboardingMessage.tasks.map((task) => (task.type === 'startChat' ? {...task, autoCompleted: true} : task));
                onboardingMessage.tasks = updatedTasks;
            }

            const onboardingData = prepareOnboardingOnyxData({
                introSelected,
                engagementChoice: choice,
                onboardingMessage,
            });

            if (onboardingData) {
                optimisticData.push(...onboardingData.optimisticData, {
                    onyxMethod: Onyx.METHOD.MERGE,
                    key: ONYXKEYS.NVP_INTRO_SELECTED,
                    value: {
                        isInviteOnboardingComplete: true,
                    },
                });

                successData.push(...onboardingData.successData);

                failureData.push(...onboardingData.failureData);

                parameters.guidedSetupData = JSON.stringify(onboardingData.guidedSetupData);
            }
        }
    }

    API.write(WRITE_COMMANDS.OPEN_REPORT, parameters, {optimisticData, successData, failureData, finallyData});
}
  1. In src/libs/Navigation/AppNavigator/AuthScreens listen for the Concierge report ID:
    const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true});
  1. And then add this useEffect:
    useEffect(() => {
        if (!conciergeReportID || !introSelected) {
            return;
        }
        Report.openOnboardingDataOnce(conciergeReportID, introSelected);
    }, [conciergeReportID, introSelected]);

What alternative solutions did you explore? (Optional)

We can also work with the BE to create a new API such as OPEN_ONBOARDING_DATA, however tasks are still supposed to be fully optimistic so I'm not sure this is the best idea.

Result:

Scenario 1:

https://github.com/user-attachments/assets/5ba2d6fc-efdb-4cf2-9755-982cd59e0f42

Scenario 2:

https://github.com/user-attachments/assets/4a75e62e-3195-4d8b-a474-af652e4e5159

LorenzoBloedow avatar Dec 06 '25 18:12 LorenzoBloedow

@trjExpensify This is a BE issue as complete data is not sent via OpenApp.

shubham1206agra avatar Dec 07 '25 01:12 shubham1206agra

It's because we're opening the app for the first time, not opening any reports, and going straight to the test drive via Reports tab. This means this line which makes sure we (optimistically) have the onboarding data (including the test drive task) never has the chance to execute:

@LorenzoBloedow are you saying that this only happens when..

  1. A user is invited to the workspace
  2. They receive an invite email (which links to their workspace chat)
  3. They don't click on that link
  4. They go to new.expensify.com natively
  5. They sign-in
  6. They go to Reports
  7. They click on the TestDrive and complete it

Whereas, if they followed this path below it would have auto-completed the task... ?

  1. A user is invited to the workspace
  2. They receive an invite email (which links to their workspace chat)
  3. They click on that link
  4. They get navigated to the workspace chat
  5. They go to the Reports page
  6. They click on the TestDrive and complete it
  7. The task auto-completes

trjExpensify avatar Dec 10 '25 01:12 trjExpensify

@trjExpensify No, it happens when a user goes straight to the test drive without manually opening any reports.

It seems when we go through the second flow you described, we aren't calling openReport, so the same thing still happens even though the report technically opened (just probably not through openReport):

https://github.com/user-attachments/assets/6e50646f-57ab-4e7a-af6a-09dffac087d7

(Notice it does auto complete after I open the Concierge report and try again ^)

When we do manually open the report after leaving the one we landed on through the link, the task does auto-complete:

https://github.com/user-attachments/assets/0433940f-ee60-41ed-bd2b-a2d63dbdef61

So basically the task does not auto-complete in both cases you described.

LorenzoBloedow avatar Dec 10 '25 10:12 LorenzoBloedow

It seems when we go through the second flow you described, we aren't calling openReport, so the same thing still happens even though the report technically opened (just probably not through openReport)

Super interesting...

Why wouldn't a deep link to a chat/report via a URL call OpenReport? πŸ€” CC: @mountiny

trjExpensify avatar Dec 10 '25 11:12 trjExpensify

Either it's not calling openReport or for some other reason not triggering this section of openReport:

https://github.com/Expensify/App/blob/2bc400a5b780d76de1a589bfe44163928c35fc6d/src/libs/actions/Report.ts#L1170-L1204

LorenzoBloedow avatar Dec 10 '25 14:12 LorenzoBloedow

πŸ“£ It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? πŸ’Έ

melvin-bot[bot] avatar Dec 11 '25 16:12 melvin-bot[bot]

yeah, but which one is it?

This is an invited workspace member, so maybe there's some clues looking at the workspace inviteType.

trjExpensify avatar Dec 15 '25 12:12 trjExpensify

@trjExpensify Just confirmed it. In the deep link flow, openReport does execute, but introSelected is undefined: https://github.com/Expensify/App/blob/2bc400a5b780d76de1a589bfe44163928c35fc6d/src/libs/actions/Report.ts#L1170

LorenzoBloedow avatar Dec 15 '25 12:12 LorenzoBloedow

Any idea why? Like, did the user account have introSelected.inviteType: workspace set on account creation when invited or not?

trjExpensify avatar Dec 15 '25 13:12 trjExpensify

@trjExpensify Because OpenReport gets called before OpenApp in that flow:

Image

I made a fix that makes it so that we have to wait for OpenApp before calling OpenReport, here's how it looks:

https://github.com/user-attachments/assets/a9f5d0b0-66e7-4a25-a34a-b3b3f97464da

LorenzoBloedow avatar Dec 15 '25 15:12 LorenzoBloedow

@shubham1206agra 8 days overdue is a lot. Should this be a Weekly issue? If so, feel free to change it!

melvin-bot[bot] avatar Dec 17 '25 00:12 melvin-bot[bot]

Mhm, something doesn't look right there. The skeleton report swipes left/right and going back you have to do twice.

@shubham1206agra can you weigh in here, please?

trjExpensify avatar Dec 17 '25 00:12 trjExpensify

@trjExpensify Not sure what is happening here. Should we consult with someone from the expert team for assistance?

shubham1206agra avatar Dec 17 '25 09:12 shubham1206agra

@mountiny reckon we can put it in #quality then? Doesn't seem like we can figure out why OpenReport is being called before OpenApp, and when Lorenzo does that we get this weird outcome: https://github.com/Expensify/App/issues/76714#issuecomment-3656261904

trjExpensify avatar Dec 17 '25 19:12 trjExpensify

@trjExpensify We get the weird UI issue either way, take a look at the first and second video before that fix: https://github.com/Expensify/App/issues/76714#issuecomment-3636465326

So there's definitely something going on but I think it's 3 separate issues:

  • This one (BE related).
  • The other one we identified where we go through the deep link flow and OpenReport gets called before OpenApp.
  • The weird UI flicker where the navigation animation happens twice through the deep link flow.

LorenzoBloedow avatar Dec 17 '25 20:12 LorenzoBloedow

Added to quality as LOW, I think we can bump the price here as I feel like we should be able to have clearer RCA from external contributor on this one before we put hourly workers on this

mountiny avatar Dec 18 '25 10:12 mountiny

Upwork job price has been updated to $500

melvin-bot[bot] avatar Dec 18 '25 10:12 melvin-bot[bot]

Upwork job price has been updated to $500

melvin-bot[bot] avatar Dec 18 '25 10:12 melvin-bot[bot]

@shubham1206agra Just updated my proposal, please let me know what you think.

@mountiny Would also like to know if you think the RCA is clearer now.

Thanks πŸ™‚

LorenzoBloedow avatar Dec 18 '25 13:12 LorenzoBloedow

I am not sure - it still looks like OpenReport comes before OpenApp, that should not happen and that is fully FE issue

mountiny avatar Dec 18 '25 17:12 mountiny

it still looks like OpenReport comes before OpenApp, that should not happen

Since there isn't a clear sequence of OpenApp > OpenReport it would make sense that something like this would happen when we use this promise:

https://github.com/Expensify/App/blob/2e4ad09929ca90236bf7765507ee1eea3aefdc5f/src/libs/actions/App.ts#L249-L265

To wait before calling openApp:

https://github.com/Expensify/App/blob/2e4ad09929ca90236bf7765507ee1eea3aefdc5f/src/libs/actions/App.ts#L363-L368

So I don't understand what the problem with this other approach is.

I can edit my proposal to combine its current decoupling with the "wait for openApp before calling openReport" approach. I could implement it by forcing the SequentialQueue to put all OpenApp requests before any OpenReport, or simply waiting with a promise like I did before.

@mountiny Does that sound like something we'd want?

LorenzoBloedow avatar Dec 18 '25 19:12 LorenzoBloedow

@trjExpensify @shubham1206agra this issue was created 2 weeks ago. Are we close to approving a proposal? If not, what's blocking us from getting this issue assigned? Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!

melvin-bot[bot] avatar Dec 18 '25 21:12 melvin-bot[bot]

🚨 Edited by proposal-police: This proposal was edited at 2025-12-19 12:08:06 UTC.

Proposal

Please re-state the problem that we are trying to solve in this issue.

The concierge task is not updated after completing test drive task

What is the root cause of that problem?

The task is not completed because of:

https://github.com/Expensify/App/blob/d8f0f267fb43a24ac93c04e1dfd335c959a1ae91/src/components/TestDrive/TestDriveDemo.tsx#L49-L53

The viewTourTaskReport value is undefined. The data (taskName) is stored in nvp_introSelected.

For this user (invited to a workspace, using a narrow screen (i.e., a mobile phone), and logging in via magic code), nvp_introSelected is not updated correctly. It currently contains:

{
  choice: 'newDotSubmit',
  inviteType: 'workspace',
  isInviteOnboardingComplete: false,
}

The task report data for the demo is not available because the onboarding flow is never displayed. As a result, the user does not complete onboarding, the CompleteGuidedSetup API call is never triggered, and nvp_introSelected is not updated.

The onboarding does not appear because the backend returns an empty object for nvp_onboarding. This is interpreted as onboarding being completed in the following locations:

https://github.com/Expensify/App/blob/bd763a95fdf9354fc83950b4af0a36fc0efd7326/src/selectors/Onboarding.ts#L13-L16

and in here:

https://github.com/Expensify/App/blob/bd763a95fdf9354fc83950b4af0a36fc0efd7326/src/libs/actions/Welcome/index.ts#L53-L56

So the onboarding flow won't show up


The nvp_introSelected is also updated when the user triggers OpenReport. That is why this issue does not occur on wide screens (the report screen is displayed, which triggers OpenReport).

For users on a narrow layout who log in via a workspace invitation link from email, there is an exitTo parameter that navigates the app to the workspace report after login:

https://github.com/Expensify/App/blob/6fe7e2768a94be21824ce76e8e711300f63f00e9/src/pages/ValidateLoginPage/index.tsx#L30

but because of the openReportFromDeepLink is called too early before signInWithValidateCode response arrive (which contains nvp_introSelected) this will make :

https://github.com/Expensify/App/blob/df5234bd3120bd81727c7e764f5af88dc878348d/src/libs/actions/Report.ts#L1163

intro selected is undefined and guidedSetupData is not embedded in the OpenReport request. So the nvp_introSelected is not updated properly.

What changes do you think we should make in order to solve the problem?

>) If the expected behavior is to display onboarding for this user, then we must also check the inviteType and isInviteOnboardingComplete fields in nvp_introSelected here:

https://github.com/Expensify/App/blob/bd763a95fdf9354fc83950b4af0a36fc0efd7326/src/selectors/Onboarding.ts#L13-L16

and in here:

https://github.com/Expensify/App/blob/bd763a95fdf9354fc83950b4af0a36fc0efd7326/src/libs/actions/Welcome/index.ts#L53-L56

Then after onboarding finish we navigate to the invited workspace chat


>) If the expected behavior is to open the workspace chat where the user was invited, we could call OpenReport of the workspace chat in isOnboardingFlowCompleted, or utilizing onServerDataReady of Welcome or in useOnboardingFlow or other place where we are sure the server data is ready, because we need the isInviteOnboardingComplete: false value in nvp_introSelected value for OpenReport to pass the GuidedSetupData to back end.

Result (if we want to open the invited workspace chat:)

https://github.com/user-attachments/assets/baca5f57-5913-4394-be7f-0e4fbabd1292

What alternative solutions did you explore? (Optional)

Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.

CompleteGuidedSetupParams

tsa321 avatar Dec 19 '25 11:12 tsa321

@shubham1206agra can you please review the proposals πŸ™Œ

mountiny avatar Dec 19 '25 12:12 mountiny

@mountiny Do we need to show Onboarding flow for invited users?

shubham1206agra avatar Dec 20 '25 05:12 shubham1206agra

I am not sure what is the structure around no actually, @danielrvidal @trjExpensify @Expensify/design might be able to help better with this one

mountiny avatar Dec 21 '25 22:12 mountiny