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
3 changes: 1 addition & 2 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -399,9 +399,8 @@ function MoneyReportHeader({
typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD | typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.REJECT | typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.REJECT_BULK
> | null>(null);

const {selectedTransactionIDs, removeTransaction, clearSelectedTransactions, currentSearchQueryJSON, currentSearchKey, currentSearchHash} = useSearchContext();
const {selectedTransactionIDs, removeTransaction, clearSelectedTransactions, currentSearchQueryJSON, currentSearchKey, currentSearchHash, currentSearchResults} = useSearchContext();
const shouldCalculateTotals = useSearchShouldCalculateTotals(currentSearchKey, currentSearchQueryJSON?.similarSearchHash, true);
const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchQueryJSON?.hash}`, {canBeMissing: true});

const [network] = useOnyx(ONYXKEYS.NETWORK, {canBeMissing: true});

Expand Down
20 changes: 5 additions & 15 deletions src/components/Navigation/SearchSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import type {ParamListBase} from '@react-navigation/native';
import {searchResultsSelector} from '@selectors/Snapshot';
import React, {useEffect, useMemo} from 'react';
import {View} from 'react-native';
import {useSearchContext} from '@components/Search/SearchContext';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import type {PlatformStackNavigationState} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SearchFullscreenNavigatorParamList} from '@libs/Navigation/types';
import {buildSearchQueryJSON} from '@libs/SearchQueryUtils';
import SearchTypeMenu from '@pages/Search/SearchTypeMenu';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import SCREENS from '@src/SCREENS';
import NavigationTabBar from './NavigationTabBar';
import NAVIGATION_TABS from './NavigationTabBar/NAVIGATION_TABS';
Expand All @@ -31,7 +27,7 @@ function SearchSidebar({state}: SearchSidebarProps) {

const route = state.routes.at(-1);
const params = route?.params as SearchFullscreenNavigatorParamList[typeof SCREENS.SEARCH.ROOT] | undefined;
const {lastSearchType, setLastSearchType} = useSearchContext();
const {lastSearchType, setLastSearchType, currentSearchResults} = useSearchContext();

const queryJSON = useMemo(() => {
if (!params?.q) {
Expand All @@ -41,21 +37,15 @@ function SearchSidebar({state}: SearchSidebarProps) {
return buildSearchQueryJSON(params.q, params.rawQuery);
}, [params?.q, params?.rawQuery]);

const currentSearchResultsKey = queryJSON?.hash ?? CONST.DEFAULT_NUMBER_ID;
const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchResultsKey}`, {
canBeMissing: true,
selector: searchResultsSelector,
});

useEffect(() => {
if (!currentSearchResults?.type) {
if (!currentSearchResults?.search?.type) {
return;
}

setLastSearchType(currentSearchResults.type);
}, [lastSearchType, queryJSON, setLastSearchType, currentSearchResults?.type]);
setLastSearchType(currentSearchResults.search.type);
}, [lastSearchType, queryJSON, setLastSearchType, currentSearchResults?.search?.type]);

const shouldShowLoadingState = route?.name === SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT ? false : !isOffline && !!currentSearchResults?.isLoading;
const shouldShowLoadingState = route?.name === SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT ? false : !isOffline && !!currentSearchResults?.search?.isLoading;

if (shouldUseNarrowLayout) {
return null;
Expand Down
4 changes: 1 addition & 3 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,7 @@ function MoneyRequestView({
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false});

const searchContext = useSearchContext();
const searchHash = searchContext?.currentSearchHash ?? CONST.DEFAULT_NUMBER_ID;
const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${searchHash}`, {canBeMissing: true});
const {currentSearchResults} = useSearchContext();

// When this component is used when merging from the search page, we might not have the parent report stored in the main collection
let [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`, {canBeMissing: true});
Expand Down
8 changes: 8 additions & 0 deletions src/components/Search/SearchContext.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, {useCallback, useContext, useMemo, useRef, useState} from 'react';
import useOnyx from '@hooks/useOnyx';
import {isMoneyRequestReport} from '@libs/ReportUtils';
import {isTransactionListItemType, isTransactionReportGroupListItemType} from '@libs/SearchUIUtils';
import type {SearchKey} from '@libs/SearchUIUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type {SearchContextData, SearchContextProps, SearchQueryJSON, SelectedTransactions} from './types';
Expand All @@ -11,6 +13,7 @@ const defaultSearchContextData: SearchContextData = {
currentSearchHash: -1,
currentSearchKey: undefined,
currentSearchQueryJSON: undefined,
currentSearchResults: undefined,
selectedTransactions: {},
selectedTransactionIDs: [],
selectedReports: [],
Expand All @@ -25,6 +28,7 @@ const defaultSearchContext: SearchContextProps = {
areAllMatchingItemsSelected: false,
showSelectAllMatchingItems: false,
shouldShowFiltersBarLoading: false,
currentSearchResults: undefined,
setLastSearchType: () => {},
setCurrentSearchHashAndKey: () => {},
setCurrentSearchQueryJSON: () => {},
Expand All @@ -47,6 +51,8 @@ function SearchContextProvider({children}: ChildrenProps) {
const [searchContextData, setSearchContextData] = useState(defaultSearchContextData);
const areTransactionsEmpty = useRef(true);

const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${searchContextData.currentSearchHash}`, {canBeMissing: true});

const setCurrentSearchHashAndKey = useCallback((searchHash: number, searchKey: SearchKey | undefined) => {
setSearchContextData((prevState) => {
if (searchHash === prevState.currentSearchHash && searchKey === prevState.currentSearchKey) {
Expand Down Expand Up @@ -200,6 +206,7 @@ function SearchContextProvider({children}: ChildrenProps) {
const searchContext = useMemo<SearchContextProps>(
() => ({
...searchContextData,
currentSearchResults,
removeTransaction,
setCurrentSearchHashAndKey,
setCurrentSearchQueryJSON,
Expand All @@ -217,6 +224,7 @@ function SearchContextProvider({children}: ChildrenProps) {
}),
[
searchContextData,
currentSearchResults,
removeTransaction,
setCurrentSearchHashAndKey,
setCurrentSearchQueryJSON,
Expand Down
8 changes: 3 additions & 5 deletions src/components/Search/SearchPageHeader/SearchFiltersBar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {isUserValidatedSelector} from '@selectors/Account';
import {emailSelector} from '@selectors/Session';
import {searchResultsErrorSelector} from '@selectors/Snapshot';
import React, {useCallback, useContext, useMemo, useRef} from 'react';
import type {ReactNode} from 'react';
import {FlatList, View} from 'react-native';
Expand Down Expand Up @@ -90,7 +89,7 @@ function SearchFiltersBar({
const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isUserValidatedSelector, canBeMissing: true});
const [searchAdvancedFiltersForm = getEmptyObject<Partial<SearchAdvancedFiltersForm>>()] = useOnyx(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, {canBeMissing: true});
// type, groupBy and status values are not guaranteed to respect the ts type as they come from user input
const {hash, type: unsafeType, groupBy: unsafeGroupBy, status: unsafeStatus, flatFilters} = queryJSON;
const {type: unsafeType, groupBy: unsafeGroupBy, status: unsafeStatus, flatFilters} = queryJSON;
const [selectedIOUReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${currentSelectedReportID}`, {canBeMissing: true});
const isCurrentSelectedExpenseReport = isExpenseReport(currentSelectedReportID);
const theme = useTheme();
Expand All @@ -102,7 +101,7 @@ function SearchFiltersBar({
const personalDetails = usePersonalDetails();
const filterFormValues = useFilterFormValues(queryJSON);
const {shouldUseNarrowLayout, isLargeScreenWidth} = useResponsiveLayout();
const {selectedTransactions, selectAllMatchingItems, areAllMatchingItemsSelected, showSelectAllMatchingItems, shouldShowFiltersBarLoading} = useSearchContext();
const {selectedTransactions, selectAllMatchingItems, areAllMatchingItemsSelected, showSelectAllMatchingItems, shouldShowFiltersBarLoading, currentSearchResults} = useSearchContext();

const [email] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true, selector: emailSelector});
const [userCardList] = useOnyx(ONYXKEYS.CARD_LIST, {selector: filterPersonalCards, canBeMissing: true});
Expand All @@ -111,7 +110,6 @@ function SearchFiltersBar({
const [allFeeds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER, {canBeMissing: true});
const [currencyList = getEmptyObject<CurrencyList>()] = useOnyx(ONYXKEYS.CURRENCY_LIST, {canBeMissing: true});
const {isAccountLocked, showLockedAccountModal} = useContext(LockedAccountContext);
const [searchResultsErrors] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, {canBeMissing: true, selector: searchResultsErrorSelector});
const expensifyIcons = useMemoizedLazyExpensifyIcons(['Filter', 'Columns']);
const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext);

Expand Down Expand Up @@ -156,7 +154,7 @@ function SearchFiltersBar({
return workspaceOptions.filter((option) => normalizedIDs.includes(option.value));
}, [searchAdvancedFiltersForm.policyID, queryJSON.policyID, workspaceOptions]);

const hasErrors = Object.keys(searchResultsErrors ?? {}).length > 0 && !isOffline;
const hasErrors = Object.keys(currentSearchResults?.errors ?? {}).length > 0 && !isOffline;
const shouldShowSelectedDropdown = headerButtonsOptions.length > 0 && (!shouldUseNarrowLayout || isMobileSelectionModeEnabled);

const [typeOptions, type] = useMemo(() => {
Expand Down
4 changes: 3 additions & 1 deletion src/components/Search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {PaymentMethod} from '@components/KYCWall/types';
import type {ReportActionListItemType, TaskListItemType, TransactionGroupListItemType, TransactionListItemType} from '@components/SelectionListWithSections/types';
import type {SearchKey} from '@libs/SearchUIUtils';
import type CONST from '@src/CONST';
import type {ReportAction} from '@src/types/onyx';
import type {ReportAction, SearchResults} from '@src/types/onyx';
import type {SearchDataTypes} from '@src/types/onyx/SearchResults';
import type IconAsset from '@src/types/utils/IconAsset';

Expand Down Expand Up @@ -125,6 +125,7 @@ type SearchContextData = {
currentSearchHash: number;
currentSearchKey: SearchKey | undefined;
currentSearchQueryJSON: SearchQueryJSON | undefined;
currentSearchResults: SearchResults | undefined;
selectedTransactions: SelectedTransactions;
selectedTransactionIDs: string[];
selectedReports: SelectedReports[];
Expand All @@ -134,6 +135,7 @@ type SearchContextData = {
};

type SearchContextProps = SearchContextData & {
currentSearchResults: SearchResults | undefined;
setCurrentSearchHashAndKey: (hash: number, key: SearchKey | undefined) => void;
setCurrentSearchQueryJSON: (searchQueryJSON: SearchQueryJSON | undefined) => void;
/** If you want to set `selectedTransactionIDs`, pass an array as the first argument, object/record otherwise */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,26 @@ function ExpenseReportListItem<TItem extends ListItem>({
const theme = useTheme();
const {translate} = useLocalize();
const {isLargeScreenWidth} = useResponsiveLayout();
const {currentSearchHash, currentSearchKey} = useSearchContext();
const {currentSearchHash, currentSearchKey, currentSearchResults} = useSearchContext();
const [lastPaymentMethod] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, {canBeMissing: true});
const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID, {canBeMissing: true});
const [snapshot] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchHash}`, {canBeMissing: true});
const [isActionLoading] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportItem.reportID}`, {canBeMissing: true, selector: isActionLoadingSelector});
const expensifyIcons = useMemoizedLazyExpensifyIcons(['DotIndicator']);

const snapshotData = snapshot?.data;
const searchData = currentSearchResults?.data;

const snapshotReport = useMemo(() => {
return (snapshotData?.[`${ONYXKEYS.COLLECTION.REPORT}${reportItem.reportID}`] ?? {}) as Report;
}, [snapshotData, reportItem.reportID]);
return (searchData?.[`${ONYXKEYS.COLLECTION.REPORT}${reportItem.reportID}`] ?? {}) as Report;
}, [searchData, reportItem.reportID]);

const snapshotPolicy = useMemo(() => {
return (snapshotData?.[`${ONYXKEYS.COLLECTION.POLICY}${reportItem.policyID}`] ?? {}) as Policy;
}, [snapshotData, reportItem.policyID]);
return (searchData?.[`${ONYXKEYS.COLLECTION.POLICY}${reportItem.policyID}`] ?? {}) as Policy;
}, [searchData, reportItem.policyID]);

