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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,8 @@ const CONST = {
SKELETON_ANIMATION_SPEED: 3,
SEARCH_OPTIONS_COMPARISON: 'search_options_comparison',
SEARCH_MOST_RECENT_OPTIONS: 'search_most_recent_options',
DEBOUNCE_HANDLE_SEARCH: 'debounce_handle_search',
FAST_SEARCH_TREE_CREATION: 'fast_search_tree_creation',
},
PRIORITY_MODE: {
GSD: 'gsd',
Expand Down
128 changes: 110 additions & 18 deletions src/components/Search/SearchAutocompleteList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import {getCardFeedKey, getCardFeedNamesWithType} from '@libs/CardFeedUtils';
import {getCardDescription, isCard, isCardHiddenFromSearch, mergeCardListWithWorkspaceFeeds} from '@libs/CardUtils';
import Log from '@libs/Log';
import memoize from '@libs/memoize';
import type {Options, SearchOption} from '@libs/OptionsListUtils';
import {combineOrderingOfReportsAndPersonalDetails, getSearchOptions, getValidPersonalDetailOptions, optionsOrderBy, recentReportComparator} from '@libs/OptionsListUtils';
Expand Down Expand Up @@ -497,33 +498,124 @@ function SearchAutocompleteList(
const {search: filterOptions, isInitialized: isFastSearchInitialized} = useFastSearchFromOptions(searchOptions, {includeUserToInvite: true});

const recentReportsOptions = useMemo(() => {
Timing.start(CONST.TIMING.SEARCH_FILTER_OPTIONS);
if (autocompleteQueryValue.trim() === '' || !isFastSearchInitialized) {
const orderedReportOptions = optionsOrderBy(searchOptions.recentReports, 20, recentReportComparator);
Timing.end(CONST.TIMING.SEARCH_FILTER_OPTIONS);
return orderedReportOptions;
}
const actionId = `filter_options_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
const startTime = Date.now();

const filteredOptions = filterOptions(autocompleteQueryValue);
const orderedOptions = combineOrderingOfReportsAndPersonalDetails(filteredOptions, autocompleteQueryValue, {
sortByReportTypeInSearch: true,
preferChatRoomsOverThreads: true,
Timing.start(CONST.TIMING.SEARCH_FILTER_OPTIONS);
Performance.markStart(CONST.TIMING.SEARCH_FILTER_OPTIONS);
Log.info('[CMD_K_DEBUG] Filter options started', false, {
actionId,
queryLength: autocompleteQueryValue.length,
queryTrimmed: autocompleteQueryValue.trim(),
isFastSearchInitialized,
recentReportsCount: searchOptions.recentReports.length,
timestamp: startTime,
});

const reportOptions: OptionData[] = [...orderedOptions.recentReports, ...orderedOptions.personalDetails];
if (filteredOptions.userToInvite) {
reportOptions.push(filteredOptions.userToInvite);
try {
if (autocompleteQueryValue.trim() === '' || !isFastSearchInitialized) {
const orderedReportOptions = optionsOrderBy(searchOptions.recentReports, 20, recentReportComparator);

const endTime = Date.now();
Timing.end(CONST.TIMING.SEARCH_FILTER_OPTIONS);
Performance.markEnd(CONST.TIMING.SEARCH_FILTER_OPTIONS);
Log.info('[CMD_K_DEBUG] Filter options completed (empty query path)', false, {
actionId,
duration: endTime - startTime,
resultCount: orderedReportOptions.length,
timestamp: endTime,
});

return orderedReportOptions;
}

const filteredOptions = filterOptions(autocompleteQueryValue);
const orderedOptions = combineOrderingOfReportsAndPersonalDetails(filteredOptions, autocompleteQueryValue, {
sortByReportTypeInSearch: true,
preferChatRoomsOverThreads: true,
});

const reportOptions: OptionData[] = [...orderedOptions.recentReports, ...orderedOptions.personalDetails];
if (filteredOptions.userToInvite) {
reportOptions.push(filteredOptions.userToInvite);
}

const finalOptions = reportOptions.slice(0, 20);
const endTime = Date.now();
Timing.end(CONST.TIMING.SEARCH_FILTER_OPTIONS);
Performance.markEnd(CONST.TIMING.SEARCH_FILTER_OPTIONS);
Log.info('[CMD_K_DEBUG] Filter options completed (search path)', false, {
actionId,
duration: endTime - startTime,
recentReportsFiltered: orderedOptions.recentReports.length,
personalDetailsFiltered: orderedOptions.personalDetails.length,
hasUserToInvite: !!filteredOptions.userToInvite,
finalResultCount: finalOptions.length,
timestamp: endTime,
});

return finalOptions;
} catch (error) {
const endTime = Date.now();
Timing.end(CONST.TIMING.SEARCH_FILTER_OPTIONS);
Performance.markEnd(CONST.TIMING.SEARCH_FILTER_OPTIONS);
Log.alert('[CMD_K_FREEZE] Filter options failed', {
actionId,
error: String(error),
duration: endTime - startTime,
queryLength: autocompleteQueryValue.length,
timestamp: endTime,
});
throw error;
}
Timing.end(CONST.TIMING.SEARCH_FILTER_OPTIONS);
return reportOptions.slice(0, 20);
}, [autocompleteQueryValue, filterOptions, searchOptions, isFastSearchInitialized]);

const debounceHandleSearch = useDebounce(
useCallback(() => {
if (!handleSearch || !autocompleteQueryWithoutFilters) {
return;
const actionId = `debounce_search_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
const startTime = Date.now();

Performance.markStart(CONST.TIMING.DEBOUNCE_HANDLE_SEARCH);
Log.info('[CMD_K_DEBUG] Debounced search started', false, {
actionId,
queryLength: autocompleteQueryWithoutFilters?.length ?? 0,
hasHandleSearch: !!handleSearch,
timestamp: startTime,
});

try {
if (!handleSearch || !autocompleteQueryWithoutFilters) {
Log.info('[CMD_K_DEBUG] Debounced search skipped - missing dependencies', false, {
actionId,
hasHandleSearch: !!handleSearch,
hasQuery: !!autocompleteQueryWithoutFilters,
timestamp: Date.now(),
});
return;
}

handleSearch(autocompleteQueryWithoutFilters);

const endTime = Date.now();
Performance.markEnd(CONST.TIMING.DEBOUNCE_HANDLE_SEARCH);
Log.info('[CMD_K_DEBUG] Debounced search completed', false, {
actionId,
duration: endTime - startTime,
queryLength: autocompleteQueryWithoutFilters.length,
timestamp: endTime,
});
} catch (error) {
const endTime = Date.now();
Performance.markEnd(CONST.TIMING.DEBOUNCE_HANDLE_SEARCH);
Log.alert('[CMD_K_FREEZE] Debounced search failed', {
actionId,
error: String(error),
duration: endTime - startTime,
queryLength: autocompleteQueryWithoutFilters?.length ?? 0,
timestamp: endTime,
});
throw error;
}
handleSearch(autocompleteQueryWithoutFilters);
}, [handleSearch, autocompleteQueryWithoutFilters]),
CONST.TIMING.SEARCH_OPTION_LIST_DEBOUNCE_TIME,
);
Expand Down
162 changes: 129 additions & 33 deletions src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {navigateToAndOpenReport} from '@libs/actions/Report';
import {clearAllFilters} from '@libs/actions/Search';
import {getCardFeedNamesWithType} from '@libs/CardFeedUtils';
import {mergeCardListWithWorkspaceFeeds} from '@libs/CardUtils';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
import {getAllTaxRates} from '@libs/PolicyUtils';
import type {OptionData} from '@libs/ReportUtils';
Expand Down Expand Up @@ -149,20 +150,51 @@ function SearchPageHeaderInput({queryJSON, searchRouterListVisible, hideSearchRo
);
const onSearchQueryChange = useCallback(
(userQuery: string) => {
const singleLineUserQuery = StringUtils.lineBreaksToSpaces(userQuery, true);
const updatedUserQuery = getAutocompleteQueryWithComma(textInputValue, singleLineUserQuery);
setTextInputValue(updatedUserQuery);
setAutocompleteQueryValue(updatedUserQuery);

const updatedSubstitutionsMap = getUpdatedSubstitutionsMap(singleLineUserQuery, autocompleteSubstitutions);
if (!deepEqual(autocompleteSubstitutions, updatedSubstitutionsMap) && !isEmpty(updatedSubstitutionsMap)) {
setAutocompleteSubstitutions(updatedSubstitutionsMap);
}
const actionId = `page_search_query_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
const startTime = Date.now();

Log.info('[CMD_K_DEBUG] Page search query change started', false, {
actionId,
inputLength: userQuery.length,
previousInputLength: textInputValue.length,
timestamp: startTime,
});

try {
const singleLineUserQuery = StringUtils.lineBreaksToSpaces(userQuery, true);
const updatedUserQuery = getAutocompleteQueryWithComma(textInputValue, singleLineUserQuery);
setTextInputValue(updatedUserQuery);
setAutocompleteQueryValue(updatedUserQuery);

const updatedSubstitutionsMap = getUpdatedSubstitutionsMap(singleLineUserQuery, autocompleteSubstitutions);
if (!deepEqual(autocompleteSubstitutions, updatedSubstitutionsMap) && !isEmpty(updatedSubstitutionsMap)) {
setAutocompleteSubstitutions(updatedSubstitutionsMap);
}

if (updatedUserQuery) {
listRef.current?.updateAndScrollToFocusedIndex(0);
} else {
listRef.current?.updateAndScrollToFocusedIndex(-1);
}

if (updatedUserQuery) {
listRef.current?.updateAndScrollToFocusedIndex(0);
} else {
listRef.current?.updateAndScrollToFocusedIndex(-1);
const endTime = Date.now();
Log.info('[CMD_K_DEBUG] Page search query change completed', false, {
actionId,
duration: endTime - startTime,
finalInputLength: updatedUserQuery.length,
substitutionsUpdated: !deepEqual(autocompleteSubstitutions, updatedSubstitutionsMap) && !isEmpty(updatedSubstitutionsMap),
timestamp: endTime,
});
} catch (error) {
const endTime = Date.now();
Log.alert('[CMD_K_FREEZE] Page search query change failed', {
actionId,
error: String(error),
duration: endTime - startTime,
inputLength: userQuery.length,
timestamp: endTime,
});
throw error;
}
},
[autocompleteSubstitutions, setTextInputValue, textInputValue],
Expand Down Expand Up @@ -191,29 +223,93 @@ function SearchPageHeaderInput({queryJSON, searchRouterListVisible, hideSearchRo

const onListItemPress = useCallback(
(item: OptionData | SearchQueryItem) => {
if (isSearchQueryItem(item)) {
if (!item.searchQuery) {
return;
}

if (item.searchItemType === CONST.SEARCH.SEARCH_ROUTER_ITEM_TYPE.AUTOCOMPLETE_SUGGESTION && textInputValue) {
const trimmedUserSearchQuery = getQueryWithoutAutocompletedPart(textInputValue);
const newSearchQuery = `${trimmedUserSearchQuery}${sanitizeSearchValue(item.searchQuery)}\u00A0`;
onSearchQueryChange(newSearchQuery);
setSelection({start: newSearchQuery.length, end: newSearchQuery.length});

if (item.mapKey && item.autocompleteID) {
const substitutions = {...autocompleteSubstitutions, [item.mapKey]: item.autocompleteID};
const actionId = `page_list_item_press_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
const startTime = Date.now();

Log.info('[CMD_K_DEBUG] Page list item press started', false, {
actionId,
itemType: isSearchQueryItem(item) ? 'SearchQueryItem' : 'OptionData',
searchItemType: isSearchQueryItem(item) ? item.searchItemType : undefined,
hasSearchQuery: isSearchQueryItem(item) ? !!item.searchQuery : undefined,
hasReportID: 'reportID' in item ? !!item.reportID : undefined,
hasLogin: 'login' in item ? !!item.login : undefined,
timestamp: startTime,
});

try {
if (isSearchQueryItem(item)) {
if (!item.searchQuery) {
Log.info('[CMD_K_DEBUG] Page list item press skipped - no search query', false, {
actionId,
itemType: 'SearchQueryItem',
timestamp: Date.now(),
});
return;
}

setAutocompleteSubstitutions(substitutions);
if (item.searchItemType === CONST.SEARCH.SEARCH_ROUTER_ITEM_TYPE.AUTOCOMPLETE_SUGGESTION && textInputValue) {
const trimmedUserSearchQuery = getQueryWithoutAutocompletedPart(textInputValue);
const newSearchQuery = `${trimmedUserSearchQuery}${sanitizeSearchValue(item.searchQuery)}\u00A0`;
onSearchQueryChange(newSearchQuery);
setSelection({start: newSearchQuery.length, end: newSearchQuery.length});

if (item.mapKey && item.autocompleteID) {
const substitutions = {...autocompleteSubstitutions, [item.mapKey]: item.autocompleteID};
setAutocompleteSubstitutions(substitutions);
}

const endTime = Date.now();
Log.info('[CMD_K_DEBUG] Page autocomplete suggestion handled', false, {
actionId,
duration: endTime - startTime,
trimmedQueryLength: trimmedUserSearchQuery.length,
newQueryLength: newSearchQuery.length,
hasMapKey: !!(item.mapKey && item.autocompleteID),
timestamp: endTime,
});
} else if (item.searchItemType === CONST.SEARCH.SEARCH_ROUTER_ITEM_TYPE.SEARCH) {
submitSearch(item.searchQuery);

const endTime = Date.now();
Log.info('[CMD_K_DEBUG] Page search submitted', false, {
actionId,
duration: endTime - startTime,
searchQuery: item.searchQuery,
timestamp: endTime,
});
}
} else if (item.searchItemType === CONST.SEARCH.SEARCH_ROUTER_ITEM_TYPE.SEARCH) {
submitSearch(item.searchQuery);
} else if (item?.reportID) {
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item?.reportID));

const endTime = Date.now();
Log.info('[CMD_K_DEBUG] Page report navigation handled', false, {
actionId,
duration: endTime - startTime,
reportID: item.reportID,
timestamp: endTime,
});
} else if ('login' in item) {
navigateToAndOpenReport(item.login ? [item.login] : [], false);

const endTime = Date.now();
Log.info('[CMD_K_DEBUG] Page user navigation handled', false, {
actionId,
duration: endTime - startTime,
hasLogin: !!item.login,
timestamp: endTime,
});
}
} else if (item?.reportID) {
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item?.reportID));
} else if ('login' in item) {
navigateToAndOpenReport(item.login ? [item.login] : [], false);
} catch (error) {
const endTime = Date.now();
Log.alert('[CMD_K_FREEZE] Page list item press failed', {
actionId,
error: String(error),
duration: endTime - startTime,
itemType: isSearchQueryItem(item) ? 'SearchQueryItem' : 'OptionData',
searchItemType: isSearchQueryItem(item) ? item.searchItemType : undefined,
timestamp: endTime,
});
throw error;
}
},
[autocompleteSubstitutions, onSearchQueryChange, submitSearch, textInputValue],
Expand Down
Loading
Loading