Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions src/DeepLinkHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {getReportIDFromLink} from './libs/ReportUtils';
import {endSpan} from './libs/telemetry/activeSpans';
import {hasSecureLinkKey} from './libs/Url';
import ONYXKEYS from './ONYXKEYS';
import {hasSeenTourSelector} from './selectors/Onboarding';
import {guidedSetupAndTourStatusSelector} from './selectors/Onboarding';
import isLoadingOnyxValue from './types/utils/isLoadingOnyxValue';

type DeepLinkHandlerProps = {
Expand All @@ -40,7 +40,7 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) {
const [session, sessionMetadata] = useOnyx(ONYXKEYS.SESSION);
const [conciergeReportID, conciergeReportIDMetadata] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const [introSelected, introSelectedMetadata] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [isSelfTourViewed, isSelfTourViewedMetadata] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
const [guidedSetupAndTourStatus, guidedSetupAndTourStatusMetadata] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: guidedSetupAndTourStatusSelector});
const [betas, betasMetadata] = useOnyx(ONYXKEYS.BETAS);
const isAuthenticated = useIsAuthenticated();

Expand All @@ -56,7 +56,7 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) {
}, []);

useEffect(() => {
if (isLoadingOnyxValue(allReportsMetadata, sessionMetadata, conciergeReportIDMetadata, introSelectedMetadata, isSelfTourViewedMetadata, betasMetadata)) {
if (isLoadingOnyxValue(allReportsMetadata, sessionMetadata, conciergeReportIDMetadata, introSelectedMetadata, guidedSetupAndTourStatusMetadata, betasMetadata)) {
return;
}

Expand Down Expand Up @@ -110,7 +110,7 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) {
isCurrentlyAuthenticated,
conciergeReportID,
introSelected,
isSelfTourViewed,
guidedSetupAndTourStatus?.isSelfTourViewed,
betas,
session?.accountID ?? CONST.DEFAULT_NUMBER_ID,
);
Expand Down Expand Up @@ -147,7 +147,16 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) {
if (hasSecureLinkKey(state.url)) {
onInitialUrl(state.url as Route);
}
openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, isSelfTourViewed, betas, session?.accountID ?? CONST.DEFAULT_NUMBER_ID);
openReportFromDeepLink(
state.url,
allReports,
isCurrentlyAuthenticated,
conciergeReportID,
introSelected,
guidedSetupAndTourStatus?.isSelfTourViewed,
betas,
session?.accountID ?? CONST.DEFAULT_NUMBER_ID,
);
trackPendingPublicRoomFromDeepLink(state.url, isCurrentlyAuthenticated);
});

Expand All @@ -165,7 +174,7 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) {
sessionMetadata.status,
conciergeReportIDMetadata.status,
introSelectedMetadata.status,
isSelfTourViewedMetadata.status,
guidedSetupAndTourStatusMetadata.status,
betasMetadata.status,
]);

Expand Down Expand Up @@ -200,8 +209,16 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) {
return;
}
hasRefetchedPublicRoom.current = true;
Report.openReport({reportID, introSelected, betas, hasReportActions: false, currentUserAccountID: session?.accountID ?? CONST.DEFAULT_NUMBER_ID});
}, [isLoadingApp, allReports, introSelected, betas, session?.accountID]);
Report.openReport({
reportID,
introSelected,
betas,
hasReportActions: false,
currentUserAccountID: session?.accountID ?? CONST.DEFAULT_NUMBER_ID,
isSelfTourViewed: guidedSetupAndTourStatus?.isSelfTourViewed,
hasCompletedGuidedSetupFlow: guidedSetupAndTourStatus?.hasCompletedGuidedSetupFlow,
});
}, [isLoadingApp, allReports, introSelected, betas, session?.accountID, guidedSetupAndTourStatus?.isSelfTourViewed, guidedSetupAndTourStatus?.hasCompletedGuidedSetupFlow]);

return null;
}
Expand Down
13 changes: 7 additions & 6 deletions src/components/TestDrive/TestDriveDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';

import {delegateEmailSelector} from '@selectors/Account';
import {hasSeenTourSelector} from '@selectors/Onboarding';
import {guidedSetupAndTourStatusSelector} from '@selectors/Onboarding';
import React, {useCallback, useEffect, useRef, useState} from 'react';

import TestDriveBanner from './TestDriveBanner';
Expand All @@ -54,13 +54,11 @@ function TestDriveDemo() {
const parentReportAction = useParentReportAction(viewTourTaskReport);
const isCurrentUserPolicyAdmin = useIsPaidPolicyAdmin();

const [hasSeenTour = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
selector: hasSeenTourSelector,
});
const [guidedSetupAndTourStatus] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: guidedSetupAndTourStatusSelector});
const hasCalledOpenReportRef = useRef(false);