const reportActions = useMemo(() => {
const actionsData = searchData?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportItem.reportID}`];
return actionsData ? Object.values(actionsData) : [];
}, [searchData, reportItem.reportID]);

const isDisabledCheckbox = useMemo(() => {
const isEmpty = reportItem.transactions.length === 0;
Expand Down Expand Up @@ -183,10 +187,10 @@ function ExpenseReportListItem<TItem extends ListItem>({
{(hovered) => (
<View style={[styles.flex1]}>
<ExpenseReportListItemRow
hash={currentSearchHash}
item={reportItem}
columns={columns}
policy={snapshotPolicy}
reportActions={reportActions}
isActionLoading={isActionLoading ?? isLoading}
showTooltip={showTooltip}
canSelectMultiple={canSelectMultiple}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import getBase62ReportID from '@libs/getBase62ReportID';
import {getMoneyRequestSpendBreakdown} from '@libs/ReportUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import type {Policy} from '@src/types/onyx';
import type {Policy, ReportAction} from '@src/types/onyx';
import ActionCell from './ActionCell';
import DateCell from './DateCell';
import ExportedIconCell from './ExportedIconCell';
Expand All @@ -28,9 +28,9 @@ import UserInfoCell from './UserInfoCell';
import WorkspaceCell from './WorkspaceCell';

type ExpenseReportListItemRowProps = {
hash: number;
item: ExpenseReportListItemType;
policy?: Policy;
reportActions?: ReportAction[];
showTooltip: boolean;
canSelectMultiple?: boolean;
isActionLoading?: boolean;
Expand All @@ -46,9 +46,9 @@ type ExpenseReportListItemRowProps = {
};

function ExpenseReportListItemRow({
hash,
item,
policy,
reportActions,
onCheckboxPress = () => {},
onButtonPress = () => {},
isActionLoading,
Expand Down Expand Up @@ -182,10 +182,7 @@ function ExpenseReportListItemRow({
),
[CONST.SEARCH.TABLE_COLUMNS.EXPORTED_TO]: (
<View style={[StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.EXPORTED_TO)]}>
<ExportedIconCell
reportID={item.reportID}
hash={hash}
/>
<ExportedIconCell reportActions={reportActions} />
</View>
),
[CONST.SEARCH.TABLE_COLUMNS.ACTION]: (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,21 @@ import {View} from 'react-native';
import Avatar from '@components/Avatar';
import Icon from '@components/Icon';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useOnyx from '@hooks/useOnyx';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {getOriginalMessage, isExportedToIntegrationAction} from '@libs/ReportActionsUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {ReportAction} from '@src/types/onyx';

type ExportedIconCellProps = {
reportID?: string;
hash?: number;
reportActions?: ReportAction[];
};

function ExportedIconCell({reportID, hash}: ExportedIconCellProps) {
function ExportedIconCell({reportActions}: ExportedIconCellProps) {
Comment thread
luacmartins marked this conversation as resolved.
const theme = useTheme();
const styles = useThemeStyles();

// We need to subscribe directly to the snapshot to get the report actions because this can be rendered in either a group
// list (which has a separate hash than the current top-level search query) or in the top-level search query.
// This selector is specific to this edge-case (and thus is not in the selectors folder) and should be used in other places where the snapshot needs to be accessed
// eslint-disable-next-line rulesdir/no-inline-useOnyx-selector
const reportActions = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, {
canBeMissing: true,
selector: (snapshot) => {
return Object.entries(snapshot?.data ?? {})
.filter(([key]) => key === `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`)
.map(([, value]) => Object.values(value ?? {}) as ReportAction[])
.flat();
},
});

const actions = Object.values(reportActions[0] ?? {});
const actions = reportActions ?? [];
const icons = useMemoizedLazyExpensifyIcons(['NetSuiteSquare', 'XeroSquare', 'IntacctSquare', 'QBOSquare', 'Table', 'ZenefitsSquare', 'BillComSquare', 'CertiniaSquare']);

let isExportedToCsv = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,12 @@ function ReportListItemHeader<TItem extends ListItem>({
const StyleUtils = useStyleUtils();
const styles = useThemeStyles();
const theme = useTheme();
const {currentSearchHash, currentSearchKey} = useSearchContext();
const {currentSearchHash, currentSearchKey, currentSearchResults: snapshot} = useSearchContext();
const {isLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout();
const [lastPaymentMethod] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, {canBeMissing: true});
const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID, {canBeMissing: true});
const thereIsFromAndTo = !!reportItem?.from && !!reportItem?.to;
const showUserInfo = (reportItem.type === CONST.REPORT.TYPE.IOU && thereIsFromAndTo) || (reportItem.type === CONST.REPORT.TYPE.EXPENSE && !!reportItem?.from);
const [snapshot] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchHash}`, {canBeMissing: true});
const snapshotReport = useMemo(() => {
return (snapshot?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${reportItem.reportID}`] ?? {}) as Report;
}, [snapshot, reportItem.reportID]);
Expand Down
Loading
Loading