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
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,9 @@ const ONYXKEYS = {
/** Set when we are loading fresh subscription/billing data from the server */
IS_LOADING_SUBSCRIPTION_DATA: 'isLoadingSubscriptionData',

/** Set whether we are loading the search filters card data */
IS_SEARCH_FILTERS_CARD_DATA_LOADED: 'isSearchFiltersCardDataLoaded',

/** Set when OpenSearchPage has been fetched for the first time */
IS_SEARCH_PAGE_DATA_LOADED: 'isSearchPageDataLoaded',

Expand Down Expand Up @@ -1363,6 +1366,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.IS_LOADING_PAYMENT_METHODS]: boolean;
[ONYXKEYS.IS_LOADING_SHARE_BANK_ACCOUNTS]: boolean;
[ONYXKEYS.IS_LOADING_POLICY_CODING_RULES_PREVIEW]: boolean;
[ONYXKEYS.IS_SEARCH_FILTERS_CARD_DATA_LOADED]: boolean;
[ONYXKEYS.IS_LOADING_SUBSCRIPTION_DATA]: boolean;
[ONYXKEYS.IS_SEARCH_PAGE_DATA_LOADED]: boolean;
[ONYXKEYS.IS_LOADING_REPORT_DATA]: boolean;
Expand Down
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,7 @@ const READ_COMMANDS = {
OPEN_PLAID_CARDS_BANK_LOGIN: 'OpenPlaidCardsBankLogin',
OPEN_PLAID_BANK_ACCOUNT_SELECTOR: 'OpenPlaidBankAccountSelector',
OPEN_SEARCH_PAGE: 'OpenSearchPage',
OPEN_SEARCH_CARD_FILTERS_PAGE: 'OpenSearchCardFiltersPage',
SEARCH: 'Search',
GET_OLDER_ACTIONS: 'GetOlderActions',
GET_NEWER_ACTIONS: 'GetNewerActions',
Expand Down Expand Up @@ -1316,6 +1317,7 @@ type ReadCommandParameters = {
[READ_COMMANDS.OPEN_POLICY_RECEIPT_PARTNERS_PAGE]: Parameters.OpenPolicyReceiptPartnersPageParams;
[READ_COMMANDS.OPEN_SUBSCRIPTION_PAGE]: null;
[READ_COMMANDS.OPEN_DRAFT_DISTANCE_EXPENSE]: null;
[READ_COMMANDS.OPEN_SEARCH_CARD_FILTERS_PAGE]: null;
[READ_COMMANDS.START_ISSUE_NEW_CARD_FLOW]: Parameters.StartIssueNewCardFlowParams;
[READ_COMMANDS.OPEN_CARD_DETAILS_PAGE]: Parameters.OpenCardDetailsPageParams;
[READ_COMMANDS.GET_CORPAY_ONBOARDING_FIELDS]: Parameters.GetCorpayOnboardingFieldsParams;
Expand Down
13 changes: 13 additions & 0 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,18 @@ function openSearchPage({includePartiallySetupBankAccounts}: OpenSearchPageParam
API.read(READ_COMMANDS.OPEN_SEARCH_PAGE, {includePartiallySetupBankAccounts}, {successData});
}

function openSearchCardFiltersPage() {
const finallyData: Array<OnyxUpdate<typeof ONYXKEYS.IS_SEARCH_FILTERS_CARD_DATA_LOADED>> = [
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need this key? It seems like it can only ever be undefined when we haven't called this API at least once, or it's always true. Can we skip this entirely and use other keys to determine if we should show the loading indicator?

Copy link
Copy Markdown
Contributor Author

@JS00001 JS00001 Mar 26, 2026

Choose a reason for hiding this comment

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

I dont think so, no, we don't have a way of checking if this command has completed before,outside of storing a key for it.

{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.IS_SEARCH_FILTERS_CARD_DATA_LOADED,
value: true,
Comment thread
JS00001 marked this conversation as resolved.
},
];

API.read(READ_COMMANDS.OPEN_SEARCH_CARD_FILTERS_PAGE, null, {finallyData});
}

// Tracks in-flight search requests by hash+offset to prevent duplicate API calls
// when both page-level (useSearchPageSetup) and Search-internal (handleSearch) effects
// fire for the same query. Cleared when the request completes.
Expand Down Expand Up @@ -1707,5 +1719,6 @@ export {
setOptimisticDataForTransactionThreadPreview,
getPayMoneyOnSearchInvoiceParams,
handlePreventSearchAPI,
openSearchCardFiltersPage,
};
export type {TransactionPreviewData};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {useEffect, useState} from 'react';
import {View} from 'react-native';
import ActivityIndicator from '@components/ActivityIndicator';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import {usePersonalDetails} from '@components/OnyxListItemProvider';
import ScreenWrapper from '@components/ScreenWrapper';
Expand All @@ -9,24 +10,30 @@ import SelectionListWithSections from '@components/SelectionList/SelectionListWi
import {useCompanyCardFeedIcons} from '@hooks/useCompanyCardIcons';
import useDebouncedState from '@hooks/useDebouncedState';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import useTheme from '@hooks/useTheme';
import useThemeIllustrations from '@hooks/useThemeIllustrations';
import useThemeStyles from '@hooks/useThemeStyles';
import {updateAdvancedFilters} from '@libs/actions/Search';
import {openSearchCardFiltersPage, updateAdvancedFilters} from '@libs/actions/Search';
import type {CardFilterItem} from '@libs/CardFeedUtils';
import {buildCardFeedsData, buildCardsData, generateSelectedCards, getDomainFeedData, getSelectedCardsFromFeeds} from '@libs/CardFeedUtils';
import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan';
import Navigation from '@navigation/Navigation';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';

function SearchFiltersCardPage() {
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const illustrations = useThemeIllustrations();
const companyCardFeedIcons = useCompanyCardFeedIcons();

const [areCardsLoaded] = useOnyx(ONYXKEYS.IS_SEARCH_FILTERS_CARD_DATA_LOADED);
const [userCardList, userCardListMetadata] = useOnyx(ONYXKEYS.CARD_LIST);
const [workspaceCardFeeds, workspaceCardFeedsMetadata] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST);
const [policies, policiesMetadata] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
Expand All @@ -36,6 +43,13 @@ function SearchFiltersCardPage() {

const [selectedCards, setSelectedCards] = useState<string[]>([]);

useEffect(() => {
if (isOffline) {
return;
}
openSearchCardFiltersPage();
}, [isOffline]);

useEffect(() => {
const generatedCards = generateSelectedCards(userCardList, workspaceCardFeeds, searchAdvancedFiltersForm?.feed, searchAdvancedFiltersForm?.cardID);
setSelectedCards(generatedCards);
Expand Down Expand Up @@ -128,38 +142,49 @@ function SearchFiltersCardPage() {
headerMessage: debouncedSearchTerm.trim() && sections.every((section) => !section.data.length) ? translate('common.noResultsFound') : '',
};

const isLoadingOnyxData = isLoadingOnyxValue(userCardListMetadata, workspaceCardFeedsMetadata, searchAdvancedFiltersFormMetadata, policiesMetadata);
const shouldShowLoadingState = isLoadingOnyxData || (!areCardsLoaded && !isOffline);
Comment thread
JS00001 marked this conversation as resolved.
const reasonAttributes: SkeletonSpanReasonAttributes = {context: 'SearchFiltersCardPage', isLoadingFromOnyx: isLoadingOnyxData};

return (
<ScreenWrapper
testID="SearchFiltersCardPage"
shouldShowOfflineIndicatorInWideScreen
offlineIndicatorStyle={styles.mtAuto}
shouldEnableMaxHeight
>
{({didScreenTransitionEnd}) => (
<>
<HeaderWithBackButton
title={translate('common.card')}
onBackButtonPress={() => {
Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS.getRoute());
}}
<HeaderWithBackButton
title={translate('common.card')}
onBackButtonPress={() => {
Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS.getRoute());
}}
/>

{!!shouldShowLoadingState && (
<View style={[styles.flex1, styles.flexColumn, styles.justifyContentCenter, styles.alignItemsCenter]}>
<ActivityIndicator
Comment thread
JS00001 marked this conversation as resolved.
color={theme.spinner}
size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
style={[styles.pl3]}
reasonAttributes={reasonAttributes}
/>
</View>
)}

{!shouldShowLoadingState && (
<View style={[styles.flex1]}>
<SelectionListWithSections<CardFilterItem>
sections={sections}
ListItem={CardListItem}
onSelectRow={updateNewCards}
footerContent={footerContent}
shouldPreventDefaultFocusOnSelectRow={false}
shouldShowTextInput={shouldShowSearchInput}
textInputOptions={textInputOptions}
shouldStopPropagation
canSelectMultiple
/>
<View style={[styles.flex1]}>
<SelectionListWithSections<CardFilterItem>
sections={sections}
ListItem={CardListItem}
onSelectRow={updateNewCards}
footerContent={footerContent}
shouldPreventDefaultFocusOnSelectRow={false}
shouldShowTextInput={shouldShowSearchInput}
textInputOptions={textInputOptions}
shouldShowLoadingPlaceholder={
isLoadingOnyxValue(userCardListMetadata, workspaceCardFeedsMetadata, searchAdvancedFiltersFormMetadata, policiesMetadata) || !didScreenTransitionEnd
}
shouldStopPropagation
canSelectMultiple
/>
</View>
</>
</View>
)}
</ScreenWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function PreviewMatchesPage({route}: PreviewMatchesPageProps) {
<View style={[styles.flex1, styles.flexColumn, styles.justifyContentCenter, styles.alignItemsCenter]}>
<ActivityIndicator
color={theme.spinner}
size={25}
size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
style={[styles.pl3]}
reasonAttributes={reasonAttributes}
/>
Expand Down
Loading