From b3a78435463806644542a9380aba74155111eaf6 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Thu, 10 Apr 2025 12:15:35 -0400 Subject: [PATCH 01/17] start updating hash --- src/libs/actions/IOU.ts | 25 +++++++++++++++++-- src/pages/Share/SubmitDetailsPage.tsx | 1 + .../step/IOURequestStepConfirmation.tsx | 3 +++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 0602c12527f7..24a7a95720c2 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -399,6 +399,7 @@ type RequestMoneyInformation = { reimbursible?: boolean; transactionParams: RequestMoneyTransactionParams; isRetry?: boolean; + hash?: number; }; type MoneyRequestInformationParams = { @@ -410,6 +411,7 @@ type MoneyRequestInformationParams = { existingTransactionID?: string; existingTransaction?: OnyxEntry; retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt; + hash?: number; }; type MoneyRequestOptimisticParams = { @@ -446,6 +448,7 @@ type BuildOnyxDataForMoneyRequestParams = { policyParams?: BasePolicyParams; optimisticParams: MoneyRequestOptimisticParams; retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt; + hash?: number; }; type DistanceRequestTransactionParams = BaseTransactionParams & { @@ -1184,6 +1187,7 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR policyParams = {}, optimisticParams, retryParams, + hash, } = moneyRequestParams; const {policy, policyCategories, policyTagList} = policyParams; const { @@ -1719,6 +1723,21 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }); } + if (hash) { + console.log(`Adding optimistic data for transaction ${transaction.transactionID} with hash ${hash}`); + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, + value: { + data: { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { + ...transaction, + }, + }, + }, + }); + } + return [optimisticData, successData, failureData]; } @@ -2967,6 +2986,7 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma existingTransactionID, moneyRequestReportID = '', retryParams, + hash, } = moneyRequestInformation; const {payeeAccountID = userAccountID, payeeEmail = currentUserEmail, participant} = participantParams; const {policy, policyCategories, policyTagList} = policyParams; @@ -3084,7 +3104,6 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma participants: [participant], transactionID: optimisticTransaction.transactionID, paymentType: isSelectedManagerMcTest(participant.login) ? CONST.IOU.PAYMENT_TYPE.ELSEWHERE : undefined, - existingTransactionThreadReportID: linkedTrackedExpenseReportAction?.childReportID, linkedTrackedExpenseReportAction, }); @@ -3122,6 +3141,7 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma // STEP 5: Build Onyx Data const [optimisticData, successData, failureData] = buildOnyxDataForMoneyRequest({ + hash, isNewChatReport, shouldCreateNewMoneyRequestReport, policyParams: { @@ -4832,7 +4852,7 @@ function shareTrackedExpense(trackedExpenseParams: TrackedExpenseParams) { * Submit expense to another user */ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { - const {report, participantParams, policyParams = {}, transactionParams, gpsPoints, action, reimbursible} = requestMoneyInformation; + const {report, participantParams, policyParams = {}, transactionParams, gpsPoints, action, reimbursible, hash} = requestMoneyInformation; const {payeeAccountID} = participantParams; const parsedComment = getParsedComment(transactionParams.comment ?? ''); transactionParams.comment = parsedComment; @@ -4906,6 +4926,7 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { existingTransactionID, existingTransaction: isDistanceRequestTransactionUtils(existingTransaction) ? existingTransaction : undefined, retryParams, + hash, }); const activeReportID = isMoneyRequestReport ? report?.reportID : chatReport.reportID; diff --git a/src/pages/Share/SubmitDetailsPage.tsx b/src/pages/Share/SubmitDetailsPage.tsx index f29b7fea6c52..4195aa3cd9f0 100644 --- a/src/pages/Share/SubmitDetailsPage.tsx +++ b/src/pages/Share/SubmitDetailsPage.tsx @@ -8,6 +8,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import LocationPermissionModal from '@components/LocationPermissionModal'; import MoneyRequestConfirmationList from '@components/MoneyRequestConfirmationList'; import ScreenWrapper from '@components/ScreenWrapper'; +import {useSearchContext} from '@components/Search/SearchContext'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index ce9ec520f85f..4e00b8b2cb78 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -14,6 +14,7 @@ import MoneyRequestConfirmationList from '@components/MoneyRequestConfirmationLi import {usePersonalDetails} from '@components/OnyxProvider'; import PDFThumbnail from '@components/PDFThumbnail'; import ScreenWrapper from '@components/ScreenWrapper'; +import {useSearchContext} from '@components/Search/SearchContext'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useFetchRoute from '@hooks/useFetchRoute'; import useLocalize from '@hooks/useLocalize'; @@ -83,6 +84,7 @@ function IOURequestStepConfirmation({ }: IOURequestStepConfirmationProps) { const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const personalDetails = usePersonalDetails(); + const {currentSearchHash} = useSearchContext(); const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${getIOURequestPolicyID(transaction, reportDraft)}`); const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getIOURequestPolicyID(transaction, reportReal)}`); @@ -381,6 +383,7 @@ function IOURequestStepConfirmation({ const isTestReceipt = receiptObj?.isTestReceipt ?? false; requestMoneyIOUActions({ report, + hash: currentSearchHash, participantParams: { payeeEmail: currentUserPersonalDetails.login, payeeAccountID: currentUserPersonalDetails.accountID, From 736ffaea0390081f1bf22abecdb555812ad0cb40 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Thu, 10 Apr 2025 13:27:04 -0400 Subject: [PATCH 02/17] initial optimistic update --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 24a7a95720c2..19d29d772c2a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1724,13 +1724,13 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR } if (hash) { - console.log(`Adding optimistic data for transaction ${transaction.transactionID} with hash ${hash}`); optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, value: { data: { [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { + accountID: currentUserPersonalDetails?.accountID, ...transaction, }, }, From 66b596760a02431ec87446b4d247783b24566554 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Fri, 11 Apr 2025 10:15:07 -0400 Subject: [PATCH 03/17] optimistics starting to work --- src/libs/actions/IOU.ts | 50 ++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 19d29d772c2a..ad2b9db25d2b 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -448,6 +448,7 @@ type BuildOnyxDataForMoneyRequestParams = { policyParams?: BasePolicyParams; optimisticParams: MoneyRequestOptimisticParams; retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt; + participant?: Participant; hash?: number; }; @@ -1187,6 +1188,7 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR policyParams = {}, optimisticParams, retryParams, + participant, hash, } = moneyRequestParams; const {policy, policyCategories, policyTagList} = policyParams; @@ -1693,6 +1695,38 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }); } + const toAccountID = participant?.accountID || participant?.ownerAccountID; + const fromAccountID = currentUserPersonalDetails?.accountID; + + if (hash && toAccountID && fromAccountID) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, + value: { + data: { + [ONYXKEYS.PERSONAL_DETAILS_LIST]: { + [toAccountID]: { + accountID: toAccountID, + displayName: participant?.displayName, + login: participant?.login, + }, + [fromAccountID]: { + accountID: fromAccountID, + avatar: currentUserPersonalDetails?.avatar, + displayName: currentUserPersonalDetails?.displayName, + login: currentUserPersonalDetails?.login, + }, + }, + [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { + accountID: fromAccountID, + managerID: toAccountID, + ...transaction, + }, + }, + }, + }); + } + // We don't need to compute violations unless we're on a paid policy if (!policy || !isPaidGroupPolicy(policy)) { return [optimisticData, successData, failureData]; @@ -1723,21 +1757,6 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }); } - if (hash) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, - value: { - data: { - [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { - accountID: currentUserPersonalDetails?.accountID, - ...transaction, - }, - }, - }, - }); - } - return [optimisticData, successData, failureData]; } @@ -3142,6 +3161,7 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma // STEP 5: Build Onyx Data const [optimisticData, successData, failureData] = buildOnyxDataForMoneyRequest({ hash, + participant, isNewChatReport, shouldCreateNewMoneyRequestReport, policyParams: { From 77905d479f474169065e983c257a1b592fffe3f9 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Fri, 11 Apr 2025 10:48:23 -0400 Subject: [PATCH 04/17] add hash checking for what type of search we are on --- src/libs/actions/IOU.ts | 73 +++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ad2b9db25d2b..73a187086a66 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -727,6 +727,19 @@ Onyx.connect({ callback: (value) => (personalDetailsList = value), }); +let snapshotList: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.SNAPSHOT, + waitForCollectionCallback: true, + callback: (value) => { + if (!value) { + return; + } + + snapshotList = value; + }, +}); + /** * @private * After finishing the action in RHP from the Inbox tab, besides dismissing the modal, we should open the report. @@ -1695,36 +1708,54 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }); } + // Don't use nullish coalescing because we also want to use the ownerAccountID + // if the accountID is 0. + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const toAccountID = participant?.accountID || participant?.ownerAccountID; const fromAccountID = currentUserPersonalDetails?.accountID; - if (hash && toAccountID && fromAccountID) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, - value: { - data: { - [ONYXKEYS.PERSONAL_DETAILS_LIST]: { - [toAccountID]: { - accountID: toAccountID, - displayName: participant?.displayName, - login: participant?.login, + if (hash && toAccountID && fromAccountID && snapshotList) { + const existingPersonalDetails = {...personalDetailsList}; + const snapshot = snapshotList[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]; + + if (snapshot?.search.status === CONST.SEARCH.STATUS.EXPENSE.ALL) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, + value: { + data: { + [ONYXKEYS.PERSONAL_DETAILS_LIST]: { + [toAccountID]: { + accountID: toAccountID, + displayName: participant?.displayName, + login: participant?.login, + }, + [fromAccountID]: { + accountID: fromAccountID, + avatar: currentUserPersonalDetails?.avatar, + displayName: currentUserPersonalDetails?.displayName, + login: currentUserPersonalDetails?.login, + }, }, - [fromAccountID]: { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { accountID: fromAccountID, - avatar: currentUserPersonalDetails?.avatar, - displayName: currentUserPersonalDetails?.displayName, - login: currentUserPersonalDetails?.login, + managerID: toAccountID, + ...transaction, }, }, - [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { - accountID: fromAccountID, - managerID: toAccountID, - ...transaction, + }, + }); + + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, + value: { + data: { + [ONYXKEYS.PERSONAL_DETAILS_LIST]: existingPersonalDetails, }, }, - }, - }); + }); + } } // We don't need to compute violations unless we're on a paid policy From 5924771793cc581f9278bbc2e0ceb95528cbd55e Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Fri, 11 Apr 2025 14:46:59 -0400 Subject: [PATCH 05/17] log stash --- .../Search/TransactionListItemRow.tsx | 2 ++ src/libs/actions/IOU.ts | 28 ++++++++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/components/SelectionList/Search/TransactionListItemRow.tsx b/src/components/SelectionList/Search/TransactionListItemRow.tsx index 1492bc0dcc6f..7b4a91ed11f5 100644 --- a/src/components/SelectionList/Search/TransactionListItemRow.tsx +++ b/src/components/SelectionList/Search/TransactionListItemRow.tsx @@ -286,6 +286,8 @@ function TransactionListItemRow({ const StyleUtils = useStyleUtils(); const theme = useTheme(); + console.log(item); + if (!isLargeScreenWidth) { return ( diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 73a187086a66..1775f9c9117d 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -202,7 +202,7 @@ import type {QuickActionName} from '@src/types/onyx/QuickAction'; import type {InvoiceReceiver, InvoiceReceiverType} from '@src/types/onyx/Report'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; -import type {SearchPolicy, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; +import type {SearchDataTypes, SearchPolicy, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; import type {Comment, Receipt, ReceiptSource, Routes, SplitShares, TransactionChanges, TransactionCustomUnit, WaypointCollection} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {clearByKey as clearPdfByOnyxKey} from './CachedPDFPaths'; @@ -1711,14 +1711,16 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR // Don't use nullish coalescing because we also want to use the ownerAccountID // if the accountID is 0. // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const toAccountID = participant?.accountID || participant?.ownerAccountID; + const toAccountID = participant?.accountID; const fromAccountID = currentUserPersonalDetails?.accountID; + console.log(participant); + if (hash && toAccountID && fromAccountID && snapshotList) { - const existingPersonalDetails = {...personalDetailsList}; const snapshot = snapshotList[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]; + const validSearchTypes: SearchDataTypes[] = [CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.DATA_TYPES.INVOICE]; - if (snapshot?.search.status === CONST.SEARCH.STATUS.EXPENSE.ALL) { + if (snapshot?.search.status === CONST.SEARCH.STATUS.EXPENSE.ALL && validSearchTypes.includes(snapshot.search.type)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, @@ -1746,15 +1748,15 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }, }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, - value: { - data: { - [ONYXKEYS.PERSONAL_DETAILS_LIST]: existingPersonalDetails, - }, - }, - }); + // failureData.push({ + // onyxMethod: Onyx.METHOD.MERGE, + // key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, + // value: { + // data: { + // [ONYXKEYS.PERSONAL_DETAILS_LIST]: existingPersonalDetails, + // }, + // }, + // }); } } From eb6ecd75514945748e4daa9e7b2eed015e259943 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 09:27:44 -0400 Subject: [PATCH 06/17] add optimistic updates --- .../Search/TransactionListItemRow.tsx | 2 -- src/libs/actions/IOU.ts | 17 +---------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/components/SelectionList/Search/TransactionListItemRow.tsx b/src/components/SelectionList/Search/TransactionListItemRow.tsx index 7b4a91ed11f5..1492bc0dcc6f 100644 --- a/src/components/SelectionList/Search/TransactionListItemRow.tsx +++ b/src/components/SelectionList/Search/TransactionListItemRow.tsx @@ -286,8 +286,6 @@ function TransactionListItemRow({ const StyleUtils = useStyleUtils(); const theme = useTheme(); - console.log(item); - if (!isLargeScreenWidth) { return ( diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 0f3ebd87c082..c4036bb10758 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1708,15 +1708,10 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }); } - // Don't use nullish coalescing because we also want to use the ownerAccountID - // if the accountID is 0. - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const toAccountID = participant?.accountID; const fromAccountID = currentUserPersonalDetails?.accountID; - console.log(participant); - - if (hash && toAccountID && fromAccountID && snapshotList) { + if (hash && toAccountID != null && fromAccountID != null && snapshotList) { const snapshot = snapshotList[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]; const validSearchTypes: SearchDataTypes[] = [CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.DATA_TYPES.INVOICE]; @@ -1747,16 +1742,6 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }, }, }); - - // failureData.push({ - // onyxMethod: Onyx.METHOD.MERGE, - // key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, - // value: { - // data: { - // [ONYXKEYS.PERSONAL_DETAILS_LIST]: existingPersonalDetails, - // }, - // }, - // }); } } From 2c6e661ed81d1d3f8d399f6de139ef66636be413 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 09:58:29 -0400 Subject: [PATCH 07/17] add hash --- src/libs/actions/IOU.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index c4036bb10758..f2cdbce540cd 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -509,6 +509,7 @@ type CreateTrackExpenseParams = { policyParams?: BasePolicyParams; transactionParams: TrackExpenseTransactionParams; isRetry?: boolean; + hash?: number; }; type BuildOnyxDataForInvoiceParams = { @@ -568,6 +569,7 @@ type GetTrackExpenseInformationParams = { policyParams: BasePolicyParams; transactionParams: GetTrackExpenseInformationTransactionParams; retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt; + hash?: number; }; let allPersonalDetails: OnyxTypes.PersonalDetailsList = {}; @@ -1213,6 +1215,8 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR personalDetailListAction, nextStep, } = optimisticParams; + const toAccountID = participant?.accountID; + const fromAccountID = currentUserPersonalDetails?.accountID; const isScanRequest = isScanRequestTransactionUtils(transaction); const isPerDiemRequest = isPerDiemRequestTransactionUtils(transaction); @@ -1708,9 +1712,6 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }); } - const toAccountID = participant?.accountID; - const fromAccountID = currentUserPersonalDetails?.accountID; - if (hash && toAccountID != null && fromAccountID != null && snapshotList) { const snapshot = snapshotList[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]; const validSearchTypes: SearchDataTypes[] = [CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.DATA_TYPES.INVOICE]; From 9a4917af5bc33cfa50bf2acf64d8e47249d37383 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 10:52:04 -0400 Subject: [PATCH 08/17] much better way of getting search data --- src/libs/SearchQueryUtils.ts | 27 +++++++++++++++++++++++++++ src/libs/actions/IOU.ts | 32 +++++++------------------------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index aae69cc384fe..228d19cef340 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -3,7 +3,9 @@ import type {OnyxCollection} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type {ASTNode, QueryFilter, QueryFilters, SearchDateFilterKeys, SearchFilterKey, SearchQueryJSON, SearchQueryString, SearchStatus, UserFriendlyKey} from '@components/Search/types'; import CONST from '@src/CONST'; +import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; +import SCREENS from '@src/SCREENS'; import type {SearchAdvancedFiltersForm} from '@src/types/form'; import FILTER_KEYS, {DATE_FILTER_KEYS} from '@src/types/form/SearchAdvancedFiltersForm'; import type * as OnyxTypes from '@src/types/onyx'; @@ -15,6 +17,8 @@ import {convertToBackendAmount, convertToFrontendAmountAsInteger} from './Curren import localeCompare from './LocaleCompare'; import Log from './Log'; import {validateAmount} from './MoneyRequestUtils'; +import navigationRef from './Navigation/navigationRef'; +import type {SearchFullscreenNavigatorParamList} from './Navigation/types'; import {getPersonalDetailByEmail} from './PersonalDetailsUtils'; import {getCleanedTagName, getTagNamesFromTagsLists} from './PolicyUtils'; import {getReportName} from './ReportUtils'; @@ -853,6 +857,28 @@ function getQueryWithUpdatedValues(query: string, policyID?: string) { return buildSearchQueryString(standardizedQuery); } +function getCurrentSearchQueryJSON() { + const rootState = navigationRef.getRootState(); + const lastPolicyRoute = rootState?.routes?.findLast((route) => route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR || route.name === NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR); + + if (!lastPolicyRoute) { + return; + } + + const lastSearchRoute = lastPolicyRoute.state?.routes.findLast((route) => route.name === SCREENS.SEARCH.ROOT); + if (!lastSearchRoute || !lastSearchRoute.params) { + return; + } + + const {q} = lastSearchRoute.params as SearchFullscreenNavigatorParamList[typeof SCREENS.SEARCH.ROOT]; + const queryJSON = buildSearchQueryJSON(q); + if (!queryJSON) { + return; + } + + return queryJSON; +} + /** * Converts a filter key from old naming (camelCase) to user friendly naming (kebab-case). * @@ -896,6 +922,7 @@ export { isCannedSearchQueryWithPolicyIDCheck, sanitizeSearchValue, getQueryWithUpdatedValues, + getCurrentSearchQueryJSON, getUserFriendlyKey, isDefaultExpensesQuery, shouldHighlight, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index f2cdbce540cd..abd72fef82ad 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -157,6 +157,7 @@ import { shouldCreateNewMoneyRequestReport as shouldCreateNewMoneyRequestReportReportUtils, updateReportPreview, } from '@libs/ReportUtils'; +import {getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; import {getSession} from '@libs/SessionUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; @@ -449,7 +450,6 @@ type BuildOnyxDataForMoneyRequestParams = { optimisticParams: MoneyRequestOptimisticParams; retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt; participant?: Participant; - hash?: number; }; type DistanceRequestTransactionParams = BaseTransactionParams & { @@ -509,7 +509,6 @@ type CreateTrackExpenseParams = { policyParams?: BasePolicyParams; transactionParams: TrackExpenseTransactionParams; isRetry?: boolean; - hash?: number; }; type BuildOnyxDataForInvoiceParams = { @@ -569,7 +568,6 @@ type GetTrackExpenseInformationParams = { policyParams: BasePolicyParams; transactionParams: GetTrackExpenseInformationTransactionParams; retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt; - hash?: number; }; let allPersonalDetails: OnyxTypes.PersonalDetailsList = {}; @@ -729,19 +727,6 @@ Onyx.connect({ callback: (value) => (personalDetailsList = value), }); -let snapshotList: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.SNAPSHOT, - waitForCollectionCallback: true, - callback: (value) => { - if (!value) { - return; - } - - snapshotList = value; - }, -}); - /** * @private * After finishing the action in RHP from the Inbox tab, besides dismissing the modal, we should open the report. @@ -1204,7 +1189,6 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR optimisticParams, retryParams, participant, - hash, } = moneyRequestParams; const {policy, policyCategories, policyTagList} = policyParams; const { @@ -1223,6 +1207,8 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR const outstandingChildRequest = getOutstandingChildRequest(iou.report); const clearedPendingFields = Object.fromEntries(Object.keys(transaction.pendingFields ?? {}).map((key) => [key, null])); const isMoneyRequestToManagerMcTest = isTestTransactionReport(iou.report); + const currentSearchQueryJSON = getCurrentSearchQueryJSON(); + const optimisticData: OnyxUpdate[] = []; const successData: OnyxUpdate[] = []; let newQuickAction: ValueOf; @@ -1712,14 +1698,13 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }); } - if (hash && toAccountID != null && fromAccountID != null && snapshotList) { - const snapshot = snapshotList[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]; + if (currentSearchQueryJSON && toAccountID != null && fromAccountID != null) { const validSearchTypes: SearchDataTypes[] = [CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.DATA_TYPES.INVOICE]; - if (snapshot?.search.status === CONST.SEARCH.STATUS.EXPENSE.ALL && validSearchTypes.includes(snapshot.search.type)) { + if (currentSearchQueryJSON.status === CONST.SEARCH.STATUS.EXPENSE.ALL && validSearchTypes.includes(currentSearchQueryJSON.type)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, + key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}`, value: { data: { [ONYXKEYS.PERSONAL_DETAILS_LIST]: { @@ -3024,7 +3009,6 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma existingTransactionID, moneyRequestReportID = '', retryParams, - hash, } = moneyRequestInformation; const {payeeAccountID = userAccountID, payeeEmail = currentUserEmail, participant} = participantParams; const {policy, policyCategories, policyTagList} = policyParams; @@ -3179,7 +3163,6 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma // STEP 5: Build Onyx Data const [optimisticData, successData, failureData] = buildOnyxDataForMoneyRequest({ - hash, participant, isNewChatReport, shouldCreateNewMoneyRequestReport, @@ -4891,7 +4874,7 @@ function shareTrackedExpense(trackedExpenseParams: TrackedExpenseParams) { * Submit expense to another user */ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { - const {report, participantParams, policyParams = {}, transactionParams, gpsPoints, action, reimbursible, hash} = requestMoneyInformation; + const {report, participantParams, policyParams = {}, transactionParams, gpsPoints, action, reimbursible} = requestMoneyInformation; const {payeeAccountID} = participantParams; const parsedComment = getParsedComment(transactionParams.comment ?? ''); transactionParams.comment = parsedComment; @@ -4965,7 +4948,6 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { existingTransactionID, existingTransaction: isDistanceRequestTransactionUtils(existingTransaction) ? existingTransaction : undefined, retryParams, - hash, }); const activeReportID = isMoneyRequestReport ? report?.reportID : chatReport.reportID; From 9928db958f9807dee6ce0c1980e30495ebecbe94 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 11:25:43 -0400 Subject: [PATCH 09/17] consolidate into one function --- src/libs/SearchUIUtils.ts | 53 ++++++++++++++++++++++++++++++++++-- src/libs/actions/IOU.ts | 56 +++++++++++++++------------------------ 2 files changed, 73 insertions(+), 36 deletions(-) diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index a2b22692cb3c..c77c8840f4ff 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1,6 +1,6 @@ import type {TextStyle, ViewStyle} from 'react-native'; import Onyx from 'react-native-onyx'; -import type {OnyxCollection} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type {MenuItemWithLink} from '@components/MenuItemList'; import type {SearchColumnType, SearchQueryJSON, SearchStatus, SortOrder} from '@components/Search/types'; @@ -15,6 +15,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; +import type {Participant} from '@src/types/onyx/IOU'; import type {SaveSearchItem} from '@src/types/onyx/SaveSearch'; import type SearchResults from '@src/types/onyx/SearchResults'; import type { @@ -49,7 +50,7 @@ import { isOpenExpenseReport, isSettled, } from './ReportUtils'; -import {buildCannedSearchQuery} from './SearchQueryUtils'; +import {buildCannedSearchQuery, getCurrentSearchQueryJSON} from './SearchQueryUtils'; import {getAmount as getTransactionAmount, getCreated as getTransactionCreatedDate, getMerchant as getTransactionMerchant, isPendingCardOrScanningTransaction} from './TransactionUtils'; import shouldShowTransactionYear from './TransactionUtils/shouldShowTransactionYear'; @@ -106,6 +107,12 @@ type SearchTypeMenuItem = { getRoute: (policyID?: string) => Route; }; +type GetSearchOnyxUpdateParams = { + transaction: OnyxTypes.Transaction; + participant?: Participant; + currentUserPersonalDetails?: OnyxEntry; +}; + /** * @private * @@ -816,6 +823,47 @@ function isSearchDataLoaded(currentSearchResults: SearchResults | undefined, las return isDataLoaded; } +function getSearchOnyxUpdate({currentUserPersonalDetails, participant, transaction}: GetSearchOnyxUpdateParams) { + const toAccountID = participant?.accountID; + const fromAccountID = currentUserPersonalDetails?.accountID; + const currentSearchQueryJSON = getCurrentSearchQueryJSON(); + + if (currentSearchQueryJSON && toAccountID && fromAccountID) { + const validSearchTypes: SearchDataTypes[] = [CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.DATA_TYPES.INVOICE]; + const shouldOptimisticallyUpdate = + currentSearchQueryJSON.status === CONST.SEARCH.STATUS.EXPENSE.ALL && validSearchTypes.includes(currentSearchQueryJSON.type) && currentSearchQueryJSON.flatFilters.length === 0; + + if (shouldOptimisticallyUpdate) { + return { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}` as const, + value: { + data: { + [ONYXKEYS.PERSONAL_DETAILS_LIST]: { + [toAccountID]: { + accountID: toAccountID, + displayName: participant?.displayName, + login: participant?.login, + }, + [fromAccountID]: { + accountID: fromAccountID, + avatar: currentUserPersonalDetails?.avatar, + displayName: currentUserPersonalDetails?.displayName, + login: currentUserPersonalDetails?.login, + }, + }, + [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { + accountID: fromAccountID, + managerID: toAccountID, + ...transaction, + }, + }, + }, + }; + } + } +} + export { getListItem, getSections, @@ -836,5 +884,6 @@ export { shouldShowEmptyState, compareValues, isSearchDataLoaded, + getSearchOnyxUpdate, }; export type {SavedSearchMenuItem, SearchTypeMenuItem}; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index abd72fef82ad..b3fccf96da65 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -157,7 +157,7 @@ import { shouldCreateNewMoneyRequestReport as shouldCreateNewMoneyRequestReportReportUtils, updateReportPreview, } from '@libs/ReportUtils'; -import {getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; +import {getSearchOnyxUpdate} from '@libs/SearchUIUtils'; import {getSession} from '@libs/SessionUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; @@ -1199,15 +1199,12 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR personalDetailListAction, nextStep, } = optimisticParams; - const toAccountID = participant?.accountID; - const fromAccountID = currentUserPersonalDetails?.accountID; const isScanRequest = isScanRequestTransactionUtils(transaction); const isPerDiemRequest = isPerDiemRequestTransactionUtils(transaction); const outstandingChildRequest = getOutstandingChildRequest(iou.report); const clearedPendingFields = Object.fromEntries(Object.keys(transaction.pendingFields ?? {}).map((key) => [key, null])); const isMoneyRequestToManagerMcTest = isTestTransactionReport(iou.report); - const currentSearchQueryJSON = getCurrentSearchQueryJSON(); const optimisticData: OnyxUpdate[] = []; const successData: OnyxUpdate[] = []; @@ -1698,37 +1695,14 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }); } - if (currentSearchQueryJSON && toAccountID != null && fromAccountID != null) { - const validSearchTypes: SearchDataTypes[] = [CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.DATA_TYPES.INVOICE]; + const searchUpdate = getSearchOnyxUpdate({ + transaction, + participant, + currentUserPersonalDetails, + }); - if (currentSearchQueryJSON.status === CONST.SEARCH.STATUS.EXPENSE.ALL && validSearchTypes.includes(currentSearchQueryJSON.type)) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}`, - value: { - data: { - [ONYXKEYS.PERSONAL_DETAILS_LIST]: { - [toAccountID]: { - accountID: toAccountID, - displayName: participant?.displayName, - login: participant?.login, - }, - [fromAccountID]: { - accountID: fromAccountID, - avatar: currentUserPersonalDetails?.avatar, - displayName: currentUserPersonalDetails?.displayName, - login: currentUserPersonalDetails?.login, - }, - }, - [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { - accountID: fromAccountID, - managerID: toAccountID, - ...transaction, - }, - }, - }, - }); - } + if (searchUpdate) { + optimisticData.push(searchUpdate); } // We don't need to compute violations unless we're on a paid policy @@ -2182,6 +2156,7 @@ type BuildOnyxDataForTrackExpenseParams = { existingTransactionThreadReportID?: string; actionableTrackExpenseWhisper?: OnyxInputValue; retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt; + participant?: Participant; }; /** Builds the Onyx data for track expense */ @@ -2194,6 +2169,7 @@ function buildOnyxDataForTrackExpense({ existingTransactionThreadReportID, actionableTrackExpenseWhisper, retryParams, + participant, }: BuildOnyxDataForTrackExpenseParams): [OnyxUpdate[], OnyxUpdate[], OnyxUpdate[]] { const {report: chatReport, previewAction: reportPreviewAction} = chat; const {report: iouReport, createdAction: iouCreatedAction, action: iouAction} = iou; @@ -2203,6 +2179,7 @@ function buildOnyxDataForTrackExpense({ const isScanRequest = isScanRequestTransactionUtils(transaction); const isDistanceRequest = isDistanceRequestTransactionUtils(transaction); const clearedPendingFields = Object.fromEntries(Object.keys(transaction.pendingFields ?? {}).map((key) => [key, null])); + const optimisticData: OnyxUpdate[] = []; const successData: OnyxUpdate[] = []; const failureData: OnyxUpdate[] = []; @@ -2592,6 +2569,16 @@ function buildOnyxDataForTrackExpense({ }); } + const searchUpdate = getSearchOnyxUpdate({ + transaction, + participant, + currentUserPersonalDetails, + }); + + if (searchUpdate) { + optimisticData.push(searchUpdate); + } + // We don't need to compute violations unless we're on a paid policy if (!policy || !isPaidGroupPolicy(policy)) { return [optimisticData, successData, failureData]; @@ -3619,6 +3606,7 @@ function getTrackExpenseInformation(params: GetTrackExpenseInformationParams): T // STEP 5: Build Onyx Data const trackExpenseOnyxData = buildOnyxDataForTrackExpense({ + participant, chat: {report: chatReport, previewAction: reportPreviewAction}, iou: {report: iouReport, action: iouAction, createdAction: optimisticCreatedActionForIOUReport}, transactionParams: { From 7f11f32f7cf997383bac320a496e1968429c1029 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 11:33:47 -0400 Subject: [PATCH 10/17] eslint fixes --- src/libs/actions/IOU.ts | 4 +--- src/pages/Share/SubmitDetailsPage.tsx | 1 - src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 3 --- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index b3fccf96da65..d10c31a33622 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -203,7 +203,7 @@ import type {QuickActionName} from '@src/types/onyx/QuickAction'; import type {InvoiceReceiver, InvoiceReceiverType} from '@src/types/onyx/Report'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; -import type {SearchDataTypes, SearchPolicy, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; +import type {SearchPolicy, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; import type {Comment, Receipt, ReceiptSource, Routes, SplitShares, TransactionChanges, TransactionCustomUnit, WaypointCollection} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {clearByKey as clearPdfByOnyxKey} from './CachedPDFPaths'; @@ -400,7 +400,6 @@ type RequestMoneyInformation = { reimbursible?: boolean; transactionParams: RequestMoneyTransactionParams; isRetry?: boolean; - hash?: number; }; type MoneyRequestInformationParams = { @@ -412,7 +411,6 @@ type MoneyRequestInformationParams = { existingTransactionID?: string; existingTransaction?: OnyxEntry; retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt; - hash?: number; }; type MoneyRequestOptimisticParams = { diff --git a/src/pages/Share/SubmitDetailsPage.tsx b/src/pages/Share/SubmitDetailsPage.tsx index 4195aa3cd9f0..f29b7fea6c52 100644 --- a/src/pages/Share/SubmitDetailsPage.tsx +++ b/src/pages/Share/SubmitDetailsPage.tsx @@ -8,7 +8,6 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import LocationPermissionModal from '@components/LocationPermissionModal'; import MoneyRequestConfirmationList from '@components/MoneyRequestConfirmationList'; import ScreenWrapper from '@components/ScreenWrapper'; -import {useSearchContext} from '@components/Search/SearchContext'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index e87bae7b2604..add8d0123f80 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -14,7 +14,6 @@ import MoneyRequestConfirmationList from '@components/MoneyRequestConfirmationLi import {usePersonalDetails} from '@components/OnyxProvider'; import PDFThumbnail from '@components/PDFThumbnail'; import ScreenWrapper from '@components/ScreenWrapper'; -import {useSearchContext} from '@components/Search/SearchContext'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useFetchRoute from '@hooks/useFetchRoute'; import useLocalize from '@hooks/useLocalize'; @@ -84,7 +83,6 @@ function IOURequestStepConfirmation({ }: IOURequestStepConfirmationProps) { const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const personalDetails = usePersonalDetails(); - const {currentSearchHash} = useSearchContext(); const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${getIOURequestPolicyID(transaction, reportDraft)}`); const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getIOURequestPolicyID(transaction, reportReal)}`); @@ -393,7 +391,6 @@ function IOURequestStepConfirmation({ const isTestReceipt = receiptObj?.isTestReceipt ?? false; requestMoneyIOUActions({ report, - hash: currentSearchHash, participantParams: { payeeEmail: currentUserPersonalDetails.login, payeeAccountID: currentUserPersonalDetails.accountID, From d1090e676cd249208ab8289819e663dc92f8f613 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 12:02:56 -0400 Subject: [PATCH 11/17] Fix bug with optimistic updates --- src/libs/SearchUIUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index c77c8840f4ff..5413e6a5c255 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -828,7 +828,7 @@ function getSearchOnyxUpdate({currentUserPersonalDetails, participant, transacti const fromAccountID = currentUserPersonalDetails?.accountID; const currentSearchQueryJSON = getCurrentSearchQueryJSON(); - if (currentSearchQueryJSON && toAccountID && fromAccountID) { + if (currentSearchQueryJSON && toAccountID != null && fromAccountID != null) { const validSearchTypes: SearchDataTypes[] = [CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.DATA_TYPES.INVOICE]; const shouldOptimisticallyUpdate = currentSearchQueryJSON.status === CONST.SEARCH.STATUS.EXPENSE.ALL && validSearchTypes.includes(currentSearchQueryJSON.type) && currentSearchQueryJSON.flatFilters.length === 0; From 33179f984a835dfe4002855f910a94dd94e0f2d7 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 13:37:11 -0400 Subject: [PATCH 12/17] fix unit tests, move stuff to better spots --- src/libs/SearchUIUtils.ts | 53 ++------------------------------------- src/libs/actions/IOU.ts | 50 ++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 53 deletions(-) diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 5413e6a5c255..a2b22692cb3c 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1,6 +1,6 @@ import type {TextStyle, ViewStyle} from 'react-native'; import Onyx from 'react-native-onyx'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type {MenuItemWithLink} from '@components/MenuItemList'; import type {SearchColumnType, SearchQueryJSON, SearchStatus, SortOrder} from '@components/Search/types'; @@ -15,7 +15,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; -import type {Participant} from '@src/types/onyx/IOU'; import type {SaveSearchItem} from '@src/types/onyx/SaveSearch'; import type SearchResults from '@src/types/onyx/SearchResults'; import type { @@ -50,7 +49,7 @@ import { isOpenExpenseReport, isSettled, } from './ReportUtils'; -import {buildCannedSearchQuery, getCurrentSearchQueryJSON} from './SearchQueryUtils'; +import {buildCannedSearchQuery} from './SearchQueryUtils'; import {getAmount as getTransactionAmount, getCreated as getTransactionCreatedDate, getMerchant as getTransactionMerchant, isPendingCardOrScanningTransaction} from './TransactionUtils'; import shouldShowTransactionYear from './TransactionUtils/shouldShowTransactionYear'; @@ -107,12 +106,6 @@ type SearchTypeMenuItem = { getRoute: (policyID?: string) => Route; }; -type GetSearchOnyxUpdateParams = { - transaction: OnyxTypes.Transaction; - participant?: Participant; - currentUserPersonalDetails?: OnyxEntry; -}; - /** * @private * @@ -823,47 +816,6 @@ function isSearchDataLoaded(currentSearchResults: SearchResults | undefined, las return isDataLoaded; } -function getSearchOnyxUpdate({currentUserPersonalDetails, participant, transaction}: GetSearchOnyxUpdateParams) { - const toAccountID = participant?.accountID; - const fromAccountID = currentUserPersonalDetails?.accountID; - const currentSearchQueryJSON = getCurrentSearchQueryJSON(); - - if (currentSearchQueryJSON && toAccountID != null && fromAccountID != null) { - const validSearchTypes: SearchDataTypes[] = [CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.DATA_TYPES.INVOICE]; - const shouldOptimisticallyUpdate = - currentSearchQueryJSON.status === CONST.SEARCH.STATUS.EXPENSE.ALL && validSearchTypes.includes(currentSearchQueryJSON.type) && currentSearchQueryJSON.flatFilters.length === 0; - - if (shouldOptimisticallyUpdate) { - return { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}` as const, - value: { - data: { - [ONYXKEYS.PERSONAL_DETAILS_LIST]: { - [toAccountID]: { - accountID: toAccountID, - displayName: participant?.displayName, - login: participant?.login, - }, - [fromAccountID]: { - accountID: fromAccountID, - avatar: currentUserPersonalDetails?.avatar, - displayName: currentUserPersonalDetails?.displayName, - login: currentUserPersonalDetails?.login, - }, - }, - [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { - accountID: fromAccountID, - managerID: toAccountID, - ...transaction, - }, - }, - }, - }; - } - } -} - export { getListItem, getSections, @@ -884,6 +836,5 @@ export { shouldShowEmptyState, compareValues, isSearchDataLoaded, - getSearchOnyxUpdate, }; export type {SavedSearchMenuItem, SearchTypeMenuItem}; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index d10c31a33622..4265404e2eb1 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -157,7 +157,7 @@ import { shouldCreateNewMoneyRequestReport as shouldCreateNewMoneyRequestReportReportUtils, updateReportPreview, } from '@libs/ReportUtils'; -import {getSearchOnyxUpdate} from '@libs/SearchUIUtils'; +import {getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; import {getSession} from '@libs/SessionUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; @@ -203,7 +203,7 @@ import type {QuickActionName} from '@src/types/onyx/QuickAction'; import type {InvoiceReceiver, InvoiceReceiverType} from '@src/types/onyx/Report'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; -import type {SearchPolicy, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; +import type {SearchDataTypes, SearchPolicy, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults'; import type {Comment, Receipt, ReceiptSource, Routes, SplitShares, TransactionChanges, TransactionCustomUnit, WaypointCollection} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import {clearByKey as clearPdfByOnyxKey} from './CachedPDFPaths'; @@ -597,6 +597,11 @@ type ReplaceReceipt = { source: string; }; +type GetSearchOnyxUpdateParams = { + transaction: OnyxTypes.Transaction; + participant?: Participant; +}; + let allTransactions: NonNullable> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, @@ -10541,6 +10546,47 @@ function resolveDuplicates(params: MergeDuplicatesParams) { API.write(WRITE_COMMANDS.RESOLVE_DUPLICATES, parameters, {optimisticData, failureData}); } +function getSearchOnyxUpdate({participant, transaction}: GetSearchOnyxUpdateParams) { + const toAccountID = participant?.accountID; + const fromAccountID = currentUserPersonalDetails?.accountID; + const currentSearchQueryJSON = getCurrentSearchQueryJSON(); + + if (currentSearchQueryJSON && toAccountID != null && fromAccountID != null) { + const validSearchTypes: SearchDataTypes[] = [CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.DATA_TYPES.INVOICE]; + const shouldOptimisticallyUpdate = + currentSearchQueryJSON.status === CONST.SEARCH.STATUS.EXPENSE.ALL && validSearchTypes.includes(currentSearchQueryJSON.type) && currentSearchQueryJSON.flatFilters.length === 0; + + if (shouldOptimisticallyUpdate) { + return { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}` as const, + value: { + data: { + [ONYXKEYS.PERSONAL_DETAILS_LIST]: { + [toAccountID]: { + accountID: toAccountID, + displayName: participant?.displayName, + login: participant?.login, + }, + [fromAccountID]: { + accountID: fromAccountID, + avatar: currentUserPersonalDetails?.avatar, + displayName: currentUserPersonalDetails?.displayName, + login: currentUserPersonalDetails?.login, + }, + }, + [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { + accountID: fromAccountID, + managerID: toAccountID, + ...transaction, + }, + }, + }, + }; + } + } +} + export { adjustRemainingSplitShares, getNextApproverAccountID, From 4e59bf496459bbefca705cf4de7e66f8ce217e47 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 13:37:41 -0400 Subject: [PATCH 13/17] fix eslint errors --- src/libs/actions/IOU.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 4265404e2eb1..f02d5d6953b3 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1701,7 +1701,6 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR const searchUpdate = getSearchOnyxUpdate({ transaction, participant, - currentUserPersonalDetails, }); if (searchUpdate) { @@ -2575,7 +2574,6 @@ function buildOnyxDataForTrackExpense({ const searchUpdate = getSearchOnyxUpdate({ transaction, participant, - currentUserPersonalDetails, }); if (searchUpdate) { From c91252b58cc24c9b073723f5775c642579fea7d9 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 13:43:01 -0400 Subject: [PATCH 14/17] add error state --- src/libs/actions/IOU.ts | 55 +++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index f02d5d6953b3..b5f21aedecbe 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1704,7 +1704,8 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }); if (searchUpdate) { - optimisticData.push(searchUpdate); + optimisticData.push(searchUpdate.optimisticData); + failureData.push(searchUpdate.failureData); } // We don't need to compute violations unless we're on a paid policy @@ -2577,7 +2578,8 @@ function buildOnyxDataForTrackExpense({ }); if (searchUpdate) { - optimisticData.push(searchUpdate); + optimisticData.push(searchUpdate.optimisticData); + failureData.push(searchUpdate.failureData); } // We don't need to compute violations unless we're on a paid policy @@ -10545,6 +10547,8 @@ function resolveDuplicates(params: MergeDuplicatesParams) { } function getSearchOnyxUpdate({participant, transaction}: GetSearchOnyxUpdateParams) { + const currentPersonalDetails = {...allPersonalDetails}; + const toAccountID = participant?.accountID; const fromAccountID = currentUserPersonalDetails?.accountID; const currentSearchQueryJSON = getCurrentSearchQueryJSON(); @@ -10556,27 +10560,40 @@ function getSearchOnyxUpdate({participant, transaction}: GetSearchOnyxUpdatePara if (shouldOptimisticallyUpdate) { return { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}` as const, - value: { - data: { - [ONYXKEYS.PERSONAL_DETAILS_LIST]: { - [toAccountID]: { - accountID: toAccountID, - displayName: participant?.displayName, - login: participant?.login, + optimisticData: { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}` as const, + value: { + data: { + [ONYXKEYS.PERSONAL_DETAILS_LIST]: { + [toAccountID]: { + accountID: toAccountID, + displayName: participant?.displayName, + login: participant?.login, + }, + [fromAccountID]: { + accountID: fromAccountID, + avatar: currentUserPersonalDetails?.avatar, + displayName: currentUserPersonalDetails?.displayName, + login: currentUserPersonalDetails?.login, + }, }, - [fromAccountID]: { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { accountID: fromAccountID, - avatar: currentUserPersonalDetails?.avatar, - displayName: currentUserPersonalDetails?.displayName, - login: currentUserPersonalDetails?.login, + managerID: toAccountID, + ...transaction, }, }, - [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { - accountID: fromAccountID, - managerID: toAccountID, - ...transaction, + }, + }, + + // On error, remove any personal details that were not successfully added + failureData: { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}` as const, + value: { + data: { + [ONYXKEYS.PERSONAL_DETAILS_LIST]: currentPersonalDetails, }, }, }, From 614a0f45e61a4d9f7ff72876c485df1cd1327786 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 14:12:50 -0400 Subject: [PATCH 15/17] remove failrue data, search corrects it --- src/libs/actions/IOU.ts | 55 ++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index b5f21aedecbe..5982bc475fc2 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1704,8 +1704,7 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR }); if (searchUpdate) { - optimisticData.push(searchUpdate.optimisticData); - failureData.push(searchUpdate.failureData); + optimisticData.push({...searchUpdate}); } // We don't need to compute violations unless we're on a paid policy @@ -2578,8 +2577,7 @@ function buildOnyxDataForTrackExpense({ }); if (searchUpdate) { - optimisticData.push(searchUpdate.optimisticData); - failureData.push(searchUpdate.failureData); + optimisticData.push({...searchUpdate}); } // We don't need to compute violations unless we're on a paid policy @@ -10547,8 +10545,6 @@ function resolveDuplicates(params: MergeDuplicatesParams) { } function getSearchOnyxUpdate({participant, transaction}: GetSearchOnyxUpdateParams) { - const currentPersonalDetails = {...allPersonalDetails}; - const toAccountID = participant?.accountID; const fromAccountID = currentUserPersonalDetails?.accountID; const currentSearchQueryJSON = getCurrentSearchQueryJSON(); @@ -10560,40 +10556,27 @@ function getSearchOnyxUpdate({participant, transaction}: GetSearchOnyxUpdatePara if (shouldOptimisticallyUpdate) { return { - optimisticData: { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}` as const, - value: { - data: { - [ONYXKEYS.PERSONAL_DETAILS_LIST]: { - [toAccountID]: { - accountID: toAccountID, - displayName: participant?.displayName, - login: participant?.login, - }, - [fromAccountID]: { - accountID: fromAccountID, - avatar: currentUserPersonalDetails?.avatar, - displayName: currentUserPersonalDetails?.displayName, - login: currentUserPersonalDetails?.login, - }, + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}` as const, + value: { + data: { + [ONYXKEYS.PERSONAL_DETAILS_LIST]: { + [toAccountID]: { + accountID: toAccountID, + displayName: participant?.displayName, + login: participant?.login, }, - [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { + [fromAccountID]: { accountID: fromAccountID, - managerID: toAccountID, - ...transaction, + avatar: currentUserPersonalDetails?.avatar, + displayName: currentUserPersonalDetails?.displayName, + login: currentUserPersonalDetails?.login, }, }, - }, - }, - - // On error, remove any personal details that were not successfully added - failureData: { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON.hash}` as const, - value: { - data: { - [ONYXKEYS.PERSONAL_DETAILS_LIST]: currentPersonalDetails, + [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { + accountID: fromAccountID, + managerID: toAccountID, + ...transaction, }, }, }, From 7ce978b58104edcad378026be61e210fe8841a83 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Tue, 15 Apr 2025 14:31:46 -0400 Subject: [PATCH 16/17] optimistic updates for Invoices --- src/libs/actions/IOU.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 5982bc475fc2..305da947860c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -535,6 +535,7 @@ type BuildOnyxDataForInvoiceParams = { }; companyName?: string; companyWebsite?: string; + participant?: Participant; }; type GetTrackExpenseInformationTransactionParams = { @@ -1742,7 +1743,8 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR /** Builds the Onyx data for an invoice */ function buildOnyxDataForInvoice(invoiceParams: BuildOnyxDataForInvoiceParams): [OnyxUpdate[], OnyxUpdate[], OnyxUpdate[]] { - const {chat, iou, transactionParams, policyParams, optimisticData: optimisticDataParams, companyName, companyWebsite} = invoiceParams; + const {chat, iou, transactionParams, policyParams, optimisticData: optimisticDataParams, companyName, companyWebsite, participant} = invoiceParams; + const transaction = transactionParams.transaction; const clearedPendingFields = Object.fromEntries(Object.keys(transactionParams.transaction.pendingFields ?? {}).map((key) => [key, null])); const optimisticData: OnyxUpdate[] = [ @@ -2122,6 +2124,15 @@ function buildOnyxDataForInvoice(invoiceParams: BuildOnyxDataForInvoiceParams): }); } + const searchUpdate = getSearchOnyxUpdate({ + transaction, + participant, + }); + + if (searchUpdate) { + optimisticData.push({...searchUpdate}); + } + // We don't need to compute violations unless we're on a paid policy if (!policyParams.policy || !isPaidGroupPolicy(policyParams.policy)) { return [optimisticData, successData, failureData]; @@ -2959,6 +2970,7 @@ function getSendInvoiceInformation( policyRecentlyUsedCategories: optimisticPolicyRecentlyUsedCategories, policyRecentlyUsedTags: optimisticPolicyRecentlyUsedTags, }, + participant: receiver, companyName, companyWebsite, }); From f021b90e4f7365cebe57f61f8f6c5dc51abf85b0 Mon Sep 17 00:00:00 2001 From: Jack Senyitko Date: Thu, 17 Apr 2025 07:31:31 -0400 Subject: [PATCH 17/17] update 1 letter var --- src/libs/SearchQueryUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index 228d19cef340..182002a5198c 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -870,8 +870,8 @@ function getCurrentSearchQueryJSON() { return; } - const {q} = lastSearchRoute.params as SearchFullscreenNavigatorParamList[typeof SCREENS.SEARCH.ROOT]; - const queryJSON = buildSearchQueryJSON(q); + const {q: searchParams} = lastSearchRoute.params as SearchFullscreenNavigatorParamList[typeof SCREENS.SEARCH.ROOT]; + const queryJSON = buildSearchQueryJSON(searchParams); if (!queryJSON) { return; }