From 86bb98d32082e3fabd14828427ff0632f898ac06 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Tue, 13 Jan 2026 15:10:59 +0100 Subject: [PATCH] fix: show proper violations when offline --- src/components/Search/index.tsx | 41 +------------------ .../Search/TransactionListItem.tsx | 21 +++++++--- 2 files changed, 17 insertions(+), 45 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 4c6c8414bb778..184e35802e5b7 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -58,7 +58,7 @@ import { shouldShowYear as shouldShowYearUtil, } from '@libs/SearchUIUtils'; import {cancelSpan, endSpan, startSpan} from '@libs/telemetry/activeSpans'; -import {getOriginalTransactionWithSplitInfo, isOnHold, isTransactionPendingDelete, mergeProhibitedViolations, shouldShowViolation} from '@libs/TransactionUtils'; +import {getOriginalTransactionWithSplitInfo, isOnHold, isTransactionPendingDelete} from '@libs/TransactionUtils'; import Navigation, {navigationRef} from '@navigation/Navigation'; import type {SearchFullscreenNavigatorParamList} from '@navigation/types'; import EmptySearchView from '@pages/Search/EmptySearchView'; @@ -71,7 +71,6 @@ import {columnsSelector} from '@src/selectors/AdvancedSearchFiltersForm'; import {isActionLoadingSetSelector} from '@src/selectors/ReportMetaData'; import type {OutstandingReportsByPolicyIDDerivedValue, Transaction} from '@src/types/onyx'; import type SearchResults from '@src/types/onyx/SearchResults'; -import type {TransactionViolation} from '@src/types/onyx/TransactionViolation'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import arraysEqual from '@src/utils/arraysEqual'; import {useSearchContext} from './SearchContext'; @@ -251,42 +250,6 @@ function Search({ const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; - // Filter violations based on user visibility - const filteredViolations = useMemo(() => { - if (!violations || !searchResults?.data) { - return violations; - } - - const filtered: Record = {}; - - const transactionKeys = Object.keys(searchResults.data).filter((key) => key.startsWith(ONYXKEYS.COLLECTION.TRANSACTION)); - - for (const key of transactionKeys) { - const transaction = searchResults.data[key as keyof typeof searchResults.data] as Transaction; - if (!transaction || typeof transaction !== 'object' || !('transactionID' in transaction) || !('reportID' in transaction)) { - continue; - } - - const report = searchResults.data[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`]; - const policy = searchResults.data[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; - - if (report && policy) { - const transactionViolations = violations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`]; - if (transactionViolations) { - const filteredTransactionViolations = mergeProhibitedViolations( - transactionViolations.filter((violation) => shouldShowViolation(report, policy, violation.name, email ?? '', true, transaction)), - ); - - if (filteredTransactionViolations.length > 0) { - filtered[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`] = filteredTransactionViolations; - } - } - } - } - - return filtered; - }, [violations, searchResults, email]); - const archivedReportsIdSet = useArchivedReportsIdSet(); const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, { @@ -1088,7 +1051,7 @@ function Search({ } queryJSON={queryJSON} columns={columnsToShow} - violations={filteredViolations} + violations={violations} onLayout={onLayout} isMobileSelectionModeEnabled={isMobileSelectionModeEnabled} shouldAnimate={type === CONST.SEARCH.DATA_TYPES.EXPENSE} diff --git a/src/components/SelectionListWithSections/Search/TransactionListItem.tsx b/src/components/SelectionListWithSections/Search/TransactionListItem.tsx index ef398b51994a7..761b2fd5747af 100644 --- a/src/components/SelectionListWithSections/Search/TransactionListItem.tsx +++ b/src/components/SelectionListWithSections/Search/TransactionListItem.tsx @@ -22,13 +22,14 @@ import useThemeStyles from '@hooks/useThemeStyles'; import type {TransactionPreviewData} from '@libs/actions/Search'; import {handleActionButtonPress as handleActionButtonPressUtil} from '@libs/actions/Search'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; -import {isViolationDismissed, shouldShowViolation} from '@libs/TransactionUtils'; +import {isViolationDismissed, mergeProhibitedViolations, shouldShowViolation} from '@libs/TransactionUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isActionLoadingSelector} from '@src/selectors/ReportMetaData'; import type {Policy, Report, ReportAction, ReportActions} from '@src/types/onyx'; import type {TransactionViolation} from '@src/types/onyx/TransactionViolation'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import UserInfoAndActionButtonRow from './UserInfoAndActionButtonRow'; function TransactionListItem({ @@ -69,6 +70,7 @@ function TransactionListItem({ const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID, {canBeMissing: true}); const [parentReport] = originalUseOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(transactionItem.reportID)}`, {canBeMissing: true}); + const [parentPolicy] = originalUseOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getNonEmptyStringOnyxID(transactionItem.policyID)}`, {canBeMissing: true}); const [transactionThreadReport] = originalUseOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionItem?.reportAction?.childReportID}`, {canBeMissing: true}); const [transaction] = originalUseOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionItem.transactionID)}`, {canBeMissing: true}); const parentReportActionSelector = useCallback( @@ -120,13 +122,20 @@ function TransactionListItem({ transactionItem.shouldShowYearExported, ]); + // Use parentReport/parentPolicy as fallbacks when snapshotReport/snapshotPolicy are empty + // to fix offline issues where newly created reports aren't in the search snapshot yet + const reportForViolations = isEmptyObject(snapshotReport) ? parentReport : snapshotReport; + const policyForViolations = isEmptyObject(snapshotPolicy) ? parentPolicy : snapshotPolicy; + const transactionViolations = useMemo(() => { - return (violations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionItem.transactionID}`] ?? []).filter( - (violation: TransactionViolation) => - !isViolationDismissed(transactionItem, violation, currentUserDetails.email ?? '', currentUserDetails.accountID, snapshotReport, snapshotPolicy) && - shouldShowViolation(snapshotReport, snapshotPolicy, violation.name, currentUserDetails.email ?? '', false, transactionItem), + return mergeProhibitedViolations( + (violations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionItem.transactionID}`] ?? []).filter( + (violation: TransactionViolation) => + !isViolationDismissed(transactionItem, violation, currentUserDetails.email ?? '', currentUserDetails.accountID, reportForViolations, policyForViolations) && + shouldShowViolation(reportForViolations, policyForViolations, violation.name, currentUserDetails.email ?? '', false, transactionItem), + ), ); - }, [snapshotPolicy, snapshotReport, transactionItem, violations, currentUserDetails.email, currentUserDetails.accountID]); + }, [policyForViolations, reportForViolations, transactionItem, violations, currentUserDetails.email, currentUserDetails.accountID]); const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext);