Skip to content

Commit be4be08

Browse files
committed
refactor createAndOpenSearchTransactionThread to use isSelfTourViewed and hasCompletedGuidedSetupFlow from useOnyx
1 parent d6a8779 commit be4be08

5 files changed

Lines changed: 133 additions & 81 deletions

File tree

src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import CONST from '@src/CONST';
3333
import ONYXKEYS from '@src/ONYXKEYS';
3434
import ROUTES from '@src/ROUTES';
3535
import {columnsSelector} from '@src/selectors/AdvancedSearchFiltersForm';
36+
import {hasCompletedGuidedSetupFlowSelector, hasSeenTourSelector} from '@src/selectors/Onboarding';
3637
import type * as OnyxTypes from '@src/types/onyx';
3738
import type {TransactionGroupListExpandedProps, TransactionListItemType} from './types';
3839

@@ -69,6 +70,8 @@ function TransactionGroupListExpanded<TItem extends ListItem>({
6970
const [isMobileSelectionModeEnabled] = useOnyx(ONYXKEYS.RAM_ONLY_MOBILE_SELECTION_MODE);
7071
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
7172
const [betas] = useOnyx(ONYXKEYS.BETAS);
73+
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
74+
const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasCompletedGuidedSetupFlowSelector});
7275
const [visibleColumns] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {selector: columnsSelector});
7376
const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION);
7477

