Skip to content

Commit f3c6ec8

Browse files
committed
Merge branch 'main' into fix/67371-invalid-rate-error-not-seen
2 parents 376007c + a534833 commit f3c6ec8

17 files changed

Lines changed: 2074 additions & 6 deletions

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@
239239
"formatjs",
240240
"Français",
241241
"Frederico",
242+
"freetext",
242243
"frontpart",
243244
"fullstory",
244245
"FWTV",

src/CONST/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ const CONST = {
664664
BETAS: {
665665
ALL: 'all',
666666
ASAP_SUBMIT: 'asapSubmit',
667+
AUTH_AUTO_REPORT_TITLE: 'authAutoReportTitle',
667668
DEFAULT_ROOMS: 'defaultRooms',
668669
P2P_DISTANCE_REQUESTS: 'p2pDistanceRequests',
669670
SPOTNANA_TRAVEL: 'spotnanaTravel',
@@ -1515,6 +1516,9 @@ const CONST = {
15151516
APPLY_AIRSHIP_UPDATES: 'apply_airship_updates',
15161517
APPLY_PUSHER_UPDATES: 'apply_pusher_updates',
15171518
APPLY_HTTPS_UPDATES: 'apply_https_updates',
1519+
COMPUTE_REPORT_NAME: 'compute_report_name',
1520+
COMPUTE_REPORT_NAME_FOR_NEW_REPORT: 'compute_report_name_for_new_report',
1521+
UPDATE_OPTIMISTIC_REPORT_NAMES: 'update_optimistic_report_names',
15181522
COLD: 'cold',
15191523
WARM: 'warm',
15201524
REPORT_ACTION_ITEM_LAYOUT_DEBOUNCE_TIME: 1500,

src/components/ReportActionItem/MoneyRequestView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ function MoneyRequestView({
283283
policy?.defaultReimbursable !== undefined && !!(updatedTransaction?.reimbursable ?? transactionReimbursable) !== policy.defaultReimbursable;
284284
const shouldShowReimbursable =
285285
isPolicyExpenseChat && (!policy?.disabledFields?.reimbursable || isCurrentTransactionReimbursableDifferentFromPolicyDefault) && !isCardTransaction && !isInvoice;
286-
const canEditReimbursable = canUserPerformWriteAction && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.REIMBURSABLE);
286+
const canEditReimbursable = isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.REIMBURSABLE, undefined, isChatReportArchived);
287287
const shouldShowAttendees = useMemo(() => shouldShowAttendeesTransactionUtils(iouType, policy), [iouType, policy]);
288288

289289
const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest, isPerDiemRequest);

src/components/Search/SearchContext.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const defaultSearchContextData: SearchContextData = {
1515
selectedReports: [],
1616
isOnSearch: false,
1717
shouldTurnOffSelectionMode: false,
18+
shouldResetSearchQuery: false,
1819
};
1920

2021
const defaultSearchContext: SearchContext = {
@@ -31,6 +32,7 @@ const defaultSearchContext: SearchContext = {
3132
setShouldShowFiltersBarLoading: () => {},
3233
shouldShowSelectAllMatchingItems: () => {},
3334
selectAllMatchingItems: () => {},
35+
setShouldResetSearchQuery: () => {},
3436
};
3537

3638
const Context = React.createContext<SearchContext>(defaultSearchContext);
@@ -173,6 +175,13 @@ function SearchContextProvider({children}: ChildrenProps) {
173175
[searchContextData.selectedTransactionIDs, searchContextData.selectedTransactions],
174176
);
175177

178+
const setShouldResetSearchQuery = useCallback((shouldReset: boolean) => {
179+
setSearchContextData((prevState) => ({
180+
...prevState,
181+
shouldResetSearchQuery: shouldReset,
182+
}));
183+
}, []);
184+
176185
const searchContext = useMemo<SearchContext>(
177186
() => ({
178187
...searchContextData,
@@ -188,6 +197,7 @@ function SearchContextProvider({children}: ChildrenProps) {
188197
shouldShowSelectAllMatchingItems,
189198
areAllMatchingItemsSelected,
190199
selectAllMatchingItems,
200+
setShouldResetSearchQuery,
191201
}),
192202
[
193203
searchContextData,
@@ -200,6 +210,7 @@ function SearchContextProvider({children}: ChildrenProps) {
200210
shouldShowSelectAllMatchingItems,
201211
showSelectAllMatchingItems,
202212
areAllMatchingItemsSelected,
213+
setShouldResetSearchQuery,
203214
],
204215
);
205216

src/components/Search/SearchRouter/SearchRouter.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import OptionsListSkeletonView from '@components/OptionsListSkeletonView';
1212
import type {AnimatedTextInputRef} from '@components/RNTextInput';
1313
import type {GetAdditionalSectionsCallback} from '@components/Search/SearchAutocompleteList';
1414
import SearchAutocompleteList from '@components/Search/SearchAutocompleteList';
15+
import {useSearchContext} from '@components/Search/SearchContext';
1516
import SearchInputSelectionWrapper from '@components/Search/SearchInputSelectionWrapper';
1617
import type {SearchFilterKey, SearchQueryString} from '@components/Search/types';
1718
import type {SearchQueryItem} from '@components/SelectionList/Search/SearchQueryListItem';
@@ -84,6 +85,7 @@ type SearchRouterProps = {
8485
function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDisplayed}: SearchRouterProps, ref: React.Ref<View>) {
8586
const {translate} = useLocalize();
8687
const styles = useThemeStyles();
88+
const {setShouldResetSearchQuery} = useSearchContext();
8789
const [, recentSearchesMetadata] = useOnyx(ONYXKEYS.RECENT_SEARCHES, {canBeMissing: true});
8890
const {areOptionsInitialized} = useOptionsList();
8991
const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false, canBeMissing: true});
@@ -264,6 +266,9 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla
264266
return;
265267
}
266268

269+
// Reset the search query flag when performing a new search
270+
setShouldResetSearchQuery(false);
271+
267272
backHistory(() => {
268273
onRouterClose();
269274
Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query: updatedQuery}));
@@ -272,7 +277,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla
272277
setTextInputValue('');
273278
setAutocompleteQueryValue('');
274279
},
275-
[autocompleteSubstitutions, onRouterClose, setTextInputValue],
280+
[autocompleteSubstitutions, onRouterClose, setTextInputValue, setShouldResetSearchQuery],
276281
);
277282

