Skip to content

Commit

Permalink
Merge pull request #45617 from software-mansion-labs/adamgrzybowski/u…
Browse files Browse the repository at this point in the history
…se-new-query-syntax

[Search v2] [App] Use new query syntax
  • Loading branch information
luacmartins authored Jul 25, 2024
2 parents 2bd9f0e + 1fc7be1 commit 5df5630
Show file tree
Hide file tree
Showing 24 changed files with 231 additions and 198 deletions.
11 changes: 9 additions & 2 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5235,12 +5235,20 @@ const CONST = {
ASC: 'asc',
DESC: 'desc',
},
TAB: {
STATUS: {
ALL: 'all',
SHARED: 'shared',
DRAFTS: 'drafts',
FINISHED: 'finished',
},
TAB: {
EXPENSE: {
ALL: 'type:expense status:all',
SHARED: 'type:expense status:shared',
DRAFTS: 'type:expense status:drafts',
FINISHED: 'type:expense status:finished',
},
},
TABLE_COLUMNS: {
RECEIPT: 'receipt',
DATE: 'date',
Expand Down Expand Up @@ -5270,7 +5278,6 @@ const CONST = {
STATUS: 'status',
SORT_BY: 'sortBy',
SORT_ORDER: 'sortOrder',
OFFSET: 'offset',
},
SYNTAX_FILTER_KEYS: {
DATE: 'date',
Expand Down
25 changes: 7 additions & 18 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type {TupleToUnion, ValueOf} from 'type-fest';
import type {SearchQueryString} from './components/Search/types';
import type CONST from './CONST';
import type {IOUAction, IOUType} from './CONST';
import type {IOURequestType} from './libs/actions/IOU';
import type {AuthScreensParamList} from './libs/Navigation/types';
import type {ConnectionName, SageIntacctMappingName} from './types/onyx/Policy';
import type {SearchQuery} from './types/onyx/SearchResults';
import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual';

// This is a file containing constants for all the routes we want to be able to go to
Expand Down Expand Up @@ -37,16 +36,9 @@ const ROUTES = {
ALL_SETTINGS: 'all-settings',

SEARCH_CENTRAL_PANE: {
route: '/search/:query',
getRoute: (searchQuery: SearchQuery, queryParams?: AuthScreensParamList['Search_Central_Pane']) => {
const {sortBy, sortOrder} = queryParams ?? {};

if (!sortBy && !sortOrder) {
return `search/${searchQuery}` as const;
}

return `search/${searchQuery}?sortBy=${sortBy}&sortOrder=${sortOrder}` as const;
},
route: 'search',
getRoute: ({query, isCustomQuery = false, policyIDs}: {query: SearchQueryString; isCustomQuery?: boolean; policyIDs?: string}) =>
`search?q=${query}&isCustomQuery=${isCustomQuery}${policyIDs ? `&policyIDs=${policyIDs}` : ''}` as const,
},

SEARCH_ADVANCED_FILTERS: 'search/filters',
Expand All @@ -56,14 +48,11 @@ const ROUTES = {
SEARCH_ADVANCED_FILTERS_TYPE: 'search/filters/type',

SEARCH_REPORT: {
route: 'search/:query/view/:reportID',
getRoute: (query: string, reportID: string) => `search/${query}/view/${reportID}` as const,
route: 'search/view/:reportID',
getRoute: (reportID: string) => `search/view/${reportID}` as const,
},

TRANSACTION_HOLD_REASON_RHP: {
route: 'search/:query/hold',
getRoute: (query: string) => `search/${query}/hold` as const,
},
TRANSACTION_HOLD_REASON_RHP: 'search/hold',

// This is a utility route used to go to the user's concierge chat, or the sign-in page if the user's not authenticated
CONCIERGE: 'concierge',
Expand Down
5 changes: 2 additions & 3 deletions src/components/PromotedActionsBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as HeaderUtils from '@libs/HeaderUtils';
import * as Localize from '@libs/Localize';
import getTopmostCentralPaneRoute from '@libs/Navigation/getTopmostCentralPaneRoute';
import Navigation, {navigationRef} from '@libs/Navigation/Navigation';
import type {AuthScreensParamList, RootStackParamList, State} from '@libs/Navigation/types';
import type {RootStackParamList, State} from '@libs/Navigation/types';
import * as ReportUtils from '@libs/ReportUtils';
import * as ReportActions from '@userActions/Report';
import * as Session from '@userActions/Session';
Expand Down Expand Up @@ -85,8 +85,7 @@ const PromotedActions = {
return;
}

const currentQuery = topmostCentralPaneRoute?.params as AuthScreensParamList['Search_Central_Pane'];
ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute(currentQuery?.query ?? CONST.SEARCH.TAB.ALL, reportAction?.childReportID ?? ''));
ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute(reportAction?.childReportID ?? ''));
},
}),
} satisfies PromotedActionsType;
Expand Down
10 changes: 5 additions & 5 deletions src/components/Search/SearchListWithHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
import * as SearchActions from '@libs/actions/Search';
import * as SearchUtils from '@libs/SearchUtils';
import CONST from '@src/CONST';
import type {SearchDataTypes, SearchQuery, SearchReport} from '@src/types/onyx/SearchResults';
import type {SearchDataTypes, SearchReport} from '@src/types/onyx/SearchResults';
import SearchPageHeader from './SearchPageHeader';
import type {SelectedTransactionInfo, SelectedTransactions} from './types';
import type {SearchStatus, SelectedTransactionInfo, SelectedTransactions} from './types';