@@ -159,15 +162,17 @@ function TransactionGroupListExpanded<TItem extends ListItem>({
159162

160163
const navigateToTransactionThread = () => {
161164
if (!transactionItem?.reportAction?.childReportID) {
162-
createAndOpenSearchTransactionThread(
163-
transactionItem,
165+
createAndOpenSearchTransactionThread({
166+
item: transactionItem,
164167
introSelected,
165168
backTo,
166-
currentUserDetails.email ?? '',
167-
currentUserDetails.accountID,
169+
currentUserLogin: currentUserDetails.email ?? '',
170+
currentUserAccountID: currentUserDetails.accountID,
168171
betas,
169-
transactionItem?.reportAction?.childReportID,
170-
);
172+
isSelfTourViewed,
173+
hasCompletedGuidedSetupFlow,
174+
IOUTransactionID: transactionItem?.reportAction?.childReportID,
175+
});
171176
return;
172177
}
173178
markReportIDAsExpense(reportID);

src/components/Search/SearchStaticList.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import StatusBadge from '@components/StatusBadge';
2424
import TransactionItemRow from '@components/TransactionItemRow';
2525
import {useCurrencyListActions} from '@hooks/useCurrencyList';
2626
import useLocalize from '@hooks/useLocalize';
27+
import useOnyx from '@hooks/useOnyx';
2728
import useStyleUtils from '@hooks/useStyleUtils';
2829
import useTheme from '@hooks/useTheme';
2930
import useThemeStyles from '@hooks/useThemeStyles';
@@ -32,7 +33,9 @@ import Navigation from '@libs/Navigation/Navigation';
3233
import {getReportStatusColorStyle, getReportStatusTranslation, isOneTransactionReport} from '@libs/ReportUtils';
3334
import {createAndOpenSearchTransactionThread, getSections, getSortedSections, getValidGroupBy} from '@libs/SearchUIUtils';
3435
import CONST from '@src/CONST';
36+
import ONYXKEYS from '@src/ONYXKEYS';
3537
import ROUTES from '@src/ROUTES';
38+
import {hasCompletedGuidedSetupFlowSelector, hasSeenTourSelector} from '@src/selectors/Onboarding';
3639
import type {SearchResults} from '@src/types/onyx';
3740
import type {TransactionListItemType} from './SearchList/ListItem/types';
3841
import UserInfoCellsWithArrow from './SearchList/ListItem/UserInfoCellsWithArrow';
@@ -73,6 +76,8 @@ function SearchStaticList({
7376
const session = useSession();
7477
const accountID = session?.accountID ?? CONST.DEFAULT_NUMBER_ID;
7578
const email = session?.email;
79+
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
80+
const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasCompletedGuidedSetupFlowSelector});
7681

7782
const [showPendingExpensePlaceholder, setShowPendingExpensePlaceholder] = useState(
7883
() => hasDeferredWrite(CONST.DEFERRED_LAYOUT_WRITE_KEYS.SEARCH) || Navigation.getIsFullscreenPreInsertedUnderRHP(),
@@ -128,7 +133,18 @@ function SearchStaticList({
128133
// betas and introSelected are passed as undefined to avoid extra Onyx subscriptions in this lightweight placeholder.
129134
// They're only used for guided-setup onboarding data, which is gated behind introSelected/onboarding checks
130135
// that won't apply here - the user has already completed onboarding if they're submitting expenses.
131-
createAndOpenSearchTransactionThread(item, undefined, backTo, email ?? '', accountID, undefined, item.reportAction?.childReportID, undefined, shouldOpenTransactionThread);
136+
createAndOpenSearchTransactionThread({
137+
item,
138+
introSelected: undefined,
139+
backTo,
140+
currentUserLogin: email ?? '',
141+
currentUserAccountID: accountID,
142+
betas: undefined,
143+
isSelfTourViewed,
144+
hasCompletedGuidedSetupFlow,
145+
IOUTransactionID: item.reportAction?.childReportID,
146+
shouldNavigate: shouldOpenTransactionThread,
147+
});
132148
if (shouldOpenTransactionThread) {
133149
return;
134150
}

src/components/Search/index.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
7474
import ROUTES from '@src/ROUTES';
7575
import SCREENS from '@src/SCREENS';
7676
import {columnsSelector} from '@src/selectors/AdvancedSearchFiltersForm';
77+
import {hasCompletedGuidedSetupFlowSelector, hasSeenTourSelector} from '@src/selectors/Onboarding';
7778
import type {OutstandingReportsByPolicyIDDerivedValue, Report, SaveSearch, Transaction} from '@src/types/onyx';
7879
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
7980
import type SearchResults from '@src/types/onyx/SearchResults';
@@ -339,6 +340,8 @@ function Search({
339340
const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION);
340341
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
341342
const [betas] = useOnyx(ONYXKEYS.BETAS);
343+
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
344+
const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasCompletedGuidedSetupFlowSelector});
342345
const previousTransactions = usePrevious(transactions);
343346
const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS);
344347
const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID);
@@ -1141,7 +1144,18 @@ function Search({
11411144
if (isTransactionItem && !item?.reportAction?.childReportID) {
11421145
// If the report is unreported (self DM), we want to open the track expense thread instead of a report with an ID of 0
11431146
const shouldOpenTransactionThread = !isOneTransactionReport(item.report) || item.reportID === CONST.REPORT.UNREPORTED_REPORT_ID;
1144-
createAndOpenSearchTransactionThread(item, introSelected, backTo, email ?? '', accountID, betas, item?.reportAction?.childReportID, undefined, shouldOpenTransactionThread);
1147+
createAndOpenSearchTransactionThread({
1148+
item,
1149+
introSelected,
1150+
backTo,
1151+
currentUserLogin: email ?? '',
1152+
currentUserAccountID: accountID,
1153+
betas,
1154+
isSelfTourViewed,
1155+
hasCompletedGuidedSetupFlow,
1156+
IOUTransactionID: item?.reportAction?.childReportID,
1157+
shouldNavigate: shouldOpenTransactionThread,
1158+
});
11451159
if (shouldOpenTransactionThread) {
11461160
return;
11471161
}
@@ -1188,17 +1202,19 @@ function Search({
11881202
const firstTransaction = item.transactions.at(0);
11891203
if (item.isOneTransactionReport && firstTransaction && transactionPreviewData) {
11901204
if (!firstTransaction?.reportAction?.childReportID) {
1191-
createAndOpenSearchTransactionThread(
1192-
firstTransaction,
1205+
createAndOpenSearchTransactionThread({
1206+
item: firstTransaction,
11931207
introSelected,
11941208
backTo,
1195-
email ?? '',
1196-
accountID,
1209+
currentUserLogin: email ?? '',
1210+
currentUserAccountID: accountID,
11971211
betas,
1198-
firstTransaction?.reportAction?.childReportID,
1212+
isSelfTourViewed,
1213+
hasCompletedGuidedSetupFlow,
1214+
IOUTransactionID: firstTransaction?.reportAction?.childReportID,
11991215
transactionPreviewData,
1200-
false,
1201-
);
1216+
shouldNavigate: false,
1217+
});
12021218
} else {
12031219
setOptimisticDataForTransactionThreadPreview(firstTransaction, transactionPreviewData, firstTransaction?.reportAction?.childReportID);
12041220
}
@@ -1257,6 +1273,8 @@ function Search({
12571273
unmarkReportIDAsMultiTransactionExpense,
12581274
introSelected,
12591275
betas,
1276+
isSelfTourViewed,
1277+
hasCompletedGuidedSetupFlow,
12601278
email,
12611279
accountID,
12621280
queryJSON,

src/libs/SearchUIUtils.ts

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2480,18 +2480,55 @@ function getTaskSections(
24802480
return [tasks, tasks.length];
24812481
}
24822482

2483+
type CreateAndOpenSearchTransactionThreadParams = {
2484+
/** The transaction list item being opened */
2485+
item: TransactionListItemType;
2486+
2487+
/** The intro selected by the user */
2488+
introSelected: OnyxEntry<OnyxTypes.IntroSelected>;
2489+
2490+
/** The route to go back to after navigation */
2491+
backTo: string;
2492+
2493+
/** The current user's login */
2494+
currentUserLogin: string;
2495+
2496+
/** The current user's account ID */
2497+
currentUserAccountID: number;
2498+
2499+
/** Beta features list */
2500+
betas: OnyxEntry<OnyxTypes.Beta[]>;
2501+
2502+
/** Whether the user has seen the self tour */
2503+
isSelfTourViewed: boolean | undefined;
2504+
2505+
/** Whether the user has completed the guided setup flow */
2506+
hasCompletedGuidedSetupFlow: boolean | undefined;
2507+
2508+
/** Existing transaction thread report ID (childReportID), if any */
2509+
IOUTransactionID?: string;
2510+
2511+
/** Preview data used to set optimistic data for the transaction thread preview */
2512+
transactionPreviewData?: TransactionPreviewData;
2513+
2514+
/** Whether to navigate to the transaction thread after creating it */
2515+
shouldNavigate?: boolean;
2516+
};
2517+
24832518
/** Creates transaction thread report and navigates to it from the search page */
2484-
function createAndOpenSearchTransactionThread(
2485-
item: TransactionListItemType,
2486-
introSelected: OnyxEntry<OnyxTypes.IntroSelected>,
2487-
backTo: string,
2488-
currentUserLogin: string,
2489-
currentUserAccountID: number,
2490-
betas: OnyxEntry<OnyxTypes.Beta[]>,
2491-
IOUTransactionID?: string,
2492-
transactionPreviewData?: TransactionPreviewData,
2519+
function createAndOpenSearchTransactionThread({
2520+
item,
2521+
introSelected,
2522+
backTo,
2523+
currentUserLogin,
2524+
currentUserAccountID,
2525+
betas,
2526+
isSelfTourViewed,
2527+
hasCompletedGuidedSetupFlow,
2528+
IOUTransactionID,
2529+
transactionPreviewData,
24932530
shouldNavigate = true,
2494-
) {
2531+
}: CreateAndOpenSearchTransactionThreadParams) {
24952532
const isFromSelfDM = item.reportID === CONST.REPORT.UNREPORTED_REPORT_ID;
24962533
const isDeleted = isDeletedTransaction(item);
24972534
const iouReportAction = getIOUActionForReportID(isFromSelfDM ? findSelfDMReportID() : item.reportID, item.transactionID);
@@ -2527,6 +2564,8 @@ function createAndOpenSearchTransactionThread(
25272564
iouReportAction: reportActionToPass,
25282565
transaction,
25292566
transactionViolations,
2567+
isSelfTourViewed,
2568+
hasCompletedGuidedSetupFlow,
25302569
});
25312570
}
25322571

tests/unit/Search/SearchUIUtilsTest.ts

Lines changed: 30 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8555,24 +8555,26 @@ describe('SearchUIUtils', () => {
85558555
const introSelectedData: OnyxTypes.IntroSelected = {choice: CONST.ONBOARDING_CHOICES.MANAGE_TEAM};
85568556
const currentUserLogin = 'test@example.com';
85578557
const currentUserAccountID = 1;
8558+
const baseParams = {
8559+
item: transactionListItem,
8560+
introSelected: introSelectedData,
8561+
backTo,
8562+
currentUserLogin,
8563+
currentUserAccountID,
8564+
betas: undefined,
8565+
isSelfTourViewed: false,
8566+
hasCompletedGuidedSetupFlow: true,
8567+
IOUTransactionID: threadReportID,
8568+
};
8569+
85588570
beforeEach(() => {
85598571
jest.clearAllMocks();
85608572
});
85618573

85628574
test('Should create transaction thread report and set optimistic data necessary for its preview', () => {
85638575
(createTransactionThreadReport as jest.Mock).mockReturnValue(threadReport);
85648576

8565-
SearchUIUtils.createAndOpenSearchTransactionThread(
8566-
transactionListItem,
8567-
introSelectedData,
8568-
backTo,
8569-
currentUserLogin,
8570-
currentUserAccountID,
8571-
undefined,
8572-
threadReportID,
8573-
undefined,
8574-
false,
8575-
);
8577+
SearchUIUtils.createAndOpenSearchTransactionThread({...baseParams, shouldNavigate: false});
85768578

85778579
expect(setOptimisticDataForTransactionThreadPreview).toHaveBeenCalled();
85788580
// The full reportAction is passed to preserve originalMessage.type for proper expense type detection
@@ -8585,41 +8587,28 @@ describe('SearchUIUtils', () => {
85858587
iouReportAction: reportAction1,
85868588
transaction: undefined,
85878589
transactionViolations: undefined,
8590+
isSelfTourViewed: false,
8591+
hasCompletedGuidedSetupFlow: true,
85888592
});
85898593
});
85908594

85918595
test('Should not navigate if shouldNavigate = false', () => {
8592-
SearchUIUtils.createAndOpenSearchTransactionThread(
8593-
transactionListItem,
8594-
introSelectedData,
8595-
backTo,
8596-
currentUserLogin,
8597-
currentUserAccountID,
8598-
undefined,
8599-
threadReportID,
8600-
undefined,
8601-
false,
8602-
);
8596+
SearchUIUtils.createAndOpenSearchTransactionThread({...baseParams, shouldNavigate: false});
86038597
expect(Navigation.navigate).not.toHaveBeenCalled();
86048598
});
86058599

86068600
test('Should handle navigation if shouldNavigate = true', () => {
8607-
SearchUIUtils.createAndOpenSearchTransactionThread(
8608-
transactionListItem,
8609-
introSelectedData,
8610-
backTo,
8611-
currentUserLogin,
8612-
currentUserAccountID,
8613-
undefined,
8614-
threadReportID,
8615-
undefined,
8616-
true,
8617-
);
8601+
SearchUIUtils.createAndOpenSearchTransactionThread({...baseParams, shouldNavigate: true});
86188602
// For one-transaction reports (isOneTransactionReport = true), navigation goes to the parent report (item.reportID)
86198603
// instead of the transaction thread report
86208604
expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_REPORT.getRoute({reportID: transactionListItem.reportID, backTo}));
86218605
});
86228606

8607+
test('Should default shouldNavigate to true when not provided', () => {
8608+
SearchUIUtils.createAndOpenSearchTransactionThread(baseParams);
8609+
expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_REPORT.getRoute({reportID: transactionListItem.reportID, backTo}));
8610+
});
8611+
86238612
test('Should fallback to childReportID from IOU action when transaction thread report is not in Onyx', async () => {
86248613
(createTransactionThreadReport as jest.Mock).mockReset();
86258614
const childReportID = 'child-thread-456';
@@ -8635,17 +8624,12 @@ describe('SearchUIUtils', () => {
86358624
});
86368625
await waitForBatchedUpdates();
86378626

8638-
SearchUIUtils.createAndOpenSearchTransactionThread(
8639-
multiTransactionItem,
8640-
introSelectedData,
8641-
backTo,
8642-
currentUserLogin,
8643-
currentUserAccountID,
8644-
undefined,
8645-
undefined,
8646-
undefined,
8647-
true,
8648-
);
8627+
SearchUIUtils.createAndOpenSearchTransactionThread({
8628+
...baseParams,
8629+
item: multiTransactionItem,
8630+
IOUTransactionID: undefined,
8631+
shouldNavigate: true,
8632+
});
86498633

86508634
expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_REPORT.getRoute({reportID: childReportID, backTo}));
86518635
});
@@ -8654,25 +8638,15 @@ describe('SearchUIUtils', () => {
86548638
(createTransactionThreadReport as jest.Mock).mockReturnValue(threadReport);
86558639
const customIntroSelected: OnyxTypes.IntroSelected = {choice: CONST.ONBOARDING_CHOICES.PERSONAL_SPEND};
86568640

8657-
SearchUIUtils.createAndOpenSearchTransactionThread(
8658-
transactionListItem,
8659-
customIntroSelected,
8660-
backTo,
8661-
currentUserLogin,
8662-
currentUserAccountID,
8663-
undefined,
8664-
threadReportID,
8665-
undefined,
8666-
false,
8667-
);
8641+
SearchUIUtils.createAndOpenSearchTransactionThread({...baseParams, introSelected: customIntroSelected, shouldNavigate: false});
86688642

86698643
expect(jest.mocked(createTransactionThreadReport).mock.calls.at(0)?.at(0)?.introSelected).toEqual(customIntroSelected);
86708644
});
86718645

86728646
test('Should pass undefined introSelected without bypassing with empty values', () => {
86738647
(createTransactionThreadReport as jest.Mock).mockReturnValue(threadReport);
86748648

8675-
SearchUIUtils.createAndOpenSearchTransactionThread(transactionListItem, undefined, backTo, currentUserLogin, currentUserAccountID, undefined, threadReportID, undefined, false);
8649+
SearchUIUtils.createAndOpenSearchTransactionThread({...baseParams, introSelected: undefined, shouldNavigate: false});
86768650

86778651
expect(jest.mocked(createTransactionThreadReport).mock.calls.at(0)?.at(0)?.introSelected).toBeUndefined();
86788652
});

0 commit comments

Comments
 (0)