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

add display name replacement and make search page router editable #49838

Merged
Show file tree
Hide file tree
Changes from 3 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
23 changes: 22 additions & 1 deletion src/components/Search/SearchPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,30 @@ type HeaderWrapperProps = Pick<HeaderWithBackButtonProps, 'icon' | 'children'> &

function HeaderWrapper({icon, children, text, isCannedQuery}: HeaderWrapperProps) {
const styles = useThemeStyles();
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
const taxRates = getAllTaxRates();
const [cardList = {}] = useOnyx(ONYXKEYS.CARD_LIST);

const [input, setInput] = useState(text);

// If the icon is present, the header bar should be taller and use different font.
const isCentralPaneSettings = !!icon;

const onSubmit = () => {
if (!input) {
return;
}
const queryJSON = SearchUtils.buildSearchQueryJSON(input);
if (queryJSON) {
const standardQuery = SearchUtils.standardizeQueryJSON(queryJSON, cardList, reports, taxRates);
const query = SearchUtils.buildSearchQueryString(standardQuery);
SearchActions.clearAllFilters();
Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query}));
} else {
// Handle query parsing error
}
};

return (
<View
dataSet={{dragArea: false}}
Expand All @@ -69,12 +89,13 @@ function HeaderWrapper({icon, children, text, isCannedQuery}: HeaderWrapperProps
) : (
<View style={styles.pr5}>
<SearchRouterInput
disabled
isFullWidth
wrapperStyle={[styles.searchRouterInputResults, styles.br2]}
wrapperFocusedStyle={styles.searchRouterInputResultsFocused}
defaultValue={text}
rightComponent={children}
onSubmit={onSubmit}
onChange={setInput}
/>
</View>
)}
Expand Down
14 changes: 12 additions & 2 deletions src/components/Search/SearchRouter/SearchRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import debounce from 'lodash/debounce';
import React, {useCallback, useState} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal';
import Modal from '@components/Modal';
import type {SearchQueryJSON} from '@components/Search/types';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import {getAllTaxRates} from '@libs/PolicyUtils';
import * as SearchUtils from '@libs/SearchUtils';
import Navigation from '@navigation/Navigation';
import * as SearchActions from '@userActions/Search';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import {useSearchRouterContext} from './SearchRouterContext';
import SearchRouterInput from './SearchRouterInput';
Expand All @@ -22,6 +26,10 @@ function SearchRouter() {
const {isSmallScreenWidth} = useResponsiveLayout();
const {isSearchRouterDisplayed, closeSearchRouter} = useSearchRouterContext();

const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
const taxRates = getAllTaxRates();
const [cardList = {}] = useOnyx(ONYXKEYS.CARD_LIST);

const [userSearchQuery, setUserSearchQuery] = useState<SearchQueryJSON | undefined>(undefined);

const clearUserQuery = () => {
Expand Down Expand Up @@ -53,11 +61,13 @@ function SearchRouter() {

closeSearchRouter();

const query = SearchUtils.buildSearchQueryString(userSearchQuery);
const standardQuery = SearchUtils.standardizeQueryJSON(userSearchQuery, cardList, reports, taxRates);
const query = SearchUtils.buildSearchQueryString(standardQuery);
SearchActions.clearAllFilters();
Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query}));

clearUserQuery();
}, [closeSearchRouter, userSearchQuery]);
}, [closeSearchRouter, userSearchQuery, cardList, reports, taxRates]);

useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ESCAPE, () => {
closeSearchRouter();
Expand Down
1 change: 1 addition & 0 deletions src/components/Search/SearchRouter/SearchRouterInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ function SearchRouterInput({isFullWidth, onChange, onSubmit, defaultValue = '',
onSubmitEditing={onSubmit}
role={CONST.ROLE.PRESENTATION}
autoCapitalize="none"
autoCorrect={false}
disabled={disabled}
shouldUseDisabledStyles={false}
textInputContainerStyles={styles.borderNone}
Expand Down
77 changes: 71 additions & 6 deletions src/libs/SearchUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
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';
Expand Down Expand Up @@ -728,10 +729,10 @@ function getPolicyIDFromSearchQuery(queryJSON: SearchQueryJSON) {

function getDisplayValue(filterName: string, filter: string, personalDetails: OnyxTypes.PersonalDetailsList, cardList: OnyxTypes.CardList, reports: OnyxCollection<OnyxTypes.Report>) {
if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO) {
return PersonalDetailsUtils.createDisplayName(personalDetails?.[filter]?.login ?? '', personalDetails?.[filter]);
return personalDetails?.[filter]?.login ?? filter;
}
if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) {
return cardList[filter].bank;
return cardList[filter]?.bank ?? filter;
}
if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) {
return ReportUtils.getReportName(reports?.[`${ONYXKEYS.COLLECTION.REPORT}${filter}`]);
Expand Down Expand Up @@ -773,9 +774,15 @@ function getSearchHeaderTitle(
let displayQueryFilters: QueryFilter[] = [];
if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) {
const taxRateIDs = queryFilter.map((filter) => filter.value.toString());
const taxRateNames = Object.entries(TaxRates)
.filter(([, taxRateKeys]) => taxRateKeys.some((taxID) => taxRateIDs.includes(taxID)))
.map(([taxRate]) => taxRate);
const taxRateNames = taxRateIDs
.map((id) => {
const taxRate = Object.entries(TaxRates)
.filter(([, IDs]) => IDs.includes(id))
.map(([name]) => name);
return taxRate?.length > 0 ? taxRate : id;
})
.flat();

displayQueryFilters = taxRateNames.map((taxRate) => ({
operator: queryFilter.at(0)?.operator ?? CONST.SEARCH.SYNTAX_OPERATORS.AND,
value: taxRate,
Expand All @@ -786,7 +793,7 @@ function getSearchHeaderTitle(
value: getDisplayValue(key, filter.value.toString(), PersonalDetails, cardList, reports),
}));
}
title += buildFilterString(key, displayQueryFilters, ' ');
title += buildFilterString(key, displayQueryFilters, key === CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD ? ' ' : ',');
});

return title;
Expand Down Expand Up @@ -833,6 +840,63 @@ function getOverflowMenu(itemName: string, hash: number, inputQuery: string, sho
];
}

function getIDFromDisplayValue(filterName: string, filter: string | string[], cardList: OnyxTypes.CardList, reports: OnyxCollection<OnyxTypes.Report>, taxRates: Record<string, string[]>) {
if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO) {
if (typeof filter === 'string') {
const email = filter;
return PersonalDetailsUtils.getPersonalDetailByEmail(email)?.accountID.toString() ?? filter;
}
const emails = filter;
return emails.map((email) => PersonalDetailsUtils.getPersonalDetailByEmail(email)?.accountID.toString() ?? email);
}
Comment on lines +893 to +900
Copy link
Contributor

Choose a reason for hiding this comment

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

NAB the API can now handle emails directly, so we could remove this.

if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) {
const names = Array.isArray(filter) ? filter : ([filter] as string[]);
return names.map((name) => taxRates[name] ?? name).flat();
}
if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) {
if (typeof filter === 'string') {
const bank = filter;
const ids =
Object.values(cardList)
.filter((card) => card.bank === bank)
.map((card) => card.cardID.toString()) ?? filter;
return ids.length > 0 ? ids : bank;
}
const banks = filter;
return banks
.map(
(bank) =>
Object.values(cardList)
.filter((card) => card.bank === bank)
.map((card) => card.cardID.toString()) ?? bank,
)
.flat();
}
return filter;
}

function standardizeQueryJSON(queryJSON: SearchQueryJSON, cardList: OnyxTypes.CardList, reports: OnyxCollection<OnyxTypes.Report>, taxRates: Record<string, string[]>) {
const standardQuery = cloneDeep(queryJSON);
const filters = standardQuery.filters;
const traverse = (node: ASTNode) => {
if (!node.operator) {
return;
}
if (typeof node.left === 'object' && node.left) {
traverse(node.left);
}
if (typeof node.right === 'object' && node.right && !Array.isArray(node.right)) {
traverse(node.right);
}
// eslint-disable-next-line no-param-reassign
node.right = getIDFromDisplayValue(node.left as string, node.right as string | string[], cardList, reports, taxRates);
};

traverse(filters);
standardQuery.flatFilters = getFilters(standardQuery);
return standardQuery;
}

/**
* Returns whether a given search query is a Canned query.
*
Expand Down Expand Up @@ -869,4 +933,5 @@ export {
getExpenseTypeTranslationKey,
getOverflowMenu,
isCorrectSearchUserName,
standardizeQueryJSON,
};
Loading