From 6ff7b6a73eb9043e7658e326b825c0691534bcdd Mon Sep 17 00:00:00 2001 From: TaduJR Date: Sat, 17 Jan 2026 23:48:48 +0300 Subject: [PATCH 1/5] fix: Reports - Total report number (x of y) does not update when new report is created offline --- .../MoneyRequestReportNavigation.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx index c2a0d096c1f6..ac348ac3f7c8 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx @@ -85,6 +85,14 @@ function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: Mo return; } + if (allReports.length > allReportsCount) { + saveLastSearchParams({ + ...lastSearchQuery, + previousLengthOfResults: allReports.length, + }); + return; + } + if (currentIndex < allReportsCount - 1) { return; } From 1c4a3f8a9f5df3046c4e84680cdebc0b646dc0bc Mon Sep 17 00:00:00 2001 From: TaduJR Date: Sat, 24 Jan 2026 17:58:34 +0300 Subject: [PATCH 2/5] fix: skip deleted reports in navigation and update count correctly --- .../MoneyRequestReportNavigation.tsx | 14 +++++++++++--- .../MoneyRequestReportView.tsx | 12 ++++++++++++ .../Search/ExpenseReportListItem.tsx | 10 ++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx index f071fb5cd591..d96bbf1cdc3b 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx @@ -24,6 +24,7 @@ type MoneyRequestReportNavigationProps = { function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: MoneyRequestReportNavigationProps) { const [lastSearchQuery] = useOnyx(ONYXKEYS.REPORT_NAVIGATION_LAST_SEARCH_QUERY, {canBeMissing: true}); const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${lastSearchQuery?.queryJSON?.hash}`, {canBeMissing: true}); + const [liveReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); const currentUserDetails = useCurrentUserPersonalDetails(); const {localeCompare, formatPhoneNumber, translate} = useLocalize(); const [isActionLoadingSet = new Set()] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}`, {canBeMissing: true, selector: isActionLoadingSetSelector}); @@ -59,7 +60,14 @@ function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: Mo }); results = getSortedSections(type, status ?? '', searchData, localeCompare, translate, sortBy, sortOrder, groupBy).map((value) => value.reportID); } - const allReports = results; + + const allReports = results.filter((id) => { + if (!id) { + return false; + } + const liveReport = liveReports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`]; + return liveReport?.pendingFields?.preview !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + }); const currentIndex = allReports.indexOf(reportID); const allReportsCount = lastSearchQuery?.previousLengthOfResults ?? 0; @@ -84,7 +92,7 @@ function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: Mo return; } - if (allReports.length > allReportsCount) { + if (allReports.length !== allReportsCount) { saveLastSearchParams({ ...lastSearchQuery, previousLengthOfResults: allReports.length, @@ -138,7 +146,7 @@ function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: Mo return; } - const prevIndex = (currentIndex - 1) % allReports.length; + const prevIndex = (currentIndex - 1 + allReports.length) % allReports.length; goToReportId(allReports.at(prevIndex)); }; diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index f96ce35ec9f3..b78768a4ac17 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -5,6 +5,7 @@ import React, {useCallback, useEffect, useMemo} from 'react'; // eslint-disable-next-line no-restricted-imports import {Animated, InteractionManager, ScrollView, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import MoneyReportHeader from '@components/MoneyReportHeader'; import MoneyRequestHeader from '@components/MoneyRequestHeader'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -193,10 +194,21 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe }; }, [reportID]); + const isReportPendingDelete = report?.pendingFields?.preview === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + if (!!(isLoadingInitialReportActions && reportActions.length === 0 && !isOffline) || shouldWaitForTransactions) { return ; } + if (reportActions.length === 0 && isReportPendingDelete) { + return ( + + ); + } + if (reportActions.length === 0) { return ; } diff --git a/src/components/SelectionListWithSections/Search/ExpenseReportListItem.tsx b/src/components/SelectionListWithSections/Search/ExpenseReportListItem.tsx index 5d7dbe67bb81..7d91c64bb4fc 100644 --- a/src/components/SelectionListWithSections/Search/ExpenseReportListItem.tsx +++ b/src/components/SelectionListWithSections/Search/ExpenseReportListItem.tsx @@ -16,6 +16,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {handleActionButtonPress} from '@libs/actions/Search'; import {isOpenExpenseReport, isProcessingReport} from '@libs/ReportUtils'; 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} from '@src/types/onyx'; @@ -67,6 +68,8 @@ function ExpenseReportListItem({ return isEmpty ?? reportItem.isDisabled ?? reportItem.isDisabledCheckbox; }, [reportItem.isDisabled, reportItem.isDisabledCheckbox, reportItem.transactions.length]); + const isReportPendingDelete = item.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext); const handleOnButtonPress = useCallback(() => { @@ -112,8 +115,9 @@ function ExpenseReportListItem({ styles.bgTransparent, item.isSelected && styles.activeComponentBG, styles.mh0, + isReportPendingDelete && styles.cursorDisabled, ], - [styles, item.isSelected], + [styles, item.isSelected, isReportPendingDelete], ); const listItemWrapperStyle = useMemo( @@ -180,9 +184,11 @@ function ExpenseReportListItem({ onLongPressRow={onLongPressRow} shouldSyncFocus={shouldSyncFocus} hoverStyle={item.isSelected && styles.activeComponentBG} - pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]} + pressableWrapperStyle={[styles.mh5, animatedHighlightStyle, isReportPendingDelete && styles.cursorDisabled]} shouldShowRightCaret={false} shouldUseDefaultRightHandSideCheckmark={false} + isDisabled={isReportPendingDelete} + shouldDisableHoverStyle={isReportPendingDelete} > {(hovered) => ( From c3d853e446a175abe8913ca3bd6eede10aa1bfb4 Mon Sep 17 00:00:00 2001 From: TaduJR Date: Sat, 24 Jan 2026 18:56:30 +0300 Subject: [PATCH 3/5] fix: optimize report navigation and check both delete flags --- .../MoneyRequestReportNavigation.tsx | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx index d96bbf1cdc3b..c19b14c2cc96 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx @@ -1,5 +1,6 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; +import type {OnyxCollection} from 'react-native-onyx'; import PrevNextButtons from '@components/PrevNextButtons'; import Text from '@components/Text'; import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; @@ -15,6 +16,29 @@ import {search} from '@userActions/Search'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isActionLoadingSetSelector} from '@src/selectors/ReportMetaData'; +import type {Report} from '@src/types/onyx'; + +type ReportPendingFields = Pick; + +/** + * Selector to only get pendingAction and pendingFields from reports. + * This reduces re-renders by not subscribing to all report field changes. + */ +function selectReportsPendingFields(reports: OnyxCollection): OnyxCollection { + if (!reports) { + return null; + } + return Object.keys(reports).reduce((acc: Record, key) => { + const report = reports[key]; + if (report) { + acc[key] = { + pendingAction: report.pendingAction, + pendingFields: report.pendingFields, + }; + } + return acc; + }, {}); +} type MoneyRequestReportNavigationProps = { reportID?: string; @@ -24,7 +48,7 @@ type MoneyRequestReportNavigationProps = { function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: MoneyRequestReportNavigationProps) { const [lastSearchQuery] = useOnyx(ONYXKEYS.REPORT_NAVIGATION_LAST_SEARCH_QUERY, {canBeMissing: true}); const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${lastSearchQuery?.queryJSON?.hash}`, {canBeMissing: true}); - const [liveReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); + const [liveReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true, selector: selectReportsPendingFields}); const currentUserDetails = useCurrentUserPersonalDetails(); const {localeCompare, formatPhoneNumber, translate} = useLocalize(); const [isActionLoadingSet = new Set()] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}`, {canBeMissing: true, selector: isActionLoadingSetSelector}); @@ -61,12 +85,14 @@ function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: Mo results = getSortedSections(type, status ?? '', searchData, localeCompare, translate, sortBy, sortOrder, groupBy).map((value) => value.reportID); } + const reportKeyPrefix = ONYXKEYS.COLLECTION.REPORT; const allReports = results.filter((id) => { if (!id) { return false; } - const liveReport = liveReports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`]; - return liveReport?.pendingFields?.preview !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + const liveReport = liveReports?.[`${reportKeyPrefix}${id}`]; + const isPendingDelete = liveReport?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || liveReport?.pendingFields?.preview === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + return !isPendingDelete; }); const currentIndex = allReports.indexOf(reportID); From 212b0cef29f9e43a10af378a2958d7dbe1a8e3cf Mon Sep 17 00:00:00 2001 From: TaduJR Date: Sat, 24 Jan 2026 19:04:44 +0300 Subject: [PATCH 4/5] refactor: use mapOnyxCollectionItems for report pending fields selector --- .../MoneyRequestReportNavigation.tsx | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx index c19b14c2cc96..09a6b8f1bccd 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx @@ -1,6 +1,6 @@ import React, {useEffect} from 'react'; import {View} from 'react-native'; -import type {OnyxCollection} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import PrevNextButtons from '@components/PrevNextButtons'; import Text from '@components/Text'; import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; @@ -17,6 +17,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isActionLoadingSetSelector} from '@src/selectors/ReportMetaData'; import type {Report} from '@src/types/onyx'; +import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; type ReportPendingFields = Pick; @@ -24,21 +25,15 @@ type ReportPendingFields = Pick; * Selector to only get pendingAction and pendingFields from reports. * This reduces re-renders by not subscribing to all report field changes. */ -function selectReportsPendingFields(reports: OnyxCollection): OnyxCollection { - if (!reports) { - return null; - } - return Object.keys(reports).reduce((acc: Record, key) => { - const report = reports[key]; - if (report) { - acc[key] = { - pendingAction: report.pendingAction, - pendingFields: report.pendingFields, - }; - } - return acc; - }, {}); -} +const selectReportsPendingFields = (reports: OnyxCollection) => + mapOnyxCollectionItems(reports, (report: OnyxEntry): ReportPendingFields | undefined => + report + ? { + pendingAction: report.pendingAction, + pendingFields: report.pendingFields, + } + : undefined, + ); type MoneyRequestReportNavigationProps = { reportID?: string; From 99c59e712a677cc56f14eb2832078b558da007d7 Mon Sep 17 00:00:00 2001 From: TaduJR Date: Sat, 24 Jan 2026 19:06:35 +0300 Subject: [PATCH 5/5] refactor: remove unnecessary comment from selector --- .../MoneyRequestReportView/MoneyRequestReportNavigation.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx index 09a6b8f1bccd..9ac6c3d71031 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx @@ -21,10 +21,6 @@ import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; type ReportPendingFields = Pick; -/** - * Selector to only get pendingAction and pendingFields from reports. - * This reduces re-renders by not subscribing to all report field changes. - */ const selectReportsPendingFields = (reports: OnyxCollection) => mapOnyxCollectionItems(reports, (report: OnyxEntry): ReportPendingFields | undefined => report