useEffect(() => {
if (hasSeenTour) {
if (guidedSetupAndTourStatus?.isSelfTourViewed) {
return;
}
if (!viewTourTaskReport) {
Expand All @@ -74,6 +72,8 @@ function TestDriveDemo() {
betas,
hasReportActions: hasConciergeReportActions,
currentUserAccountID: currentUserPersonalDetails.accountID,
isSelfTourViewed: guidedSetupAndTourStatus?.isSelfTourViewed,
hasCompletedGuidedSetupFlow: guidedSetupAndTourStatus?.hasCompletedGuidedSetupFlow,
});
}
return;
Expand All @@ -93,7 +93,8 @@ function TestDriveDemo() {
false,
);
}, [
hasSeenTour,
guidedSetupAndTourStatus?.isSelfTourViewed,
guidedSetupAndTourStatus?.hasCompletedGuidedSetupFlow,
hasConciergeReportActions,
viewTourTaskReport,
viewTourTaskParentReport,
Expand Down
18 changes: 13 additions & 5 deletions src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {ReportAttributesDerivedValue} from '@src/types/onyx';

import {hasSeenTourSelector} from '@selectors/Onboarding';
import {guidedSetupAndTourStatusSelector} from '@selectors/Onboarding';
import {useEffect, useRef} from 'react';

function initializePusher(
Expand All @@ -62,7 +62,7 @@ function initializePusher(
*
* Extracted from AuthScreens to isolate useOnyx subscriptions:
* - SESSION, NVP_INTRO_SELECTED, NVP_ACTIVE_POLICY_ID,
* NVP_ONBOARDING (tour selector), ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT (x2),
* NVP_ONBOARDING (guided-setup and tour status selector), ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT (x2),
* IS_LOADING_APP
*/
function AuthScreensInitHandler() {
Expand All @@ -78,7 +78,7 @@ function AuthScreensInitHandler() {
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [initialLastUpdateIDAppliedToClient] = useOnyx(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT);
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
const [guidedSetupAndTourStatus] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: guidedSetupAndTourStatusSelector});
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const [conciergeChat] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${conciergeReportID}`);
const lastWorkspaceNumber = useLastWorkspaceNumber(ownerEmail ?? undefined);
Expand Down Expand Up @@ -170,7 +170,15 @@ function AuthScreensInitHandler() {
} else if (SessionUtils.didUserLogInDuringSession()) {
const reportID = getReportIDFromLink(initialURL ?? null);
if (reportID && !isAuthenticatedAtStartup) {
Report.openReport({reportID, introSelected, betas, hasReportActions: false, currentUserAccountID: session?.accountID ?? CONST.DEFAULT_NUMBER_ID});
Report.openReport({
reportID,
introSelected,
betas,
hasReportActions: false,
currentUserAccountID: session?.accountID ?? CONST.DEFAULT_NUMBER_ID,
isSelfTourViewed: guidedSetupAndTourStatus?.isSelfTourViewed,
hasCompletedGuidedSetupFlow: guidedSetupAndTourStatus?.hasCompletedGuidedSetupFlow,
});
// Don't want to call `openReport` again when logging out and then logging in
setIsAuthenticatedAtStartup(true);
}
Expand All @@ -185,7 +193,7 @@ function AuthScreensInitHandler() {
introSelected,
currentUserPersonalDetails.localCurrencyCode ?? CONST.CURRENCY.USD,
activePolicy,
isSelfTourViewed,
guidedSetupAndTourStatus?.isSelfTourViewed,
betas,
hasActiveAdminPolicies,
lastWorkspaceNumber,
Expand Down
27 changes: 24 additions & 3 deletions src/pages/inbox/ReportFetchHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import SCREENS from '@src/SCREENS';
import type {Transaction} from '@src/types/onyx';

import {useIsFocused, useNavigation, useRoute} from '@react-navigation/native';
import {guidedSetupAndTourStatusSelector} from '@selectors/Onboarding';
import {useEffect, useEffectEvent, useRef} from 'react';

type ReportScreenRoute =
Expand Down Expand Up @@ -116,6 +117,7 @@ function ReportFetchHandler() {
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [onboarding] = useOnyx(ONYXKEYS.NVP_ONBOARDING);
const [guidedSetupAndTourStatus] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: guidedSetupAndTourStatusSelector});
const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP);
const [isLoadingReportData = true] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA);
const prevIsLoadingReportData = usePrevious(isLoadingReportData);
Expand Down Expand Up @@ -153,6 +155,8 @@ function ReportFetchHandler() {

const isInviteOnboardingComplete = introSelected?.isInviteOnboardingComplete ?? false;
const isOnboardingCompleted = onboarding?.hasCompletedGuidedSetupFlow ?? false;
const isSelfTourViewed = guidedSetupAndTourStatus?.isSelfTourViewed;
const hasCompletedGuidedSetupFlow = guidedSetupAndTourStatus?.hasCompletedGuidedSetupFlow;
const isRegularOnboardingPending = !!introSelected && !introSelected.inviteType && isSupportedInviteOnboardingChoice(introSelected.choice) && !isOnboardingCompleted;
const isPendingInviteOnboarding = isSupportedPendingInviteOnboarding(introSelected);
const onboardingSignal = introSelected ? `${introSelected.choice ?? ''}:${introSelected.inviteType ?? ''}:${isInviteOnboardingComplete ? 'complete' : 'pending'}` : '';
Expand Down Expand Up @@ -199,7 +203,16 @@ function ReportFetchHandler() {
return;
}

openReport({reportID: reportIDFromRoute, introSelected, reportActionID: reportActionIDFromRoute, betas, hasReportActions, currentUserAccountID});
openReport({
reportID: reportIDFromRoute,
introSelected,
reportActionID: reportActionIDFromRoute,
betas,
hasReportActions,
currentUserAccountID,
isSelfTourViewed,
hasCompletedGuidedSetupFlow,
});
});

const createOneTransactionThread = useEffectEvent(() => {
Expand Down Expand Up @@ -232,15 +245,23 @@ function ReportFetchHandler() {
if (!shouldUseNarrowLayout || !isChatThread(report) || !isHiddenForCurrentUser(report) || isTransactionThreadView) {
return;
}
openReport({reportID, introSelected, betas, hasReportActions, currentUserAccountID});
openReport({reportID, introSelected, betas, hasReportActions, currentUserAccountID, isSelfTourViewed, hasCompletedGuidedSetupFlow});
});

const joinPublicRoomIfNeeded = useEffectEvent(() => {
// Return early if the viewing public room is the current report since we will fetch the current report in another function
if (!viewingPublicRoomReportID || viewingPublicRoomReportID === reportIDFromRoute) {
return;
}
openReport({reportID: viewingPublicRoomReportID, introSelected, betas, hasReportActions: hasViewingPublicRoomReportActions, currentUserAccountID});
openReport({
reportID: viewingPublicRoomReportID,
introSelected,
betas,
hasReportActions: hasViewingPublicRoomReportActions,
currentUserAccountID,
isSelfTourViewed,
hasCompletedGuidedSetupFlow,
});
});

// Effect order below matches the original declaration order in ReportScreen.tsx.
Expand Down
35 changes: 35 additions & 0 deletions tests/unit/DeepLinkHandlerTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,41 @@ describe('DeepLinkHandler', () => {
expect(Report.openReport).toHaveBeenCalledWith(expect.objectContaining({reportID: PUBLIC_ROOM_ID}));
});

it('threads onboarding status from NVP_ONBOARDING into the refetch openReport (#66424)', async () => {
await act(async () => {
await Onyx.multiSet({
[ONYXKEYS.SESSION]: {authTokenType: CONST.AUTH_TOKEN_TYPES.ANONYMOUS},
[ONYXKEYS.IS_LOADING_APP]: true,
[ONYXKEYS.CONCIERGE_REPORT_ID]: '',
[ONYXKEYS.NVP_INTRO_SELECTED]: {},
[ONYXKEYS.BETAS]: [],
// Deliberately mismatched flags so the assertion proves each field maps to its own source and they are not swapped.
// selfTourViewed feeds isSelfTourViewed (true), while hasCompletedGuidedSetupFlow passes through unchanged (false).
[ONYXKEYS.NVP_ONBOARDING]: {selfTourViewed: true, hasCompletedGuidedSetupFlow: false},
});
});

Linking.setInitialURL(`new-expensify://r/${PUBLIC_ROOM_ID}`);

render(<DeepLinkHandler onInitialUrl={jest.fn()} />);
await waitForBatchedUpdatesWithAct();

await act(async () => {
await Onyx.merge(ONYXKEYS.IS_LOADING_APP, false);
});
await waitForBatchedUpdatesWithAct();

// The combined NVP_ONBOARDING selector feeds both onboarding flags into openReport so guided-setup optimistic data
// is derived from real Onyx data instead of the deprecated module-level Onyx.connect fallback.
expect(Report.openReport).toHaveBeenCalledWith(
expect.objectContaining({
reportID: PUBLIC_ROOM_ID,
isSelfTourViewed: true,
hasCompletedGuidedSetupFlow: false,
}),
);
});

it('does not refetch when the public room is already present in Onyx', async () => {
await act(async () => {
await Onyx.multiSet({
Expand Down
Loading