Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search router polish #50921

Merged
4 changes: 2 additions & 2 deletions src/components/HeaderWithBackButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ function HeaderWithBackButton({
/>
)}
{middleContent}
<View style={[styles.reportOptions, styles.flexRow, styles.pr5, styles.gap4, styles.alignItemsCenter]}>
<View style={[styles.reportOptions, styles.flexRow, styles.pr5, styles.alignItemsCenter]}>
{children}
{shouldShowDownloadButton && (
<Tooltip text={translate('common.download')}>
Expand Down Expand Up @@ -263,7 +263,7 @@ function HeaderWithBackButton({
</PressableWithoutFeedback>
</Tooltip>
)}
{shouldDisplaySearchRouter && <SearchButton />}
{shouldDisplaySearchRouter && <SearchButton style={styles.ml2} />}
</View>
</View>
</View>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Search/SearchPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function HeaderWrapper({icon, children, text, value, isCannedQuery, onSubmit, se
/>
)}
<Header subtitle={<Text style={[styles.textLarge, styles.textHeadlineH2]}>{text}</Text>} />
<View style={[styles.reportOptions, styles.flexRow, styles.pr5, styles.alignItemsCenter, styles.gap4]}>{children}</View>
<View style={[styles.reportOptions, styles.flexRow, styles.pr5, styles.alignItemsCenter, styles.gap2]}>{children}</View>
</View>
) : (
<View style={styles.pr5}>
Expand Down
31 changes: 17 additions & 14 deletions src/components/Search/SearchRouter/SearchButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {StyleProp, ViewStyle} from 'react-native';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import {PressableWithoutFeedback} from '@components/Pressable';
import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
Expand All @@ -23,21 +24,23 @@ function SearchButton({style}: SearchButtonProps) {
const {openSearchRouter} = useSearchRouterContext();

return (
<PressableWithoutFeedback
accessibilityLabel={translate('common.search')}
style={[styles.flexRow, styles.touchableButtonImage, style]}
onPress={Session.checkIfActionIsAllowed(() => {
Timing.start(CONST.TIMING.SEARCH_ROUTER_RENDER);
Performance.markStart(CONST.TIMING.SEARCH_ROUTER_RENDER);
<Tooltip text={translate('common.search')}>
<PressableWithoutFeedback
accessibilityLabel={translate('common.search')}
style={[styles.flexRow, styles.touchableButtonImage, style]}
onPress={Session.checkIfActionIsAllowed(() => {
Timing.start(CONST.TIMING.SEARCH_ROUTER_RENDER);
Performance.markStart(CONST.TIMING.SEARCH_ROUTER_RENDER);

openSearchRouter();
})}
>
<Icon
src={Expensicons.MagnifyingGlass}
fill={theme.icon}
/>
</PressableWithoutFeedback>
openSearchRouter();
})}
>
<Icon
src={Expensicons.MagnifyingGlass}
fill={theme.icon}
/>
</PressableWithoutFeedback>
</Tooltip>
);
}

Expand Down
11 changes: 7 additions & 4 deletions src/components/Search/SearchRouter/SearchRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,13 @@ function SearchRouter({onRouterClose}: SearchRouterProps) {
}, [debouncedInputValue, searchOptions]);

const recentReports: OptionData[] = useMemo(() => {
const currentSearchOptions = debouncedInputValue === '' ? searchOptions : filteredOptions;
const reports: OptionData[] = [...currentSearchOptions.recentReports, ...currentSearchOptions.personalDetails];
if (currentSearchOptions.userToInvite) {
reports.push(currentSearchOptions.userToInvite);
if (debouncedInputValue === '') {
return searchOptions.recentReports.slice(0, 10);
Copy link
Member

Choose a reason for hiding this comment

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

I'm curious if that doesn't change previous logic.

Originally we had debouncedInputValue === '' ? searchOptions - so it meant that if the input is empty, we push in the searchOptions (so I guess contextual search?).

But now we only show recent reports? Is that the change we want?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Contextual search is added independently earlier in the code. This block of code was only responsible for adding recent chats section(which consisted of recentReports, personalDetails and userToInvite). Right now in case when text input is empty, we are limiting the recent chats section to only recentReports.

}

const reports: OptionData[] = [...filteredOptions.recentReports, ...filteredOptions.personalDetails];
if (filteredOptions.userToInvite) {
reports.push(filteredOptions.userToInvite);
}
return reports.slice(0, 10);
}, [debouncedInputValue, filteredOptions, searchOptions]);
Expand Down
39 changes: 34 additions & 5 deletions src/libs/SearchUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import cloneDeep from 'lodash/cloneDeep';
import type {OnyxCollection} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import type {ASTNode, QueryFilter, QueryFilters, SearchColumnType, SearchQueryJSON, SearchQueryString, SearchStatus, SortOrder} from '@components/Search/types';
import type {AdvancedFiltersKeys, ASTNode, QueryFilter, QueryFilters, SearchColumnType, SearchQueryJSON, SearchQueryString, SearchStatus, SortOrder} from '@components/Search/types';
import ChatListItem from '@components/SelectionList/ChatListItem';
import ReportListItem from '@components/SelectionList/Search/ReportListItem';
import TransactionListItem from '@components/SelectionList/Search/TransactionListItem';
Expand Down Expand Up @@ -406,8 +406,34 @@ function isSearchResultsEmpty(searchResults: SearchResults) {
return !Object.keys(searchResults?.data).some((key) => key.startsWith(ONYXKEYS.COLLECTION.TRANSACTION));
}

function getQueryHashFromString(query: SearchQueryString): number {
return UserUtils.hashText(query, 2 ** 32);
function getQueryHash(query: SearchQueryJSON): number {
let orderedQuery = '';
if (query.policyID) {
orderedQuery += `policyID ${query.policyID} `;
}
orderedQuery += `type ${query.type} `;
orderedQuery += ` status ${query.status}`;
orderedQuery += ` sortBy ${query.sortBy} `;
orderedQuery += ` sortOrder ${query.sortOrder} `;
Copy link
Member

Choose a reason for hiding this comment

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

please use consts here, eg. SYNTAX_ROOT_KEYS.TYPE etc


Object.keys(query.flatFilters)
.sort()
.forEach((key) => {
const filterValues = query.flatFilters?.[key as AdvancedFiltersKeys];
orderedQuery += ` ${key}`;
filterValues
?.sort((queryFilter1, queryFilter2) => {
if (queryFilter1.value > queryFilter2.value) {
return 1;
}
return -1;
})
?.forEach((queryFilter) => {
orderedQuery += ` ${queryFilter.operator} ${queryFilter.value}`;
});
});

return UserUtils.hashText(orderedQuery, 2 ** 32);
}

function getExpenseTypeTranslationKey(expenseType: ValueOf<typeof CONST.SEARCH.TRANSACTION_TYPE>): TranslationPaths {
Expand Down Expand Up @@ -541,12 +567,13 @@ function getFilters(queryJSON: SearchQueryJSON) {
function buildSearchQueryJSON(query: SearchQueryString) {
try {
const result = searchParser.parse(query) as SearchQueryJSON;
// console.log('%%%%%\n', 'result', result);
Copy link
Member

Choose a reason for hiding this comment

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

remove this :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Haste makes waste 😄

const flatFilters = getFilters(result);

// Add the full input and hash to the results
result.inputQuery = query;
result.hash = getQueryHashFromString(query);
result.flatFilters = flatFilters;
result.hash = getQueryHash(result);
return result;
} catch (e) {
console.error(`Error when parsing SearchQuery: "${query}"`, e);
Expand Down Expand Up @@ -592,6 +619,9 @@ function buildQueryStringFromFilterFormValues(filterValues: Partial<SearchAdvanc
const {type, status, policyID, ...otherFilters} = filterValues;
const filtersString: string[] = [];

filtersString.push(`${CONST.SEARCH.SYNTAX_ROOT_KEYS.SORT_BY}:${CONST.SEARCH.TABLE_COLUMNS.DATE}`);
filtersString.push(`${CONST.SEARCH.SYNTAX_ROOT_KEYS.SORT_ORDER}:${CONST.SEARCH.SORT_ORDER.DESC}`);
Comment on lines +617 to +618
Copy link
Contributor

Choose a reason for hiding this comment

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

This will cause the saved query name to include sortBy and sortOrder, and the highlight on the saved search item will be lost when we change the sort order.

Screen.Recording.2024-10-18.at.11.06.40.AM.mov

Copy link
Contributor

@rayane-d rayane-d Oct 18, 2024

Choose a reason for hiding this comment

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

The highlight on the saved search item will be lost when we change the sort order bug is reproducible on main and can be fixed in a separate PR. But the saved query name include sortBy and sortOrder bug is specific to this PR

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you know why the saved query name includes sortBy and sortOrder but not policyID?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

highlight on the saved search item will be lost when we change the sort order

I tend to lean toward idea that this is not a bug. For me searches differing only with sortBy or sortOrder are two different queries. Reason:

  1. There might be a user which has about 1000 expenses with category 'Advertising'
  2. When he queries for category 'Advertising' he will get a paginated result with only 50 of newest expenses
  3. When we will change sortOrder then he will get 50 oldest expenses. These two queries are giving us different responses

Copy link
Contributor

@rayane-d rayane-d Oct 18, 2024

Choose a reason for hiding this comment

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

Good point. However, we are not including sortBy, sortOrder, and policyID in the displayed query (in the header input and recent searches) because we want users to rely on the contextual filters rather than manually typing these parameters. Therefore, it doesn't make sense to include them in the saved search item name.

Also, please see this comment: #50921 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree that those are different queries. We should treat these params the same way we do with status, since to @rayane-djouah's point, we use the contextual filters to change these values instead of manually typing them in.


if (type) {
const sanitizedType = sanitizeString(type);
filtersString.push(`${CONST.SEARCH.SYNTAX_ROOT_KEYS.TYPE}:${sanitizedType}`);
Expand Down Expand Up @@ -652,7 +682,6 @@ function buildQueryStringFromFilterFormValues(filterValues: Partial<SearchAdvanc

const amountFilter = buildAmountFilterQuery(filterValues);
filtersString.push(amountFilter);

return filtersString.join(' ').trim();
}

Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/HeaderView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ function HeaderView({report, parentReportAction, reportID, onNavigationMenuButto
{isTaskReport && !shouldUseNarrowLayout && ReportUtils.isOpenTaskReport(report, parentReportAction) && <TaskHeaderActionButton report={report} />}
{canJoin && !shouldUseNarrowLayout && joinButton}
</View>
{shouldDisplaySearchRouter && <SearchButton style={styles.ml4} />}
{shouldDisplaySearchRouter && <SearchButton style={styles.ml2} />}
</View>
<ConfirmModal
isVisible={isDeleteTaskConfirmModalVisible}
Expand Down
Loading