Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
27 changes: 27 additions & 0 deletions src/libs/SearchQueryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -852,6 +856,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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of the route.name === NAVIGATORS.REPORTS_SPLIT_NAVIGATOR check here led to the following issue:

which we addressed by removing the check (see proposal for details).


if (!lastPolicyRoute) {
return;
}

const lastSearchRoute = lastPolicyRoute.state?.routes.findLast((route) => route.name === SCREENS.SEARCH.ROOT);
if (!lastSearchRoute || !lastSearchRoute.params) {
return;
}

const {q: searchParams} = lastSearchRoute.params as SearchFullscreenNavigatorParamList[typeof SCREENS.SEARCH.ROOT];
const queryJSON = buildSearchQueryJSON(searchParams);
if (!queryJSON) {
return;
}

return queryJSON;
}

/**
* Converts a filter key from old naming (camelCase) to user friendly naming (kebab-case).
*
Expand Down Expand Up @@ -895,6 +921,7 @@ export {
isCannedSearchQueryWithPolicyIDCheck,
sanitizeSearchValue,
getQueryWithUpdatedValues,
getCurrentSearchQueryJSON,
getUserFriendlyKey,
isDefaultExpensesQuery,
shouldHighlight,
Expand Down
90 changes: 87 additions & 3 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -202,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';
Expand Down Expand Up @@ -446,6 +447,7 @@ type BuildOnyxDataForMoneyRequestParams = {
policyParams?: BasePolicyParams;
optimisticParams: MoneyRequestOptimisticParams;
retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt;
participant?: Participant;
};

type DistanceRequestTransactionParams = BaseTransactionParams & {
Expand Down Expand Up @@ -533,6 +535,7 @@ type BuildOnyxDataForInvoiceParams = {
};
companyName?: string;
companyWebsite?: string;
participant?: Participant;
};

type GetTrackExpenseInformationTransactionParams = {
Expand Down Expand Up @@ -595,6 +598,11 @@ type ReplaceReceipt = {
source: string;
};

type GetSearchOnyxUpdateParams = {
transaction: OnyxTypes.Transaction;
participant?: Participant;
};

let allTransactions: NonNullable<OnyxCollection<OnyxTypes.Transaction>> = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.TRANSACTION,
Expand Down Expand Up @@ -1184,6 +1192,7 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR
policyParams = {},
optimisticParams,
retryParams,
participant,
} = moneyRequestParams;
const {policy, policyCategories, policyTagList} = policyParams;
const {
Expand All @@ -1200,6 +1209,7 @@ 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 optimisticData: OnyxUpdate[] = [];
const successData: OnyxUpdate[] = [];
let newQuickAction: ValueOf<typeof CONST.QUICK_ACTIONS>;
Expand Down Expand Up @@ -1689,6 +1699,15 @@ function buildOnyxDataForMoneyRequest(moneyRequestParams: BuildOnyxDataForMoneyR
});
}

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 (!policy || !isPaidGroupPolicy(policy)) {
return [optimisticData, successData, failureData];
Expand Down Expand Up @@ -1724,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[] = [
Expand Down Expand Up @@ -2104,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];
Expand Down Expand Up @@ -2140,6 +2169,7 @@ type BuildOnyxDataForTrackExpenseParams = {
existingTransactionThreadReportID?: string;
actionableTrackExpenseWhisper?: OnyxInputValue<OnyxTypes.ReportAction>;
retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt;
participant?: Participant;
};

/** Builds the Onyx data for track expense */
Expand All @@ -2152,6 +2182,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;
Expand All @@ -2161,6 +2192,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[] = [];
Expand Down Expand Up @@ -2550,6 +2582,15 @@ function buildOnyxDataForTrackExpense({
});
}

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 (!policy || !isPaidGroupPolicy(policy)) {
return [optimisticData, successData, failureData];
Expand Down Expand Up @@ -2929,6 +2970,7 @@ function getSendInvoiceInformation(
policyRecentlyUsedCategories: optimisticPolicyRecentlyUsedCategories,
policyRecentlyUsedTags: optimisticPolicyRecentlyUsedTags,
},
participant: receiver,
companyName,
companyWebsite,
});
Expand Down Expand Up @@ -3084,7 +3126,6 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma
participants: [participant],
transactionID: optimisticTransaction.transactionID,
paymentType: isSelectedManagerMcTest(participant.login) ? CONST.IOU.PAYMENT_TYPE.ELSEWHERE : undefined,

existingTransactionThreadReportID: linkedTrackedExpenseReportAction?.childReportID,
linkedTrackedExpenseReportAction,
});
Expand Down Expand Up @@ -3122,6 +3163,7 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma

// STEP 5: Build Onyx Data
const [optimisticData, successData, failureData] = buildOnyxDataForMoneyRequest({
participant,
isNewChatReport,
shouldCreateNewMoneyRequestReport,
policyParams: {
Expand Down Expand Up @@ -3577,6 +3619,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: {
Expand Down Expand Up @@ -10513,6 +10556,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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipping the filters here led to this issue #64128 where the query with filters do not get optimistically updated. This is not a mistake, just a case that was not considered before the to do queries are implemented.


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,
Expand Down