278283
const setTextAndUpdateSelection = useCallback(

src/components/Search/index.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNa
2626
import Performance from '@libs/Performance';
2727
import {getIOUActionForTransactionID, isExportIntegrationAction, isIntegrationMessageAction} from '@libs/ReportActionsUtils';
2828
import {canEditFieldOfMoneyRequest, generateReportID, isArchivedReport} from '@libs/ReportUtils';
29-
import {buildSearchQueryString} from '@libs/SearchQueryUtils';
29+
import {buildCannedSearchQuery, buildSearchQueryString} from '@libs/SearchQueryUtils';
3030
import {
3131
getListItem,
3232
getSections,
@@ -185,6 +185,8 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
185185
shouldShowSelectAllMatchingItems,
186186
areAllMatchingItemsSelected,
187187
selectAllMatchingItems,
188+
shouldResetSearchQuery,
189+
setShouldResetSearchQuery,
188190
} = useSearchContext();
189191
const [offset, setOffset] = useState(0);
190192

@@ -658,6 +660,19 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
658660

659661
const hasErrors = Object.keys(searchResults?.errors ?? {}).length > 0 && !isOffline;
660662

663+
useEffect(() => {
664+
const currentRoute = Navigation.getActiveRouteWithoutParams();
665+
if (hasErrors && (currentRoute === '/' || (shouldResetSearchQuery && currentRoute === '/search'))) {
666+
// Use requestAnimationFrame to safely update navigation params without overriding the current route
667+
requestAnimationFrame(() => {
668+
Navigation.setParams({q: buildCannedSearchQuery()});
669+
});
670+
if (shouldResetSearchQuery) {
671+
setShouldResetSearchQuery(false);
672+
}
673+
}
674+
}, [hasErrors, queryJSON, searchResults, shouldResetSearchQuery, setShouldResetSearchQuery]);
675+
661676
const fetchMoreResults = useCallback(() => {
662677
if (!isFocused || !searchResults?.search?.hasMoreResults || shouldShowLoadingState || shouldShowLoadingMoreItems) {
663678
return;

src/components/Search/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ type SearchContextData = {
7777
selectedReports: SelectedReports[];
7878
isOnSearch: boolean;
7979
shouldTurnOffSelectionMode: boolean;
80+
shouldResetSearchQuery: boolean;
8081
};
8182

8283
type SearchContext = SearchContextData & {
@@ -100,6 +101,7 @@ type SearchContext = SearchContextData & {
100101
shouldShowSelectAllMatchingItems: (shouldShow: boolean) => void;
101102
areAllMatchingItemsSelected: boolean;
102103
selectAllMatchingItems: (on: boolean) => void;
104+
setShouldResetSearchQuery: (shouldReset: boolean) => void;
103105
};
104106

105107
type ASTNode = {

src/libs/API/index.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import Log from '@libs/Log';
77
import {handleDeletedAccount, HandleUnusedOptimisticID, Logging, Pagination, Reauthentication, RecheckConnection, SaveResponseInOnyx} from '@libs/Middleware';
88
import {isOffline} from '@libs/Network/NetworkStore';
99
import {push as pushToSequentialQueue, waitForIdle as waitForSequentialQueueIdle} from '@libs/Network/SequentialQueue';
10+
import * as OptimisticReportNames from '@libs/OptimisticReportNames';
11+
import {getUpdateContext, initialize as initializeOptimisticReportNamesContext} from '@libs/OptimisticReportNamesConnectionManager';
1012
import Pusher from '@libs/Pusher';
1113
import {processWithMiddleware, use} from '@libs/Request';
1214
import {getAll, getLength as getPersistedRequestsLength} from '@userActions/PersistedRequests';
@@ -15,7 +17,7 @@ import type OnyxRequest from '@src/types/onyx/Request';
1517
import type {PaginatedRequest, PaginationConfig, RequestConflictResolver} from '@src/types/onyx/Request';
1618
import type Response from '@src/types/onyx/Response';
1719
import type {ApiCommand, ApiRequestCommandParameters, ApiRequestType, CommandOfType, ReadCommand, SideEffectRequestCommand, WriteCommand} from './types';
18-
import {READ_COMMANDS} from './types';
20+
import {READ_COMMANDS, WRITE_COMMANDS} from './types';
1921

2022
// Setup API middlewares. Each request made will pass through a series of middleware functions that will get called in sequence (each one passing the result of the previous to the next).
2123
// Note: The ordering here is intentional as we want to Log, Recheck Connection, Reauthenticate, and Save the Response in Onyx. Errors thrown in one middleware will bubble to the next.
@@ -42,6 +44,11 @@ use(Pagination);
4244
// middlewares after this, because the SequentialQueue depends on the result of this middleware to pause the queue (if needed) to bring the app to an up-to-date state.
4345
use(SaveResponseInOnyx);
4446

47+
// Initialize OptimisticReportNames context on module load
48+
initializeOptimisticReportNamesContext().catch(() => {
49+
Log.warn('Failed to initialize OptimisticReportNames context');
50+
});
51+
4552
let requestIndex = 0;
4653

4754
type OnyxData = {
@@ -74,7 +81,22 @@ function prepareRequest<TCommand extends ApiCommand>(
7481
const {optimisticData, ...onyxDataWithoutOptimisticData} = onyxData;
7582
if (optimisticData && shouldApplyOptimisticData) {
7683
Log.info('[API] Applying optimistic data', false, {command, type});
77-
Onyx.update(optimisticData);
84+
85+
// Process optimistic data through report name middleware
86+
// Skip for OpenReport command to avoid unnecessary processing
87+
if (command === WRITE_COMMANDS.OPEN_REPORT) {
88+
Onyx.update(optimisticData);
89+
} else {
90+
try {
91+
const context = getUpdateContext();
92+
const processedOptimisticData = OptimisticReportNames.updateOptimisticReportNamesFromUpdates(optimisticData, context);
93+
Onyx.update(processedOptimisticData);
94+
} catch (error) {
95+
Log.warn('[API] Failed to process optimistic report names', {error});
96+
// Fallback to original optimistic data if processing fails
97+
Onyx.update(optimisticData);
98+
}
99+
}
78100
}
79101

80102
const isWriteRequest = type === CONST.API_REQUEST_TYPE.WRITE;

0 commit comments

Comments
 (0)