type SearchListWithHeaderProps = Omit<BaseSelectionListProps<ReportListItemType | TransactionListItemType>, 'onSelectAll' | 'onCheckboxPress' | 'sections'> & {
query: SearchQuery;
status: SearchStatus;
hash: number;
data: TransactionListItemType[] | ReportListItemType[];
searchType: SearchDataTypes;
Expand All @@ -44,7 +44,7 @@ function mapToItemWithSelectionInfo(item: TransactionListItemType | ReportListIt
}

function SearchListWithHeader(
{ListItem, onSelectRow, query, hash, data, searchType, isMobileSelectionModeActive, setIsMobileSelectionModeActive, ...props}: SearchListWithHeaderProps,
{ListItem, onSelectRow, status, hash, data, searchType, isMobileSelectionModeActive, setIsMobileSelectionModeActive, ...props}: SearchListWithHeaderProps,
ref: ForwardedRef<SelectionListHandle>,
) {
const {isSmallScreenWidth} = useWindowDimensions();
Expand Down Expand Up @@ -188,7 +188,7 @@ function SearchListWithHeader(
<SearchPageHeader
selectedTransactions={selectedTransactions}
clearSelectedItems={clearSelectedItems}
query={query}
status={status}
hash={hash}
onSelectDeleteOption={handleOnSelectDeleteOption}
isMobileSelectionModeActive={isMobileSelectionModeActive}
Expand Down
20 changes: 10 additions & 10 deletions src/components/Search/SearchPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import SearchSelectedNarrow from '@pages/Search/SearchSelectedNarrow';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type {SearchQuery, SearchReport} from '@src/types/onyx/SearchResults';
import type {SearchReport} from '@src/types/onyx/SearchResults';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import type IconAsset from '@src/types/utils/IconAsset';
import getDownloadOption from './SearchActionOptionsUtils';
import {useSearchContext} from './SearchContext';
import type {SelectedTransactions} from './types';
import type {SearchStatus, SelectedTransactions} from './types';

type SearchPageHeaderProps = {
query: SearchQuery;
status: SearchStatus;
selectedTransactions?: SelectedTransactions;
selectedReports?: Array<SearchReport['reportID']>;
clearSelectedItems?: () => void;
Expand All @@ -39,7 +39,7 @@ type SearchPageHeaderProps = {
type SearchHeaderOptionValue = DeepValueOf<typeof CONST.SEARCH.BULK_ACTION_TYPES> | undefined;

function SearchPageHeader({
query,
status,
selectedTransactions = {},
hash,
clearSelectedItems,
Expand All @@ -58,7 +58,7 @@ function SearchPageHeader({
const {isSmallScreenWidth} = useResponsiveLayout();
const {setSelectedTransactionIDs} = useSearchContext();

const headerContent: {[key in SearchQuery]: {icon: IconAsset; title: string}} = {
const headerContent: {[key in SearchStatus]: {icon: IconAsset; title: string}} = {
all: {icon: Illustrations.MoneyReceipts, title: translate('common.expenses')},
shared: {icon: Illustrations.SendMoney, title: translate('common.shared')},
drafts: {icon: Illustrations.Pencil, title: translate('common.drafts')},
Expand All @@ -81,7 +81,7 @@ function SearchPageHeader({
return;
}

SearchActions.exportSearchItemsToCSV(query, selectedReports, selectedTransactionsKeys, [activeWorkspaceID ?? ''], () => {
SearchActions.exportSearchItemsToCSV(status, selectedReports, selectedTransactionsKeys, [activeWorkspaceID ?? ''], () => {
setDownloadErrorModalOpen?.();
});
});
Expand Down Expand Up @@ -109,7 +109,7 @@ function SearchPageHeader({
setIsMobileSelectionModeActive?.(false);
}
setSelectedTransactionIDs(selectedTransactionsKeys);
Navigation.navigate(ROUTES.TRANSACTION_HOLD_REASON_RHP.getRoute(query));
Navigation.navigate(ROUTES.TRANSACTION_HOLD_REASON_RHP);
},
});
}
Expand Down Expand Up @@ -176,6 +176,7 @@ function SearchPageHeader({

return options;
}, [
status,
selectedTransactionsKeys,
selectedTransactions,
translate,
Expand All @@ -187,7 +188,6 @@ function SearchPageHeader({
theme.icon,
styles.colorMuted,
styles.fontWeightNormal,
query,
isOffline,
setOfflineModalOpen,
setDownloadErrorModalOpen,
Expand All @@ -211,8 +211,8 @@ function SearchPageHeader({

return (
<HeaderWithBackButton
title={headerContent[query]?.title}
icon={headerContent[query]?.icon}
title={headerContent[status]?.title}
icon={headerContent[status]?.icon}
shouldShowBackButton={false}
>
{headerButtonsOptions.length > 0 && (
Expand Down
43 changes: 22 additions & 21 deletions src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,35 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SearchResults from '@src/types/onyx/SearchResults';
import type {SearchDataTypes, SearchQuery} from '@src/types/onyx/SearchResults';
import type {SearchDataTypes} from '@src/types/onyx/SearchResults';
import {useSearchContext} from './SearchContext';
import SearchListWithHeader from './SearchListWithHeader';
import SearchPageHeader from './SearchPageHeader';
import type {SearchColumnType, SortOrder} from './types';
import type {SearchColumnType, SearchQueryJSON, SearchStatus, SortOrder} from './types';

type SearchProps = {
query: SearchQuery;
queryJSON: SearchQueryJSON;
policyIDs?: string;
sortBy?: SearchColumnType;
sortOrder?: SortOrder;
isMobileSelectionModeActive?: boolean;
setIsMobileSelectionModeActive?: (isMobileSelectionModeActive: boolean) => void;
};

const sortableSearchTabs: SearchQuery[] = [CONST.SEARCH.TAB.ALL];
const sortableSearchTabs: SearchStatus[] = [CONST.SEARCH.STATUS.ALL];
const transactionItemMobileHeight = 100;
const reportItemTransactionHeight = 52;
const listItemPadding = 12; // this is equivalent to 'mb3' on every transaction/report list item
const searchHeaderHeight = 54;
function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActive, setIsMobileSelectionModeActive}: SearchProps) {
function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileSelectionModeActive}: SearchProps) {
const {isOffline} = useNetwork();
const styles = useThemeStyles();
const {isLargeScreenWidth, isSmallScreenWidth} = useWindowDimensions();
const navigation = useNavigation<StackNavigationProp<AuthScreensParamList>>();
const lastSearchResultsRef = useRef<OnyxEntry<SearchResults>>();
const {setCurrentSearchHash} = useSearchContext();
const [offset, setOffset] = React.useState(0);

const {status, sortBy, sortOrder, hash} = queryJSON;

const hash = SearchUtils.getQueryHash(query, policyIDs, sortBy, sortOrder);
const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`);

const getItemHeight = useCallback(
Expand Down Expand Up @@ -96,9 +96,10 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv
}

setCurrentSearchHash(hash);
SearchActions.search({hash, query, policyIDs, offset: 0, sortBy, sortOrder});

SearchActions.search({hash, query: status, policyIDs, offset, sortBy, sortOrder});
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [hash, isOffline]);
}, [hash, isOffline, offset]);

const isDataLoaded = searchResults?.data !== undefined;
const shouldShowLoadingState = !isOffline && !isDataLoaded;
Expand All @@ -108,7 +109,7 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv
return (
<>
<SearchPageHeader
query={query}
status={status}
hash={hash}
/>
<SearchRowSkeleton shouldAnimate />
Expand All @@ -122,7 +123,7 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv
return (
<>
<SearchPageHeader
query={query}
status={status}
hash={hash}
/>
<EmptySearchView />
Expand All @@ -143,15 +144,14 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv
SearchActions.createTransactionThread(hash, item.transactionID, reportID, item.moneyRequestReportActionID);
}

Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(query, reportID));
Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(reportID));
};

const fetchMoreResults = () => {
if (!searchResults?.search?.hasMoreResults || shouldShowLoadingState || shouldShowLoadingMoreItems) {
return;
}
const currentOffset = searchResults?.search?.offset ?? 0;
SearchActions.search({hash, query, offset: currentOffset + CONST.SEARCH.RESULTS_PAGE_SIZE, sortBy, sortOrder});
setOffset(offset + CONST.SEARCH.RESULTS_PAGE_SIZE);
};

const type = SearchUtils.getSearchType(searchResults?.search);
Expand All @@ -167,21 +167,22 @@ function Search({query, policyIDs, sortBy, sortOrder, isMobileSelectionModeActiv
const sortedData = SearchUtils.getSortedSections(type, data, sortBy, sortOrder);

const onSortPress = (column: SearchColumnType, order: SortOrder) => {
navigation.setParams({
sortBy: column,
sortOrder: order,
});
const currentSearchParams = SearchUtils.getCurrentSearchParams();
const currentQueryJSON = SearchUtils.buildSearchQueryJSON(currentSearchParams.q, policyIDs);

const newQuery = SearchUtils.buildSearchQueryString({...currentQueryJSON, sortBy: column, sortOrder: order});
navigation.setParams({q: newQuery});
};

const isSortingAllowed = sortableSearchTabs.includes(query);
const isSortingAllowed = sortableSearchTabs.includes(status);

const shouldShowYear = SearchUtils.shouldShowYear(searchResults?.data);

const canSelectMultiple = isSmallScreenWidth ? isMobileSelectionModeActive : true;

return (
<SearchListWithHeader
query={query}
status={status}
hash={hash}
data={sortedData}
searchType={searchResults?.search?.type as SearchDataTypes}
Expand Down
32 changes: 31 additions & 1 deletion src/components/Search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type SelectedTransactions = Record<string, SelectedTransactionInfo>;

type SortOrder = ValueOf<typeof CONST.SEARCH.SORT_ORDER>;
type SearchColumnType = ValueOf<typeof CONST.SEARCH.TABLE_COLUMNS>;
type SearchStatus = ValueOf<typeof CONST.SEARCH.STATUS>;

type SearchContext = {
currentSearchHash: number;
Expand All @@ -49,4 +50,33 @@ type QueryFilters = {
[K in AllFieldKeys]: QueryFilter | QueryFilter[];
};

export type {SelectedTransactionInfo, SelectedTransactions, SearchColumnType, SortOrder, SearchContext, ASTNode, QueryFilter, QueryFilters, AllFieldKeys};
type SearchQueryString = string;

type SearchQueryAST = {
type: string;
status: SearchStatus;
sortBy: SearchColumnType;
sortOrder: SortOrder;
filters: ASTNode;
};

type SearchQueryJSON = {
input: string;
hash: number;
} & SearchQueryAST;

export type {
SelectedTransactionInfo,
SelectedTransactions,
SearchColumnType,
SearchStatus,
SearchQueryAST,
SearchQueryJSON,
SearchQueryString,
SortOrder,
SearchContext,
ASTNode,
QueryFilter,
QueryFilters,
AllFieldKeys,
};
5 changes: 1 addition & 4 deletions src/components/SelectionList/Search/ReportListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import Navigation from '@libs/Navigation/Navigation';
import {getSearchParams} from '@libs/SearchUtils';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import ActionCell from './ActionCell';
Expand Down Expand Up @@ -85,9 +84,7 @@ function ReportListItem<TItem extends ListItem>({
};

const openReportInRHP = (transactionItem: TransactionListItemType) => {
const searchParams = getSearchParams();
const currentQuery = searchParams?.query ?? CONST.SEARCH.TAB.ALL;
Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(currentQuery, transactionItem.transactionThreadReportID));
Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(transactionItem.transactionThreadReportID));
};

if (!reportItem?.reportName && reportItem.transactions.length > 1) {
Expand Down
Loading

0 comments on commit 5df5630

Please sign in to comment.