From c2de4e47c391bda9469a0a9bdf4671be27922c95 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 18 Jan 2026 02:26:33 +0530 Subject: [PATCH 01/78] Update outstanding report filters --- .../MoneyRequestConfirmationListFooter.tsx | 27 ++++++++++--------- .../step/IOURequestEditReportCommon.tsx | 12 ++++++--- .../step/IOURequestStepConfirmation.tsx | 7 ++++- .../iou/request/step/IOURequestStepReport.tsx | 5 +++- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 84d906d83b7b..251145de21b0 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -6,6 +6,7 @@ import React, {memo, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -24,7 +25,7 @@ import {canSendInvoice, getPerDiemCustomUnit} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; import {computeReportName} from '@libs/ReportNameUtils'; -import {generateReportID, getDefaultWorkspaceAvatar, getOutstandingReportsForUser, isArchivedReport, isMoneyRequestReport, isReportOutstanding} from '@libs/ReportUtils'; +import {generateReportID, getDefaultWorkspaceAvatar, getOutstandingReportsForUser, isMoneyRequestReport, isReportOutstanding} from '@libs/ReportUtils'; import {getTagVisibility, hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getTagForDisplay, @@ -276,7 +277,8 @@ function MoneyRequestConfirmationListFooter({ const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); - const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {canBeMissing: true}); + const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchived = (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID, { canBeMissing: true, }); @@ -292,11 +294,8 @@ function MoneyRequestConfirmationListFooter({ const allOutstandingReports = useMemo(() => { const outstandingReports = Object.values(outstandingReportsByPolicyID ?? {}).flatMap((outstandingReportsPolicy) => Object.values(outstandingReportsPolicy ?? {})); - return outstandingReports.filter((report) => { - const reportNameValuePair = reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`]; - return !isArchivedReport(reportNameValuePair) && isReportOutstanding(report, report?.policyID, reportNameValuePairs, false); - }); - }, [outstandingReportsByPolicyID, reportNameValuePairs]); + return outstandingReports.filter((report) => !isReportArchived(report?.reportID) && isReportOutstanding(report, report?.policyID, isReportArchived, false)); + }, [isReportArchived, outstandingReportsByPolicyID]); const isTrackExpense = iouType === CONST.IOU.TYPE.TRACK; const shouldShowTags = useMemo( @@ -328,15 +327,19 @@ function MoneyRequestConfirmationListFooter({ */ const transactionReport = transaction?.reportID ? Object.values(allReports ?? {}).find((report) => report?.reportID === transaction.reportID) : undefined; const policyID = selectedParticipants?.at(0)?.policyID; - const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, policyID, undefined, false)) || isUnreported; + const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, policyID, isReportArchived, false)) || isUnreported; const ownerAccountID = selectedParticipants?.at(0)?.ownerAccountID; const availableOutstandingReports = useMemo(() => { - return getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, reportNameValuePairs, false).sort((a, b) => - localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? ''), - ); - }, [policyID, ownerAccountID, outstandingReportsByPolicyID, reportNameValuePairs, localeCompare]); + return getOutstandingReportsForUser( + policyID, + ownerAccountID, + outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, + isReportArchived, + false, + ).sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? '')); + }, [policyID, ownerAccountID, outstandingReportsByPolicyID, isReportArchived, localeCompare]); const iouReportID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.iouReportID; const outstandingReportID = isPolicyExpenseChat ? (iouReportID ?? availableOutstandingReports.at(0)?.reportID) : reportID; diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 875bd0e26dfb..9cfa07c3ff04 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -9,6 +9,7 @@ import {useOptionsList} from '@components/OptionListContextProvider'; import SelectionList from '@components/SelectionList'; import InviteMemberListItem from '@components/SelectionList/ListItem/InviteMemberListItem'; import type {ListItem} from '@components/SelectionList/types'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDebouncedState from '@hooks/useDebouncedState'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; @@ -102,6 +103,11 @@ function IOURequestEditReportCommon({ const [perDiemWarningModalVisible, setPerDiemWarningModalVisible] = useState(false); const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {canBeMissing: true}); + const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchived = useCallback( + (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), + [archivedReportsIdSet], + ); const [allPoliciesID] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: policiesSelector, canBeMissing: false}); @@ -143,7 +149,7 @@ function IOURequestEditReportCommon({ policyID, resolvedReportOwnerAccountID, outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, - reportNameValuePairs, + isReportArchived, isEditing, ); @@ -154,10 +160,10 @@ function IOURequestEditReportCommon({ selectedPolicyID, resolvedReportOwnerAccountID, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, - reportNameValuePairs, + isReportArchived, isEditing, ); - }, [outstandingReportsByPolicyID, resolvedReportOwnerAccountID, allPoliciesID, reportNameValuePairs, selectedReport, selectedPolicyID, isEditing, personalPolicyID]); + }, [outstandingReportsByPolicyID, resolvedReportOwnerAccountID, allPoliciesID, reportNameValuePairs, selectedReport, selectedPolicyID, isEditing, personalPolicyID, isReportArchived]); const reportOptions: TransactionGroupListItem[] = useMemo(() => { if (!outstandingReportsByPolicyID || isEmptyObject(outstandingReportsByPolicyID)) { diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 818df51c68e3..8b589e86df3e 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -179,7 +179,8 @@ function IOURequestStepConfirmation({ */ const transactionReport = getReportOrDraftReport(transaction?.reportID); const reportWithDraftFallback = useMemo(() => reportReal ?? reportDraft, [reportDraft, reportReal]); - const canUseReport = !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, undefined, false); + const canUseReport = + !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, isReportArchived, false); const shouldUseTransactionReport = !!transactionReport && (canUseReport || !reportWithDraftFallback); const isTransactionReportDifferentFromRoute = useMemo( @@ -262,6 +263,10 @@ function IOURequestStepConfirmation({ hasOutstandingChildTask, } = useOnboardingTaskInformation(CONST.ONBOARDING_TASK_TYPE.VIEW_TOUR); const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchived = useCallback( + (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), + [archivedReportsIdSet], + ); const parentReportAction = useParentReportAction(viewTourTaskReport); const receiptFilename = transaction?.receipt?.filename; diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index fe57f6fe497a..8395c578c174 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -4,6 +4,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {useSession} from '@components/OnyxListItemProvider'; import {useSearchContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionListWithSections/types'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnyx from '@hooks/useOnyx'; @@ -39,11 +40,13 @@ type IOURequestStepReportProps = WithWritableReportOrNotFoundProps !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); const participantReportID = transaction?.participants?.at(0)?.reportID; const participantReport = Object.values(allReports ?? {}).find((report) => report?.reportID === participantReportID); - const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID)) || isUnreported; + const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID, isReportArchived)) || isUnreported; const outstandingReportID = isPolicyExpenseChat(participantReport) ? participantReport?.iouReportID : participantReportID; const selectedReportID = shouldUseTransactionReport ? transactionReport?.reportID : outstandingReportID; const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); From df6513bc3aa69ffa0fe14d8373c00770cbb51f21 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 18 Jan 2026 02:29:18 +0530 Subject: [PATCH 02/78] Thread archived checks through edit gating --- .../MoneyRequestReceiptView.tsx | 19 ++++++- .../ReportActionItem/MoneyRequestView.tsx | 53 ++++++++++++++++--- .../TransactionPreviewContent.tsx | 5 +- src/hooks/useSelectedTransactionsActions.ts | 15 +++++- src/hooks/useShowNotFoundPageInIOUStep.ts | 8 ++- .../ReportActionCompose.tsx | 8 ++- src/pages/iou/HoldReasonPage.tsx | 9 ++-- .../routes/TransactionReceiptModalContent.tsx | 7 ++- 8 files changed, 106 insertions(+), 18 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx index 07fc3fa7d0d3..2d50ebce679a 100644 --- a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx +++ b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx @@ -8,6 +8,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ReceiptAudit, {ReceiptAuditMessages} from '@components/ReceiptAudit'; import ReceiptEmptyState from '@components/ReceiptEmptyState'; import useActiveRoute from '@hooks/useActiveRoute'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useEnvironment from '@hooks/useEnvironment'; import useGetIOUReportFromReportAction from '@hooks/useGetIOUReportFromReportAction'; import useLocalize from '@hooks/useLocalize'; @@ -121,6 +122,8 @@ function MoneyRequestReceiptView({ const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(linkedTransactionID)}`, {canBeMissing: true}); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${moneyRequestReport?.policyID}`, {canBeMissing: true}); const transactionViolations = useTransactionViolations(transaction?.transactionID); + const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const isDistanceRequest = isDistanceRequestTransactionUtils(transaction); const hasReceipt = hasReceiptTransactionUtils(updatedTransaction ?? transaction); @@ -133,11 +136,23 @@ function MoneyRequestReceiptView({ // Used for non-restricted fields such as: description, category, tag, billable, etc... const isReportArchived = useReportIsArchived(report?.reportID); const isEditable = !!canUserPerformWriteActionReportUtils(report, isReportArchived) && !readonly; - const canEdit = isMoneyRequestAction(parentReportAction) && canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction) && isEditable; + const canEdit = + isMoneyRequestAction(parentReportAction) && canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction, isReportArchivedByID) && isEditable; const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(report?.policyID)}`; const canEditReceipt = - isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy); + isEditable && + canEditFieldOfMoneyRequest( + parentReportAction, + CONST.EDIT_REQUEST_FIELD.RECEIPT, + undefined, + isChatReportArchived, + undefined, + transaction, + moneyRequestReport, + policy, + isReportArchivedByID, + ); const iouType = useMemo(() => { if (isTrackExpense) { diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index aa90f4a0a721..3854f397d895 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -15,6 +15,7 @@ import Text from '@components/Text'; import ViolationMessages from '@components/ViolationMessages'; import {WideRHPContext} from '@components/WideRHPContextProvider'; import useActiveRoute from '@hooks/useActiveRoute'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useEnvironment from '@hooks/useEnvironment'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; @@ -315,6 +316,8 @@ function MoneyRequestView({ const isSettled = isSettledReportUtils(moneyRequestReport); const isCancelled = moneyRequestReport && moneyRequestReport?.isCancelledIOU; const isChatReportArchived = useReportIsArchived(moneyRequestReport?.chatReportID); + const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const pendingAction = transaction?.pendingAction; const shouldShowPaid = isSettled && transactionReimbursable && !pendingAction; @@ -322,7 +325,8 @@ function MoneyRequestView({ // Used for non-restricted fields such as: description, category, tag, billable, etc... const isReportArchived = useReportIsArchived(transactionThreadReport?.reportID); const isEditable = !!canUserPerformWriteActionReportUtils(transactionThreadReport, isReportArchived) && !readonly; - const canEdit = isMoneyRequestAction(parentReportAction) && canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction) && isEditable; + const canEdit = + isMoneyRequestAction(parentReportAction) && canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction, isReportArchivedByID) && isEditable; const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(transactionThreadReport?.policyID)}`; const [originalTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transaction?.comment?.originalTransactionID)}`, {canBeMissing: true}); const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction, originalTransaction); @@ -330,21 +334,45 @@ function MoneyRequestView({ const canEditTaxFields = canEdit && !isDistanceRequest; const canEditAmount = - isEditable && (canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.AMOUNT, undefined, isChatReportArchived) || (isExpenseSplit && isSplitAvailable)); + isEditable && + (canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.AMOUNT, undefined, isChatReportArchived, undefined, undefined, undefined, undefined, isReportArchivedByID) || + (isExpenseSplit && isSplitAvailable)); const canEditMerchant = - isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.MERCHANT, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy); + isEditable && + canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.MERCHANT, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy, isReportArchivedByID); const canEditDate = - isEditable && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DATE, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy); + isEditable && + canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DATE, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy, isReportArchivedByID); const canEditDistance = isEditable && - canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy) && + canEditFieldOfMoneyRequest( + parentReportAction, + CONST.EDIT_REQUEST_FIELD.DISTANCE, + undefined, + isChatReportArchived, + undefined, + transaction, + moneyRequestReport, + policy, + isReportArchivedByID, + ) && isPolicyAccessible(policy, currentUserEmailParam); const canEditDistanceRate = isEditable && - canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy) && + canEditFieldOfMoneyRequest( + parentReportAction, + CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE, + undefined, + isChatReportArchived, + undefined, + transaction, + moneyRequestReport, + policy, + isReportArchivedByID, + ) && isPolicyAccessible(policy, currentUserEmailParam); const canEditReport = @@ -358,6 +386,7 @@ function MoneyRequestView({ transaction, moneyRequestReport, policy, + isReportArchivedByID, ) && (!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy) || (isExpenseUnreported && !!perDiemOriginalPolicy)); @@ -389,7 +418,17 @@ function MoneyRequestView({ !isInvoice; const canEditReimbursable = isEditable && - canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.REIMBURSABLE, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy); + canEditFieldOfMoneyRequest( + parentReportAction, + CONST.EDIT_REQUEST_FIELD.REIMBURSABLE, + undefined, + isChatReportArchived, + undefined, + transaction, + moneyRequestReport, + policy, + isReportArchivedByID, + ); const shouldShowAttendees = shouldShowAttendeesTransactionUtils(iouType, policy); const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest, isPerDiemRequest, isTimeRequest); diff --git a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx index a417b3917126..998a9bddcf6a 100644 --- a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx +++ b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx @@ -11,6 +11,7 @@ import ReportActionItemImages from '@components/ReportActionItem/ReportActionIte import UserInfoCellsWithArrow from '@components/SelectionListWithSections/Search/UserInfoCellsWithArrow'; import Text from '@components/Text'; import TransactionPreviewSkeletonView from '@components/TransactionPreviewSkeletonView'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useEnvironment from '@hooks/useEnvironment'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; @@ -83,6 +84,8 @@ function TransactionPreviewContent({ const isReportAPolicyExpenseChat = isPolicyExpenseChat(chatReport); const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.reportID)}`, {canBeMissing: true}); const isChatReportArchived = useReportIsArchived(chatReport?.reportID); + const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const currentUserDetails = useCurrentUserPersonalDetails(); const currentUserEmail = currentUserDetails.email ?? ''; const currentUserAccountID = currentUserDetails.accountID; @@ -115,7 +118,7 @@ function TransactionPreviewContent({ const firstViolation = violations.at(0); const isIOUActionType = isMoneyRequestAction(action); - const canEdit = isIOUActionType && canEditMoneyRequest(action, isChatReportArchived, report, policy, transaction); + const canEdit = isIOUActionType && canEditMoneyRequest(action, isChatReportArchived, report, policy, transaction, isReportArchivedByID); const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(report?.policyID)}`; const violationMessage = firstViolation ? ViolationsUtils.getViolationTranslation(firstViolation, translate, canEdit, undefined, companyCardPageURL) : undefined; diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index 6a640914a309..e9929370a908 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -35,6 +35,7 @@ import {useMemoizedLazyExpensifyIcons} from './useLazyAsset'; import useLocalize from './useLocalize'; import useNetworkWithOfflineStatus from './useNetworkWithOfflineStatus'; import useOnyx from './useOnyx'; +import useArchivedReportsIdSet from './useArchivedReportsIdSet'; import useReportIsArchived from './useReportIsArchived'; // We do not use PRIMARY_REPORT_ACTIONS or SECONDARY_REPORT_ACTIONS because they weren't meant to be used in this situation. `value` property of returned options is later ignored. @@ -78,6 +79,8 @@ function useSelectedTransactionsActions({ const expensifyIcons = useMemoizedLazyExpensifyIcons(['Stopwatch', 'Trashcan', 'ArrowRight', 'Table', 'DocumentMerge', 'Export', 'ArrowCollapse', 'ArrowSplit', 'ThumbsDown']); const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(selectedTransactionIDs); const isReportArchived = useReportIsArchived(report?.reportID); + const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const {deleteTransactions} = useDeleteTransactions({report, reportActions, policy}); const {login} = useCurrentUserPersonalDetails(); const selectedTransactionsList = useMemo( @@ -298,7 +301,17 @@ function useSelectedTransactionsActions({ } const iouReportAction = getIOUActionForTransactionID(reportActions, transaction.transactionID); - const canMoveExpense = canEditFieldOfMoneyRequest(iouReportAction, CONST.EDIT_REQUEST_FIELD.REPORT, undefined, undefined, outstandingReportsByPolicyID); + const canMoveExpense = canEditFieldOfMoneyRequest( + iouReportAction, + CONST.EDIT_REQUEST_FIELD.REPORT, + undefined, + undefined, + outstandingReportsByPolicyID, + undefined, + undefined, + undefined, + isReportArchivedByID, + ); return canMoveExpense; }); diff --git a/src/hooks/useShowNotFoundPageInIOUStep.ts b/src/hooks/useShowNotFoundPageInIOUStep.ts index 443dcfe29ffe..7a2bef5f4958 100644 --- a/src/hooks/useShowNotFoundPageInIOUStep.ts +++ b/src/hooks/useShowNotFoundPageInIOUStep.ts @@ -8,6 +8,7 @@ import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {OnyxInputOrEntry, Report, ReportAction, ReportActions, Transaction} from '@src/types/onyx'; +import useArchivedReportsIdSet from './useArchivedReportsIdSet'; import useOnyx from './useOnyx'; /** @@ -25,6 +26,11 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true}); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(transaction?.reportID)}`, {canBeMissing: true}); + const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchived = useCallback( + (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), + [archivedReportsIdSet], + ); const reportActionsReportID = useMemo(() => { let actionsReportID; @@ -63,7 +69,7 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor } else if (isSplitExpense) { shouldShowNotFoundPage = !canEditSplitExpense; } else { - shouldShowNotFoundPage = !isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, false, iouReport, policy, transaction); + shouldShowNotFoundPage = !isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, false, iouReport, policy, transaction, isReportArchived); } } diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index f7e4f81aafcd..c7dfd6938ea1 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -20,6 +20,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import useAgentZeroStatusIndicator from '@hooks/useAgentZeroStatusIndicator'; import useAncestors from '@hooks/useAncestors'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHandleExceedMaxCommentLength from '@hooks/useHandleExceedMaxCommentLength'; import useHandleExceedMaxTaskTitleLength from '@hooks/useHandleExceedMaxTaskTitleLength'; @@ -235,6 +236,8 @@ function ReportActionCompose({ const userBlockedFromConcierge = useMemo(() => isBlockedFromConciergeUserAction(blockedFromConcierge), [blockedFromConcierge]); const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); const isReportArchived = useReportIsArchived(report?.reportID); + const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const isConciergeChat = useMemo(() => isConciergeChatReport(report), [report]); const isTransactionThreadView = useMemo(() => isReportTransactionThread(report), [report]); @@ -257,7 +260,10 @@ function ReportActionCompose({ const isSingleTransactionView = useMemo(() => !!transaction && !!reportTransactions && reportTransactions.length === 1, [transaction, reportTransactions]); const parentReportAction = isSingleTransactionView ? iouAction : getReportAction(report?.parentReportID, report?.parentReportActionID); const canUserPerformWriteAction = !!canUserPerformWriteActionReportUtils(report, isReportArchived); - const canEditReceipt = canUserPerformWriteAction && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT) && !transaction?.receipt?.isTestDriveReceipt; + const canEditReceipt = + canUserPerformWriteAction && + canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, undefined, undefined, undefined, undefined, undefined, undefined, isReportArchivedByID) && + !transaction?.receipt?.isTestDriveReceipt; const shouldAddOrReplaceReceipt = (isTransactionThreadView || isSingleTransactionView) && canEditReceipt; const hasReceipt = useMemo(() => hasReceiptTransactionUtils(transaction), [transaction]); diff --git a/src/pages/iou/HoldReasonPage.tsx b/src/pages/iou/HoldReasonPage.tsx index cca028e70168..26bd22da3952 100644 --- a/src/pages/iou/HoldReasonPage.tsx +++ b/src/pages/iou/HoldReasonPage.tsx @@ -1,6 +1,7 @@ import React, {useCallback, useEffect} from 'react'; import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import useAncestors from '@hooks/useAncestors'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import {putOnHold} from '@libs/actions/IOU/Hold'; @@ -28,6 +29,8 @@ function HoldReasonPage({route}: HoldReasonPageProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: true}); const ancestors = useAncestors(report); + const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchived = (reportId?: string) => !!reportId && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportId}`); // We first check if the report is part of a policy - if not, then it's a personal request (1:1 request) // For personal requests, we need to allow both users to put the request on hold @@ -38,7 +41,7 @@ function HoldReasonPage({route}: HoldReasonPageProps) { // We have extra isWorkspaceRequest condition since, for 1:1 requests, canEditMoneyRequest will rightly return false // as we do not allow requestee to edit fields like description and amount. // But, we still want the requestee to be able to put the request on hold - if (isMoneyRequestAction(parentReportAction) && !canEditMoneyRequest(parentReportAction) && isWorkspaceRequest) { + if (isMoneyRequestAction(parentReportAction) && !canEditMoneyRequest(parentReportAction, false, undefined, undefined, undefined, isReportArchived) && isWorkspaceRequest) { return; } @@ -56,7 +59,7 @@ function HoldReasonPage({route}: HoldReasonPageProps) { // We have extra isWorkspaceRequest condition since, for 1:1 requests, canEditMoneyRequest will rightly return false // as we do not allow requestee to edit fields like description and amount. // But, we still want the requestee to be able to put the request on hold - if (isMoneyRequestAction(parentReportAction) && !canEditMoneyRequest(parentReportAction) && isWorkspaceRequest) { + if (isMoneyRequestAction(parentReportAction) && !canEditMoneyRequest(parentReportAction, false, undefined, undefined, undefined, isReportArchived) && isWorkspaceRequest) { const formErrors = {}; addErrorMessage(formErrors, 'reportModified', translate('common.error.requestModified')); setErrors(ONYXKEYS.FORMS.MONEY_REQUEST_HOLD_FORM, formErrors); @@ -64,7 +67,7 @@ function HoldReasonPage({route}: HoldReasonPageProps) { return errors; }, - [parentReportAction, isWorkspaceRequest, translate], + [parentReportAction, isWorkspaceRequest, translate, isReportArchived], ); useEffect(() => { diff --git a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx index d58dd6d0fb9c..c5947083e347 100644 --- a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx @@ -4,6 +4,7 @@ import ConfirmModal from '@components/ConfirmModal'; // eslint-disable-next-line no-restricted-imports import * as Expensicons from '@components/Icon/Expensicons'; import useAllTransactions from '@hooks/useAllTransactions'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -46,6 +47,8 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report?.policyID}`, {canBeMissing: true}); const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true}); const policy = usePolicy(report?.policyID); + const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchived = (reportId?: string) => !!reportId && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportId}`); // If we have a merge transaction, we need to use the receipt from the merge transaction const [mergeTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.MERGE_TRANSACTION}${getNonEmptyStringOnyxID(mergeTransactionID)}`, {canBeMissing: true}); @@ -78,8 +81,8 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre const [sourceUri, setSourceUri] = useState(''); const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); - const canEditReceipt = canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT); - const canDeleteReceipt = canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, true); + const canEditReceipt = canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, undefined, undefined, undefined, undefined, undefined, undefined, isReportArchived); + const canDeleteReceipt = canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, true, undefined, undefined, undefined, undefined, undefined, isReportArchived); const shouldShowReplaceReceiptButton = ((canEditReceipt && !readonly) || isDraftTransaction) && !transaction?.receipt?.isTestDriveReceipt; const shouldShowDeleteReceiptButton = canDeleteReceipt && !readonly && !isDraftTransaction && !transaction?.receipt?.isTestDriveReceipt; From cf65086d97fc7d99a9b5ee9b5b6ee8e7b48c4e49 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 18 Jan 2026 02:29:39 +0530 Subject: [PATCH 03/78] Use archived report checks in search selection --- src/components/Search/index.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index f7348a64ddbe..6991f5389f25 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -97,6 +97,7 @@ function mapTransactionItemToSelectedEntry( originalItemTransaction: OnyxEntry, currentUserLogin: string, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, + isReportArchived?: (reportID?: string) => boolean, ): [string, SelectedTransactionInfo] { const {canHoldRequest, canUnholdRequest} = canHoldUnholdReportAction(item.report, item.reportAction, item.holdReportAction, item, item.policy); const canRejectRequest = item.report ? canRejectReportAction(currentUserLogin, item.report, item.policy) : false; @@ -119,6 +120,7 @@ function mapTransactionItemToSelectedEntry( item, item.report, item.policy, + isReportArchived, ), action: item.action, groupCurrency: item.groupCurrency, @@ -142,6 +144,7 @@ function prepareTransactionsList( selectedTransactions: SelectedTransactions, currentUserLogin: string, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, + isReportArchived?: (reportID?: string) => boolean, ) { if (selectedTransactions[item.keyForList]?.isSelected) { const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions; @@ -171,6 +174,7 @@ function prepareTransactionsList( item, item.report, item.policy, + isReportArchived, ), action: item.action, reportID: item.reportID, @@ -253,6 +257,7 @@ function Search({ const {markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useContext(WideRHPContext); const archivedReportsIdSet = useArchivedReportsIdSet(); + const isReportArchived = (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, { canEvict: false, @@ -552,6 +557,7 @@ function Search({ transactionItem, transactionItem.report, transactionItem.policy, + isReportArchived, ), // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID]?.isSelected || isExpenseReportType, @@ -606,6 +612,7 @@ function Search({ transactionItem, transactionItem.report, transactionItem.policy, + isReportArchived, ), // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID].isSelected, @@ -694,7 +701,7 @@ function Search({ const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${item.transactionID}`] as OnyxEntry; const originalItemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; setSelectedTransactions( - prepareTransactionsList(item, itemTransaction, originalItemTransaction, selectedTransactions, email ?? '', outstandingReportsByPolicyID), + prepareTransactionsList(item, itemTransaction, originalItemTransaction, selectedTransactions, email ?? '', outstandingReportsByPolicyID, isReportArchived), filteredData, ); return; @@ -721,14 +728,14 @@ function Search({ .map((transactionItem) => { const itemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', outstandingReportsByPolicyID); + return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', outstandingReportsByPolicyID, isReportArchived); }), ), }, filteredData, ); }, - [setSelectedTransactions, selectedTransactions, filteredData, transactions, outstandingReportsByPolicyID, searchResults?.data, email], + [setSelectedTransactions, selectedTransactions, filteredData, transactions, outstandingReportsByPolicyID, searchResults?.data, email, isReportArchived], ); const onSelectRow = useCallback( @@ -960,7 +967,7 @@ function Search({ .map((transactionItem) => { const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', outstandingReportsByPolicyID); + return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', outstandingReportsByPolicyID, isReportArchived); }), ), ), @@ -977,7 +984,7 @@ function Search({ .map((transactionItem) => { const itemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', outstandingReportsByPolicyID); + return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', outstandingReportsByPolicyID, isReportArchived); }), ), filteredData, @@ -993,6 +1000,7 @@ function Search({ outstandingReportsByPolicyID, searchResults?.data, email, + isReportArchived, ]); const onLayout = useCallback(() => { From 4fefcb934d47d19fee1c4d30536af271c2625ef5 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 18 Jan 2026 02:25:44 +0530 Subject: [PATCH 04/78] Refactor ReportUtils archive checks --- src/libs/ReportUtils.ts | 50 +++++++++++++++-------------------- tests/unit/ReportUtilsTest.ts | 7 ++--- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c2b81f677dfb..5924b754c7c6 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -348,6 +348,8 @@ type SpendBreakdown = { totalDisplaySpend: number; }; +type IsReportArchived = (reportID?: string) => boolean; + type OptimisticAddCommentReportAction = Pick< ReportAction, | 'reportActionID' @@ -1162,18 +1164,6 @@ Onyx.connectWithoutView({ }, }); -let allReportNameValuePair: OnyxCollection; -Onyx.connectWithoutView({ - key: ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, - waitForCollectionCallback: true, - callback: (value) => { - if (!value) { - return; - } - allReportNameValuePair = value; - }, -}); - let allReportsViolations: OnyxCollection; Onyx.connectWithoutView({ key: ONYXKEYS.COLLECTION.REPORT_VIOLATIONS, @@ -2225,7 +2215,6 @@ function findLastAccessedReport( openOnAdminRoom = false, policyID?: string, excludeReportID?: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars -- it will be used in the future isReportArchived?: IsReportArchived, ): OnyxEntry { // If it's the user's first time using New Expensify, then they could either have: @@ -2276,8 +2265,8 @@ function findLastAccessedReport( // and it prompts the user to use the Concierge chat instead. reportsValues = reportsValues.filter((report) => { - const reportNameValuePairs = allReportNameValuePair?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`]; - return !isSystemChat(report) && !isArchivedReport(reportNameValuePairs); + const isArchived = isReportArchived?.(report?.reportID) ?? false; + return !isSystemChat(report) && !isArchived; }) ?? []; // At least two reports remain: self DM and Concierge chat. @@ -4522,6 +4511,7 @@ function canEditMoneyRequest( report?: OnyxInputOrEntry, policy?: OnyxEntry, linkedTransaction?: OnyxEntry, + isReportArchived?: IsReportArchived, ): boolean { const isDeleted = isDeletedAction(reportAction); @@ -4666,6 +4656,7 @@ function canEditFieldOfMoneyRequest( linkedTransaction?: OnyxEntry, report?: OnyxInputOrEntry, policy?: OnyxEntry, + isReportArchived?: IsReportArchived, ): boolean { // A list of fields that cannot be edited by anyone, once an expense has been settled const restrictedFields: string[] = [ @@ -4680,7 +4671,7 @@ function canEditFieldOfMoneyRequest( CONST.EDIT_REQUEST_FIELD.REPORT, ]; - if (!isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, isChatReportArchived, report, policy, linkedTransaction)) { + if (!isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, isChatReportArchived, report, policy, linkedTransaction, isReportArchived)) { return false; } @@ -4754,7 +4745,7 @@ function canEditFieldOfMoneyRequest( return true; } - if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)) { + if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, isReportArchived)) { return false; } @@ -4770,6 +4761,7 @@ function canEditFieldOfMoneyRequest( moneyRequestReport?.policyID, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[moneyRequestReport?.policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, + isReportArchived, ).length > 0 ); } @@ -4783,9 +4775,14 @@ function canEditFieldOfMoneyRequest( return ( Object.values(allPolicies ?? {}).flatMap((currentPolicy) => - getOutstandingReportsForUser(currentPolicy?.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id ?? CONST.DEFAULT_NUMBER_ID] ?? {}), + getOutstandingReportsForUser( + currentPolicy?.id, + moneyRequestReport?.ownerAccountID, + outstandingReportsByPolicyID?.[currentPolicy?.id ?? CONST.DEFAULT_NUMBER_ID] ?? {}, + isReportArchived, + ), ).length > 1 || - ((isOwner || isAdmin || isManager) && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)) + ((isOwner || isAdmin || isManager) && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, isReportArchived)) ); } @@ -11353,12 +11350,7 @@ function hasForwardedAction(reportID: string): boolean { return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); } -function isReportOutstanding( - iouReport: OnyxInputOrEntry, - policyID: string | undefined, - reportNameValuePairs: OnyxCollection = allReportNameValuePair, - allowSubmitted = true, -): boolean { +function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: string | undefined, isReportArchived?: IsReportArchived, allowSubmitted = true): boolean { if (!iouReport || isEmptyObject(iouReport)) { return false; } @@ -11366,7 +11358,7 @@ function isReportOutstanding( const params = currentRoute?.params as MoneyRequestNavigatorParamList[typeof SCREENS.MONEY_REQUEST.STEP_CONFIRMATION] | ReportsSplitNavigatorParamList[typeof SCREENS.REPORT]; const activeReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${params?.reportID}`]; const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const reportNameValuePair = reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport.reportID}`]; + const isArchived = isReportArchived?.(iouReport.reportID) ?? false; const shouldAllowSubmittedReport = allowSubmitted || isInstantSubmitEnabled(policy) || isProcessingReport(activeReport); return ( isExpenseReport(iouReport) && @@ -11376,7 +11368,7 @@ function isReportOutstanding( (shouldAllowSubmittedReport ? iouReport?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED : iouReport?.stateNum < CONST.REPORT.STATE_NUM.SUBMITTED) && (shouldAllowSubmittedReport ? iouReport?.statusNum <= CONST.REPORT.STATE_NUM.SUBMITTED : iouReport?.statusNum < CONST.REPORT.STATE_NUM.SUBMITTED) && !hasForwardedAction(iouReport.reportID) && - !isArchivedReport(reportNameValuePair) + !isArchived ); } @@ -11391,7 +11383,7 @@ function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, reports: OnyxCollection = allReports, - reportNameValuePairs: OnyxCollection = allReportNameValuePair, + isReportArchived?: IsReportArchived, allowSubmitted = true, ): Array> { if (!reports) { @@ -11400,7 +11392,7 @@ function getOutstandingReportsForUser( return Object.values(reports).filter( (report) => report?.pendingFields?.preview !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - isReportOutstanding(report, policyID, reportNameValuePairs, allowSubmitted) && + isReportOutstanding(report, policyID, isReportArchived, allowSubmitted) && report?.ownerAccountID === reportOwnerAccountID, ); } diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 6f927e6f1270..f37ce564116e 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -5239,7 +5239,8 @@ describe('ReportUtils', () => { }); it('should not return an archived report even if it was most recently accessed', () => { - const result = findLastAccessedReport(false); + const isReportArchived = (reportID?: string) => reportID === archivedReport.reportID; + const result = findLastAccessedReport(false, false, undefined, undefined, isReportArchived); // Even though the archived report has a more recent lastVisitTime, // the function should filter it out and return the normal report @@ -6051,8 +6052,8 @@ describe('ReportUtils', () => { statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`, {private_isArchived: DateUtils.getDBTime()}); - expect(isReportOutstanding(report, policy.id)).toBe(false); + const isReportArchived = (reportID?: string) => reportID === report.reportID; + expect(isReportOutstanding(report, policy.id, isReportArchived)).toBe(false); }); }); From 63f40562a37e37d4f285940e3c67983e3879cc35 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 18 Jan 2026 17:12:53 +0530 Subject: [PATCH 05/78] Prettier fixes --- .../MoneyRequestConfirmationListFooter.tsx | 10 ++----- .../MoneyRequestReceiptView.tsx | 4 ++- .../ReportActionItem/MoneyRequestView.tsx | 28 +++++++++++++++++-- src/components/Search/index.tsx | 18 ++++++++++-- src/hooks/useSelectedTransactionsActions.ts | 2 +- src/hooks/useShowNotFoundPageInIOUStep.ts | 5 +--- .../step/IOURequestEditReportCommon.tsx | 5 +--- .../step/IOURequestStepConfirmation.tsx | 8 ++---- .../routes/TransactionReceiptModalContent.tsx | 12 +++++++- 9 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 251145de21b0..41a24cb94a63 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -332,13 +332,9 @@ function MoneyRequestConfirmationListFooter({ const ownerAccountID = selectedParticipants?.at(0)?.ownerAccountID; const availableOutstandingReports = useMemo(() => { - return getOutstandingReportsForUser( - policyID, - ownerAccountID, - outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, - isReportArchived, - false, - ).sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? '')); + return getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, isReportArchived, false).sort((a, b) => + localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? ''), + ); }, [policyID, ownerAccountID, outstandingReportsByPolicyID, isReportArchived, localeCompare]); const iouReportID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.iouReportID; diff --git a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx index 2d50ebce679a..27b096f39efd 100644 --- a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx +++ b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx @@ -137,7 +137,9 @@ function MoneyRequestReceiptView({ const isReportArchived = useReportIsArchived(report?.reportID); const isEditable = !!canUserPerformWriteActionReportUtils(report, isReportArchived) && !readonly; const canEdit = - isMoneyRequestAction(parentReportAction) && canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction, isReportArchivedByID) && isEditable; + isMoneyRequestAction(parentReportAction) && + canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction, isReportArchivedByID) && + isEditable; const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(report?.policyID)}`; const canEditReceipt = diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 3854f397d895..6e9f945bef61 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -326,7 +326,9 @@ function MoneyRequestView({ const isReportArchived = useReportIsArchived(transactionThreadReport?.reportID); const isEditable = !!canUserPerformWriteActionReportUtils(transactionThreadReport, isReportArchived) && !readonly; const canEdit = - isMoneyRequestAction(parentReportAction) && canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction, isReportArchivedByID) && isEditable; + isMoneyRequestAction(parentReportAction) && + canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction, isReportArchivedByID) && + isEditable; const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(transactionThreadReport?.policyID)}`; const [originalTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transaction?.comment?.originalTransactionID)}`, {canBeMissing: true}); const {isExpenseSplit} = getOriginalTransactionWithSplitInfo(transaction, originalTransaction); @@ -339,11 +341,31 @@ function MoneyRequestView({ (isExpenseSplit && isSplitAvailable)); const canEditMerchant = isEditable && - canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.MERCHANT, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy, isReportArchivedByID); + canEditFieldOfMoneyRequest( + parentReportAction, + CONST.EDIT_REQUEST_FIELD.MERCHANT, + undefined, + isChatReportArchived, + undefined, + transaction, + moneyRequestReport, + policy, + isReportArchivedByID, + ); const canEditDate = isEditable && - canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DATE, undefined, isChatReportArchived, undefined, transaction, moneyRequestReport, policy, isReportArchivedByID); + canEditFieldOfMoneyRequest( + parentReportAction, + CONST.EDIT_REQUEST_FIELD.DATE, + undefined, + isChatReportArchived, + undefined, + transaction, + moneyRequestReport, + policy, + isReportArchivedByID, + ); const canEditDistance = isEditable && diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 6991f5389f25..9bb85b5c4057 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -728,7 +728,14 @@ function Search({ .map((transactionItem) => { const itemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', outstandingReportsByPolicyID, isReportArchived); + return mapTransactionItemToSelectedEntry( + transactionItem, + itemTransaction, + originalItemTransaction, + email ?? '', + outstandingReportsByPolicyID, + isReportArchived, + ); }), ), }, @@ -967,7 +974,14 @@ function Search({ .map((transactionItem) => { const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', outstandingReportsByPolicyID, isReportArchived); + return mapTransactionItemToSelectedEntry( + transactionItem, + itemTransaction, + originalItemTransaction, + email ?? '', + outstandingReportsByPolicyID, + isReportArchived, + ); }), ), ), diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index e9929370a908..91db0876ff8c 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -28,6 +28,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import useAllTransactions from './useAllTransactions'; +import useArchivedReportsIdSet from './useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useDeleteTransactions from './useDeleteTransactions'; import useDuplicateTransactionsAndViolations from './useDuplicateTransactionsAndViolations'; @@ -35,7 +36,6 @@ import {useMemoizedLazyExpensifyIcons} from './useLazyAsset'; import useLocalize from './useLocalize'; import useNetworkWithOfflineStatus from './useNetworkWithOfflineStatus'; import useOnyx from './useOnyx'; -import useArchivedReportsIdSet from './useArchivedReportsIdSet'; import useReportIsArchived from './useReportIsArchived'; // We do not use PRIMARY_REPORT_ACTIONS or SECONDARY_REPORT_ACTIONS because they weren't meant to be used in this situation. `value` property of returned options is later ignored. diff --git a/src/hooks/useShowNotFoundPageInIOUStep.ts b/src/hooks/useShowNotFoundPageInIOUStep.ts index 7a2bef5f4958..4a9873d6dc4b 100644 --- a/src/hooks/useShowNotFoundPageInIOUStep.ts +++ b/src/hooks/useShowNotFoundPageInIOUStep.ts @@ -27,10 +27,7 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(transaction?.reportID)}`, {canBeMissing: true}); const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchived = useCallback( - (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), - [archivedReportsIdSet], - ); + const isReportArchived = useCallback((reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), [archivedReportsIdSet]); const reportActionsReportID = useMemo(() => { let actionsReportID; diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 9cfa07c3ff04..377150201d1d 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -104,10 +104,7 @@ function IOURequestEditReportCommon({ const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {canBeMissing: true}); const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchived = useCallback( - (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), - [archivedReportsIdSet], - ); + const isReportArchived = useCallback((reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), [archivedReportsIdSet]); const [allPoliciesID] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: policiesSelector, canBeMissing: false}); diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 8b589e86df3e..17e5d9bdc27d 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -179,8 +179,7 @@ function IOURequestStepConfirmation({ */ const transactionReport = getReportOrDraftReport(transaction?.reportID); const reportWithDraftFallback = useMemo(() => reportReal ?? reportDraft, [reportDraft, reportReal]); - const canUseReport = - !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, isReportArchived, false); + const canUseReport = !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, isReportArchived, false); const shouldUseTransactionReport = !!transactionReport && (canUseReport || !reportWithDraftFallback); const isTransactionReportDifferentFromRoute = useMemo( @@ -263,10 +262,7 @@ function IOURequestStepConfirmation({ hasOutstandingChildTask, } = useOnboardingTaskInformation(CONST.ONBOARDING_TASK_TYPE.VIEW_TOUR); const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchived = useCallback( - (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), - [archivedReportsIdSet], - ); + const isReportArchived = useCallback((reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), [archivedReportsIdSet]); const parentReportAction = useParentReportAction(viewTourTaskReport); const receiptFilename = transaction?.receipt?.filename; diff --git a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx index c5947083e347..ecd0c64ec78a 100644 --- a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx @@ -81,7 +81,17 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre const [sourceUri, setSourceUri] = useState(''); const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); - const canEditReceipt = canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, undefined, undefined, undefined, undefined, undefined, undefined, isReportArchived); + const canEditReceipt = canEditFieldOfMoneyRequest( + parentReportAction, + CONST.EDIT_REQUEST_FIELD.RECEIPT, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + isReportArchived, + ); const canDeleteReceipt = canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, true, undefined, undefined, undefined, undefined, undefined, isReportArchived); const shouldShowReplaceReceiptButton = ((canEditReceipt && !readonly) || isDraftTransaction) && !transaction?.receipt?.isTestDriveReceipt; From b3a1b9839271d5e5239cfba62d90a4e0b9449f86 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 18 Jan 2026 17:14:17 +0530 Subject: [PATCH 06/78] Fix a duplicate declaration --- src/libs/ReportUtils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5924b754c7c6..dbb0489d5731 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2204,8 +2204,6 @@ function getMostRecentlyVisitedReport(reports: Array>, reportM return lodashMaxBy(filteredReports, (a) => [reportMetadata?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${a?.reportID}`]?.lastVisitTime ?? '', a?.lastReadTime ?? '']); } -type IsReportArchived = (reportID?: string) => boolean; - /** * This function is used to find the last accessed report and we don't need to subscribe the data in the UI. * So please use `Onyx.connectWithoutView()` to get the necessary data when we remove the `Onyx.connect()` From 11d888ab98fa14a1acab0757ec04070088cbf85d Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Feb 2026 15:15:28 +0530 Subject: [PATCH 07/78] Change Id to ID --- .../MoneyRequestConfirmationListFooter.tsx | 6 +++--- .../MoneyRequestReportNavigation.tsx | 6 +++--- src/components/Search/index.tsx | 10 +++++----- src/hooks/useArchivedReportsIdSet.ts | 8 ++++---- src/hooks/useDeleteTransactions.ts | 8 ++++---- src/hooks/useSelectedTransactionsActions.ts | 6 +++--- src/hooks/useShowNotFoundPageInIOUStep.ts | 6 +++--- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 6 +++--- .../Navigators/ReportsSplitNavigator.tsx | 6 +++--- src/libs/actions/Report/MarkAllMessageAsRead.tsx | 4 ++-- src/libs/navigateAfterOnboarding.ts | 12 ++++++------ .../BaseOnboardingInterestedFeatures.tsx | 8 ++++---- .../BaseOnboardingPersonalDetails.tsx | 8 ++++---- .../BaseOnboardingWorkspaceInvite.tsx | 6 +++--- .../BaseOnboardingWorkspaceOptional.tsx | 8 ++++---- .../BaseOnboardingWorkspaces.tsx | 8 ++++---- src/pages/inbox/ReportScreen.tsx | 8 ++++---- .../ReportActionCompose/ReportActionCompose.tsx | 6 +++--- src/pages/iou/HoldReasonPage.tsx | 6 +++--- src/pages/iou/request/step/IOURequestStepReport.tsx | 6 +++--- .../routes/TransactionReceiptModalContent.tsx | 6 +++--- .../tasks/TaskShareDestinationSelectorModal.tsx | 8 ++++---- tests/unit/navigateAfterOnboardingTest.ts | 8 ++++---- 23 files changed, 82 insertions(+), 82 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 77aef5eaf56d..eb44340762ef 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -6,7 +6,7 @@ import React, {memo, useCallback, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; @@ -297,8 +297,8 @@ function MoneyRequestConfirmationListFooter({ const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); - const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchived = useCallback((value?: string) => !!value && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${value}`), [archivedReportsIdSet]); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const isReportArchived = useCallback((value?: string) => !!value && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${value}`), [archivedReportsIDSet]); const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID, { canBeMissing: true, }); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx index 1267749a019c..0a85da6128b4 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportNavigation.tsx @@ -3,7 +3,7 @@ import {View} from 'react-native'; import PrevNextButtons from '@components/PrevNextButtons'; import Text from '@components/Text'; import useActionLoadingReportIDs from '@hooks/useActionLoadingReportIDs'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -39,7 +39,7 @@ function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: Mo const [allReportMetadata] = useOnyx(ONYXKEYS.COLLECTION.REPORT_METADATA, {canBeMissing: true}); const [cardList] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true}); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const {type, status, sortBy, sortOrder, groupBy} = lastSearchQuery?.queryJSON ?? {}; let results: Array = []; @@ -55,7 +55,7 @@ function MoneyRequestReportNavigation({reportID, shouldDisplayNarrowVersion}: Mo groupBy, reportActions: exportReportActions, currentSearch: lastSearchQuery?.searchKey, - archivedReportsIDList: archivedReportsIdSet, + archivedReportsIDList: archivedReportsIDSet, isActionLoadingSet, cardFeeds, allReportMetadata, diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 0efad1552def..eb40f4451a7f 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -20,7 +20,7 @@ import type { import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; import {useWideRHPActions} from '@components/WideRHPContextProvider'; import useActionLoadingReportIDs from '@hooks/useActionLoadingReportIDs'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCardFeedsForDisplay from '@hooks/useCardFeedsForDisplay'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -267,8 +267,8 @@ function Search({ const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; const {markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); - const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchived = useCallback((reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), [archivedReportsIdSet]); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const isReportArchived = useCallback((reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), [archivedReportsIDSet]); const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, { canEvict: false, @@ -438,7 +438,7 @@ function Search({ groupBy: validGroupBy, reportActions: exportReportActions, currentSearch: searchKey, - archivedReportsIDList: archivedReportsIdSet, + archivedReportsIDList: archivedReportsIDSet, queryJSON, isActionLoadingSet, cardFeeds, @@ -457,7 +457,7 @@ function Search({ isDataLoaded, searchResults, type, - archivedReportsIdSet, + archivedReportsIDSet, translate, formatPhoneNumber, accountID, diff --git a/src/hooks/useArchivedReportsIdSet.ts b/src/hooks/useArchivedReportsIdSet.ts index 1d7d4d8d9479..083e60046241 100644 --- a/src/hooks/useArchivedReportsIdSet.ts +++ b/src/hooks/useArchivedReportsIdSet.ts @@ -27,17 +27,17 @@ const getArchivedReportsIdSet = (reportNameValuePairs: OnyxCollection(); + return useDeepCompareRef(archivedReportsIDSet) ?? new Set(); } -export default useArchivedReportsIdSet; +export default useArchivedReportsIDSet; diff --git a/src/hooks/useDeleteTransactions.ts b/src/hooks/useDeleteTransactions.ts index 4158f29c01e3..1021c8c5deaf 100644 --- a/src/hooks/useDeleteTransactions.ts +++ b/src/hooks/useDeleteTransactions.ts @@ -9,7 +9,7 @@ import {getChildTransactions, getOriginalTransactionWithSplitInfo} from '@libs/T import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Report, ReportAction, Transaction, TransactionViolations} from '@src/types/onyx'; -import useArchivedReportsIdSet from './useArchivedReportsIdSet'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useOnyx from './useOnyx'; import usePermissions from './usePermissions'; @@ -40,7 +40,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac const [iouReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${getNonEmptyStringOnyxID(report?.reportID)}`, {canBeMissing: true}); const [betas] = useOnyx(ONYXKEYS.BETAS, {canBeMissing: true}); const {isBetaEnabled} = usePermissions(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); /** * Delete transactions by IDs @@ -167,7 +167,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`]; const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`]; const chatIOUReportID = chatReport?.reportID; - const isChatIOUReportArchived = archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${chatIOUReportID}`); + const isChatIOUReportArchived = archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${chatIOUReportID}`); deleteMoneyRequest({ transactionID, reportAction: action, @@ -195,7 +195,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac allReportNameValuePairs, allReports, allTransactions, - archivedReportsIdSet, + archivedReportsIDSet, currentUserPersonalDetails, iouReportNextStep, isBetaEnabled, diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index 6e220d7db310..00e7abf37c10 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -33,7 +33,7 @@ import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import useAllTransactions from './useAllTransactions'; -import useArchivedReportsIdSet from './useArchivedReportsIdSet'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useDeleteTransactions from './useDeleteTransactions'; import useDuplicateTransactionsAndViolations from './useDuplicateTransactionsAndViolations'; @@ -88,8 +88,8 @@ function useSelectedTransactionsActions({ const expensifyIcons = useMemoizedLazyExpensifyIcons(['Stopwatch', 'Trashcan', 'ArrowRight', 'Table', 'DocumentMerge', 'Export', 'ArrowCollapse', 'ArrowSplit', 'ThumbsDown']); const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(selectedTransactionIDs); const isReportArchived = useReportIsArchived(report?.reportID); - const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const {deleteTransactions} = useDeleteTransactions({report, reportActions, policy}); const {login, accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const selectedTransactionsList = selectedTransactionIDs.reduce((acc, transactionID) => { diff --git a/src/hooks/useShowNotFoundPageInIOUStep.ts b/src/hooks/useShowNotFoundPageInIOUStep.ts index b34553b7951d..6d77f87706e4 100644 --- a/src/hooks/useShowNotFoundPageInIOUStep.ts +++ b/src/hooks/useShowNotFoundPageInIOUStep.ts @@ -8,7 +8,7 @@ import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {OnyxInputOrEntry, Report, ReportAction, ReportActions, Transaction} from '@src/types/onyx'; -import useArchivedReportsIdSet from './useArchivedReportsIdSet'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useOnyx from './useOnyx'; /** @@ -26,8 +26,8 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true}); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(transaction?.reportID)}`, {canBeMissing: true}); - const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchived = useCallback((reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), [archivedReportsIdSet]); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const isReportArchived = useCallback((reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), [archivedReportsIDSet]); const reportActionsReportID = useMemo(() => { let actionsReportID; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 8b0ed73cdc68..98eb02ce5902 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -19,7 +19,7 @@ import {useSearchRouterActions} from '@components/Search/SearchRouter/SearchRout import SearchRouterModal from '@components/Search/SearchRouter/SearchRouterModal'; import SupportalPermissionDeniedModalProvider from '@components/SupportalPermissionDeniedModalProvider'; import {useWideRHPState} from '@components/WideRHPContextProvider'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useAutoUpdateTimezone from '@hooks/useAutoUpdateTimezone'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnboardingFlowRouter from '@hooks/useOnboardingFlow'; @@ -161,7 +161,7 @@ function AuthScreens() { const {initialURL, isAuthenticatedAtStartup} = useInitialURLState(); const {setIsAuthenticatedAtStartup} = useInitialURLActions(); const modalCardStyleInterpolator = useModalCardStyleInterpolator(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const {shouldRenderSecondaryOverlayForWideRHP, shouldRenderSecondaryOverlayForRHPOnWideRHP, shouldRenderSecondaryOverlayForRHPOnSuperWideRHP, shouldRenderTertiaryOverlay} = useWideRHPState(); @@ -339,7 +339,7 @@ function AuthScreens() { const unsubscribeMarkAllMessagesAsReadShortcut = KeyboardShortcut.subscribe( markAllMessagesAsReadShortcutConfig.shortcutKey, - () => markAllMessagesAsRead(archivedReportsIdSet), + () => markAllMessagesAsRead(archivedReportsIDSet), markAllMessagesAsReadShortcutConfig.descriptionKey, markAllMessagesAsReadShortcutConfig.modifiers, true, diff --git a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx index 9eec6c2f5d8d..7b23221b335b 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx @@ -1,5 +1,5 @@ import React, {useState} from 'react'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import usePermissions from '@hooks/usePermissions'; import createSplitNavigator from '@libs/Navigation/AppNavigator/createSplitNavigator'; import FreezeWrapper from '@libs/Navigation/AppNavigator/FreezeWrapper'; @@ -27,7 +27,7 @@ const Split = createSplitNavigator(); function ReportsSplitNavigator({route}: PlatformStackScreenProps) { const {isBetaEnabled} = usePermissions(); const splitNavigatorScreenOptions = useSplitNavigatorScreenOptions(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [initialReportID] = useState(() => { const currentURL = getCurrentUrl(); @@ -45,7 +45,7 @@ function ReportsSplitNavigator({route}: PlatformStackScreenProps (allReports = value), }); -function markAllMessagesAsRead(archivedReportsIdSet: ArchivedReportsIDSet) { +function markAllMessagesAsRead(archivedReportsIDSet: ArchivedReportsIDSet) { if (isAnonymousUser()) { return; } @@ -48,7 +48,7 @@ function markAllMessagesAsRead(archivedReportsIdSet: ArchivedReportsIDSet) { const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`]; const oneTransactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]); const oneTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneTransactionThreadReportID}`]; - const isArchivedReport = archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`); + const isArchivedReport = archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`); if (!isUnread(report, oneTransactionThreadReport, isArchivedReport)) { continue; } diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts index cfb8de469374..e9975100ce8d 100644 --- a/src/libs/navigateAfterOnboarding.ts +++ b/src/libs/navigateAfterOnboarding.ts @@ -15,7 +15,7 @@ function getReportIDAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIdSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -30,7 +30,7 @@ function getReportIDAfterOnboarding( return undefined; } - const lastAccessedReport = findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom() && !shouldPreventOpenAdminRoom, undefined, archivedReportsIdSet); + const lastAccessedReport = findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom() && !shouldPreventOpenAdminRoom, undefined, archivedReportsIDSet); const lastAccessedReportID = lastAccessedReport?.reportID; // When the user goes through the onboarding flow, a workspace can be created if the user selects specific options. The user should be taken to the #admins room for that workspace because it is the most natural place for them to start their experience in the app. @@ -46,7 +46,7 @@ function navigateAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIdSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -62,7 +62,7 @@ function navigateAfterOnboarding( isSmallScreenWidth, canUseDefaultRooms, conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, @@ -79,7 +79,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIdSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -90,7 +90,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth, canUseDefaultRooms, conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, diff --git a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx index 9df0d9ac60dd..6603a9a8cc29 100644 --- a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx +++ b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx @@ -11,7 +11,7 @@ import ScrollView from '@components/ScrollView'; import Section from '@components/Section'; import isSidePanelReportSupported from '@components/SidePanel/isSidePanelReportSupported'; import Text from '@components/Text'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -55,7 +55,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin const {isBetaEnabled} = usePermissions(); const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false}); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true}); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const paidGroupPolicy = Object.values(allPolicies ?? {}).find((policy) => isPaidGroupPolicy(policy) && isPolicyAdmin(policy, session?.email)); const {isOffline} = useNetwork(); @@ -234,7 +234,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin isSmallScreenWidth, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, policyID, adminsChatReportID, // Onboarding tasks would show in Concierge instead of admins room for testing accounts, we should open where onboarding tasks are located @@ -249,7 +249,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin }, [ isBetaEnabled, isSmallScreenWidth, - archivedReportsIdSet, + archivedReportsIDSet, onboardingAdminsChatReportID, onboardingCompanySize, onboardingMessages, diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 3509c47206f8..6fc090650a25 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -8,7 +8,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useOnboardingMessages from '@hooks/useOnboardingMessages'; @@ -40,7 +40,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID, {canBeMissing: true}); const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true}); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST, {canBeMissing: true}); const [onboardingValues] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {canBeMissing: true}); const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true}); @@ -91,7 +91,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat isSmallScreenWidth, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeChatReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, mergedAccountConciergeReportID, false, @@ -103,7 +103,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat onboardingMessages, onboardingPolicyID, isBetaEnabled, - archivedReportsIdSet, + archivedReportsIDSet, isSmallScreenWidth, mergedAccountConciergeReportID, conciergeChatReportID, diff --git a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx index cabb804a8400..ee5fb695fd4f 100644 --- a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx +++ b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx @@ -8,7 +8,7 @@ import InviteMemberListItem from '@components/SelectionList/ListItem/InviteMembe import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections'; import type {Section} from '@components/SelectionList/SelectionListWithSections/types'; import Text from '@components/Text'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnboardingMessages from '@hooks/useOnboardingMessages'; @@ -59,7 +59,7 @@ function BaseOnboardingWorkspaceInvite({shouldUseNativeStyles}: BaseOnboardingWo const session = useSession(); const {isBetaEnabled} = usePermissions(); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true}); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const ineligibleInvitees = getIneligibleInvitees(policy?.employeeList); const excludedUsers: Record = {}; @@ -137,7 +137,7 @@ function BaseOnboardingWorkspaceInvite({shouldUseNativeStyles}: BaseOnboardingWo isSmallScreenWidth, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, onboardingAdminsChatReportID, // Onboarding tasks would show in Concierge instead of admins room for testing accounts, we should open where onboarding tasks are located diff --git a/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx b/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx index 89651adec04f..f397c8c6c0fc 100644 --- a/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx +++ b/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx @@ -6,7 +6,7 @@ import Icon from '@components/Icon'; import RenderHTML from '@components/RenderHTML'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -40,7 +40,7 @@ function BaseOnboardingWorkspaceOptional({shouldUseNativeStyles}: BaseOnboarding const [onboardingPolicyID] = useOnyx(ONYXKEYS.ONBOARDING_POLICY_ID, {canBeMissing: true}); const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID, {canBeMissing: true}); const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true}); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const {onboardingMessages} = useOnboardingMessages(); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); const {isRestrictedPolicyCreation} = usePreferredPolicy(); @@ -99,7 +99,7 @@ function BaseOnboardingWorkspaceOptional({shouldUseNativeStyles}: BaseOnboarding isSmallScreenWidth, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeChatReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, mergedAccountConciergeReportID, false, @@ -111,7 +111,7 @@ function BaseOnboardingWorkspaceOptional({shouldUseNativeStyles}: BaseOnboarding onboardingAdminsChatReportID, onboardingMessages, onboardingPolicyID, - archivedReportsIdSet, + archivedReportsIDSet, isSmallScreenWidth, isBetaEnabled, mergedAccountConciergeReportID, diff --git a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx index 8cfb13d01e55..ebbf98dabbe1 100644 --- a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx +++ b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx @@ -7,7 +7,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; import Text from '@components/Text'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -51,7 +51,7 @@ function BaseOnboardingWorkspaces({route, shouldUseNativeStyles}: BaseOnboarding const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST, {canBeMissing: true}); const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true}); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const isValidated = isCurrentUserValidated(loginList, session?.email); @@ -85,7 +85,7 @@ function BaseOnboardingWorkspaces({route, shouldUseNativeStyles}: BaseOnboarding isSmallScreenWidth, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, policy.automaticJoiningEnabled ? policy.policyID : undefined, undefined, false, @@ -98,7 +98,7 @@ function BaseOnboardingWorkspaces({route, shouldUseNativeStyles}: BaseOnboarding isSmallScreenWidth, isBetaEnabled, onboardingCompanySize, - archivedReportsIdSet, + archivedReportsIDSet, introSelected, ], ); diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index c2e15f7ab163..7a99788d1622 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -24,7 +24,7 @@ import useShowWideRHPVersion from '@components/WideRHPContextProvider/useShowWid import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; import useAppFocusEvent from '@hooks/useAppFocusEvent'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import {useCurrentReportIDState} from '@hooks/useCurrentReportID'; import useIsAnonymousUser from '@hooks/useIsAnonymousUser'; import useIsReportReadyToDisplay from '@hooks/useIsReportReadyToDisplay'; @@ -186,7 +186,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr const [onboarding] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {canBeMissing: true}); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true}); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const parentReportAction = useParentReportAction(reportOnyx); @@ -214,7 +214,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr !isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), 'openOnAdminRoom' in route.params && !!route.params.openOnAdminRoom, undefined, - archivedReportsIdSet, + archivedReportsIDSet, )?.reportID; // It's possible that reports aren't fully loaded yet @@ -226,7 +226,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr Log.info(`[ReportScreen] no reportID found in params, setting it to lastAccessedReportID: ${lastAccessedReportID}`); navigation.setParams({reportID: lastAccessedReportID}); }); - }, [archivedReportsIdSet, isBetaEnabled, navigation, route.params]), + }, [archivedReportsIDSet, isBetaEnabled, navigation, route.params]), ); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true}); diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index d42b3738d25f..36342f51c19f 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -19,7 +19,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import useAgentZeroStatusIndicator from '@hooks/useAgentZeroStatusIndicator'; import useAncestors from '@hooks/useAncestors'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHandleExceedMaxCommentLength from '@hooks/useHandleExceedMaxCommentLength'; import useHandleExceedMaxTaskTitleLength from '@hooks/useHandleExceedMaxTaskTitleLength'; @@ -221,8 +221,8 @@ function ReportActionCompose({ const userBlockedFromConcierge = useMemo(() => isBlockedFromConciergeUserAction(blockedFromConcierge), [blockedFromConcierge]); const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); const isReportArchived = useReportIsArchived(report?.reportID); - const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const isConciergeChat = useMemo(() => isConciergeChatReport(report), [report]); const isAdminsRoom = useMemo(() => isAdminRoom(report), [report]); diff --git a/src/pages/iou/HoldReasonPage.tsx b/src/pages/iou/HoldReasonPage.tsx index 353434f323e8..b66dcfffaa5f 100644 --- a/src/pages/iou/HoldReasonPage.tsx +++ b/src/pages/iou/HoldReasonPage.tsx @@ -2,7 +2,7 @@ import React, {useCallback, useEffect} from 'react'; import {useDelegateNoAccessActions, useDelegateNoAccessState} from '@components/DelegateNoAccessModalProvider'; import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import useAncestors from '@hooks/useAncestors'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import {putOnHold} from '@libs/actions/IOU/Hold'; @@ -30,8 +30,8 @@ function HoldReasonPage({route}: HoldReasonPageProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: true}); const ancestors = useAncestors(report); - const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchived = (reportId?: string) => !!reportId && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportId}`); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const isReportArchived = (reportId?: string) => !!reportId && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportId}`); // We first check if the report is part of a policy - if not, then it's a personal request (1:1 request) // For personal requests, we need to allow both users to put the request on hold diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 6074f5883437..4519bc570af4 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -4,7 +4,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {usePersonalDetails, useSession} from '@components/OnyxListItemProvider'; import {useSearchContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionListWithSections/types'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useOnyx from '@hooks/useOnyx'; import useOptimisticDraftTransactions from '@hooks/useOptimisticDraftTransactions'; @@ -46,8 +46,8 @@ const getIOUActionsSelector = (actions: OnyxEntry): ReportAction[ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {backTo, action, iouType, transactionID, reportID: reportIDFromRoute, reportActionID} = route.params; const [allReports] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}`, {canBeMissing: false}); - const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchived = (reportID?: string) => !!reportID && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const isReportArchived = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); const participantReportID = transaction?.participants?.at(0)?.reportID; diff --git a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx index 09dbe5382e3a..9786f2f64786 100644 --- a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx @@ -4,7 +4,7 @@ import ConfirmModal from '@components/ConfirmModal'; // eslint-disable-next-line no-restricted-imports import * as Expensicons from '@components/Icon/Expensicons'; import useAllTransactions from '@hooks/useAllTransactions'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -49,8 +49,8 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true}); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); const policy = usePolicy(report?.policyID); - const archivedReportsIdSet = useArchivedReportsIdSet(); - const isReportArchived = (reportId?: string) => !!reportId && archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportId}`); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const isReportArchived = (reportId?: string) => !!reportId && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportId}`); // If we have a merge transaction, we need to use the receipt from the merge transaction const [mergeTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.MERGE_TRANSACTION}${getNonEmptyStringOnyxID(mergeTransactionID)}`, {canBeMissing: true}); diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index 7d8327fa2dcd..afa45cfb09b2 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -4,7 +4,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -64,15 +64,15 @@ function TaskShareDestinationSelectorModal() { onSingleSelect: selectReportHandler, }); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const filteredOptions = useMemo(() => { - const filteredReports = reportFilter(availableOptions.recentReports as Array>, archivedReportsIdSet); + const filteredReports = reportFilter(availableOptions.recentReports as Array>, archivedReportsIDSet); return { ...availableOptions, recentReports: filteredReports ?? [], }; - }, [availableOptions, archivedReportsIdSet]); + }, [availableOptions, archivedReportsIDSet]); const data = useMemo( () => diff --git a/tests/unit/navigateAfterOnboardingTest.ts b/tests/unit/navigateAfterOnboardingTest.ts index 600cedbd5896..7f7a6bbe177e 100644 --- a/tests/unit/navigateAfterOnboardingTest.ts +++ b/tests/unit/navigateAfterOnboardingTest.ts @@ -123,14 +123,14 @@ describe('navigateAfterOnboarding', () => { expect(navigate).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(REPORT_ID)); }); - it('should pass archivedReportsIdSet when looking up last accessed report', () => { - const archivedReportsIdSet = new Set(['report_1']); + it('should pass archivedReportsIDSet when looking up last accessed report', () => { + const archivedReportsIDSet = new Set(['report_1']); mockFindLastAccessedReport.mockReturnValue(undefined); mockShouldOpenOnAdminRoom.mockReturnValue(false); - navigateAfterOnboarding(true, true, '', archivedReportsIdSet, ONBOARDING_POLICY_ID, ONBOARDING_ADMINS_CHAT_REPORT_ID); + navigateAfterOnboarding(true, true, '', archivedReportsIDSet, ONBOARDING_POLICY_ID, ONBOARDING_ADMINS_CHAT_REPORT_ID); - expect(mockFindLastAccessedReport).toHaveBeenCalledWith(false, false, undefined, archivedReportsIdSet); + expect(mockFindLastAccessedReport).toHaveBeenCalledWith(false, false, undefined, archivedReportsIDSet); }); it('should navigate to Concierge room if user uses a test email', () => { From e3e932fee75940831d43fb34e3784f81fa5f9755 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Feb 2026 15:57:14 +0530 Subject: [PATCH 08/78] Change Id to ID --- src/hooks/useArchivedReportsIdSet.ts | 4 ++-- src/libs/ReportUtils.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/hooks/useArchivedReportsIdSet.ts b/src/hooks/useArchivedReportsIdSet.ts index 083e60046241..f84a942278b1 100644 --- a/src/hooks/useArchivedReportsIdSet.ts +++ b/src/hooks/useArchivedReportsIdSet.ts @@ -10,7 +10,7 @@ import useOnyx from './useOnyx'; /** * Function that creates a Set of archived report IDs from report name value pairs */ -const getArchivedReportsIdSet = (reportNameValuePairs: OnyxCollection): ArchivedReportsIDSet | undefined => { +const getArchivedReportsIDSet = (reportNameValuePairs: OnyxCollection): ArchivedReportsIDSet | undefined => { if (!reportNameValuePairs) { return undefined; } @@ -31,7 +31,7 @@ function useArchivedReportsIDSet(): ArchivedReportsIDSet { const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, { canBeMissing: true, }); - const archivedReportsIDSet = getArchivedReportsIdSet(reportNameValuePairs) ?? CONST.EMPTY_SET; + const archivedReportsIDSet = getArchivedReportsIDSet(reportNameValuePairs) ?? CONST.EMPTY_SET; // useDeepCompareRef is used here to prevent unnecessary re-renders by maintaining referential equality // when the Set contents are the same, even if it's a new Set instance. This is important for performance diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index de39292b13b2..66c8ed72b25b 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2255,18 +2255,18 @@ function findLastAccessedReport( ignoreDomainRooms: boolean, openOnAdminRoom = false, policyIDOrExcludeReportID?: string, - excludeReportIDOrArchivedReportsIdSetOrCallback?: string | ArchivedReportsIDSet | IsReportArchived, - archivedReportsIdSetOrCallback?: ArchivedReportsIDSet | IsReportArchived, + excludeReportIDOrArchivedReportsIDSetOrCallback?: string | ArchivedReportsIDSet | IsReportArchived, + archivedReportsIDSetOrCallback?: ArchivedReportsIDSet | IsReportArchived, ): OnyxEntry { - const isUsingOldArgs = archivedReportsIdSetOrCallback !== undefined || typeof excludeReportIDOrArchivedReportsIdSetOrCallback === 'string'; + const isUsingOldArgs = archivedReportsIDSetOrCallback !== undefined || typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string'; const excludeReportID = isUsingOldArgs - ? typeof excludeReportIDOrArchivedReportsIdSetOrCallback === 'string' - ? excludeReportIDOrArchivedReportsIdSetOrCallback + ? typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string' + ? excludeReportIDOrArchivedReportsIDSetOrCallback : undefined : policyIDOrExcludeReportID; const archivedReportsArg = isUsingOldArgs - ? (archivedReportsIdSetOrCallback ?? (typeof excludeReportIDOrArchivedReportsIdSetOrCallback === 'string' ? undefined : excludeReportIDOrArchivedReportsIdSetOrCallback)) - : excludeReportIDOrArchivedReportsIdSetOrCallback; + ? (archivedReportsIDSetOrCallback ?? (typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string' ? undefined : excludeReportIDOrArchivedReportsIDSetOrCallback)) + : excludeReportIDOrArchivedReportsIDSetOrCallback; let reportsValues = Object.values(allReports ?? {}); From 4564e7a0fcd4fb6cf2359d160a887f36632328c4 Mon Sep 17 00:00:00 2001 From: Shridhar Goel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Feb 2026 23:28:44 +0530 Subject: [PATCH 09/78] Change useArchivedReportsIdSet to useArchivedReportsIDSet --- .../{useArchivedReportsIdSet.ts => useArchivedReportsIDSet.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/hooks/{useArchivedReportsIdSet.ts => useArchivedReportsIDSet.ts} (100%) diff --git a/src/hooks/useArchivedReportsIdSet.ts b/src/hooks/useArchivedReportsIDSet.ts similarity index 100% rename from src/hooks/useArchivedReportsIdSet.ts rename to src/hooks/useArchivedReportsIDSet.ts From 335ad6a617b199bb879e109195d9e6afb2860e19 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 22 Feb 2026 00:55:20 +0530 Subject: [PATCH 10/78] Fix --- src/components/MoneyRequestConfirmationListFooter.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index eb44340762ef..f609b71d1d56 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -27,7 +27,7 @@ import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getT import {canSendInvoice, getPerDiemCustomUnit} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; -import {computeReportName} from '@libs/ReportNameUtils'; +import {getReportName} from '@libs/ReportNameUtils'; import {generateReportID, getDefaultWorkspaceAvatar, getOutstandingReportsForUser, isMoneyRequestReport, isReportOutstanding} from '@libs/ReportUtils'; import {getTagVisibility, hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { @@ -305,7 +305,6 @@ function MoneyRequestConfirmationListFooter({ const {policyForMovingExpensesID, policyForMovingExpenses, shouldSelectPolicy} = usePolicyForMovingExpenses(); const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: emailSelector, canBeMissing: true}); - const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const isCreatingTrackExpense = action === CONST.IOU.ACTION.CREATE && iouType === CONST.IOU.TYPE.TRACK; @@ -364,13 +363,14 @@ function MoneyRequestConfirmationListFooter({ return [reportIDToUse, reportToUse]; }, [allReports, shouldUseTransactionReport, transaction?.reportID, outstandingReportID]); + const reportAttributes = useReportAttributes(); const reportName = useMemo(() => { - const name = computeReportName(selectedReport, allReports, allPolicies, undefined, undefined, undefined, undefined, currentUserAccountID); + const name = getReportName(selectedReport, reportAttributes); if (!name) { return isUnreported ? translate('common.none') : translate('iou.newReport'); } return name; - }, [isUnreported, selectedReport, allReports, allPolicies, translate, currentUserAccountID]); + }, [isUnreported, selectedReport, reportAttributes, translate]); const outstandingReports = useOutstandingReports(undefined, isFromGlobalCreate && !isPerDiemRequest ? undefined : policyID, ownerAccountID, false); // When creating an expense in an individual report, the report field becomes read-only From a0465bfeefdc57195cf972dee55c9ea851426740 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 22 Feb 2026 02:20:30 +0530 Subject: [PATCH 11/78] Lint and prettier fixes --- .../MoneyRequestConfirmationListFooter.tsx | 1 - src/libs/ReportUtils.ts | 13 ++++++++----- .../ReportActionCompose/ReportActionCompose.tsx | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index f609b71d1d56..e39ecbf4dfbd 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -7,7 +7,6 @@ import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 66c8ed72b25b..4ee9944795a1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2259,11 +2259,14 @@ function findLastAccessedReport( archivedReportsIDSetOrCallback?: ArchivedReportsIDSet | IsReportArchived, ): OnyxEntry { const isUsingOldArgs = archivedReportsIDSetOrCallback !== undefined || typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string'; - const excludeReportID = isUsingOldArgs - ? typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string' - ? excludeReportIDOrArchivedReportsIDSetOrCallback - : undefined - : policyIDOrExcludeReportID; + let excludeReportID: string | undefined; + if (isUsingOldArgs) { + if (typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string') { + excludeReportID = excludeReportIDOrArchivedReportsIDSetOrCallback; + } + } else { + excludeReportID = policyIDOrExcludeReportID; + } const archivedReportsArg = isUsingOldArgs ? (archivedReportsIDSetOrCallback ?? (typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string' ? undefined : excludeReportIDOrArchivedReportsIDSetOrCallback)) : excludeReportIDOrArchivedReportsIDSetOrCallback; diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index 36342f51c19f..6755938bc65b 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -222,7 +222,7 @@ function ReportActionCompose({ const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); const isReportArchived = useReportIsArchived(report?.reportID); const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const isReportArchivedByID = (targetReportID?: string) => !!targetReportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${targetReportID}`); const isConciergeChat = useMemo(() => isConciergeChatReport(report), [report]); const isAdminsRoom = useMemo(() => isAdminRoom(report), [report]); From fe0a427eaf1c0924c0b942e39cfc9e25392bf5a2 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 22 Feb 2026 11:26:52 +0530 Subject: [PATCH 12/78] Update more usages to pass the ID-set --- src/Expensify.tsx | 8 +++--- .../ReportActionItem/MoneyRequestView.tsx | 5 +++- src/components/Search/index.tsx | 4 ++- src/libs/actions/Link.ts | 16 +++++++++--- src/libs/actions/Report/index.ts | 25 +++++++++++++------ src/libs/actions/Task.ts | 8 +++--- src/pages/ReportDetailsPage.tsx | 11 +++++--- tests/actions/ReportTest.ts | 23 +++++++++-------- 8 files changed, 66 insertions(+), 34 deletions(-) diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 5847395a6fab..f34147831b22 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -16,6 +16,7 @@ import SplashScreenHider from './components/SplashScreenHider'; import UpdateAppModal from './components/UpdateAppModal'; import CONFIG from './CONFIG'; import CONST from './CONST'; +import useArchivedReportsIDSet from './hooks/useArchivedReportsIDSet'; import useDebugShortcut from './hooks/useDebugShortcut'; import useIsAuthenticated from './hooks/useIsAuthenticated'; import useLocalize from './hooks/useLocalize'; @@ -124,6 +125,7 @@ function Expensify() { const [stashedSession] = useOnyx(ONYXKEYS.STASHED_SESSION, {canBeMissing: true}); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true}); + const archivedReportsIDSet = useArchivedReportsIDSet(); useDebugShortcut(); usePriorityMode(); @@ -344,7 +346,7 @@ function Expensify() { if (introSelected === undefined) { Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); } - openReportFromDeepLink(url, allReports, isAuthenticated, conciergeReportID, introSelected); + openReportFromDeepLink(url, allReports, isAuthenticated, conciergeReportID, introSelected, archivedReportsIDSet); } else { Report.doneCheckingPublicRoom(); } @@ -361,14 +363,14 @@ function Expensify() { Log.info('[Deep link] introSelected is undefined when processing URL change', false, {url: state.url}); } const isCurrentlyAuthenticated = hasAuthToken(); - openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected); + openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, archivedReportsIDSet); }); return () => { linkingChangeListener.current?.remove(); }; // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want this effect to re-run when conciergeReportID changes - }, [sessionMetadata?.status, conciergeReportID, introSelected]); + }, [sessionMetadata?.status, conciergeReportID, introSelected, archivedReportsIDSet]); useLayoutEffect(() => { if (!isNavigationReady || !lastRoute) { diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 7a19d0185a3e..79e09b82409b 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -336,7 +336,10 @@ function MoneyRequestView({ // Used for non-restricted fields such as: description, category, tag, billable, etc... const isReportArchived = useReportIsArchived(transactionThreadReport?.reportID); const isEditable = !!canUserPerformWriteActionReportUtils(transactionThreadReport, isReportArchived) && !readonly; - const canEdit = isMoneyRequestAction(parentReportAction) && canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction) && isEditable; + const canEdit = + isMoneyRequestAction(parentReportAction) && + canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction, isReportArchivedByID) && + isEditable; const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(transactionThreadReport?.policyID)}`; const {personalCardsWithBrokenConnection} = useCardFeedErrors(); const connectionLink = getBrokenConnectionUrlToFixPersonalCard(personalCardsWithBrokenConnection, environmentURL); diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index eb40f4451a7f..468b9c0e3bb8 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -667,6 +667,7 @@ function Search({ transactionItem, transactionItem.report, transactionItem.policy, + isReportArchived, ), // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID]?.isSelected || isExpenseReportType, @@ -723,6 +724,7 @@ function Search({ transactionItem, transactionItem.report, transactionItem.policy, + isReportArchived, ), // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID].isSelected, @@ -749,7 +751,7 @@ function Search({ isRefreshingSelection.current = true; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [filteredData, setSelectedTransactions, areAllMatchingItemsSelected, isFocused, outstandingReportsByPolicyID, isExpenseReportType]); + }, [filteredData, setSelectedTransactions, areAllMatchingItemsSelected, isFocused, outstandingReportsByPolicyID, isExpenseReportType, isReportArchived]); useEffect(() => { if (!isSearchResultsEmpty || prevIsSearchResultEmpty) { diff --git a/src/libs/actions/Link.ts b/src/libs/actions/Link.ts index 8e951001b893..59afef516149 100644 --- a/src/libs/actions/Link.ts +++ b/src/libs/actions/Link.ts @@ -17,6 +17,7 @@ import Navigation from '@libs/Navigation/Navigation'; import navigationRef from '@libs/Navigation/navigationRef'; import type {NetworkStatus} from '@libs/NetworkConnection'; import {findLastAccessedReport, getReportIDFromLink, getRouteFromLink} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; import {endSpan, getSpan, startSpan} from '@libs/telemetry/activeSpans'; import * as Url from '@libs/Url'; @@ -230,7 +231,14 @@ function openLink(href: string, environmentURL: string, isAttachment = false) { openExternalLink(href); } -function openReportFromDeepLink(url: string, reports: OnyxCollection, isAuthenticated: boolean, conciergeReportID: string | undefined, introSelected: OnyxEntry) { +function openReportFromDeepLink( + url: string, + reports: OnyxCollection, + isAuthenticated: boolean, + conciergeReportID: string | undefined, + introSelected: OnyxEntry, + archivedReportsIDSet: ArchivedReportsIDSet, +) { const reportID = getReportIDFromLink(url); if (reportID && !isAuthenticated) { @@ -322,12 +330,14 @@ function openReportFromDeepLink(url: string, reports: OnyxCollection, is return; } - const navigateHandler = (reportParam?: OnyxEntry) => { + const navigateHandler = async (reportParam?: OnyxEntry) => { // Check if the report exists in the collection const report = reportParam ?? reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; // If the report does not exist, navigate to the last accessed report or Concierge chat if (reportID && (!report?.reportID || report.errorFields?.notFound)) { - const lastAccessedReportID = findLastAccessedReport(false, shouldOpenOnAdminRoom(), reportID)?.reportID; + const isReportArchived = (currentReportID?: string) => + !!currentReportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${currentReportID}`); + const lastAccessedReportID = findLastAccessedReport(false, shouldOpenOnAdminRoom(), reportID, undefined, isReportArchived)?.reportID; if (lastAccessedReportID) { const lastAccessedReportRoute = ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID); Navigation.navigate(lastAccessedReportRoute, {forceReplace: Navigation.getTopmostReportId() === reportID}); diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index d57d5bb49541..93961718cbba 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -159,6 +159,7 @@ import { isValidReportIDFromPath, prepareOnboardingOnyxData, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import {getAmount, getCurrency, hasValidModifiedAmount, isOnHold, shouldClearConvertedAmount} from '@libs/TransactionUtils'; @@ -3780,8 +3781,9 @@ function doneCheckingPublicRoom() { Onyx.set(ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, false); } -function navigateToMostRecentReport(currentReport: OnyxEntry, conciergeReportID: string | undefined) { - const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID)?.reportID; +function navigateToMostRecentReport(currentReport: OnyxEntry, conciergeReportID: string | undefined, archivedReportsIDSet: ArchivedReportsIDSet) { + const isReportArchived = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, undefined, isReportArchived)?.reportID; if (lastAccessedReportID) { // Check if route exists for super wide RHP vs regular full screen report @@ -3805,8 +3807,9 @@ function navigateToMostRecentReport(currentReport: OnyxEntry, conciergeR } } -function getMostRecentReportID(currentReport: OnyxEntry) { - const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID)?.reportID; +function getMostRecentReportID(currentReport: OnyxEntry, archivedReportsIDSet: ArchivedReportsIDSet) { + const isReportArchived = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, undefined, isReportArchived)?.reportID; return lastAccessedReportID ?? conciergeReportIDOnyxConnect; } @@ -3824,7 +3827,7 @@ function joinRoom(report: OnyxEntry, currentUserAccountID: number) { ); } -function leaveGroupChat(report: Report, shouldClearQuickAction: boolean, currentUserAccountID: number, conciergeReportID: string | undefined) { +function leaveGroupChat(report: Report, shouldClearQuickAction: boolean, currentUserAccountID: number, conciergeReportID: string | undefined, archivedReportsIDSet: ArchivedReportsIDSet) { const reportID = report.reportID; // Use merge instead of set to avoid deleting the report too quickly, which could cause a brief "not found" page to appear. // The remaining parts of the report object will be removed after the API call is successful. @@ -3871,12 +3874,18 @@ function leaveGroupChat(report: Report, shouldClearQuickAction: boolean, current }, ]; - navigateToMostRecentReport(report, conciergeReportID); + navigateToMostRecentReport(report, conciergeReportID, archivedReportsIDSet); API.write(WRITE_COMMANDS.LEAVE_GROUP_CHAT, {reportID}, {optimisticData, successData, failureData}); } /** Leave a report by setting the state to submitted and closed */ -function leaveRoom(report: Report, currentUserAccountID: number, conciergeReportID: string | undefined, isWorkspaceMemberLeavingWorkspaceRoom = false) { +function leaveRoom( + report: Report, + currentUserAccountID: number, + conciergeReportID: string | undefined, + archivedReportsIDSet: ArchivedReportsIDSet, + isWorkspaceMemberLeavingWorkspaceRoom = false, +) { const reportID = report.reportID; const isChatThread = isChatThreadReportUtils(report); @@ -3980,7 +3989,7 @@ function leaveRoom(report: Report, currentUserAccountID: number, conciergeReport return; } // In other cases, the report is deleted and we should move the user to another report. - navigateToMostRecentReport(report, conciergeReportID); + navigateToMostRecentReport(report, conciergeReportID, archivedReportsIDSet); } function buildInviteToRoomOnyxData(report: Report, inviteeEmailsToAccountIDs: InvitedEmailsToAccountIDs, formatPhoneNumber: LocaleContextProps['formatPhoneNumber']) { diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index e00ba5ea1f24..a2cd5d4d90fd 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -15,6 +15,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -1025,7 +1026,7 @@ function getShareDestination( * @param report - The task report being deleted * @returns The URL to navigate to */ -function getNavigationUrlOnTaskDelete(report: OnyxEntry): string | undefined { +function getNavigationUrlOnTaskDelete(report: OnyxEntry, archivedReportsIDSet: ArchivedReportsIDSet): string | undefined { if (!report) { return undefined; } @@ -1040,7 +1041,7 @@ function getNavigationUrlOnTaskDelete(report: OnyxEntry): stri } // If no parent report, try to navigate to most recent report - const mostRecentReportID = getMostRecentReportID(report); + const mostRecentReportID = getMostRecentReportID(report, archivedReportsIDSet); if (mostRecentReportID) { return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID); } @@ -1058,6 +1059,7 @@ function deleteTask( currentUserAccountID: number, hasOutstandingChildTask: boolean, parentReportAction: OnyxEntry, + archivedReportsIDSet: ArchivedReportsIDSet, ancestors: ReportUtils.Ancestor[] = [], ) { if (!report) { @@ -1183,7 +1185,7 @@ function deleteTask( API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData}); notifyNewAction(report.reportID, undefined, true); - const urlToNavigateBack = getNavigationUrlOnTaskDelete(report); + const urlToNavigateBack = getNavigationUrlOnTaskDelete(report, archivedReportsIDSet); if (urlToNavigateBack) { Navigation.goBack(); return urlToNavigateBack; diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 4a164662a6ea..27e7444c1521 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -24,6 +24,7 @@ import {useSearchContext} from '@components/Search/SearchContext'; import {SUPER_WIDE_RIGHT_MODALS} from '@components/WideRHPContextProvider/WIDE_RIGHT_MODALS'; import useActivePolicy from '@hooks/useActivePolicy'; import useAncestors from '@hooks/useAncestors'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDeleteTransactions from '@hooks/useDeleteTransactions'; @@ -190,6 +191,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); const [allTransactionDrafts] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {canBeMissing: true}); const [allTransactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true}); + const archivedReportsIDSet = useArchivedReportsIDSet(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const {showConfirmModal} = useConfirmModal(); const isPolicyAdmin = useMemo(() => isPolicyAdminUtil(policy), [policy]); @@ -326,13 +328,13 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail const leaveChat = useCallback(() => { if (isRootGroupChat) { - leaveGroupChat(report, quickAction?.chatReportID?.toString() === report.reportID, currentUserPersonalDetails.accountID, conciergeReportID); + leaveGroupChat(report, quickAction?.chatReportID?.toString() === report.reportID, currentUserPersonalDetails.accountID, conciergeReportID, archivedReportsIDSet); return; } const isWorkspaceMemberLeavingWorkspaceRoom = isWorkspaceMemberLeavingWorkspaceRoomUtil(report, isPolicyEmployee, isPolicyAdmin); - leaveRoom(report, currentUserPersonalDetails.accountID, conciergeReportID, isWorkspaceMemberLeavingWorkspaceRoom); - }, [isRootGroupChat, isPolicyEmployee, isPolicyAdmin, quickAction?.chatReportID, report, currentUserPersonalDetails.accountID, conciergeReportID]); + leaveRoom(report, currentUserPersonalDetails.accountID, conciergeReportID, archivedReportsIDSet, isWorkspaceMemberLeavingWorkspaceRoom); + }, [isRootGroupChat, isPolicyEmployee, isPolicyAdmin, quickAction?.chatReportID, report, currentUserPersonalDetails.accountID, conciergeReportID, archivedReportsIDSet]); const showLastMemberLeavingModal = useCallback(async () => { const {action} = await showConfirmModal({ @@ -870,7 +872,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail const deleteTransaction = useCallback(() => { if (caseID === CASES.DEFAULT) { - deleteTask(report, parentReport, isReportArchived, currentUserPersonalDetails.accountID, hasOutstandingChildTask, parentReportAction, ancestors); + deleteTask(report, parentReport, isReportArchived, currentUserPersonalDetails.accountID, hasOutstandingChildTask, parentReportAction, archivedReportsIDSet, ancestors); return; } @@ -923,6 +925,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail deleteTransactions, currentSearchHash, removeTransaction, + archivedReportsIDSet, ]); // Where to navigate back to after deleting the transaction and its report. diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 0592afed8ece..402c5105039f 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -4216,6 +4216,7 @@ describe('actions/Report', () => { // Shared test constants for leave functions const TEST_CONCIERGE_REPORT_ID = '999'; const TEST_CURRENT_USER_ACCOUNT_ID = 1; + const TEST_ARCHIVED_REPORTS_ID_SET = new Set(); describe('leaveGroupChat', () => { const GROUP_CHAT_REPORT_ID = '1001'; @@ -4239,7 +4240,7 @@ describe('actions/Report', () => { await Onyx.merge(ONYXKEYS.CONCIERGE_REPORT_ID, TEST_CONCIERGE_REPORT_ID); await waitForBatchedUpdates(); - Report.leaveGroupChat(groupChatReport, false, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID); + Report.leaveGroupChat(groupChatReport, false, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, TEST_ARCHIVED_REPORTS_ID_SET); await waitForBatchedUpdates(); TestHelper.expectAPICommandToHaveBeenCalled(WRITE_COMMANDS.LEAVE_GROUP_CHAT, 1); @@ -4258,7 +4259,7 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${GROUP_CHAT_REPORT_ID}`, groupChatReport); await waitForBatchedUpdates(); - Report.leaveGroupChat(groupChatReport, false, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID); + Report.leaveGroupChat(groupChatReport, false, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, TEST_ARCHIVED_REPORTS_ID_SET); await waitForBatchedUpdates(); // After success, the report should be removed from Onyx (set to null) @@ -4283,7 +4284,7 @@ describe('actions/Report', () => { }); await waitForBatchedUpdates(); - Report.leaveGroupChat(groupChatReport, true, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID); + Report.leaveGroupChat(groupChatReport, true, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, TEST_ARCHIVED_REPORTS_ID_SET); await waitForBatchedUpdates(); const quickAction = await getOnyxValue(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); @@ -4310,7 +4311,7 @@ describe('actions/Report', () => { await Onyx.merge(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, quickActionValue); await waitForBatchedUpdates(); - Report.leaveGroupChat(groupChatReport, false, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID); + Report.leaveGroupChat(groupChatReport, false, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, TEST_ARCHIVED_REPORTS_ID_SET); await waitForBatchedUpdates(); const quickAction = await getOnyxValue(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); @@ -4332,7 +4333,7 @@ describe('actions/Report', () => { // Should not throw an error with undefined conciergeReportID expect(() => { - Report.leaveGroupChat(groupChatReport, false, TEST_CURRENT_USER_ACCOUNT_ID, undefined); + Report.leaveGroupChat(groupChatReport, false, TEST_CURRENT_USER_ACCOUNT_ID, undefined, TEST_ARCHIVED_REPORTS_ID_SET); }).not.toThrow(); await waitForBatchedUpdates(); @@ -4364,7 +4365,7 @@ describe('actions/Report', () => { await Onyx.merge(ONYXKEYS.CONCIERGE_REPORT_ID, TEST_CONCIERGE_REPORT_ID); await waitForBatchedUpdates(); - Report.leaveRoom(roomReport, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, false); + Report.leaveRoom(roomReport, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, TEST_ARCHIVED_REPORTS_ID_SET, false); await waitForBatchedUpdates(); TestHelper.expectAPICommandToHaveBeenCalled(WRITE_COMMANDS.LEAVE_ROOM, 1); @@ -4383,7 +4384,7 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${ROOM_REPORT_ID}`, roomReport); await waitForBatchedUpdates(); - Report.leaveRoom(roomReport, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, true); + Report.leaveRoom(roomReport, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, TEST_ARCHIVED_REPORTS_ID_SET, true); await waitForBatchedUpdates(); const updatedReport = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT}${ROOM_REPORT_ID}` as const); @@ -4407,7 +4408,7 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${ROOM_REPORT_ID}`, roomReport); await waitForBatchedUpdates(); - Report.leaveRoom(roomReport, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, false); + Report.leaveRoom(roomReport, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, TEST_ARCHIVED_REPORTS_ID_SET, false); await waitForBatchedUpdates(); // After success, the report should only have reportName (all other fields removed) @@ -4441,7 +4442,7 @@ describe('actions/Report', () => { }); await waitForBatchedUpdates(); - Report.leaveRoom(threadReport, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, false); + Report.leaveRoom(threadReport, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, TEST_ARCHIVED_REPORTS_ID_SET, false); await waitForBatchedUpdates(); const updatedParentReportActions = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${PARENT_REPORT_ID}` as const); @@ -4463,7 +4464,7 @@ describe('actions/Report', () => { // Should not throw an error with undefined conciergeReportID expect(() => { - Report.leaveRoom(roomReport, TEST_CURRENT_USER_ACCOUNT_ID, undefined, false); + Report.leaveRoom(roomReport, TEST_CURRENT_USER_ACCOUNT_ID, undefined, TEST_ARCHIVED_REPORTS_ID_SET, false); }).not.toThrow(); await waitForBatchedUpdates(); @@ -4488,7 +4489,7 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${ROOM_REPORT_ID}`, threadReport); await waitForBatchedUpdates(); - Report.leaveRoom(threadReport, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, false); + Report.leaveRoom(threadReport, TEST_CURRENT_USER_ACCOUNT_ID, TEST_CONCIERGE_REPORT_ID, TEST_ARCHIVED_REPORTS_ID_SET, false); await waitForBatchedUpdates(); const updatedReport = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT}${ROOM_REPORT_ID}` as const); From 9806560eb5a3fa5ee0c5f9b8b2a8f6d3c20e5723 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 22 Feb 2026 11:57:33 +0530 Subject: [PATCH 13/78] Update --- src/libs/actions/Link.ts | 2 +- src/libs/actions/Report/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Link.ts b/src/libs/actions/Link.ts index 59afef516149..e83ea823243c 100644 --- a/src/libs/actions/Link.ts +++ b/src/libs/actions/Link.ts @@ -330,7 +330,7 @@ function openReportFromDeepLink( return; } - const navigateHandler = async (reportParam?: OnyxEntry) => { + const navigateHandler = (reportParam?: OnyxEntry) => { // Check if the report exists in the collection const report = reportParam ?? reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; // If the report does not exist, navigate to the last accessed report or Concierge chat diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 93961718cbba..b4c32efad735 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -4183,7 +4183,7 @@ function inviteToGroupChat(report: Report, inviteeEmailsToAccountIDs: InvitedEma inviteToRoom(report, inviteeEmailsToAccountIDs, formatPhoneNumber); } -/** Removes people from a room +/** Removes people from a room. * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details */ function removeFromRoom(report: Report, targetAccountIDs: number[]) { From b6abb3d6f6c4d973c8e9c277b8869bce7409d667 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 22 Feb 2026 12:16:54 +0530 Subject: [PATCH 14/78] Update --- src/libs/actions/Report/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index b4c32efad735..93961718cbba 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -4183,7 +4183,7 @@ function inviteToGroupChat(report: Report, inviteeEmailsToAccountIDs: InvitedEma inviteToRoom(report, inviteeEmailsToAccountIDs, formatPhoneNumber); } -/** Removes people from a room. +/** Removes people from a room * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details */ function removeFromRoom(report: Report, targetAccountIDs: number[]) { From 8409bccd6b10fecd536d3df600a02601d5862417 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 22 Feb 2026 15:50:12 +0530 Subject: [PATCH 15/78] Address bot's consistency comment --- .../MoneyRequestConfirmationListFooter.tsx | 4 ++-- .../ReportActionItem/MoneyRequestReceiptView.tsx | 5 ++--- src/components/ReportActionItem/MoneyRequestView.tsx | 5 ++--- .../TransactionPreview/TransactionPreviewContent.tsx | 5 ++--- src/components/Search/index.tsx | 4 ++-- src/hooks/useArchivedReportsIDSet.ts | 10 +++++++++- src/hooks/useDeleteTransactions.ts | 3 ++- src/hooks/useSelectedTransactionsActions.ts | 5 ++--- src/hooks/useShowNotFoundPageInIOUStep.ts | 5 ++--- src/libs/ReportUtils.ts | 12 ++++++++++++ src/libs/actions/Link.ts | 5 ++--- src/libs/actions/Report/MarkAllMessageAsRead.tsx | 4 ++-- src/libs/actions/Report/index.ts | 5 +++-- .../ReportActionCompose/ReportActionCompose.tsx | 5 ++--- src/pages/iou/HoldReasonPage.tsx | 5 ++--- src/pages/iou/request/step/IOURequestStepReport.tsx | 5 ++--- .../routes/TransactionReceiptModalContent.tsx | 5 ++--- 17 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index e39ecbf4dfbd..932be44903ca 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -27,7 +27,7 @@ import {canSendInvoice, getPerDiemCustomUnit} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; import {getReportName} from '@libs/ReportNameUtils'; -import {generateReportID, getDefaultWorkspaceAvatar, getOutstandingReportsForUser, isMoneyRequestReport, isReportOutstanding} from '@libs/ReportUtils'; +import {generateReportID, getDefaultWorkspaceAvatar, getOutstandingReportsForUser, isMoneyRequestReport, isReportArchivedByID, isReportOutstanding} from '@libs/ReportUtils'; import {getTagVisibility, hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getTagForDisplay, @@ -297,7 +297,7 @@ function MoneyRequestConfirmationListFooter({ const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchived = useCallback((value?: string) => !!value && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${value}`), [archivedReportsIDSet]); + const isReportArchived = useCallback((value?: string) => isReportArchivedByID(archivedReportsIDSet, value), [archivedReportsIDSet]); const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID, { canBeMissing: true, }); diff --git a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx index 5de370196bd9..86675342edc0 100644 --- a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx +++ b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx @@ -9,7 +9,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ReceiptAudit, {ReceiptAuditMessages} from '@components/ReceiptAudit'; import ReceiptEmptyState from '@components/ReceiptEmptyState'; import useActiveRoute from '@hooks/useActiveRoute'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useCardFeedErrors from '@hooks/useCardFeedErrors'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -135,8 +135,7 @@ function MoneyRequestReceiptView({ const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${moneyRequestReport?.policyID}`, {canBeMissing: true}); const [cardList] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true}); const transactionViolations = useTransactionViolations(transaction?.transactionID); - const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const isReportArchivedByID = useIsReportArchivedByID(); const isDistanceRequest = isDistanceRequestTransactionUtils(transaction); const hasReceipt = hasReceiptTransactionUtils(updatedTransaction ?? transaction); diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 79e09b82409b..b7c260d38c69 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -15,7 +15,7 @@ import Text from '@components/Text'; import ViolationMessages from '@components/ViolationMessages'; import {useWideRHPState} from '@components/WideRHPContextProvider'; import useActiveRoute from '@hooks/useActiveRoute'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useCardFeedErrors from '@hooks/useCardFeedErrors'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -327,8 +327,7 @@ function MoneyRequestView({ const isSettled = isSettledReportUtils(moneyRequestReport); const isCancelled = moneyRequestReport && moneyRequestReport?.isCancelledIOU; const isChatReportArchived = useReportIsArchived(moneyRequestReport?.chatReportID); - const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const isReportArchivedByID = useIsReportArchivedByID(); const pendingAction = transaction?.pendingAction; const shouldShowPaid = isSettled && transactionReimbursable && !pendingAction; diff --git a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx index bcbbc7c59d50..02f81721e2ba 100644 --- a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx +++ b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx @@ -11,7 +11,7 @@ import ReportActionItemImages from '@components/ReportActionItem/ReportActionIte import UserInfoCellsWithArrow from '@components/SelectionListWithSections/Search/UserInfoCellsWithArrow'; import Text from '@components/Text'; import TransactionPreviewSkeletonView from '@components/TransactionPreviewSkeletonView'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useCardFeedErrors from '@hooks/useCardFeedErrors'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useEnvironment from '@hooks/useEnvironment'; @@ -90,8 +90,7 @@ function TransactionPreviewContent({ const isReportAPolicyExpenseChat = isPolicyExpenseChat(chatReport); const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.reportID)}`, {canBeMissing: true}); const isChatReportArchived = useReportIsArchived(chatReport?.reportID); - const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const isReportArchivedByID = useIsReportArchivedByID(); const currentUserDetails = useCurrentUserPersonalDetails(); const currentUserEmail = currentUserDetails.email ?? ''; const currentUserLogin = currentUserDetails.login; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 468b9c0e3bb8..f4d1ba53c897 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -44,7 +44,7 @@ import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTop import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types'; import Performance from '@libs/Performance'; import {isSplitAction} from '@libs/ReportSecondaryActionUtils'; -import {canEditFieldOfMoneyRequest, canHoldUnholdReportAction, canRejectReportAction, isOneTransactionReport, selectFilteredReportActions} from '@libs/ReportUtils'; +import {canEditFieldOfMoneyRequest, canHoldUnholdReportAction, canRejectReportAction, isOneTransactionReport, isReportArchivedByID, selectFilteredReportActions} from '@libs/ReportUtils'; import {buildCannedSearchQuery, buildSearchQueryString} from '@libs/SearchQueryUtils'; import { createAndOpenSearchTransactionThread, @@ -268,7 +268,7 @@ function Search({ const {markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchived = useCallback((reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), [archivedReportsIDSet]); + const isReportArchived = useCallback((reportID?: string) => isReportArchivedByID(archivedReportsIDSet, reportID), [archivedReportsIDSet]); const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, { canEvict: false, diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts index f84a942278b1..4b4e26f98d6e 100644 --- a/src/hooks/useArchivedReportsIDSet.ts +++ b/src/hooks/useArchivedReportsIDSet.ts @@ -1,5 +1,6 @@ +import {useCallback} from 'react'; import type {OnyxCollection} from 'react-native-onyx'; -import {isArchivedReport} from '@libs/ReportUtils'; +import {isArchivedReport, isReportArchivedByID} from '@libs/ReportUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -40,4 +41,11 @@ function useArchivedReportsIDSet(): ArchivedReportsIDSet { return useDeepCompareRef(archivedReportsIDSet) ?? new Set(); } +function useIsReportArchivedByID() { + const archivedReportsIDSet = useArchivedReportsIDSet(); + + return useCallback((reportID?: string) => isReportArchivedByID(archivedReportsIDSet, reportID), [archivedReportsIDSet]); +} + export default useArchivedReportsIDSet; +export {useIsReportArchivedByID}; diff --git a/src/hooks/useDeleteTransactions.ts b/src/hooks/useDeleteTransactions.ts index 1021c8c5deaf..f1e00d8320c8 100644 --- a/src/hooks/useDeleteTransactions.ts +++ b/src/hooks/useDeleteTransactions.ts @@ -5,6 +5,7 @@ import {getIOUActionForTransactions} from '@libs/actions/IOU/Duplicate'; import {initSplitExpenseItemData, updateSplitTransactions} from '@libs/actions/IOU/Split'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; +import {isReportArchivedByID} from '@libs/ReportUtils'; import {getChildTransactions, getOriginalTransactionWithSplitInfo} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -167,7 +168,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`]; const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`]; const chatIOUReportID = chatReport?.reportID; - const isChatIOUReportArchived = archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${chatIOUReportID}`); + const isChatIOUReportArchived = isReportArchivedByID(archivedReportsIDSet, chatIOUReportID); deleteMoneyRequest({ transactionID, reportAction: action, diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index 00e7abf37c10..744e5f4d4daa 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -33,7 +33,7 @@ import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import useAllTransactions from './useAllTransactions'; -import useArchivedReportsIDSet from './useArchivedReportsIDSet'; +import {useIsReportArchivedByID} from './useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useDeleteTransactions from './useDeleteTransactions'; import useDuplicateTransactionsAndViolations from './useDuplicateTransactionsAndViolations'; @@ -88,8 +88,7 @@ function useSelectedTransactionsActions({ const expensifyIcons = useMemoizedLazyExpensifyIcons(['Stopwatch', 'Trashcan', 'ArrowRight', 'Table', 'DocumentMerge', 'Export', 'ArrowCollapse', 'ArrowSplit', 'ThumbsDown']); const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(selectedTransactionIDs); const isReportArchived = useReportIsArchived(report?.reportID); - const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchivedByID = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const isReportArchivedByID = useIsReportArchivedByID(); const {deleteTransactions} = useDeleteTransactions({report, reportActions, policy}); const {login, accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const selectedTransactionsList = selectedTransactionIDs.reduce((acc, transactionID) => { diff --git a/src/hooks/useShowNotFoundPageInIOUStep.ts b/src/hooks/useShowNotFoundPageInIOUStep.ts index 6d77f87706e4..90bbfc51c8ac 100644 --- a/src/hooks/useShowNotFoundPageInIOUStep.ts +++ b/src/hooks/useShowNotFoundPageInIOUStep.ts @@ -8,7 +8,7 @@ import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {OnyxInputOrEntry, Report, ReportAction, ReportActions, Transaction} from '@src/types/onyx'; -import useArchivedReportsIDSet from './useArchivedReportsIDSet'; +import {useIsReportArchivedByID} from './useArchivedReportsIDSet'; import useOnyx from './useOnyx'; /** @@ -26,8 +26,7 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true}); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(transaction?.reportID)}`, {canBeMissing: true}); - const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchived = useCallback((reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`), [archivedReportsIDSet]); + const isReportArchived = useIsReportArchivedByID(); const reportActionsReportID = useMemo(() => { let actionsReportID; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 4ee9944795a1..ac7e236c2eda 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2376,6 +2376,17 @@ function isArchivedReport(reportNameValuePairs?: OnyxInputOrEntry, reportID?: string): boolean { + if (!reportID) { + return false; + } + + return archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); +} + /** * Whether the report was created during harvesting */ @@ -13274,6 +13285,7 @@ export { getReportForHeader, isReportOpenOrUnsubmitted, getIconsForExpenseReport, + isReportArchivedByID, }; export type { diff --git a/src/libs/actions/Link.ts b/src/libs/actions/Link.ts index e83ea823243c..e5ddac25dd95 100644 --- a/src/libs/actions/Link.ts +++ b/src/libs/actions/Link.ts @@ -16,7 +16,7 @@ import willRouteNavigateToRHP from '@libs/Navigation/helpers/willRouteNavigateTo import Navigation from '@libs/Navigation/Navigation'; import navigationRef from '@libs/Navigation/navigationRef'; import type {NetworkStatus} from '@libs/NetworkConnection'; -import {findLastAccessedReport, getReportIDFromLink, getRouteFromLink} from '@libs/ReportUtils'; +import {findLastAccessedReport, getReportIDFromLink, getRouteFromLink, isReportArchivedByID} from '@libs/ReportUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; import {endSpan, getSpan, startSpan} from '@libs/telemetry/activeSpans'; @@ -335,8 +335,7 @@ function openReportFromDeepLink( const report = reportParam ?? reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; // If the report does not exist, navigate to the last accessed report or Concierge chat if (reportID && (!report?.reportID || report.errorFields?.notFound)) { - const isReportArchived = (currentReportID?: string) => - !!currentReportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${currentReportID}`); + const isReportArchived = (currentReportID?: string) => isReportArchivedByID(archivedReportsIDSet, currentReportID); const lastAccessedReportID = findLastAccessedReport(false, shouldOpenOnAdminRoom(), reportID, undefined, isReportArchived)?.reportID; if (lastAccessedReportID) { const lastAccessedReportRoute = ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID); diff --git a/src/libs/actions/Report/MarkAllMessageAsRead.tsx b/src/libs/actions/Report/MarkAllMessageAsRead.tsx index 61d743c951a2..0579d726f14e 100644 --- a/src/libs/actions/Report/MarkAllMessageAsRead.tsx +++ b/src/libs/actions/Report/MarkAllMessageAsRead.tsx @@ -6,7 +6,7 @@ import type {MarkAllMessagesAsReadParams} from '@libs/API/parameters'; import {WRITE_COMMANDS} from '@libs/API/types'; import NetworkConnection from '@libs/NetworkConnection'; import {getOneTransactionThreadReportID} from '@libs/ReportActionsUtils'; -import {isUnread} from '@libs/ReportUtils'; +import {isReportArchivedByID, isUnread} from '@libs/ReportUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Report, ReportActions} from '@src/types/onyx'; @@ -48,7 +48,7 @@ function markAllMessagesAsRead(archivedReportsIDSet: ArchivedReportsIDSet) { const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`]; const oneTransactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]); const oneTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneTransactionThreadReportID}`]; - const isArchivedReport = archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`); + const isArchivedReport = isReportArchivedByID(archivedReportsIDSet, report.reportID); if (!isUnread(report, oneTransactionThreadReport, isArchivedReport)) { continue; } diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 93961718cbba..211618aa75c0 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -154,6 +154,7 @@ import { isIOUReportUsingReport, isOpenExpenseReport, isProcessingReport, + isReportArchivedByID, isReportManuallyReimbursed, isSelfDM, isValidReportIDFromPath, @@ -3782,7 +3783,7 @@ function doneCheckingPublicRoom() { } function navigateToMostRecentReport(currentReport: OnyxEntry, conciergeReportID: string | undefined, archivedReportsIDSet: ArchivedReportsIDSet) { - const isReportArchived = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const isReportArchived = (reportID?: string) => isReportArchivedByID(archivedReportsIDSet, reportID); const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, undefined, isReportArchived)?.reportID; if (lastAccessedReportID) { @@ -3808,7 +3809,7 @@ function navigateToMostRecentReport(currentReport: OnyxEntry, conciergeR } function getMostRecentReportID(currentReport: OnyxEntry, archivedReportsIDSet: ArchivedReportsIDSet) { - const isReportArchived = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const isReportArchived = (reportID?: string) => isReportArchivedByID(archivedReportsIDSet, reportID); const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, undefined, isReportArchived)?.reportID; return lastAccessedReportID ?? conciergeReportIDOnyxConnect; } diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index 6755938bc65b..498c7118fccd 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -19,7 +19,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import useAgentZeroStatusIndicator from '@hooks/useAgentZeroStatusIndicator'; import useAncestors from '@hooks/useAncestors'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHandleExceedMaxCommentLength from '@hooks/useHandleExceedMaxCommentLength'; import useHandleExceedMaxTaskTitleLength from '@hooks/useHandleExceedMaxTaskTitleLength'; @@ -221,8 +221,7 @@ function ReportActionCompose({ const userBlockedFromConcierge = useMemo(() => isBlockedFromConciergeUserAction(blockedFromConcierge), [blockedFromConcierge]); const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); const isReportArchived = useReportIsArchived(report?.reportID); - const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchivedByID = (targetReportID?: string) => !!targetReportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${targetReportID}`); + const isReportArchivedByID = useIsReportArchivedByID(); const isConciergeChat = useMemo(() => isConciergeChatReport(report), [report]); const isAdminsRoom = useMemo(() => isAdminRoom(report), [report]); diff --git a/src/pages/iou/HoldReasonPage.tsx b/src/pages/iou/HoldReasonPage.tsx index b66dcfffaa5f..c8c094ab4695 100644 --- a/src/pages/iou/HoldReasonPage.tsx +++ b/src/pages/iou/HoldReasonPage.tsx @@ -2,7 +2,7 @@ import React, {useCallback, useEffect} from 'react'; import {useDelegateNoAccessActions, useDelegateNoAccessState} from '@components/DelegateNoAccessModalProvider'; import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; import useAncestors from '@hooks/useAncestors'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import {putOnHold} from '@libs/actions/IOU/Hold'; @@ -30,8 +30,7 @@ function HoldReasonPage({route}: HoldReasonPageProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: true}); const ancestors = useAncestors(report); - const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchived = (reportId?: string) => !!reportId && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportId}`); + const isReportArchived = useIsReportArchivedByID(); // We first check if the report is part of a policy - if not, then it's a personal request (1:1 request) // For personal requests, we need to allow both users to put the request on hold diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 4519bc570af4..b5216d09c106 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -4,7 +4,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {usePersonalDetails, useSession} from '@components/OnyxListItemProvider'; import {useSearchContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionListWithSections/types'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useOnyx from '@hooks/useOnyx'; import useOptimisticDraftTransactions from '@hooks/useOptimisticDraftTransactions'; @@ -46,8 +46,7 @@ const getIOUActionsSelector = (actions: OnyxEntry): ReportAction[ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {backTo, action, iouType, transactionID, reportID: reportIDFromRoute, reportActionID} = route.params; const [allReports] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}`, {canBeMissing: false}); - const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchived = (reportID?: string) => !!reportID && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`); + const isReportArchived = useIsReportArchivedByID(); const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); const participantReportID = transaction?.participants?.at(0)?.reportID; diff --git a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx index 9786f2f64786..253349767d04 100644 --- a/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/TransactionReceiptModalContent.tsx @@ -4,7 +4,7 @@ import ConfirmModal from '@components/ConfirmModal'; // eslint-disable-next-line no-restricted-imports import * as Expensicons from '@components/Icon/Expensicons'; import useAllTransactions from '@hooks/useAllTransactions'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -49,8 +49,7 @@ function TransactionReceiptModalContent({navigation, route}: AttachmentModalScre const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true}); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); const policy = usePolicy(report?.policyID); - const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchived = (reportId?: string) => !!reportId && archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportId}`); + const isReportArchived = useIsReportArchivedByID(); // If we have a merge transaction, we need to use the receipt from the merge transaction const [mergeTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.MERGE_TRANSACTION}${getNonEmptyStringOnyxID(mergeTransactionID)}`, {canBeMissing: true}); From 889ffdd1da3c05e177bae439c544848a73a367db Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 22 Feb 2026 15:53:54 +0530 Subject: [PATCH 16/78] Address the codex comment --- src/libs/ReportUtils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ac7e236c2eda..c0e13a9f302f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2263,6 +2263,8 @@ function findLastAccessedReport( if (isUsingOldArgs) { if (typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string') { excludeReportID = excludeReportIDOrArchivedReportsIDSetOrCallback; + } else if (archivedReportsIDSetOrCallback !== undefined) { + excludeReportID = policyIDOrExcludeReportID; } } else { excludeReportID = policyIDOrExcludeReportID; From 5a740d9874c10f00134757b8a95558a4bdc03b30 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 22 Feb 2026 17:05:35 +0530 Subject: [PATCH 17/78] Address the codex comment --- src/Expensify.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Expensify.tsx b/src/Expensify.tsx index f34147831b22..49e7eb6a40e5 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -126,10 +126,15 @@ function Expensify() { const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true}); const archivedReportsIDSet = useArchivedReportsIDSet(); + const archivedReportsIDSetRef = useRef(archivedReportsIDSet); useDebugShortcut(); usePriorityMode(); + useEffect(() => { + archivedReportsIDSetRef.current = archivedReportsIDSet; + }, [archivedReportsIDSet]); + useEffect(() => { initializeMemoryTrackingTelemetry(); return () => { @@ -346,7 +351,7 @@ function Expensify() { if (introSelected === undefined) { Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); } - openReportFromDeepLink(url, allReports, isAuthenticated, conciergeReportID, introSelected, archivedReportsIDSet); + openReportFromDeepLink(url, allReports, isAuthenticated, conciergeReportID, introSelected, archivedReportsIDSetRef.current); } else { Report.doneCheckingPublicRoom(); } @@ -363,14 +368,14 @@ function Expensify() { Log.info('[Deep link] introSelected is undefined when processing URL change', false, {url: state.url}); } const isCurrentlyAuthenticated = hasAuthToken(); - openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, archivedReportsIDSet); + openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, archivedReportsIDSetRef.current); }); return () => { linkingChangeListener.current?.remove(); }; // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want this effect to re-run when conciergeReportID changes - }, [sessionMetadata?.status, conciergeReportID, introSelected, archivedReportsIDSet]); + }, [sessionMetadata?.status, conciergeReportID, introSelected]); useLayoutEffect(() => { if (!isNavigationReady || !lastRoute) { From a0fbcca713cd1b9d99fd0dcca3c6984dae2fe61b Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 22 Feb 2026 18:52:16 +0530 Subject: [PATCH 18/78] Pass isReportArchivedByID to canEditMoneyRequest when finding canEdit --- src/components/ReportActionItem/MoneyRequestReceiptView.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx index 86675342edc0..4ed4925ee73c 100644 --- a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx +++ b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx @@ -149,7 +149,10 @@ function MoneyRequestReceiptView({ // Used for non-restricted fields such as: description, category, tag, billable, etc... const isReportArchived = useReportIsArchived(report?.reportID); const isEditable = !!canUserPerformWriteActionReportUtils(report, isReportArchived) && !readonly; - const canEdit = isMoneyRequestAction(parentReportAction) && canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction) && isEditable; + const canEdit = + isMoneyRequestAction(parentReportAction) && + canEditMoneyRequest(parentReportAction, isChatReportArchived, moneyRequestReport, policy, transaction, isReportArchivedByID) && + isEditable; const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(report?.policyID)}`; const {personalCardsWithBrokenConnection} = useCardFeedErrors(); const connectionLink = getBrokenConnectionUrlToFixPersonalCard(personalCardsWithBrokenConnection, environmentURL); From 6a75e62bd1343d6397193508877d08ba6742ed69 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 22 Feb 2026 22:59:18 +0530 Subject: [PATCH 19/78] Improvements --- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 5 ++++- .../ReportActionItem/MoneyRequestReceiptView.tsx | 6 ++++-- src/components/ReportActionItem/MoneyRequestView.tsx | 7 +++++-- src/hooks/useActionListContextValue.ts | 4 ++-- src/pages/Search/SearchMoneyRequestReportPage.tsx | 4 +++- src/pages/TransactionDuplicate/Confirmation.tsx | 3 +++ src/pages/TransactionMerge/ConfirmationPage.tsx | 3 +++ src/pages/inbox/ReportScreen.tsx | 5 ++++- src/pages/inbox/ReportScreenContext.ts | 8 +++++++- src/pages/inbox/report/ReportActionItemContentCreated.tsx | 6 +++++- tests/perf-test/ReportActionsList.perf-test.tsx | 2 +- 11 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 324fd1a748b0..8224d5086fa4 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -1,5 +1,5 @@ import {PortalHost} from '@gorhom/portal'; -import React, {useCallback, useEffect, useMemo} from 'react'; +import React, {useCallback, useContext, useEffect, useMemo} from 'react'; // We use Animated for all functionality related to wide RHP to make it easier // to interact with react-navigation components (e.g., CardContainer, interpolator), which also use Animated. // eslint-disable-next-line no-restricted-imports @@ -29,6 +29,7 @@ import {canEditReportAction, getReportOfflinePendingActionAndErrors, isReportTra import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; import {cancelSpan} from '@libs/telemetry/activeSpans'; import Navigation from '@navigation/Navigation'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import ReportActionsView from '@pages/inbox/report/ReportActionsView'; import ReportFooter from '@pages/inbox/report/ReportFooter'; import CONST from '@src/CONST'; @@ -97,6 +98,7 @@ function InitialLoadingSkeleton({styles}: {styles: ThemeStyles}) { function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayReportFooter, backToRoute}: MoneyRequestReportViewProps) { const styles = useThemeStyles(); + const {isReportArchivedByID} = useContext(ActionListContext); const {isOffline} = useNetwork(); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth @@ -254,6 +256,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe report={transactionThreadReport} fillSpace isDisplayedInWideRHP + isReportArchivedByID={isReportArchivedByID} /> diff --git a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx index 4ed4925ee73c..813e022f7d2b 100644 --- a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx +++ b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx @@ -9,7 +9,6 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ReceiptAudit, {ReceiptAuditMessages} from '@components/ReceiptAudit'; import ReceiptEmptyState from '@components/ReceiptEmptyState'; import useActiveRoute from '@hooks/useActiveRoute'; -import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useCardFeedErrors from '@hooks/useCardFeedErrors'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -75,6 +74,9 @@ type MoneyRequestReceiptViewProps = { /** Merge transaction ID to show in merge transaction flow */ mergeTransactionID?: string; + /** Whether a report with provided reportID is archived */ + isReportArchivedByID: (reportID?: string) => boolean; + /** Whether the receipt view should fill the given space */ fillSpace?: boolean; @@ -101,6 +103,7 @@ function MoneyRequestReceiptView({ updatedTransaction, fillSpace = false, mergeTransactionID, + isReportArchivedByID, isDisplayedInWideRHP = false, }: MoneyRequestReceiptViewProps) { const styles = useThemeStyles(); @@ -135,7 +138,6 @@ function MoneyRequestReceiptView({ const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${moneyRequestReport?.policyID}`, {canBeMissing: true}); const [cardList] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true}); const transactionViolations = useTransactionViolations(transaction?.transactionID); - const isReportArchivedByID = useIsReportArchivedByID(); const isDistanceRequest = isDistanceRequestTransactionUtils(transaction); const hasReceipt = hasReceiptTransactionUtils(updatedTransaction ?? transaction); diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index b7c260d38c69..831ce27e9027 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -15,7 +15,6 @@ import Text from '@components/Text'; import ViolationMessages from '@components/ViolationMessages'; import {useWideRHPState} from '@components/WideRHPContextProvider'; import useActiveRoute from '@hooks/useActiveRoute'; -import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useCardFeedErrors from '@hooks/useCardFeedErrors'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -147,6 +146,9 @@ type MoneyRequestViewProps = { /** Merge transaction ID to show in merge transaction flow */ mergeTransactionID?: string; + + /** Whether a report with provided reportID is archived */ + isReportArchivedByID: (reportID?: string) => boolean; }; const perDiemPoliciesSelector = (policies: OnyxCollection) => { @@ -170,6 +172,7 @@ function MoneyRequestView({ updatedTransaction, isFromReviewDuplicates = false, mergeTransactionID, + isReportArchivedByID, }: MoneyRequestViewProps) { const icons = useMemoizedLazyExpensifyIcons(['DotIndicator', 'Checkmark', 'Suitcase']); const styles = useThemeStyles(); @@ -327,7 +330,6 @@ function MoneyRequestView({ const isSettled = isSettledReportUtils(moneyRequestReport); const isCancelled = moneyRequestReport && moneyRequestReport?.isCancelledIOU; const isChatReportArchived = useReportIsArchived(moneyRequestReport?.chatReportID); - const isReportArchivedByID = useIsReportArchivedByID(); const pendingAction = transaction?.pendingAction; const shouldShowPaid = isSettled && transactionReimbursable && !pendingAction; @@ -893,6 +895,7 @@ function MoneyRequestView({ readonly={readonly} updatedTransaction={updatedTransaction} mergeTransactionID={mergeTransactionID} + isReportArchivedByID={isReportArchivedByID} /> )} {isCustomUnitOutOfPolicy && isPerDiemRequest && ( diff --git a/src/hooks/useActionListContextValue.ts b/src/hooks/useActionListContextValue.ts index ba73ff804cc0..9ef9f8bc9962 100644 --- a/src/hooks/useActionListContextValue.ts +++ b/src/hooks/useActionListContextValue.ts @@ -2,12 +2,12 @@ import {useRef} from 'react'; import type {FlatList} from 'react-native'; import type {ActionListContextType, ScrollPosition} from '@pages/inbox/ReportScreenContext'; -function useActionListContextValue(): ActionListContextType { +function useActionListContextValue(isReportArchivedByID: ActionListContextType['isReportArchivedByID'] = () => false): ActionListContextType { const flatListRef = useRef(null); const scrollPositionRef = useRef({}); const scrollOffsetRef = useRef(0); - return {flatListRef, scrollPositionRef, scrollOffsetRef}; + return {flatListRef, scrollPositionRef, scrollOffsetRef, isReportArchivedByID}; } export default useActionListContextValue; diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index a139fef8e4fa..0fc90bdb73b6 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -10,6 +10,7 @@ import {useSearchContext} from '@components/Search/SearchContext'; import useShowSuperWideRHPVersion from '@components/WideRHPContextProvider/useShowSuperWideRHPVersion'; import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; +import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useIsReportReadyToDisplay from '@hooks/useIsReportReadyToDisplay'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -121,7 +122,8 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute, isReportArchived); - const actionListValue = useActionListContextValue(); + const isReportArchivedByID = useIsReportArchivedByID(); + const actionListValue = useActionListContextValue(isReportArchivedByID); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`, {canBeMissing: true}); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true}); diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index 66357ebf741b..c7ce64ad489f 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -14,6 +14,7 @@ import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import {useWideRHPState} from '@components/WideRHPContextProvider'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; @@ -39,6 +40,7 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; function Confirmation() { const styles = useThemeStyles(); const {translate} = useLocalize(); + const isReportArchivedByID = useIsReportArchivedByID(); const route = useRoute>(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [reviewDuplicates, reviewDuplicatesResult] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES, {canBeMissing: true}); @@ -157,6 +159,7 @@ function Confirmation() { readonly updatedTransaction={newTransaction as OnyxEntry} isFromReviewDuplicates + isReportArchivedByID={isReportArchivedByID} /> diff --git a/src/pages/TransactionMerge/ConfirmationPage.tsx b/src/pages/TransactionMerge/ConfirmationPage.tsx index 832260cf6518..dec2a11ab494 100644 --- a/src/pages/TransactionMerge/ConfirmationPage.tsx +++ b/src/pages/TransactionMerge/ConfirmationPage.tsx @@ -10,6 +10,7 @@ import MoneyRequestView from '@components/ReportActionItem/MoneyRequestView'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; +import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useMergeTransactions from '@hooks/useMergeTransactions'; @@ -37,6 +38,7 @@ function ConfirmationPage({route}: ConfirmationPageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [isMergingExpenses, setIsMergingExpenses] = useState(false); + const isReportArchivedByID = useIsReportArchivedByID(); const {transactionID, isOnSearch, backTo} = route.params; @@ -143,6 +145,7 @@ function ConfirmationPage({route}: ConfirmationPageProps) { readonly updatedTransaction={mergedTransactionData as unknown as OnyxEntry} mergeTransactionID={transactionID} + isReportArchivedByID={isReportArchivedByID} /> diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index 7a99788d1622..e7e744b4b70d 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -86,6 +86,7 @@ import { isPolicyExpenseChat, isReportTransactionThread, isTaskReport, + isReportArchivedByID as isReportArchivedByIDUtil, isValidReportIDFromPath, } from '@libs/ReportUtils'; import {cancelSpan, cancelSpansByPrefix} from '@libs/telemetry/activeSpans'; @@ -187,6 +188,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID, {canBeMissing: true}); const archivedReportsIDSet = useArchivedReportsIDSet(); + const isReportArchivedByID = useCallback((reportID?: string) => isReportArchivedByIDUtil(archivedReportsIDSet, reportID), [archivedReportsIDSet]); const parentReportAction = useParentReportAction(reportOnyx); @@ -877,7 +879,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr }; }, [report?.reportID, didSubscribeToReportLeavingEvents, reportIDFromRoute, report?.pendingFields, currentUserAccountID]); - const actionListValue = useActionListContextValue(); + const actionListValue = useActionListContextValue(isReportArchivedByID); // This helps in tracking from the moment 'route' triggers useMemo until isLoadingInitialReportActions becomes true. It prevents blinking when loading reportActions from cache. useEffect(() => { @@ -1060,6 +1062,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr report={transactionThreadReport ?? report} fillSpace isDisplayedInWideRHP + isReportArchivedByID={isReportArchivedByID} /> diff --git a/src/pages/inbox/ReportScreenContext.ts b/src/pages/inbox/ReportScreenContext.ts index 50b1e3e707f9..622be89d3362 100644 --- a/src/pages/inbox/ReportScreenContext.ts +++ b/src/pages/inbox/ReportScreenContext.ts @@ -21,10 +21,16 @@ type ActionListContextType = { flatListRef: FlatListRefType; scrollPositionRef: RefObject; scrollOffsetRef: RefObject; + isReportArchivedByID: (reportID?: string) => boolean; }; type ReactionListContextType = RefObject | null; -const ActionListContext = createContext({flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}}); +const ActionListContext = createContext({ + flatListRef: null, + scrollPositionRef: {current: {}}, + scrollOffsetRef: {current: 0}, + isReportArchivedByID: () => false, +}); const ReactionListContext = createContext(null); export {ActionListContext, ReactionListContext}; diff --git a/src/pages/inbox/report/ReportActionItemContentCreated.tsx b/src/pages/inbox/report/ReportActionItemContentCreated.tsx index f0ba4d9edaed..bd8eafa8a05e 100644 --- a/src/pages/inbox/report/ReportActionItemContentCreated.tsx +++ b/src/pages/inbox/report/ReportActionItemContentCreated.tsx @@ -1,4 +1,4 @@ -import React, {memo, useMemo} from 'react'; +import React, {memo, useContext, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -23,6 +23,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import AnimatedEmptyStateBackground from './AnimatedEmptyStateBackground'; import ReportActionItemCreated from './ReportActionItemCreated'; import ReportActionItemSingle from './ReportActionItemSingle'; @@ -61,6 +62,7 @@ function ReportActionItemContentCreated({ }: ReportActionItemContentCreatedProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); + const {isReportArchivedByID} = useContext(ActionListContext); const {report, action, transactionThreadReport} = contextValue; const policy = usePolicy(report?.policyID === CONST.POLICY.OWNER_EMAIL_FAKE ? undefined : report?.policyID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`, {canBeMissing: true}); @@ -124,6 +126,7 @@ function ReportActionItemContentCreated({ parentReportID={report?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground + isReportArchivedByID={isReportArchivedByID} /> {renderThreadDivider} @@ -187,6 +190,7 @@ function ReportActionItemContentCreated({ parentReportID={transactionThreadReport?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground={false} + isReportArchivedByID={isReportArchivedByID} /> {renderThreadDivider} diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index 72122f8bf02a..ea2694f94896 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -70,7 +70,7 @@ beforeAll(() => const mockOnLayout = jest.fn(); const mockOnScroll = jest.fn(); const mockLoadChats = jest.fn(); -const mockRef = {current: null, flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}}; +const mockRef = {current: null, flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}, isReportArchivedByID: () => false}; const TEST_USER_ACCOUNT_ID = 1; const TEST_USER_LOGIN = 'test@test.com'; From 3f540d58217b4e77faa9686f2b2245c1d1ab2d83 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:45:27 +0530 Subject: [PATCH 20/78] Prettier fixes --- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 2 +- src/pages/TransactionDuplicate/Confirmation.tsx | 2 +- src/pages/inbox/ReportScreen.tsx | 2 +- src/pages/inbox/report/ReportActionItemContentCreated.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 8224d5086fa4..4b94d1b0b64f 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -29,9 +29,9 @@ import {canEditReportAction, getReportOfflinePendingActionAndErrors, isReportTra import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; import {cancelSpan} from '@libs/telemetry/activeSpans'; import Navigation from '@navigation/Navigation'; -import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import ReportActionsView from '@pages/inbox/report/ReportActionsView'; import ReportFooter from '@pages/inbox/report/ReportFooter'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index c7ce64ad489f..2978cd9a903b 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -13,8 +13,8 @@ import ScrollView from '@components/ScrollView'; import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import {useWideRHPState} from '@components/WideRHPContextProvider'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation'; diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index e7e744b4b70d..1351977f90a0 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -84,9 +84,9 @@ import { isMoneyRequestReportPendingDeletion, isOneTransactionThread, isPolicyExpenseChat, + isReportArchivedByID as isReportArchivedByIDUtil, isReportTransactionThread, isTaskReport, - isReportArchivedByID as isReportArchivedByIDUtil, isValidReportIDFromPath, } from '@libs/ReportUtils'; import {cancelSpan, cancelSpansByPrefix} from '@libs/telemetry/activeSpans'; diff --git a/src/pages/inbox/report/ReportActionItemContentCreated.tsx b/src/pages/inbox/report/ReportActionItemContentCreated.tsx index bd8eafa8a05e..4e6bd6aa1c48 100644 --- a/src/pages/inbox/report/ReportActionItemContentCreated.tsx +++ b/src/pages/inbox/report/ReportActionItemContentCreated.tsx @@ -18,12 +18,12 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {isMessageDeleted, isReversedTransaction as isReversedTransactionReportActionsUtils, isTransactionThread} from '@libs/ReportActionsUtils'; import {isCanceledTaskReport, isExpenseReport, isInvoiceReport, isIOUReport, isTaskReport} from '@libs/ReportUtils'; import {getCurrency} from '@libs/TransactionUtils'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import AnimatedEmptyStateBackground from './AnimatedEmptyStateBackground'; import ReportActionItemCreated from './ReportActionItemCreated'; import ReportActionItemSingle from './ReportActionItemSingle'; From c1ba85bb5407fc65c5496a78dbd2973aec5a78e8 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 28 Feb 2026 12:14:55 +0530 Subject: [PATCH 21/78] Prettier and lint fixes --- src/hooks/useArchivedReportsIDSet.ts | 6 +++--- src/libs/actions/Report/index.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts index 8fbd329a62c8..63c60f08c7a4 100644 --- a/src/hooks/useArchivedReportsIDSet.ts +++ b/src/hooks/useArchivedReportsIDSet.ts @@ -2,9 +2,9 @@ import {useCallback} from 'react'; import type {OnyxCollection} from 'react-native-onyx'; import {isArchivedReport, isReportArchivedByID} from '@libs/ReportUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {ReportNameValuePairs} from '@src/types/onyx'; +import getEmptyArray from '@src/types/utils/getEmptyArray'; import useOnyx from './useOnyx'; /** @@ -14,7 +14,7 @@ import useOnyx from './useOnyx'; */ const archivedReportIDsSelector = (reportNameValuePairs: OnyxCollection): string[] => { if (!reportNameValuePairs) { - return CONST.EMPTY_ARRAY; + return []; } const ids: string[] = []; @@ -30,7 +30,7 @@ const archivedReportIDsSelector = (reportNameValuePairs: OnyxCollection()] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {selector: archivedReportIDsSelector}); return new Set(archivedReportIDs); } diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 458b27b6f3a9..2f268fc97c89 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -161,8 +161,8 @@ import { isValidReportIDFromPath, prepareOnboardingOnyxData, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import {getAmount, getCurrency, hasValidModifiedAmount, isOnHold, shouldClearConvertedAmount} from '@libs/TransactionUtils'; import addTrailingForwardSlash from '@libs/UrlUtils'; From f49180707a5bba5e65a7e20a2b63896036e4b88d Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 28 Feb 2026 12:49:42 +0530 Subject: [PATCH 22/78] Use set instead of function --- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 5 +++-- src/hooks/useActionListContextValue.ts | 5 +++-- src/pages/Search/SearchMoneyRequestReportPage.tsx | 6 +++--- src/pages/inbox/ReportScreen.tsx | 2 +- src/pages/inbox/ReportScreenContext.ts | 5 +++-- .../inbox/report/ReportActionItemContentCreated.tsx | 11 ++++++----- tests/ui/components/MoneyRequestReceiptViewTest.tsx | 1 + 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index ffa80e747582..d28493b048f1 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -25,7 +25,7 @@ import Log from '@libs/Log'; import {getAllNonDeletedTransactions, shouldDisplayReportTableView, shouldWaitForTransactions as shouldWaitForTransactionsUtil} from '@libs/MoneyRequestReportUtils'; import navigationRef from '@libs/Navigation/navigationRef'; import {getFilteredReportActionsForReportView, getOneTransactionThreadReportID, isMoneyRequestAction, isSentMoneyReportAction} from '@libs/ReportActionsUtils'; -import {canEditReportAction, getReportOfflinePendingActionAndErrors, isReportTransactionThread} from '@libs/ReportUtils'; +import {canEditReportAction, getReportOfflinePendingActionAndErrors, isReportArchivedByID as isReportArchivedByIDUtil, isReportTransactionThread} from '@libs/ReportUtils'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; import {cancelSpan} from '@libs/telemetry/activeSpans'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; @@ -99,7 +99,8 @@ function InitialLoadingSkeleton({styles}: {styles: ThemeStyles}) { function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayReportFooter, backToRoute}: MoneyRequestReportViewProps) { const styles = useThemeStyles(); - const {isReportArchivedByID} = useContext(ActionListContext); + const {archivedReportsIDSet} = useContext(ActionListContext); + const isReportArchivedByID = useCallback((id?: string) => isReportArchivedByIDUtil(archivedReportsIDSet, id), [archivedReportsIDSet]); const {isOffline} = useNetwork(); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth diff --git a/src/hooks/useActionListContextValue.ts b/src/hooks/useActionListContextValue.ts index 9ef9f8bc9962..fc176df77c10 100644 --- a/src/hooks/useActionListContextValue.ts +++ b/src/hooks/useActionListContextValue.ts @@ -1,13 +1,14 @@ import {useRef} from 'react'; import type {FlatList} from 'react-native'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import type {ActionListContextType, ScrollPosition} from '@pages/inbox/ReportScreenContext'; -function useActionListContextValue(isReportArchivedByID: ActionListContextType['isReportArchivedByID'] = () => false): ActionListContextType { +function useActionListContextValue(archivedReportsIDSet: ArchivedReportsIDSet): ActionListContextType { const flatListRef = useRef(null); const scrollPositionRef = useRef({}); const scrollOffsetRef = useRef(0); - return {flatListRef, scrollPositionRef, scrollOffsetRef, isReportArchivedByID}; + return {flatListRef, scrollPositionRef, scrollOffsetRef, archivedReportsIDSet}; } export default useActionListContextValue; diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index 203d20fce2e9..cb156d096dd2 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -11,7 +11,7 @@ import {useSearchStateContext} from '@components/Search/SearchContext'; import useShowSuperWideRHPVersion from '@components/WideRHPContextProvider/useShowSuperWideRHPVersion'; import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; -import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useIsReportReadyToDisplay from '@hooks/useIsReportReadyToDisplay'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -145,8 +145,8 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute, isReportArchived); - const isReportArchivedByID = useIsReportArchivedByID(); - const actionListValue = useActionListContextValue(isReportArchivedByID); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const actionListValue = useActionListContextValue(archivedReportsIDSet); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index c9a49cb98df1..aad012787d94 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -850,7 +850,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr }; }, [report?.reportID, didSubscribeToReportLeavingEvents, reportIDFromRoute, report?.pendingFields, currentUserAccountID]); - const actionListValue = useActionListContextValue(isReportArchivedByID); + const actionListValue = useActionListContextValue(archivedReportsIDSet); // This helps in tracking from the moment 'route' triggers useMemo until isLoadingInitialReportActions becomes true. It prevents blinking when loading reportActions from cache. useEffect(() => { diff --git a/src/pages/inbox/ReportScreenContext.ts b/src/pages/inbox/ReportScreenContext.ts index 622be89d3362..fa2389f1ec13 100644 --- a/src/pages/inbox/ReportScreenContext.ts +++ b/src/pages/inbox/ReportScreenContext.ts @@ -2,6 +2,7 @@ import type {RefObject, SyntheticEvent} from 'react'; import {createContext} from 'react'; // eslint-disable-next-line no-restricted-imports import type {FlatList, GestureResponderEvent, Text, View} from 'react-native'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; type ReactionListAnchor = View | Text | HTMLDivElement | null; @@ -21,7 +22,7 @@ type ActionListContextType = { flatListRef: FlatListRefType; scrollPositionRef: RefObject; scrollOffsetRef: RefObject; - isReportArchivedByID: (reportID?: string) => boolean; + archivedReportsIDSet: ArchivedReportsIDSet; }; type ReactionListContextType = RefObject | null; @@ -29,7 +30,7 @@ const ActionListContext = createContext({ flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}, - isReportArchivedByID: () => false, + archivedReportsIDSet: new Set(), }); const ReactionListContext = createContext(null); diff --git a/src/pages/inbox/report/ReportActionItemContentCreated.tsx b/src/pages/inbox/report/ReportActionItemContentCreated.tsx index 3706e345cc4f..022af22556f4 100644 --- a/src/pages/inbox/report/ReportActionItemContentCreated.tsx +++ b/src/pages/inbox/report/ReportActionItemContentCreated.tsx @@ -1,4 +1,4 @@ -import React, {memo, useContext, useMemo} from 'react'; +import React, {memo, useCallback, useContext, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -16,7 +16,7 @@ import usePolicy from '@hooks/usePolicy'; import useThemeStyles from '@hooks/useThemeStyles'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {isMessageDeleted, isReversedTransaction as isReversedTransactionReportActionsUtils, isTransactionThread} from '@libs/ReportActionsUtils'; -import {isCanceledTaskReport, isExpenseReport, isInvoiceReport, isIOUReport, isTaskReport} from '@libs/ReportUtils'; +import {isCanceledTaskReport, isExpenseReport, isInvoiceReport, isIOUReport, isReportArchivedByID, isTaskReport} from '@libs/ReportUtils'; import {getCurrency} from '@libs/TransactionUtils'; import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import CONST from '@src/CONST'; @@ -51,7 +51,8 @@ type ReportActionItemContentCreatedProps = { function ReportActionItemContentCreated({contextValue, parentReport, parentReportAction, transactionID, draftMessage, shouldHideThreadDividerLine}: ReportActionItemContentCreatedProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const {isReportArchivedByID} = useContext(ActionListContext); + const {archivedReportsIDSet} = useContext(ActionListContext); + const isReportArchivedByIDCallback = useCallback((id?: string) => isReportArchivedByID(archivedReportsIDSet, id), [archivedReportsIDSet]); const {report, action, transactionThreadReport} = contextValue; const policy = usePolicy(report?.policyID === CONST.POLICY.OWNER_EMAIL_FAKE ? undefined : report?.policyID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`); @@ -114,7 +115,7 @@ function ReportActionItemContentCreated({contextValue, parentReport, parentRepor parentReportID={report?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground - isReportArchivedByID={isReportArchivedByID} + isReportArchivedByID={isReportArchivedByIDCallback} /> {renderThreadDivider} @@ -177,7 +178,7 @@ function ReportActionItemContentCreated({contextValue, parentReport, parentRepor parentReportID={transactionThreadReport?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground={false} - isReportArchivedByID={isReportArchivedByID} + isReportArchivedByID={isReportArchivedByIDCallback} /> {renderThreadDivider} diff --git a/tests/ui/components/MoneyRequestReceiptViewTest.tsx b/tests/ui/components/MoneyRequestReceiptViewTest.tsx index e4e81de60e4e..f959aac26883 100644 --- a/tests/ui/components/MoneyRequestReceiptViewTest.tsx +++ b/tests/ui/components/MoneyRequestReceiptViewTest.tsx @@ -161,6 +161,7 @@ describe('MoneyRequestReceiptView', () => { report={testReport} fillSpace isDisplayedInWideRHP + isReportArchivedByID={() => false} /> , ); From 4be214688d18b44c8880aabb5fa5dfff0b4a978a Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 28 Feb 2026 14:07:31 +0530 Subject: [PATCH 23/78] Update test --- tests/perf-test/ReportActionsList.perf-test.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index ea2694f94896..f4d020f3c418 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -70,7 +70,8 @@ beforeAll(() => const mockOnLayout = jest.fn(); const mockOnScroll = jest.fn(); const mockLoadChats = jest.fn(); -const mockRef = {current: null, flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}, isReportArchivedByID: () => false}; +const mockReactionListRef = {current: null}; +const mockActionListContext = {flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}, archivedReportsIDSet: new Set()}; const TEST_USER_ACCOUNT_ID = 1; const TEST_USER_LOGIN = 'test@test.com'; @@ -97,8 +98,8 @@ function ReportActionsListWrapper() { const reportActions = ReportTestUtils.getMockedSortedReportActions(500); return ( - - + + Date: Sat, 28 Feb 2026 14:13:12 +0530 Subject: [PATCH 24/78] Use useMemo --- src/hooks/useArchivedReportsIDSet.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts index 63c60f08c7a4..e942a882374d 100644 --- a/src/hooks/useArchivedReportsIDSet.ts +++ b/src/hooks/useArchivedReportsIDSet.ts @@ -1,4 +1,4 @@ -import {useCallback} from 'react'; +import {useCallback, useMemo} from 'react'; import type {OnyxCollection} from 'react-native-onyx'; import {isArchivedReport, isReportArchivedByID} from '@libs/ReportUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; @@ -32,7 +32,7 @@ const archivedReportIDsSelector = (reportNameValuePairs: OnyxCollection()] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {selector: archivedReportIDsSelector}); - return new Set(archivedReportIDs); + return useMemo(() => new Set(archivedReportIDs), [archivedReportIDs]); } function useIsReportArchivedByID() { From bece789d077f52acb11750257926d7ab6c363dff Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 28 Feb 2026 16:28:32 +0530 Subject: [PATCH 25/78] Update deep link handler --- src/DeepLinkHandler.tsx | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/DeepLinkHandler.tsx b/src/DeepLinkHandler.tsx index 93df9c6faa13..65cd0732fac9 100644 --- a/src/DeepLinkHandler.tsx +++ b/src/DeepLinkHandler.tsx @@ -32,20 +32,35 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const archivedReportsIDSet = useArchivedReportsIDSet(); + const allReportsRef = useRef(allReports); + const onInitialURLRef = useRef(onInitialUrl); const archivedReportsIDSetRef = useRef(archivedReportsIDSet); const isAuthenticated = useIsAuthenticated(); + const isAuthenticatedRef = useRef(isAuthenticated); + + useEffect(() => { + allReportsRef.current = allReports; + }, [allReports]); + + useEffect(() => { + onInitialURLRef.current = onInitialUrl; + }, [onInitialUrl]); useEffect(() => { archivedReportsIDSetRef.current = archivedReportsIDSet; }, [archivedReportsIDSet]); + useEffect(() => { + isAuthenticatedRef.current = isAuthenticated; + }, [isAuthenticated]); + useEffect(() => { if (isLoadingOnyxValue(sessionMetadata)) { return; } // If the app is opened from a deep link, get the reportID (if exists) from the deep link and navigate to the chat report Linking.getInitialURL().then((url) => { - onInitialUrl(url as Route); + onInitialURLRef.current(url as Route); if (url) { if (conciergeReportID === undefined) { @@ -54,7 +69,7 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { if (introSelected === undefined) { Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); } - openReportFromDeepLink(url, allReports, isAuthenticated, conciergeReportID, introSelected, archivedReportsIDSetRef.current); + openReportFromDeepLink(url, allReportsRef.current, isAuthenticatedRef.current, conciergeReportID, introSelected, archivedReportsIDSetRef.current); } else { Report.doneCheckingPublicRoom(); } @@ -71,14 +86,14 @@ function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { Log.info('[Deep link] introSelected is undefined when processing URL change', false, {url: state.url}); } const isCurrentlyAuthenticated = hasAuthToken(); - openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, archivedReportsIDSetRef.current); + openReportFromDeepLink(state.url, allReportsRef.current, isCurrentlyAuthenticated, conciergeReportID, introSelected, archivedReportsIDSetRef.current); }); return () => { linkingChangeListener.current?.remove(); }; // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want this effect to re-run when conciergeReportID changes - }, [sessionMetadata?.status, conciergeReportID, introSelected, allReports, isAuthenticated, onInitialUrl]); + }, [sessionMetadata?.status, conciergeReportID, introSelected]); return null; } From a90cf635f8adb4dab6329ab78552ac89d296f812 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 8 Mar 2026 00:29:25 +0530 Subject: [PATCH 26/78] Update money request archive prop to pass archived report set --- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 5 ++--- .../ReportActionItem/MoneyRequestReceiptView.tsx | 11 +++++++---- src/components/ReportActionItem/MoneyRequestView.tsx | 4 +++- src/libs/Permissions.ts | 1 + src/pages/inbox/ReportScreen.tsx | 4 +--- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index d28493b048f1..4444d1a7b71c 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -25,7 +25,7 @@ import Log from '@libs/Log'; import {getAllNonDeletedTransactions, shouldDisplayReportTableView, shouldWaitForTransactions as shouldWaitForTransactionsUtil} from '@libs/MoneyRequestReportUtils'; import navigationRef from '@libs/Navigation/navigationRef'; import {getFilteredReportActionsForReportView, getOneTransactionThreadReportID, isMoneyRequestAction, isSentMoneyReportAction} from '@libs/ReportActionsUtils'; -import {canEditReportAction, getReportOfflinePendingActionAndErrors, isReportArchivedByID as isReportArchivedByIDUtil, isReportTransactionThread} from '@libs/ReportUtils'; +import {canEditReportAction, getReportOfflinePendingActionAndErrors, isReportTransactionThread} from '@libs/ReportUtils'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; import {cancelSpan} from '@libs/telemetry/activeSpans'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; @@ -100,7 +100,6 @@ function InitialLoadingSkeleton({styles}: {styles: ThemeStyles}) { function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayReportFooter, backToRoute}: MoneyRequestReportViewProps) { const styles = useThemeStyles(); const {archivedReportsIDSet} = useContext(ActionListContext); - const isReportArchivedByID = useCallback((id?: string) => isReportArchivedByIDUtil(archivedReportsIDSet, id), [archivedReportsIDSet]); const {isOffline} = useNetwork(); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth @@ -271,7 +270,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe report={transactionThreadReport} fillSpace isDisplayedInWideRHP - isReportArchivedByID={isReportArchivedByID} + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx index d65848e1c2bf..92ee57092799 100644 --- a/src/components/ReportActionItem/MoneyRequestReceiptView.tsx +++ b/src/components/ReportActionItem/MoneyRequestReceiptView.tsx @@ -1,5 +1,5 @@ import mapValues from 'lodash/mapValues'; -import React, {useMemo, useState} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -34,8 +34,10 @@ import { getCreationReportErrors, isInvoiceReport, isPaidGroupPolicy, + isReportArchivedByID as isReportArchivedByIDUtil, isTrackExpenseReportNew, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import trackExpenseCreationError from '@libs/telemetry/trackExpenseCreationError'; import { didReceiptScanSucceed as didReceiptScanSucceedTransactionUtils, @@ -72,8 +74,8 @@ type MoneyRequestReceiptViewProps = { /** Merge transaction ID to show in merge transaction flow */ mergeTransactionID?: string; - /** Whether a report with provided reportID is archived */ - isReportArchivedByID: (reportID?: string) => boolean; + /** Archived report identifiers keyed by report name value pairs */ + archivedReportsIDSet: ArchivedReportsIDSet; /** Whether the receipt view should fill the given space */ fillSpace?: boolean; @@ -100,7 +102,7 @@ function MoneyRequestReceiptView({ updatedTransaction, fillSpace = false, mergeTransactionID, - isReportArchivedByID, + archivedReportsIDSet, isDisplayedInWideRHP = false, }: MoneyRequestReceiptViewProps) { const styles = useThemeStyles(); @@ -143,6 +145,7 @@ function MoneyRequestReceiptView({ const isInvoice = isInvoiceReport(moneyRequestReport); const isChatReportArchived = useReportIsArchived(moneyRequestReport?.chatReportID); const {login: currentUserLogin} = useCurrentUserPersonalDetails(); + const isReportArchivedByID = useCallback((reportID?: string) => isReportArchivedByIDUtil(archivedReportsIDSet, reportID), [archivedReportsIDSet]); // Flags for allowing or disallowing editing an expense // Used for non-restricted fields such as: description, category, tag, billable, etc... diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 202bf170a146..d02ca239c423 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -16,6 +16,7 @@ import Text from '@components/Text'; import ViolationMessages from '@components/ViolationMessages'; import {useWideRHPState} from '@components/WideRHPContextProvider'; import useActiveRoute from '@hooks/useActiveRoute'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCardFeedErrors from '@hooks/useCardFeedErrors'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -844,6 +845,7 @@ function MoneyRequestView({ const shouldShowReport = !!parentReportID || (isFromMergeTransaction && !!reportNameToDisplay); const reportCopyValue = !canEditReport && reportNameToDisplay !== translate('common.none') ? reportNameToDisplay : undefined; const shouldShowCategoryAnalyzing = isCategoryBeingAnalyzed(updatedTransaction ?? transaction); + const archivedReportsIDSet = useArchivedReportsIDSet(); // In this case we want to use this value. The shouldUseNarrowLayout will always be true as this case is handled when we display ReportScreen in RHP. // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth @@ -865,7 +867,7 @@ function MoneyRequestView({ readonly={readonly} updatedTransaction={updatedTransaction} mergeTransactionID={mergeTransactionID} - isReportArchivedByID={isReportArchivedByID} + archivedReportsIDSet={archivedReportsIDSet} /> )} {isCustomUnitOutOfPolicy && isPerDiemRequest && ( diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 7d0e831f3100..3ceac2f27faf 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -16,6 +16,7 @@ function canUseLinkPreviews(): boolean { } function isBetaEnabled(beta: Beta, betas: OnyxEntry, betaConfiguration?: OnyxEntry): boolean { + return true; const hasAllBetasEnabled = canUseAllBetas(betas); const isFeatureEnabled = !!betas?.includes(beta); diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index aad012787d94..8f5daa044f13 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -76,7 +76,6 @@ import { isMoneyRequestReportPendingDeletion, isOneTransactionThread, isPolicyExpenseChat, - isReportArchivedByID as isReportArchivedByIDUtil, isReportTransactionThread, isTaskReport, isValidReportIDFromPath, @@ -177,7 +176,6 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchivedByID = useCallback((reportID?: string) => isReportArchivedByIDUtil(archivedReportsIDSet, reportID), [archivedReportsIDSet]); const parentReportAction = useParentReportAction(reportOnyx); @@ -1022,7 +1020,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr report={transactionThreadReport ?? report} fillSpace isDisplayedInWideRHP - isReportArchivedByID={isReportArchivedByID} + archivedReportsIDSet={archivedReportsIDSet} /> From 8779eb871ff637c8ef61062a578966cbc80f8270 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 8 Mar 2026 02:26:39 +0530 Subject: [PATCH 27/78] revert extra change in Permissions.ts --- src/libs/Permissions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 3ceac2f27faf..7d0e831f3100 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -16,7 +16,6 @@ function canUseLinkPreviews(): boolean { } function isBetaEnabled(beta: Beta, betas: OnyxEntry, betaConfiguration?: OnyxEntry): boolean { - return true; const hasAllBetasEnabled = canUseAllBetas(betas); const isFeatureEnabled = !!betas?.includes(beta); From 5e034c175209a35f4096b7c51c5c185dd338dd8d Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 8 Mar 2026 02:42:30 +0530 Subject: [PATCH 28/78] Test fixes --- tests/actions/TaskTest.ts | 32 ++++++++++--------- .../MoneyRequestReceiptViewTest.tsx | 1 - 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/tests/actions/TaskTest.ts b/tests/actions/TaskTest.ts index 97e69a53ccce..7f891a7c0753 100644 --- a/tests/actions/TaskTest.ts +++ b/tests/actions/TaskTest.ts @@ -854,6 +854,7 @@ describe('actions/Task', () => { describe('getNavigationUrlOnTaskDelete', () => { let doesReportHaveVisibleActionsSpy: jest.SpyInstance; let getMostRecentReportIDSpy: jest.SpyInstance; + const archivedReportsIDSet = new Set(); beforeEach(() => { doesReportHaveVisibleActionsSpy = jest.spyOn(ReportActionsUtils, 'doesReportHaveVisibleActions'); @@ -866,14 +867,14 @@ describe('actions/Task', () => { }); it('should return undefined when report is undefined', () => { - expect(getNavigationUrlOnTaskDelete(undefined, 'concierge_123')).toBeUndefined(); + expect(getNavigationUrlOnTaskDelete(undefined, 'concierge_123', archivedReportsIDSet)).toBeUndefined(); }); it('should return undefined when report has visible actions (should not delete)', () => { const taskReport = getFakeReport(); doesReportHaveVisibleActionsSpy.mockReturnValue(true); - expect(getNavigationUrlOnTaskDelete(taskReport, 'concierge_123')).toBeUndefined(); + expect(getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', archivedReportsIDSet)).toBeUndefined(); }); it('should return parent report route when report has parentReportID and no visible actions', () => { @@ -881,7 +882,7 @@ describe('actions/Task', () => { const taskReport = {...getFakeReport(), parentReportID}; doesReportHaveVisibleActionsSpy.mockReturnValue(false); - const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123'); + const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', archivedReportsIDSet); expect(result).toBe(`r/${parentReportID}`); }); @@ -891,9 +892,9 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(mostRecentReportID); - const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123'); + const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', archivedReportsIDSet); expect(result).toBe(`r/${mostRecentReportID}`); - expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, 'concierge_123'); + expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, 'concierge_123', archivedReportsIDSet); }); it('should pass conciergeReportID to getMostRecentReportID as fallback', () => { @@ -902,9 +903,9 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(conciergeReportID); - const result = getNavigationUrlOnTaskDelete(taskReport, conciergeReportID); + const result = getNavigationUrlOnTaskDelete(taskReport, conciergeReportID, archivedReportsIDSet); expect(result).toBe(`r/${conciergeReportID}`); - expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID); + expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID, archivedReportsIDSet); }); it('should return undefined when no parentReportID, no most recent report, and conciergeReportID is undefined', () => { @@ -912,7 +913,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(undefined); - expect(getNavigationUrlOnTaskDelete(taskReport, undefined)).toBeUndefined(); + expect(getNavigationUrlOnTaskDelete(taskReport, undefined, archivedReportsIDSet)).toBeUndefined(); }); }); @@ -920,6 +921,7 @@ describe('actions/Task', () => { let doesReportHaveVisibleActionsSpy: jest.SpyInstance; let getMostRecentReportIDSpy: jest.SpyInstance; const mockCurrentUserAccountID = 123; + const archivedReportsIDSet = new Set(); beforeEach(async () => { jest.clearAllMocks(); @@ -946,7 +948,7 @@ describe('actions/Task', () => { }); it('should return early when report is undefined', () => { - deleteTask(undefined, undefined, false, mockCurrentUserAccountID, false, undefined, 'concierge_123'); + deleteTask(undefined, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123'); // eslint-disable-next-line rulesdir/no-multiple-api-calls expect(API.write).not.toHaveBeenCalled(); @@ -987,7 +989,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(true); - deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, parentReportAction, 'concierge_123'); + deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, parentReportAction, archivedReportsIDSet, 'concierge_123'); // eslint-disable-next-line rulesdir/no-multiple-api-calls expect(API.write).toHaveBeenCalledWith( @@ -1031,7 +1033,7 @@ describe('actions/Task', () => { // No visible actions means the task report should be deleted doesReportHaveVisibleActionsSpy.mockReturnValue(false); - const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, 'concierge_123'); + const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123'); expect(result).toBe(`r/${parentReportID}`); expect(Navigation.goBack).toHaveBeenCalled(); @@ -1059,10 +1061,10 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(conciergeReportID); - const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, conciergeReportID); + const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, conciergeReportID); expect(result).toBe(`r/${conciergeReportID}`); - expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID); + expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID, archivedReportsIDSet); expect(Navigation.goBack).toHaveBeenCalled(); }); @@ -1094,7 +1096,7 @@ describe('actions/Task', () => { // Has visible actions, so should not navigate away doesReportHaveVisibleActionsSpy.mockReturnValue(true); - const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, 'concierge_123'); + const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123'); expect(result).toBeUndefined(); expect(Navigation.goBack).not.toHaveBeenCalled(); @@ -1121,7 +1123,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(undefined); - const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, undefined); + const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, undefined); // API.write should still be called // eslint-disable-next-line rulesdir/no-multiple-api-calls diff --git a/tests/ui/components/MoneyRequestReceiptViewTest.tsx b/tests/ui/components/MoneyRequestReceiptViewTest.tsx index 89fcf221186a..6a14647d407e 100644 --- a/tests/ui/components/MoneyRequestReceiptViewTest.tsx +++ b/tests/ui/components/MoneyRequestReceiptViewTest.tsx @@ -222,7 +222,6 @@ describe('MoneyRequestReceiptView', () => { report={testReport} fillSpace isDisplayedInWideRHP - isReportArchivedByID={() => false} /> , ); From c41af725f16abadea0f0fcd91e6fb27732046553 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:21:32 +0530 Subject: [PATCH 29/78] Updates --- .../TransactionPreviewContent.tsx | 12 +++++++----- src/hooks/useActionListContextValue.ts | 5 +++-- src/hooks/useArchivedReportsIDSet.ts | 10 +--------- src/hooks/useSelectedTransactionsActions.ts | 7 ++++--- src/hooks/useShowNotFoundPageInIOUStep.ts | 10 ++++++---- .../Search/SearchMoneyRequestReportPage.tsx | 4 +++- src/pages/inbox/ReportScreen.tsx | 2 +- src/pages/inbox/ReportScreenContext.ts | 4 +++- .../ReportActionCompose/ReportActionCompose.tsx | 17 +++++++++++++---- .../iou/request/step/IOURequestStepReport.tsx | 10 ++++++---- 10 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx index a661f29abb40..e6196ba33bed 100644 --- a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx +++ b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx @@ -1,5 +1,5 @@ import truncate from 'lodash/truncate'; -import React, {useMemo} from 'react'; +import React, {useContext, useMemo} from 'react'; import {View} from 'react-native'; import Animated from 'react-native-reanimated'; import Button from '@components/Button'; @@ -11,7 +11,6 @@ import UserInfoCellsWithArrow from '@components/SelectionListWithSections/Search import Text from '@components/Text'; import TransactionPreviewSkeletonView from '@components/TransactionPreviewSkeletonView'; import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle'; -import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useCardFeedErrors from '@hooks/useCardFeedErrors'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useEnvironment from '@hooks/useEnvironment'; @@ -32,12 +31,13 @@ import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {isMarkAsCashActionForTransaction} from '@libs/ReportPrimaryActionUtils'; import type {TransactionDetails} from '@libs/ReportUtils'; -import {canEditMoneyRequest, getTransactionDetails, isPolicyExpenseChat, isReportApproved, isSettled} from '@libs/ReportUtils'; +import {canEditMoneyRequest, getTransactionDetails, isPolicyExpenseChat, isReportApproved, isReportArchivedByID, isSettled} from '@libs/ReportUtils'; import StringUtils from '@libs/StringUtils'; import type {TranslationPathOrText} from '@libs/TransactionPreviewUtils'; import {createTransactionPreviewConditionals, getIOUPayerAndReceiver, getTransactionPreviewTextAndTranslationPaths} from '@libs/TransactionPreviewUtils'; import {isManagedCardTransaction as isCardTransactionUtils, isGPSDistanceRequest, isMapDistanceRequest, isScanning} from '@libs/TransactionUtils'; import ViolationsUtils, {filterReceiptViolations} from '@libs/Violations/ViolationsUtils'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -73,6 +73,7 @@ function TransactionPreviewContent({ const styles = useThemeStyles(); const {translate} = useLocalize(); const {environmentURL} = useEnvironment(); + const {archivedReportsIDSet} = useContext(ActionListContext); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`); const isParentPolicyExpenseChat = isPolicyExpenseChat(chatReport); @@ -89,7 +90,6 @@ function TransactionPreviewContent({ const isReportAPolicyExpenseChat = isPolicyExpenseChat(chatReport); const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.reportID)}`); const isChatReportArchived = useReportIsArchived(chatReport?.reportID); - const isReportArchivedByID = useIsReportArchivedByID(); const currentUserDetails = useCurrentUserPersonalDetails(); const currentUserEmail = currentUserDetails.email ?? ''; const currentUserLogin = currentUserDetails.login; @@ -125,7 +125,9 @@ function TransactionPreviewContent({ const filteredViolations = filterReceiptViolations(violations); const firstViolation = filteredViolations.at(0); const isIOUActionType = isMoneyRequestAction(action); - const canEdit = isIOUActionType && canEditMoneyRequest(action, isChatReportArchived, report, policy, transaction, isReportArchivedByID); + const canEdit = + isIOUActionType && + canEditMoneyRequest(action, isChatReportArchived, report, policy, transaction, (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID)); const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(report?.policyID)}`; const {personalCardsWithBrokenConnection} = useCardFeedErrors(); const connectionLink = getBrokenConnectionUrlToFixPersonalCard(personalCardsWithBrokenConnection, environmentURL); diff --git a/src/hooks/useActionListContextValue.ts b/src/hooks/useActionListContextValue.ts index ba73ff804cc0..fc176df77c10 100644 --- a/src/hooks/useActionListContextValue.ts +++ b/src/hooks/useActionListContextValue.ts @@ -1,13 +1,14 @@ import {useRef} from 'react'; import type {FlatList} from 'react-native'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import type {ActionListContextType, ScrollPosition} from '@pages/inbox/ReportScreenContext'; -function useActionListContextValue(): ActionListContextType { +function useActionListContextValue(archivedReportsIDSet: ArchivedReportsIDSet): ActionListContextType { const flatListRef = useRef(null); const scrollPositionRef = useRef({}); const scrollOffsetRef = useRef(0); - return {flatListRef, scrollPositionRef, scrollOffsetRef}; + return {flatListRef, scrollPositionRef, scrollOffsetRef, archivedReportsIDSet}; } export default useActionListContextValue; diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts index b2d80abe4512..cc695e2a4c30 100644 --- a/src/hooks/useArchivedReportsIDSet.ts +++ b/src/hooks/useArchivedReportsIDSet.ts @@ -1,6 +1,5 @@ -import {useCallback} from 'react'; import type {OnyxCollection} from 'react-native-onyx'; -import {isArchivedReport, isReportArchivedByID} from '@libs/ReportUtils'; +import {isArchivedReport} from '@libs/ReportUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -35,11 +34,4 @@ function useArchivedReportsIdSet(): ArchivedReportsIDSet { return new Set(archivedReportIds); } -function useIsReportArchivedByID() { - const archivedReportsIdSet = useArchivedReportsIdSet(); - - return useCallback((reportID?: string) => isReportArchivedByID(archivedReportsIdSet, reportID), [archivedReportsIdSet]); -} - export default useArchivedReportsIdSet; -export {useIsReportArchivedByID}; diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index 92509f3ba303..820bb8b04b32 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -21,6 +21,7 @@ import { canUserPerformWriteAction as canUserPerformWriteActionReportUtils, getReportOrDraftReport, isInvoiceReport, + isReportArchivedByID, isMoneyRequestReport as isMoneyRequestReportUtils, isTrackExpenseReport, } from '@libs/ReportUtils'; @@ -33,7 +34,7 @@ import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import useAllTransactions from './useAllTransactions'; -import {useIsReportArchivedByID} from './useArchivedReportsIDSet'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useDeleteTransactions from './useDeleteTransactions'; import useDuplicateTransactionsAndViolations from './useDuplicateTransactionsAndViolations'; @@ -88,7 +89,7 @@ function useSelectedTransactionsActions({ const expensifyIcons = useMemoizedLazyExpensifyIcons(['Stopwatch', 'Trashcan', 'ArrowRight', 'Table', 'DocumentMerge', 'Export', 'ArrowCollapse', 'ArrowSplit', 'ThumbsDown']); const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(selectedTransactionIDs); const isReportArchived = useReportIsArchived(report?.reportID); - const isReportArchivedByID = useIsReportArchivedByID(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const {deleteTransactions} = useDeleteTransactions({report, reportActions, policy}); const {login, accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); const selectedTransactionsList = selectedTransactionIDs.reduce((acc, transactionID) => { @@ -340,7 +341,7 @@ function useSelectedTransactionsActions({ undefined, undefined, undefined, - isReportArchivedByID, + (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID), ); return canMoveExpense; }); diff --git a/src/hooks/useShowNotFoundPageInIOUStep.ts b/src/hooks/useShowNotFoundPageInIOUStep.ts index 6a77a2e09fea..2824925f7fbb 100644 --- a/src/hooks/useShowNotFoundPageInIOUStep.ts +++ b/src/hooks/useShowNotFoundPageInIOUStep.ts @@ -2,13 +2,13 @@ import {useCallback, useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {isMoneyRequestAction} from '@libs/ReportActionsUtils'; -import {canEditMoneyRequest} from '@libs/ReportUtils'; +import {canEditMoneyRequest, isReportArchivedByID} from '@libs/ReportUtils'; import {areRequiredFieldsEmpty} from '@libs/TransactionUtils'; import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {OnyxInputOrEntry, Report, ReportAction, ReportActions, Transaction} from '@src/types/onyx'; -import {useIsReportArchivedByID} from './useArchivedReportsIDSet'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useOnyx from './useOnyx'; /** @@ -26,7 +26,7 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor const [session] = useOnyx(ONYXKEYS.SESSION); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(transaction?.reportID)}`); - const isReportArchived = useIsReportArchivedByID(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const reportActionsReportID = useMemo(() => { let actionsReportID; @@ -64,7 +64,9 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor } else if (isSplitExpense) { shouldShowNotFoundPage = !canEditSplitExpense; } else { - shouldShowNotFoundPage = !isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, false, iouReport, policy, transaction, isReportArchived); + shouldShowNotFoundPage = + !isMoneyRequestAction(reportAction) || + !canEditMoneyRequest(reportAction, false, iouReport, policy, transaction, (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID)); } } diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index d2d012142d97..a29d5fac2642 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -11,6 +11,7 @@ import {useSearchStateContext} from '@components/Search/SearchContext'; import useShowSuperWideRHPVersion from '@components/WideRHPContextProvider/useShowSuperWideRHPVersion'; import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useIsReportReadyToDisplay from '@hooks/useIsReportReadyToDisplay'; import useNetwork from '@hooks/useNetwork'; @@ -145,7 +146,8 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute, isReportArchived); - const actionListValue = useActionListContextValue(); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const actionListValue = useActionListContextValue(archivedReportsIDSet); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index e49983db0348..cc388f35c0c3 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -849,7 +849,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr }; }, [report?.reportID, didSubscribeToReportLeavingEvents, reportIDFromRoute, report?.pendingFields, currentUserAccountID]); - const actionListValue = useActionListContextValue(); + const actionListValue = useActionListContextValue(archivedReportsIDSet); // This helps in tracking from the moment 'route' triggers useMemo until isLoadingInitialReportActions becomes true. It prevents blinking when loading reportActions from cache. useEffect(() => { diff --git a/src/pages/inbox/ReportScreenContext.ts b/src/pages/inbox/ReportScreenContext.ts index 50b1e3e707f9..83f6dd9d1ddc 100644 --- a/src/pages/inbox/ReportScreenContext.ts +++ b/src/pages/inbox/ReportScreenContext.ts @@ -2,6 +2,7 @@ import type {RefObject, SyntheticEvent} from 'react'; import {createContext} from 'react'; // eslint-disable-next-line no-restricted-imports import type {FlatList, GestureResponderEvent, Text, View} from 'react-native'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; type ReactionListAnchor = View | Text | HTMLDivElement | null; @@ -21,10 +22,11 @@ type ActionListContextType = { flatListRef: FlatListRefType; scrollPositionRef: RefObject; scrollOffsetRef: RefObject; + archivedReportsIDSet: ArchivedReportsIDSet; }; type ReactionListContextType = RefObject | null; -const ActionListContext = createContext({flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}}); +const ActionListContext = createContext({flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}, archivedReportsIDSet: new Set()}); const ReactionListContext = createContext(null); export {ActionListContext, ReactionListContext}; diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index 2c44c1aa11ad..8f78a9e8c38e 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -18,7 +18,6 @@ import OfflineIndicator from '@components/OfflineIndicator'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import useAncestors from '@hooks/useAncestors'; -import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHandleExceedMaxCommentLength from '@hooks/useHandleExceedMaxCommentLength'; import useHandleExceedMaxTaskTitleLength from '@hooks/useHandleExceedMaxTaskTitleLength'; @@ -52,6 +51,7 @@ import { isConciergeChatReport, isGroupChat, isInvoiceReport, + isReportArchivedByID, isReportApproved, isReportTransactionThread, isSettled, @@ -167,7 +167,7 @@ function ReportActionCompose({ const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`); const ancestors = useAncestors(transactionThreadReport ?? report); - const {scrollOffsetRef} = useContext(ActionListContext); + const {scrollOffsetRef, archivedReportsIDSet} = useContext(ActionListContext); /** * Updates the Highlight state of the composer @@ -219,7 +219,6 @@ function ReportActionCompose({ const userBlockedFromConcierge = useMemo(() => isBlockedFromConciergeUserAction(blockedFromConcierge), [blockedFromConcierge]); const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); const isReportArchived = useReportIsArchived(report?.reportID); - const isReportArchivedByID = useIsReportArchivedByID(); const isConciergeChat = useMemo(() => isConciergeChatReport(report), [report]); const isTransactionThreadView = useMemo(() => isReportTransactionThread(report), [report]); @@ -243,7 +242,17 @@ function ReportActionCompose({ const canUserPerformWriteAction = !!canUserPerformWriteActionReportUtils(report, isReportArchived); const canEditReceipt = canUserPerformWriteAction && - canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, undefined, undefined, undefined, undefined, undefined, undefined, isReportArchivedByID) && + canEditFieldOfMoneyRequest( + parentReportAction, + CONST.EDIT_REQUEST_FIELD.RECEIPT, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID), + ) && !transaction?.receipt?.isTestDriveReceipt; const shouldAddOrReplaceReceipt = (isTransactionThreadView || isSingleTransactionView) && canEditReceipt; diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index a3ffcf2dc8e1..321ea3a84b38 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -4,7 +4,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {usePersonalDetails, useSession} from '@components/OnyxListItemProvider'; import {useSearchActionsContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionListWithSections/types'; -import {useIsReportArchivedByID} from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -19,7 +19,7 @@ import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Tran import Navigation from '@libs/Navigation/Navigation'; import {getPerDiemCustomUnit, getPolicyByCustomUnitID} from '@libs/PolicyUtils'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; -import {getPersonalDetailsForAccountID, getReportOrDraftReport, hasViolations as hasViolationsReportUtils, isPolicyExpenseChat, isReportOutstanding} from '@libs/ReportUtils'; +import {getPersonalDetailsForAccountID, getReportOrDraftReport, hasViolations as hasViolationsReportUtils, isPolicyExpenseChat, isReportArchivedByID, isReportOutstanding} from '@libs/ReportUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import {isPerDiemRequest} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; @@ -48,12 +48,14 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {backTo, action, iouType, transactionID, reportID: reportIDFromRoute, reportActionID} = route.params; const {translate, toLocaleDigit} = useLocalize(); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const isReportArchived = useIsReportArchivedByID(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); const participantReportID = transaction?.participants?.at(0)?.reportID; const participantReport = Object.values(allReports ?? {}).find((report) => report?.reportID === participantReportID); - const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID, isReportArchived)) || isUnreported; + const shouldUseTransactionReport = + (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID, (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID))) || + isUnreported; const outstandingReportID = isPolicyExpenseChat(participantReport) ? participantReport?.iouReportID : participantReportID; const selectedReportID = shouldUseTransactionReport ? transactionReport?.reportID : outstandingReportID; const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); From fbcdb499e856b72b1748b80433fa8ddef9b47a21 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Mar 2026 15:26:54 +0530 Subject: [PATCH 30/78] Lint and TS fixes --- .../TransactionPreviewContent.tsx | 4 +--- src/hooks/useSelectedTransactionsActions.ts | 2 +- src/libs/actions/Report/index.ts | 2 +- src/pages/ReportDetailsPage.tsx | 2 +- .../ReportActionCompose/ReportActionCompose.tsx | 14 +++----------- .../iou/request/step/IOURequestStepReport.tsx | 12 +++++++++--- tests/actions/ReportTest.ts | 2 -- 7 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx index d5e818af7d82..aa1e1fe0029c 100644 --- a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx +++ b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx @@ -127,9 +127,7 @@ function TransactionPreviewContent({ const filteredViolations = filterReceiptViolations(violations); const firstViolation = filteredViolations.at(0); const isIOUActionType = isMoneyRequestAction(action); - const canEdit = - isIOUActionType && - canEditMoneyRequest(action, isChatReportArchived, report, policy, transaction, (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID)); + const canEdit = isIOUActionType && canEditMoneyRequest(action, isChatReportArchived, report, policy, transaction, (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID)); const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(report?.policyID)}`; const {personalCardsWithBrokenConnection} = useCardFeedErrors(); const connectionLink = getBrokenConnectionUrlToFixPersonalCard(personalCardsWithBrokenConnection, environmentURL); diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index 451687c3b1d5..cf2b77e17c4b 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -21,8 +21,8 @@ import { canUserPerformWriteAction as canUserPerformWriteActionReportUtils, getReportOrDraftReport, isInvoiceReport, - isReportArchivedByID, isMoneyRequestReport as isMoneyRequestReportUtils, + isReportArchivedByID, isTrackExpenseReport, } from '@libs/ReportUtils'; import {getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index d1e292fbdc03..130e5fc87157 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -4428,8 +4428,8 @@ function leaveRoom( currentUserAccountID: number, conciergeReportID: string | undefined, introSelected: OnyxEntry, - archivedReportsIDSet: ArchivedReportsIDSet = new Set(), isWorkspaceMemberLeavingWorkspaceRoom = false, + archivedReportsIDSet: ArchivedReportsIDSet = new Set(), ) { const reportID = report.reportID; const isChatThread = isChatThreadReportUtils(report); diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 3bb6861d05c5..435b128dbfa3 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -336,7 +336,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail } const isWorkspaceMemberLeavingWorkspaceRoom = isWorkspaceMemberLeavingWorkspaceRoomUtil(report, isPolicyEmployee, isPolicyAdmin); - leaveRoom(report, currentUserPersonalDetails.accountID, conciergeReportID, introSelected, archivedReportsIDSet, isWorkspaceMemberLeavingWorkspaceRoom); + leaveRoom(report, currentUserPersonalDetails.accountID, conciergeReportID, introSelected, isWorkspaceMemberLeavingWorkspaceRoom, archivedReportsIDSet); }, [isRootGroupChat, isPolicyEmployee, isPolicyAdmin, quickAction?.chatReportID, report, currentUserPersonalDetails.accountID, conciergeReportID, introSelected, archivedReportsIDSet]); const showLastMemberLeavingModal = useCallback(async () => { diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index 8562d0d481f5..f9b4ad08f064 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -53,8 +53,8 @@ import { isConciergeChatReport, isGroupChat, isInvoiceReport, - isReportArchivedByID, isReportApproved, + isReportArchivedByID, isReportTransactionThread, isSettled, temporary_getMoneyRequestOptions, @@ -245,16 +245,8 @@ function ReportActionCompose({ const canUserPerformWriteAction = !!canUserPerformWriteActionReportUtils(report, isReportArchived); const canEditReceipt = canUserPerformWriteAction && - canEditFieldOfMoneyRequest( - parentReportAction, - CONST.EDIT_REQUEST_FIELD.RECEIPT, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID), + canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, undefined, undefined, undefined, undefined, undefined, undefined, (reportID) => + isReportArchivedByID(archivedReportsIDSet, reportID), ) && !transaction?.receipt?.isTestDriveReceipt; const shouldAddOrReplaceReceipt = (isTransactionThreadView || isSingleTransactionView) && canEditReceipt; diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index a7c895dbe9d2..b2652a366e3f 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -20,7 +20,14 @@ import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Tran import Navigation from '@libs/Navigation/Navigation'; import {getPerDiemCustomUnit, getPolicyByCustomUnitID} from '@libs/PolicyUtils'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; -import {getPersonalDetailsForAccountID, getReportOrDraftReport, hasViolations as hasViolationsReportUtils, isPolicyExpenseChat, isReportArchivedByID, isReportOutstanding} from '@libs/ReportUtils'; +import { + getPersonalDetailsForAccountID, + getReportOrDraftReport, + hasViolations as hasViolationsReportUtils, + isPolicyExpenseChat, + isReportArchivedByID, + isReportOutstanding, +} from '@libs/ReportUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import {isPerDiemRequest, isTimeRequest as isTimeRequestUtil} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; @@ -55,8 +62,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const participantReportID = transaction?.participants?.at(0)?.reportID; const participantReport = Object.values(allReports ?? {}).find((report) => report?.reportID === participantReportID); const shouldUseTransactionReport = - (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID, (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID))) || - isUnreported; + (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID, (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID))) || isUnreported; const outstandingReportID = isPolicyExpenseChat(participantReport) ? participantReport?.iouReportID : participantReportID; const selectedReportID = shouldUseTransactionReport ? transactionReport?.reportID : outstandingReportID; const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 4ac9380180f6..9438f1616ba4 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -4804,8 +4804,6 @@ describe('actions/Report', () => { // Shared test constants for leave functions const TEST_CONCIERGE_REPORT_ID = '999'; const TEST_CURRENT_USER_ACCOUNT_ID = 1; - const TEST_ARCHIVED_REPORTS_ID_SET = new Set(); - describe('leaveGroupChat', () => { const GROUP_CHAT_REPORT_ID = '1001'; From 6e1041d3abb95aaa0457f452e4ce44aaae4494f1 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Mar 2026 16:18:43 +0530 Subject: [PATCH 31/78] remove the now unused Onyx.connect field --- src/libs/ReportUtils.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 88894487ef2f..a9b935ff9356 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1224,18 +1224,6 @@ Onyx.connect({ }, }); -let allReportsViolations: OnyxCollection; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT_VIOLATIONS, - waitForCollectionCallback: true, - callback: (value) => { - if (!value) { - return; - } - allReportsViolations = value; - }, -}); - let allReportNameValuePair: OnyxCollection; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, From 0610b57d4abb3aa7e3978169a5538bcdfc11cee3 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Mar 2026 16:31:22 +0530 Subject: [PATCH 32/78] Fixes --- src/libs/ReportUtils.ts | 12 ------------ .../ReportActionCompose/ReportActionCompose.tsx | 4 ++-- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a9b935ff9356..17569527cef3 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1224,18 +1224,6 @@ Onyx.connect({ }, }); -let allReportNameValuePair: OnyxCollection; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, - waitForCollectionCallback: true, - callback: (value) => { - if (!value) { - return; - } - allReportNameValuePair = value; - }, -}); - let onboarding: OnyxEntry; Onyx.connect({ key: ONYXKEYS.NVP_ONBOARDING, diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index f9b4ad08f064..bf32ce11015f 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -245,8 +245,8 @@ function ReportActionCompose({ const canUserPerformWriteAction = !!canUserPerformWriteActionReportUtils(report, isReportArchived); const canEditReceipt = canUserPerformWriteAction && - canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, undefined, undefined, undefined, undefined, undefined, undefined, (reportID) => - isReportArchivedByID(archivedReportsIDSet, reportID), + canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, undefined, undefined, undefined, undefined, undefined, undefined, (targetReportID) => + isReportArchivedByID(archivedReportsIDSet, targetReportID), ) && !transaction?.receipt?.isTestDriveReceipt; const shouldAddOrReplaceReceipt = (isTransactionThreadView || isSingleTransactionView) && canEditReceipt; From f78046c983e63fe82c8363ffed05cab466756602 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:05:07 +0530 Subject: [PATCH 33/78] Address codex comment --- src/components/MoneyReportHeader.tsx | 20 ++++++++++++-- src/components/MoneyRequestHeader.tsx | 9 +++++-- .../ReportActionItem/MoneyRequestView.tsx | 6 ++++- src/libs/ReportSecondaryActionUtils.ts | 27 +++++++++++++++++-- src/libs/ReportUtils.ts | 3 ++- tests/unit/ReportUtilsTest.ts | 1 + 6 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 29bc45586935..50b55e375043 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -110,6 +110,7 @@ import { isOpenExpenseReport, isOpenReport, isProcessingReport, + isReportArchivedByID, isReportOwner, isSelfDM, navigateOnDeleteExpense, @@ -161,6 +162,7 @@ import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import type IconAsset from '@src/types/utils/IconAsset'; import ActivityIndicator from './ActivityIndicator'; import AnimatedSubmitButton from './AnimatedSubmitButton'; @@ -481,6 +483,8 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa const isPayAtEndExpense = isPayAtEndExpenseTransactionUtils(transaction); const isArchivedReport = useReportIsArchived(moneyRequestReport?.reportID); const isChatReportArchived = useReportIsArchived(chatReport?.reportID); + const {archivedReportsIDSet} = useContext(ActionListContext); + const isReportArchived = useCallback((reportID?: string) => isReportArchivedByID(archivedReportsIDSet, reportID), [archivedReportsIDSet]); const canMoveSingleExpense = useMemo(() => { if (nonPendingDeleteTransactions.length !== 1) { @@ -493,12 +497,22 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa } const iouReportAction = getIOUActionForTransactionID(reportActions, transactionToMove.transactionID); - const canMoveExpense = canEditFieldOfMoneyRequest(iouReportAction, CONST.EDIT_REQUEST_FIELD.REPORT, undefined, isChatReportArchived, outstandingReportsByPolicyID); + const canMoveExpense = canEditFieldOfMoneyRequest( + iouReportAction, + CONST.EDIT_REQUEST_FIELD.REPORT, + undefined, + isChatReportArchived, + outstandingReportsByPolicyID, + undefined, + undefined, + undefined, + isReportArchived, + ); const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(moneyRequestReport, isChatReportArchived); return canMoveExpense && canUserPerformWriteAction; - }, [nonPendingDeleteTransactions, reportActions, isChatReportArchived, outstandingReportsByPolicyID, moneyRequestReport]); + }, [nonPendingDeleteTransactions, reportActions, isChatReportArchived, outstandingReportsByPolicyID, moneyRequestReport, isReportArchived]); const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`, {selector: getArchiveReason}); @@ -1540,6 +1554,7 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa policies, outstandingReportsByPolicyID, isChatReportArchived, + isReportArchived, }); }, [ moneyRequestReport, @@ -1557,6 +1572,7 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa isChatReportArchived, bankAccountList, outstandingReportsByPolicyID, + isReportArchived, ]); const secondaryExportActions = useMemo(() => { diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 7aa751b7ddc2..b0b151c11c5c 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -3,7 +3,7 @@ import {shouldFailAllRequestsSelector} from '@selectors/Network'; import {hasSeenTourSelector} from '@selectors/Onboarding'; import {validTransactionDraftsSelector} from '@selectors/TransactionDraft'; import type {ReactNode} from 'react'; -import React, {useCallback, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useContext, useMemo, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import type {ValueOf} from 'type-fest'; import useConfirmModal from '@hooks/useConfirmModal'; @@ -46,6 +46,7 @@ import { isDM, isExpenseReport, isOpenReport, + isReportArchivedByID, isSelfDM, navigateToDetailsPage, rejectMoneyRequestReason, @@ -76,6 +77,7 @@ import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type {Transaction} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import BrokenConnectionDescription from './BrokenConnectionDescription'; import Button from './Button'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; @@ -197,6 +199,8 @@ function MoneyRequestHeader({reportID: reportIDProp, onBackButtonPress}: MoneyRe const shouldDisplayTransactionNavigation = !!(reportID && isReportInRHP); const isParentReportArchived = useReportIsArchived(report?.parentReportID); const {iouReport, chatReport: chatIOUReport, isChatIOUReportArchived} = useGetIOUReportFromReportAction(parentReportAction); + const {archivedReportsIDSet} = useContext(ActionListContext); + const isReportArchived = useCallback((targetReportID?: string) => isReportArchivedByID(archivedReportsIDSet, targetReportID), [archivedReportsIDSet]); const hasPendingRTERViolation = hasPendingRTERViolationTransactionUtils(transactionViolations); const hasCustomUnitOutOfPolicyViolation = hasCustomUnitOutOfPolicyViolationTransactionUtils(transactionViolations); @@ -441,8 +445,9 @@ function MoneyRequestHeader({reportID: reportIDProp, onBackButtonPress}: MoneyRe report, outstandingReportsByPolicyID, isChatIOUReportArchived, + isReportArchived, ); - }, [parentReport, transaction, parentReportAction, currentUserLogin, policy, report, originalTransaction, accountID, outstandingReportsByPolicyID, isChatIOUReportArchived]); + }, [parentReport, transaction, parentReportAction, currentUserLogin, policy, report, originalTransaction, accountID, outstandingReportsByPolicyID, isChatIOUReportArchived, isReportArchived]); const dismissModalAndUpdateUseHold = () => { setIsHoldEducationalModalVisible(false); diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 4c8291c90a7a..fadbd10c5987 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -1,5 +1,5 @@ import {Str} from 'expensify-common'; -import React, {useMemo, useState} from 'react'; +import React, {useContext, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; @@ -71,6 +71,7 @@ import { isInvoiceReport, isOpenReport, isPaidGroupPolicy, + isReportArchivedByID, isReportApproved, isReportInGroupPolicy, isSettled as isSettledReportUtils, @@ -113,6 +114,7 @@ import { import {isInvalidMerchantValue} from '@libs/ValidationUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import Navigation from '@navigation/Navigation'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import AnimatedEmptyStateBackground from '@pages/inbox/report/AnimatedEmptyStateBackground'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -179,6 +181,7 @@ function MoneyRequestView({ const {getCurrencySymbol} = useCurrencyListActions(); const {getReportRHPActiveRoute} = useActiveRoute(); const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH); + const {archivedReportsIDSet} = useContext(ActionListContext); const {currentSearchResults} = useSearchStateContext(); const reportAttributes = useReportAttributes(); @@ -375,6 +378,7 @@ function MoneyRequestView({ transaction, moneyRequestReport, policy, + (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID), ) && (!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy) || (isExpenseUnreported && !!perDiemOriginalPolicy)); diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index 0cb1ade99857..6b7c9321bbc7 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -855,6 +855,7 @@ function getSecondaryReportActions({ policies, outstandingReportsByPolicyID, isChatReportArchived = false, + isReportArchived, }: { currentUserLogin: string; currentUserAccountID: number; @@ -872,6 +873,7 @@ function getSecondaryReportActions({ outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; canUseNewDotSplits?: boolean; isChatReportArchived?: boolean; + isReportArchived?: (reportID?: string) => boolean; }): Array> { const options: Array> = []; @@ -985,7 +987,17 @@ function getSecondaryReportActions({ const transaction = reportTransactions.at(0); if (transaction?.transactionID) { const iouReportAction = getIOUActionForTransactionID(reportActions, transaction.transactionID); - const canMoveExpense = canEditFieldOfMoneyRequest(iouReportAction, CONST.EDIT_REQUEST_FIELD.REPORT, undefined, isChatReportArchived, outstandingReportsByPolicyID); + const canMoveExpense = canEditFieldOfMoneyRequest( + iouReportAction, + CONST.EDIT_REQUEST_FIELD.REPORT, + undefined, + isChatReportArchived, + outstandingReportsByPolicyID, + undefined, + undefined, + undefined, + isReportArchived, + ); const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(report, isChatReportArchived); if (canMoveExpense && canUserPerformWriteAction) { @@ -1046,6 +1058,7 @@ function getSecondaryTransactionThreadActions( transactionThreadReport?: OnyxEntry, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, isChatReportArchived?: boolean, + isReportArchived?: (reportID?: string) => boolean, ): Array> { const options: Array> = []; @@ -1076,7 +1089,17 @@ function getSecondaryTransactionThreadActions( if ( reportTransaction?.transactionID && reportAction && - canEditFieldOfMoneyRequest(reportAction, CONST.EDIT_REQUEST_FIELD.REPORT, undefined, isChatReportArchived, outstandingReportsByPolicyID) && + canEditFieldOfMoneyRequest( + reportAction, + CONST.EDIT_REQUEST_FIELD.REPORT, + undefined, + isChatReportArchived, + outstandingReportsByPolicyID, + undefined, + undefined, + undefined, + isReportArchived, + ) && canUserPerformWriteActionReportUtils(parentReport, isChatReportArchived) ) { options.push(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 17569527cef3..e412eaf9d5dc 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -11578,10 +11578,11 @@ function isReportOutstanding( return false; } const reportNameValuePairsKey = `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport.reportID}`; + const reportNameValuePairs = typeof reportNameValuePairsOrIsReportArchived === 'function' ? undefined : reportNameValuePairsOrIsReportArchived; const isArchived = typeof reportNameValuePairsOrIsReportArchived === 'function' ? reportNameValuePairsOrIsReportArchived(iouReport.reportID) - : isArchivedReport(reportNameValuePairsOrIsReportArchived?.[reportNameValuePairsKey]); + : isArchivedReport(reportNameValuePairs?.[reportNameValuePairsKey]); if (isArchived) { return false; } diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index e75bb5c52458..61f8d35cb802 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -7564,6 +7564,7 @@ describe('ReportUtils', () => { const isReportArchived = (reportID?: string) => reportID === report.reportID; expect(isReportOutstanding(report, policy.id, isReportArchived)).toBe(false); }); + }); describe('canAddTransaction', () => { From 78ec6591e826fcdced3e936307aa61070633cb68 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:59:39 +0530 Subject: [PATCH 34/78] Lint and prettier fixes --- src/components/MoneyReportHeader.tsx | 2 +- src/components/MoneyRequestHeader.tsx | 16 +++++------ src/libs/ReportSecondaryActionUtils.ts | 38 +++++++++++++++++--------- tests/unit/ReportUtilsTest.ts | 1 - 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 50b55e375043..974273e79a6a 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -137,6 +137,7 @@ import { shouldShowBrokenConnectionViolationForMultipleTransactions, } from '@libs/TransactionUtils'; import type {ExportType} from '@pages/inbox/report/ReportDetailsExportPage'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import variables from '@styles/variables'; import { approveMoneyRequest, @@ -162,7 +163,6 @@ import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import type IconAsset from '@src/types/utils/IconAsset'; import ActivityIndicator from './ActivityIndicator'; import AnimatedSubmitButton from './AnimatedSubmitButton'; diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index b0b151c11c5c..9417203a58d8 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -434,19 +434,19 @@ function MoneyRequestHeader({reportID: reportIDProp, onBackButtonPress}: MoneyRe if (!transaction || !parentReportAction || !parentReport) { return []; } - return getSecondaryTransactionThreadActions( - currentUserLogin ?? '', - accountID, + return getSecondaryTransactionThreadActions({ + currentUserLogin: currentUserLogin ?? '', + currentUserAccountID: accountID, parentReport, - transaction, - parentReportAction, + reportTransaction: transaction, + reportAction: parentReportAction, originalTransaction, policy, - report, + transactionThreadReport: report, outstandingReportsByPolicyID, - isChatIOUReportArchived, + isChatReportArchived: isChatIOUReportArchived, isReportArchived, - ); + }); }, [parentReport, transaction, parentReportAction, currentUserLogin, policy, report, originalTransaction, accountID, outstandingReportsByPolicyID, isChatIOUReportArchived, isReportArchived]); const dismissModalAndUpdateUseHold = () => { diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index 6b7c9321bbc7..a314596350e9 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -1047,19 +1047,31 @@ function getSecondaryExportReportActions( return options; } -function getSecondaryTransactionThreadActions( - currentUserLogin: string, - currentUserAccountID: number, - parentReport: Report, - reportTransaction: Transaction, - reportAction: ReportAction | undefined, - originalTransaction: OnyxEntry, - policy: OnyxEntry, - transactionThreadReport?: OnyxEntry, - outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, - isChatReportArchived?: boolean, - isReportArchived?: (reportID?: string) => boolean, -): Array> { +function getSecondaryTransactionThreadActions({ + currentUserLogin, + currentUserAccountID, + parentReport, + reportTransaction, + reportAction, + originalTransaction, + policy, + transactionThreadReport, + outstandingReportsByPolicyID, + isChatReportArchived, + isReportArchived, +}: { + currentUserLogin: string; + currentUserAccountID: number; + parentReport: Report; + reportTransaction: Transaction; + reportAction: ReportAction | undefined; + originalTransaction: OnyxEntry; + policy: OnyxEntry; + transactionThreadReport?: OnyxEntry; + outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; + isChatReportArchived?: boolean; + isReportArchived?: (reportID?: string) => boolean; +}): Array> { const options: Array> = []; if (!!reportAction && isHoldActionForTransaction(parentReport, reportTransaction, reportAction, policy)) { diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 61f8d35cb802..e75bb5c52458 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -7564,7 +7564,6 @@ describe('ReportUtils', () => { const isReportArchived = (reportID?: string) => reportID === report.reportID; expect(isReportOutstanding(report, policy.id, isReportArchived)).toBe(false); }); - }); describe('canAddTransaction', () => { From c61b18fd1dab741c9aead3e2b5c276b12c51a27d Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Mar 2026 19:17:13 +0530 Subject: [PATCH 35/78] Lint and prettier fixes --- src/components/MoneyRequestHeader.tsx | 16 ++++++++++++++-- .../ReportActionItem/MoneyRequestView.tsx | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 9417203a58d8..a64f44d8cb7b 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -67,6 +67,7 @@ import { removeSettledAndApprovedTransactions, shouldShowBrokenConnectionViolation as shouldShowBrokenConnectionViolationTransactionUtils, } from '@libs/TransactionUtils'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import variables from '@styles/variables'; import {dismissRejectUseExplanation} from '@userActions/IOU'; import {setDeleteTransactionNavigateBackUrl} from '@userActions/Report'; @@ -77,7 +78,6 @@ import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type {Transaction} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; -import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import BrokenConnectionDescription from './BrokenConnectionDescription'; import Button from './Button'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; @@ -447,7 +447,19 @@ function MoneyRequestHeader({reportID: reportIDProp, onBackButtonPress}: MoneyRe isChatReportArchived: isChatIOUReportArchived, isReportArchived, }); - }, [parentReport, transaction, parentReportAction, currentUserLogin, policy, report, originalTransaction, accountID, outstandingReportsByPolicyID, isChatIOUReportArchived, isReportArchived]); + }, [ + parentReport, + transaction, + parentReportAction, + currentUserLogin, + policy, + report, + originalTransaction, + accountID, + outstandingReportsByPolicyID, + isChatIOUReportArchived, + isReportArchived, + ]); const dismissModalAndUpdateUseHold = () => { setIsHoldEducationalModalVisible(false); diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index fadbd10c5987..e7394b817721 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -71,8 +71,8 @@ import { isInvoiceReport, isOpenReport, isPaidGroupPolicy, - isReportArchivedByID, isReportApproved, + isReportArchivedByID, isReportInGroupPolicy, isSettled as isSettledReportUtils, isTrackExpenseReportNew, @@ -114,8 +114,8 @@ import { import {isInvalidMerchantValue} from '@libs/ValidationUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import Navigation from '@navigation/Navigation'; -import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import AnimatedEmptyStateBackground from '@pages/inbox/report/AnimatedEmptyStateBackground'; +import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; From e383c27ddeba3ec318fdb4b4299e615fd6231dec Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 21 Mar 2026 19:42:09 +0530 Subject: [PATCH 36/78] Update tests --- tests/unit/ReportSecondaryActionUtilsTest.ts | 144 ++++++++++++++----- 1 file changed, 112 insertions(+), 32 deletions(-) diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index 543758aaa438..d4824882bc77 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -1729,7 +1729,15 @@ describe('getSecondaryAction', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, {} as Transaction, undefined, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: {} as Transaction, + reportAction: undefined, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.DELETE)).toBe(true); }); @@ -2783,7 +2791,17 @@ describe('getSecondaryTransactionThreadActions', () => { const policy = {} as unknown as Policy; const result = [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.VIEW_DETAILS]; - expect(getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, {} as Transaction, undefined, {} as Transaction, policy)).toEqual(result); + expect( + getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: {} as Transaction, + reportAction: undefined, + originalTransaction: {} as Transaction, + policy, + }), + ).toEqual(result); }); it('includes HOLD option', () => { @@ -2803,7 +2821,15 @@ describe('getSecondaryTransactionThreadActions', () => { jest.spyOn(ReportUtils, 'canHoldUnholdReportAction').mockReturnValueOnce({canHoldRequest: true, canUnholdRequest: true}); jest.spyOn(ReportUtils, 'isAwaitingFirstLevelApproval').mockReturnValueOnce(true); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.HOLD)).toBe(true); }); @@ -2820,12 +2846,30 @@ describe('getSecondaryTransactionThreadActions', () => { } as unknown as Transaction; jest.spyOn(ReportUtils, 'isHoldCreator').mockReturnValue(false); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, undefined, {} as Transaction, policy, transactionThreadReport); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: undefined, + originalTransaction: {} as Transaction, + policy, + transactionThreadReport, + }); expect(result).toContain(CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD); // Do not show if admin is the holder jest.spyOn(ReportUtils, 'isHoldCreator').mockReturnValue(true); - const result2 = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, undefined, {} as Transaction, policy, transactionThreadReport); + const result2 = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: undefined, + originalTransaction: {} as Transaction, + policy, + transactionThreadReport, + }); expect(result2).not.toContain(CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD); }); @@ -2842,7 +2886,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, {} as Transaction, undefined, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: {} as Transaction, + reportAction: undefined, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.DELETE)).toBe(true); }); @@ -2930,7 +2982,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(true); }); @@ -2966,7 +3026,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3003,7 +3071,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3044,7 +3120,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3063,18 +3147,16 @@ describe('getSecondaryTransactionThreadActions', () => { jest.spyOn(ReportUtils, 'canEditFieldOfMoneyRequest').mockReturnValue(true); jest.spyOn(ReportUtils, 'canUserPerformWriteAction').mockReturnValue(true); - const result = getSecondaryTransactionThreadActions( - EMPLOYEE_EMAIL, - EMPLOYEE_ACCOUNT_ID, + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, parentReport, - transaction, - actionR14932, - {} as Transaction, - policy, - undefined, - undefined, - false, - ); + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + isChatReportArchived: false, + }); expect(result).toContain(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); }); @@ -3093,18 +3175,16 @@ describe('getSecondaryTransactionThreadActions', () => { jest.spyOn(ReportUtils, 'canEditFieldOfMoneyRequest').mockReturnValue(false); jest.spyOn(ReportUtils, 'canUserPerformWriteAction').mockReturnValue(true); - const result = getSecondaryTransactionThreadActions( - EMPLOYEE_EMAIL, - EMPLOYEE_ACCOUNT_ID, + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, parentReport, - transaction, - actionR14932, - {} as Transaction, - policy, - undefined, - undefined, - false, - ); + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + isChatReportArchived: false, + }); expect(result).not.toContain(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); }); From ba541869594def665ba36bf938c44affa2c29a22 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Wed, 1 Apr 2026 00:47:28 +0530 Subject: [PATCH 37/78] Simplify archived report checks with ArchivedReportsIDSet --- src/components/MoneyReportHeader.tsx | 10 ++- .../MoneyRequestConfirmationListFooter.tsx | 9 ++- src/components/MoneyRequestHeader.tsx | 6 +- .../ReportActionItem/MoneyRequestView.tsx | 3 +- .../TransactionPreviewContent.tsx | 4 +- src/components/Search/index.tsx | 30 ++++----- src/hooks/useOutstandingReports.ts | 7 ++- src/hooks/useSelectedTransactionsActions.ts | 3 +- src/hooks/useShowNotFoundPageInIOUStep.ts | 6 +- src/libs/ReportSecondaryActionUtils.ts | 13 ++-- src/libs/ReportUtils.ts | 63 +++++-------------- src/libs/actions/Link.ts | 5 +- src/libs/actions/Report/index.ts | 7 +-- .../ReportActionCompose.tsx | 5 +- .../step/IOURequestStepConfirmation.tsx | 5 +- .../iou/request/step/IOURequestStepReport.tsx | 12 +--- 16 files changed, 66 insertions(+), 122 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 974273e79a6a..92ac71f419d7 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -110,7 +110,6 @@ import { isOpenExpenseReport, isOpenReport, isProcessingReport, - isReportArchivedByID, isReportOwner, isSelfDM, navigateOnDeleteExpense, @@ -484,7 +483,6 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa const isArchivedReport = useReportIsArchived(moneyRequestReport?.reportID); const isChatReportArchived = useReportIsArchived(chatReport?.reportID); const {archivedReportsIDSet} = useContext(ActionListContext); - const isReportArchived = useCallback((reportID?: string) => isReportArchivedByID(archivedReportsIDSet, reportID), [archivedReportsIDSet]); const canMoveSingleExpense = useMemo(() => { if (nonPendingDeleteTransactions.length !== 1) { @@ -506,13 +504,13 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa undefined, undefined, undefined, - isReportArchived, + archivedReportsIDSet, ); const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(moneyRequestReport, isChatReportArchived); return canMoveExpense && canUserPerformWriteAction; - }, [nonPendingDeleteTransactions, reportActions, isChatReportArchived, outstandingReportsByPolicyID, moneyRequestReport, isReportArchived]); + }, [nonPendingDeleteTransactions, reportActions, isChatReportArchived, outstandingReportsByPolicyID, moneyRequestReport, archivedReportsIDSet]); const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`, {selector: getArchiveReason}); @@ -1554,7 +1552,7 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa policies, outstandingReportsByPolicyID, isChatReportArchived, - isReportArchived, + archivedReportsIDSet, }); }, [ moneyRequestReport, @@ -1572,7 +1570,7 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa isChatReportArchived, bankAccountList, outstandingReportsByPolicyID, - isReportArchived, + archivedReportsIDSet, ]); const secondaryExportActions = useMemo(() => { diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index e5adbd08e4fd..83772b5db230 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -30,7 +30,7 @@ import {canSendInvoice, getPerDiemCustomUnit} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; import {getReportName} from '@libs/ReportNameUtils'; -import {generateReportID, getDefaultWorkspaceAvatar, getOutstandingReportsForUser, isMoneyRequestReport, isReportArchivedByID, isReportOutstanding} from '@libs/ReportUtils'; +import {generateReportID, getDefaultWorkspaceAvatar, getOutstandingReportsForUser, isMoneyRequestReport, isReportOutstanding} from '@libs/ReportUtils'; import {getTagVisibility, hasEnabledTags} from '@libs/TagsOptionsListUtils'; import {endSpan} from '@libs/telemetry/activeSpans'; import { @@ -331,7 +331,6 @@ function MoneyRequestConfirmationListFooter({ const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchived = useCallback((value?: string) => isReportArchivedByID(archivedReportsIDSet, value), [archivedReportsIDSet]); const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID); const {policyForMovingExpensesID, policyForMovingExpenses, shouldSelectPolicy} = usePolicyForMovingExpenses(); @@ -372,15 +371,15 @@ function MoneyRequestConfirmationListFooter({ */ const transactionReport = transaction?.reportID ? Object.values(allReports ?? {}).find((report) => report?.reportID === transaction.reportID) : undefined; const policyID = selectedParticipants?.at(0)?.policyID; - const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, policyID, isReportArchived, false)) || isUnreported; + const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, policyID, archivedReportsIDSet, false)) || isUnreported; const ownerAccountID = selectedParticipants?.at(0)?.ownerAccountID; const availableOutstandingReports = useMemo(() => { - return getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, isReportArchived, false).sort((a, b) => + return getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, archivedReportsIDSet, false).sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? ''), ); - }, [policyID, ownerAccountID, outstandingReportsByPolicyID, isReportArchived, localeCompare]); + }, [policyID, ownerAccountID, outstandingReportsByPolicyID, archivedReportsIDSet, localeCompare]); const iouReportID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.iouReportID; const outstandingReportID = isPolicyExpenseChat ? (iouReportID ?? availableOutstandingReports.at(0)?.reportID) : reportID; diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index a64f44d8cb7b..07312d2f20bd 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -46,7 +46,6 @@ import { isDM, isExpenseReport, isOpenReport, - isReportArchivedByID, isSelfDM, navigateToDetailsPage, rejectMoneyRequestReason, @@ -200,7 +199,6 @@ function MoneyRequestHeader({reportID: reportIDProp, onBackButtonPress}: MoneyRe const isParentReportArchived = useReportIsArchived(report?.parentReportID); const {iouReport, chatReport: chatIOUReport, isChatIOUReportArchived} = useGetIOUReportFromReportAction(parentReportAction); const {archivedReportsIDSet} = useContext(ActionListContext); - const isReportArchived = useCallback((targetReportID?: string) => isReportArchivedByID(archivedReportsIDSet, targetReportID), [archivedReportsIDSet]); const hasPendingRTERViolation = hasPendingRTERViolationTransactionUtils(transactionViolations); const hasCustomUnitOutOfPolicyViolation = hasCustomUnitOutOfPolicyViolationTransactionUtils(transactionViolations); @@ -445,7 +443,7 @@ function MoneyRequestHeader({reportID: reportIDProp, onBackButtonPress}: MoneyRe transactionThreadReport: report, outstandingReportsByPolicyID, isChatReportArchived: isChatIOUReportArchived, - isReportArchived, + archivedReportsIDSet, }); }, [ parentReport, @@ -458,7 +456,7 @@ function MoneyRequestHeader({reportID: reportIDProp, onBackButtonPress}: MoneyRe accountID, outstandingReportsByPolicyID, isChatIOUReportArchived, - isReportArchived, + archivedReportsIDSet, ]); const dismissModalAndUpdateUseHold = () => { diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index e7394b817721..df8dcaf20529 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -72,7 +72,6 @@ import { isOpenReport, isPaidGroupPolicy, isReportApproved, - isReportArchivedByID, isReportInGroupPolicy, isSettled as isSettledReportUtils, isTrackExpenseReportNew, @@ -378,7 +377,7 @@ function MoneyRequestView({ transaction, moneyRequestReport, policy, - (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID), + archivedReportsIDSet, ) && (!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy) || (isExpenseUnreported && !!perDiemOriginalPolicy)); diff --git a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx index aa1e1fe0029c..7eabead10b3e 100644 --- a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx +++ b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx @@ -31,7 +31,7 @@ import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {isMarkAsCashActionForTransaction} from '@libs/ReportPrimaryActionUtils'; import type {TransactionDetails} from '@libs/ReportUtils'; -import {canEditMoneyRequest, getTransactionDetails, isPolicyExpenseChat, isReportApproved, isReportArchivedByID, isSettled} from '@libs/ReportUtils'; +import {canEditMoneyRequest, getTransactionDetails, isPolicyExpenseChat, isReportApproved, isSettled} from '@libs/ReportUtils'; import StringUtils from '@libs/StringUtils'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import type {TranslationPathOrText} from '@libs/TransactionPreviewUtils'; @@ -127,7 +127,7 @@ function TransactionPreviewContent({ const filteredViolations = filterReceiptViolations(violations); const firstViolation = filteredViolations.at(0); const isIOUActionType = isMoneyRequestAction(action); - const canEdit = isIOUActionType && canEditMoneyRequest(action, isChatReportArchived, report, policy, transaction, (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID)); + const canEdit = isIOUActionType && canEditMoneyRequest(action, isChatReportArchived, report, policy, transaction, archivedReportsIDSet); const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(report?.policyID)}`; const {personalCardsWithBrokenConnection} = useCardFeedErrors(); const connectionLink = getBrokenConnectionUrlToFixPersonalCard(personalCardsWithBrokenConnection, environmentURL); diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 4af2d973d14c..d87a141795d4 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -43,7 +43,7 @@ import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTop import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types'; import {isCreatedTaskReportAction} from '@libs/ReportActionsUtils'; import {isSplitAction} from '@libs/ReportSecondaryActionUtils'; -import {canEditFieldOfMoneyRequest, canHoldUnholdReportAction, canRejectReportAction, isOneTransactionReport, isReportArchivedByID, selectFilteredReportActions} from '@libs/ReportUtils'; +import {canEditFieldOfMoneyRequest, canHoldUnholdReportAction, canRejectReportAction, isOneTransactionReport, selectFilteredReportActions} from '@libs/ReportUtils'; import {buildCannedSearchQuery, buildSearchQueryString, isDefaultExpensesQuery} from '@libs/SearchQueryUtils'; import { createAndOpenSearchTransactionThread, @@ -64,6 +64,7 @@ import { shouldShowEmptyState, shouldShowYear as shouldShowYearUtil, } from '@libs/SearchUIUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {cancelSpan, endSpanWithAttributes, getSpan, startSpan} from '@libs/telemetry/activeSpans'; import markNavigateAfterExpenseCreateEnd from '@libs/telemetry/markNavigateAfterExpenseCreateEnd'; import {cancelSubmitFollowUpActionSpan, endSubmitFollowUpActionSpan, getPendingSubmitFollowUpAction} from '@libs/telemetry/submitFollowUpAction'; @@ -109,7 +110,7 @@ function mapTransactionItemToSelectedEntry( currentUserLogin: string, currentUserAccountID: number, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, - isReportArchived?: (reportID?: string) => boolean, + archivedReportsIDSet?: ArchivedReportsIDSet, allowNegativeAmount = true, ): [string, SelectedTransactionInfo] { const {canHoldRequest, canUnholdRequest} = canHoldUnholdReportAction(item.report, item.reportAction, item.holdReportAction, item, item.policy); @@ -136,7 +137,7 @@ function mapTransactionItemToSelectedEntry( item, item.report, item.policy, - isReportArchived, + archivedReportsIDSet, ), action: item.action, groupCurrency: item.groupCurrency, @@ -184,7 +185,7 @@ function prepareTransactionsList( currentUserLogin: string, currentUserAccountID: number, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, - isReportArchived?: (reportID?: string) => boolean, + archivedReportsIDSet?: ArchivedReportsIDSet, ) { if (selectedTransactions[item.keyForList]?.isSelected) { const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions; @@ -199,7 +200,7 @@ function prepareTransactionsList( currentUserLogin, currentUserAccountID, outstandingReportsByPolicyID, - isReportArchived, + archivedReportsIDSet, false, ); @@ -270,7 +271,6 @@ function Search({ const {markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); const archivedReportsIDSet = useArchivedReportsIDSet(); - const isReportArchived = useCallback((reportID?: string) => isReportArchivedByID(archivedReportsIDSet, reportID), [archivedReportsIDSet]); const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, { canEvict: false, @@ -743,7 +743,7 @@ function Search({ transactionItem, transactionItem.report, transactionItem.policy, - isReportArchived, + archivedReportsIDSet, ), // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID]?.isSelected || isExpenseReportType, @@ -800,7 +800,7 @@ function Search({ transactionItem, transactionItem.report, transactionItem.policy, - isReportArchived, + archivedReportsIDSet, ), // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID].isSelected, @@ -827,7 +827,7 @@ function Search({ isRefreshingSelection.current = true; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [filteredData, setSelectedTransactions, areAllMatchingItemsSelected, isFocused, outstandingReportsByPolicyID, isExpenseReportType, isReportArchived]); + }, [filteredData, setSelectedTransactions, areAllMatchingItemsSelected, isFocused, outstandingReportsByPolicyID, isExpenseReportType, archivedReportsIDSet]); useEffect(() => { if (!isSearchResultsEmpty || prevIsSearchResultEmpty) { @@ -934,7 +934,7 @@ function Search({ email ?? '', accountID, outstandingReportsByPolicyID, - isReportArchived, + archivedReportsIDSet, ); setSelectedTransactions(updatedTransactions, filteredData); updateSelectAllMatchingItemsState(updatedTransactions); @@ -1007,7 +1007,7 @@ function Search({ email ?? '', accountID, outstandingReportsByPolicyID, - isReportArchived, + archivedReportsIDSet, ); }), ), @@ -1025,7 +1025,7 @@ function Search({ accountID, outstandingReportsByPolicyID, searchResults?.data, - isReportArchived, + archivedReportsIDSet, ], ); @@ -1278,7 +1278,7 @@ function Search({ email ?? '', accountID, outstandingReportsByPolicyID, - isReportArchived, + archivedReportsIDSet, ); }); }); @@ -1298,7 +1298,7 @@ function Search({ email ?? '', accountID, outstandingReportsByPolicyID, - isReportArchived, + archivedReportsIDSet, ); }), ); @@ -1318,7 +1318,7 @@ function Search({ accountID, outstandingReportsByPolicyID, searchResults?.data, - isReportArchived, + archivedReportsIDSet, ]); const onLayout = useCallback(() => { diff --git a/src/hooks/useOutstandingReports.ts b/src/hooks/useOutstandingReports.ts index b23b255bf3b7..43b4ea29d6b0 100644 --- a/src/hooks/useOutstandingReports.ts +++ b/src/hooks/useOutstandingReports.ts @@ -4,6 +4,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useMappedPolicies from './useMappedPolicies'; import useOnyx from './useOnyx'; @@ -13,7 +14,7 @@ export default function useOutstandingReports(selectedReportID: string | undefin const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); const [allPoliciesID] = useMappedPolicies(policyIdMapper); - const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); // Early return if no reports are available to prevent useless loop @@ -28,11 +29,11 @@ export default function useOutstandingReports(selectedReportID: string | undefin continue; } - const reports = getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID[policyID] ?? {}, reportNameValuePairs, isEditing); + const reports = getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID[policyID] ?? {}, archivedReportsIDSet, isEditing); result.push(...reports); } return result; } - return getOutstandingReportsForUser(selectedPolicyID, ownerAccountID, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, reportNameValuePairs, isEditing); + return getOutstandingReportsForUser(selectedPolicyID, ownerAccountID, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, archivedReportsIDSet, isEditing); } diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index cf2b77e17c4b..0df6b2dbd301 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -22,7 +22,6 @@ import { getReportOrDraftReport, isInvoiceReport, isMoneyRequestReport as isMoneyRequestReportUtils, - isReportArchivedByID, isTrackExpenseReport, } from '@libs/ReportUtils'; import {getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; @@ -341,7 +340,7 @@ function useSelectedTransactionsActions({ undefined, undefined, undefined, - (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID), + archivedReportsIDSet, ); return canMoveExpense; }); diff --git a/src/hooks/useShowNotFoundPageInIOUStep.ts b/src/hooks/useShowNotFoundPageInIOUStep.ts index 2824925f7fbb..4b7e322d58bd 100644 --- a/src/hooks/useShowNotFoundPageInIOUStep.ts +++ b/src/hooks/useShowNotFoundPageInIOUStep.ts @@ -2,7 +2,7 @@ import {useCallback, useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {isMoneyRequestAction} from '@libs/ReportActionsUtils'; -import {canEditMoneyRequest, isReportArchivedByID} from '@libs/ReportUtils'; +import {canEditMoneyRequest} from '@libs/ReportUtils'; import {areRequiredFieldsEmpty} from '@libs/TransactionUtils'; import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; @@ -64,9 +64,7 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor } else if (isSplitExpense) { shouldShowNotFoundPage = !canEditSplitExpense; } else { - shouldShowNotFoundPage = - !isMoneyRequestAction(reportAction) || - !canEditMoneyRequest(reportAction, false, iouReport, policy, transaction, (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID)); + shouldShowNotFoundPage = !isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, false, iouReport, policy, transaction, archivedReportsIDSet); } } diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index a314596350e9..a5845939e2db 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -77,6 +77,7 @@ import { isSettled, isWorkspaceEligibleForReportChange, } from './ReportUtils'; +import type {ArchivedReportsIDSet} from './SearchUIUtils'; import { allHavePendingRTERViolation, getOriginalTransactionWithSplitInfo, @@ -855,7 +856,7 @@ function getSecondaryReportActions({ policies, outstandingReportsByPolicyID, isChatReportArchived = false, - isReportArchived, + archivedReportsIDSet, }: { currentUserLogin: string; currentUserAccountID: number; @@ -873,7 +874,7 @@ function getSecondaryReportActions({ outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; canUseNewDotSplits?: boolean; isChatReportArchived?: boolean; - isReportArchived?: (reportID?: string) => boolean; + archivedReportsIDSet?: ArchivedReportsIDSet; }): Array> { const options: Array> = []; @@ -996,7 +997,7 @@ function getSecondaryReportActions({ undefined, undefined, undefined, - isReportArchived, + archivedReportsIDSet, ); const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(report, isChatReportArchived); @@ -1058,7 +1059,7 @@ function getSecondaryTransactionThreadActions({ transactionThreadReport, outstandingReportsByPolicyID, isChatReportArchived, - isReportArchived, + archivedReportsIDSet, }: { currentUserLogin: string; currentUserAccountID: number; @@ -1070,7 +1071,7 @@ function getSecondaryTransactionThreadActions({ transactionThreadReport?: OnyxEntry; outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; isChatReportArchived?: boolean; - isReportArchived?: (reportID?: string) => boolean; + archivedReportsIDSet?: ArchivedReportsIDSet; }): Array> { const options: Array> = []; @@ -1110,7 +1111,7 @@ function getSecondaryTransactionThreadActions({ undefined, undefined, undefined, - isReportArchived, + archivedReportsIDSet, ) && canUserPerformWriteActionReportUtils(parentReport, isChatReportArchived) ) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e412eaf9d5dc..9ba6af9c5612 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -329,8 +329,6 @@ type SpendBreakdown = { totalDisplaySpend: number; }; -type IsReportArchived = (reportID?: string) => boolean; - type OptimisticAddCommentReportAction = Pick< ReportAction, | 'reportActionID' @@ -2272,28 +2270,7 @@ function getMostRecentlyVisitedReport(reports: Array>, reportM * This function is used to find the last accessed report and we don't need to subscribe the data in the UI. * So please use `Onyx.connectWithoutView()` to get the necessary data when we remove the `Onyx.connect()` */ -function findLastAccessedReport( - ignoreDomainRooms: boolean, - openOnAdminRoom = false, - policyIDOrExcludeReportID?: string, - excludeReportIDOrArchivedReportsIDSetOrCallback?: string | ArchivedReportsIDSet | IsReportArchived, - archivedReportsIDSetOrCallback?: ArchivedReportsIDSet | IsReportArchived, -): OnyxEntry { - const isUsingOldArgs = archivedReportsIDSetOrCallback !== undefined || typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string'; - let excludeReportID: string | undefined; - if (isUsingOldArgs) { - if (typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string') { - excludeReportID = excludeReportIDOrArchivedReportsIDSetOrCallback; - } else if (archivedReportsIDSetOrCallback !== undefined) { - excludeReportID = policyIDOrExcludeReportID; - } - } else { - excludeReportID = policyIDOrExcludeReportID; - } - const archivedReportsArg = isUsingOldArgs - ? (archivedReportsIDSetOrCallback ?? (typeof excludeReportIDOrArchivedReportsIDSetOrCallback === 'string' ? undefined : excludeReportIDOrArchivedReportsIDSetOrCallback)) - : excludeReportIDOrArchivedReportsIDSetOrCallback; - +function findLastAccessedReport(ignoreDomainRooms: boolean, openOnAdminRoom = false, excludeReportID?: string, archivedReportsIDSet?: ArchivedReportsIDSet): OnyxEntry { let reportsValues = Object.values(allReports ?? {}); if (openOnAdminRoom) { @@ -2329,8 +2306,7 @@ function findLastAccessedReport( // and it prompts the user to use the Concierge chat instead. reportsValues = reportsValues.filter((report) => { - const reportNameValuePairsKey = `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`; - const isArchived = typeof archivedReportsArg === 'function' ? archivedReportsArg(report?.reportID) : !!archivedReportsArg?.has(reportNameValuePairsKey); + const isArchived = isReportArchivedByID(archivedReportsIDSet ?? CONST.EMPTY_SET, report?.reportID); return !isSystemChat(report) && !isArchived; }) ?? []; @@ -4755,7 +4731,7 @@ function canEditMoneyRequest( report?: OnyxInputOrEntry, policy?: OnyxEntry, linkedTransaction?: OnyxEntry, - isReportArchived?: IsReportArchived, + archivedReportsIDSet?: ArchivedReportsIDSet, ): boolean { const isDeleted = isDeletedAction(reportAction); @@ -4791,7 +4767,7 @@ function canEditMoneyRequest( } const moneyRequestReport = report ?? getReportOrDraftReport(String(moneyRequestReportID)); - const isCurrentReportArchived = isReportArchived?.(moneyRequestReport?.reportID) ?? false; + const isCurrentReportArchived = isReportArchivedByID(archivedReportsIDSet ?? CONST.EMPTY_SET, moneyRequestReport?.reportID); const isSubmitted = isProcessingReport(moneyRequestReport); if (isIOUReport(moneyRequestReport)) { @@ -4911,7 +4887,7 @@ function canEditFieldOfMoneyRequest( linkedTransaction?: OnyxEntry, report?: OnyxInputOrEntry, policy?: OnyxEntry, - isReportArchived?: IsReportArchived, + archivedReportsIDSet?: ArchivedReportsIDSet, ): boolean { // A list of fields that cannot be edited by anyone, once an expense has been settled const restrictedFields: string[] = [ @@ -4926,7 +4902,7 @@ function canEditFieldOfMoneyRequest( CONST.EDIT_REQUEST_FIELD.REPORT, ]; - if (!isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, isChatReportArchived, report, policy, linkedTransaction, isReportArchived)) { + if (!isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, isChatReportArchived, report, policy, linkedTransaction, archivedReportsIDSet)) { return false; } @@ -5006,7 +4982,7 @@ function canEditFieldOfMoneyRequest( return true; } - if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, isReportArchived)) { + if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportsIDSet)) { return false; } @@ -5022,7 +4998,7 @@ function canEditFieldOfMoneyRequest( moneyRequestReport?.policyID, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[moneyRequestReport?.policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, - isReportArchived, + archivedReportsIDSet, ).length > 0 ); } @@ -5035,14 +5011,14 @@ function canEditFieldOfMoneyRequest( } // Check the cheaper condition first - if ((isOwner || isAdmin || isManager) && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, isReportArchived)) { + if ((isOwner || isAdmin || isManager) && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportsIDSet)) { return true; } // Check if there are multiple outstanding reports across policies let outstandingReportsCount = 0; for (const currentPolicy of policiesArray) { - const reports = getOutstandingReportsForUser(currentPolicy.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, isReportArchived); + const reports = getOutstandingReportsForUser(currentPolicy.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, archivedReportsIDSet); outstandingReportsCount += reports.length; // Short-circuit once we find more than 1 @@ -11560,12 +11536,7 @@ function hasForwardedAction(reportID: string): boolean { return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); } -function isReportOutstanding( - iouReport: OnyxInputOrEntry, - policyID: string | undefined, - reportNameValuePairsOrIsReportArchived?: OnyxCollection | IsReportArchived, - allowSubmitted = true, -): boolean { +function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: string | undefined, archivedReportsIDSet?: ArchivedReportsIDSet, allowSubmitted = true): boolean { if ( !iouReport || isEmptyObject(iouReport) || @@ -11577,13 +11548,7 @@ function isReportOutstanding( ) { return false; } - const reportNameValuePairsKey = `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport.reportID}`; - const reportNameValuePairs = typeof reportNameValuePairsOrIsReportArchived === 'function' ? undefined : reportNameValuePairsOrIsReportArchived; - const isArchived = - typeof reportNameValuePairsOrIsReportArchived === 'function' - ? reportNameValuePairsOrIsReportArchived(iouReport.reportID) - : isArchivedReport(reportNameValuePairs?.[reportNameValuePairsKey]); - if (isArchived) { + if (isReportArchivedByID(archivedReportsIDSet ?? CONST.EMPTY_SET, iouReport.reportID)) { return false; } const currentRoute = navigationRef.getCurrentRoute(); @@ -11606,7 +11571,7 @@ function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, reports: OnyxCollection = allReports, - reportNameValuePairsOrIsReportArchived?: OnyxCollection | IsReportArchived, + archivedReportsIDSet?: ArchivedReportsIDSet, allowSubmitted = true, ): Array> { if (!reports) { @@ -11615,7 +11580,7 @@ function getOutstandingReportsForUser( return Object.values(reports).filter( (report) => report?.pendingFields?.preview !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - isReportOutstanding(report, policyID, reportNameValuePairsOrIsReportArchived, allowSubmitted) && + isReportOutstanding(report, policyID, archivedReportsIDSet, allowSubmitted) && report?.ownerAccountID === reportOwnerAccountID, ); } diff --git a/src/libs/actions/Link.ts b/src/libs/actions/Link.ts index 13a08340f391..ca47db66c171 100644 --- a/src/libs/actions/Link.ts +++ b/src/libs/actions/Link.ts @@ -16,7 +16,7 @@ import willRouteNavigateToRHP from '@libs/Navigation/helpers/willRouteNavigateTo import Navigation from '@libs/Navigation/Navigation'; import navigationRef from '@libs/Navigation/navigationRef'; import type {NetworkStatus} from '@libs/NetworkConnection'; -import {findLastAccessedReport, getReportIDFromLink, getRouteFromLink, isReportArchivedByID} from '@libs/ReportUtils'; +import {findLastAccessedReport, getReportIDFromLink, getRouteFromLink} from '@libs/ReportUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import shouldSkipDeepLinkNavigation from '@libs/shouldSkipDeepLinkNavigation'; import {endSpan, getSpan, startSpan} from '@libs/telemetry/activeSpans'; @@ -338,8 +338,7 @@ function openReportFromDeepLink( const report = reportParam ?? reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; // If the report does not exist, navigate to the last accessed report or Concierge chat if (reportID && (!report?.reportID || report.errorFields?.notFound)) { - const isReportArchived = (currentReportID?: string) => isReportArchivedByID(archivedReportsIDSet, currentReportID); - const lastAccessedReportID = findLastAccessedReport(false, shouldOpenOnAdminRoom(), reportID, undefined, isReportArchived)?.reportID; + const lastAccessedReportID = findLastAccessedReport(false, shouldOpenOnAdminRoom(), reportID, archivedReportsIDSet)?.reportID; if (lastAccessedReportID) { const lastAccessedReportRoute = ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID); Navigation.navigate(lastAccessedReportRoute, {forceReplace: Navigation.getTopmostReportId() === reportID}); diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 130e5fc87157..d2670e3f214a 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -155,7 +155,6 @@ import { isIOUReportUsingReport, isOpenExpenseReport, isProcessingReport, - isReportArchivedByID, isReportManuallyReimbursed, isSelfDM, isValidReportIDFromPath, @@ -4319,8 +4318,7 @@ function navigateToMostRecentReport( introSelected: OnyxEntry, archivedReportsIDSet: ArchivedReportsIDSet = new Set(), ) { - const isReportArchived = (reportID?: string) => isReportArchivedByID(archivedReportsIDSet, reportID); - const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, undefined, isReportArchived)?.reportID; + const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, archivedReportsIDSet)?.reportID; if (lastAccessedReportID) { // Check if route exists for super wide RHP vs regular full screen report @@ -4345,8 +4343,7 @@ function navigateToMostRecentReport( } function getMostRecentReportID(currentReport: OnyxEntry, conciergeReportID: string | undefined, archivedReportsIDSet: ArchivedReportsIDSet) { - const isReportArchived = (reportID?: string) => isReportArchivedByID(archivedReportsIDSet, reportID); - const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, undefined, isReportArchived)?.reportID; + const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, archivedReportsIDSet)?.reportID; return lastAccessedReportID ?? conciergeReportID; } diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index bf32ce11015f..96b5869f0a48 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -54,7 +54,6 @@ import { isGroupChat, isInvoiceReport, isReportApproved, - isReportArchivedByID, isReportTransactionThread, isSettled, temporary_getMoneyRequestOptions, @@ -245,9 +244,7 @@ function ReportActionCompose({ const canUserPerformWriteAction = !!canUserPerformWriteActionReportUtils(report, isReportArchived); const canEditReceipt = canUserPerformWriteAction && - canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, undefined, undefined, undefined, undefined, undefined, undefined, (targetReportID) => - isReportArchivedByID(archivedReportsIDSet, targetReportID), - ) && + canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.RECEIPT, undefined, undefined, undefined, undefined, undefined, undefined, archivedReportsIDSet) && !transaction?.receipt?.isTestDriveReceipt; const shouldAddOrReplaceReceipt = (isTransactionThreadView || isSingleTransactionView) && canEditReceipt; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 78ce77e07f49..7efbb09aa9ad 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -13,6 +13,7 @@ import MoneyRequestConfirmationList from '@components/MoneyRequestConfirmationLi import {usePersonalDetails, usePolicyCategories} from '@components/OnyxListItemProvider'; import PrevNextButtons from '@components/PrevNextButtons'; import ScreenWrapper from '@components/ScreenWrapper'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useFetchRoute from '@hooks/useFetchRoute'; @@ -204,8 +205,8 @@ function IOURequestStepConfirmation({ const transactionReport = getReportOrDraftReport(transaction?.reportID); const reportWithDraftFallback = useMemo(() => reportReal ?? reportDraft, [reportDraft, reportReal]); const privateIsArchivedMap = usePrivateIsArchivedMap(); - const isReportArchived = useCallback((value?: string) => !!value && !!privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${value}`], [privateIsArchivedMap]); - const canUseReport = !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, isReportArchived, false); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const canUseReport = !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled) && isReportOutstanding(transactionReport, policyReal?.id, archivedReportsIDSet, false); const shouldUseTransactionReport = !!transactionReport && (canUseReport || !reportWithDraftFallback); const shouldHideToSection = useMemo(() => isMoneyRequestReport(reportWithDraftFallback), [reportWithDraftFallback]); diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index b2652a366e3f..b144456fa6dc 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -20,14 +20,7 @@ import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Tran import Navigation from '@libs/Navigation/Navigation'; import {getPerDiemCustomUnit, getPolicyByCustomUnitID} from '@libs/PolicyUtils'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; -import { - getPersonalDetailsForAccountID, - getReportOrDraftReport, - hasViolations as hasViolationsReportUtils, - isPolicyExpenseChat, - isReportArchivedByID, - isReportOutstanding, -} from '@libs/ReportUtils'; +import {getPersonalDetailsForAccountID, getReportOrDraftReport, hasViolations as hasViolationsReportUtils, isPolicyExpenseChat, isReportOutstanding} from '@libs/ReportUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import {isPerDiemRequest, isTimeRequest as isTimeRequestUtil} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; @@ -61,8 +54,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); const participantReportID = transaction?.participants?.at(0)?.reportID; const participantReport = Object.values(allReports ?? {}).find((report) => report?.reportID === participantReportID); - const shouldUseTransactionReport = - (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID, (reportID) => isReportArchivedByID(archivedReportsIDSet, reportID))) || isUnreported; + const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID, archivedReportsIDSet)) || isUnreported; const outstandingReportID = isPolicyExpenseChat(participantReport) ? participantReport?.iouReportID : participantReportID; const selectedReportID = shouldUseTransactionReport ? transactionReport?.reportID : outstandingReportID; const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); From cda62946d3fe877e3c92a3319a10dce8334629db Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Wed, 1 Apr 2026 04:00:10 +0530 Subject: [PATCH 38/78] Fix --- src/pages/inbox/ReportScreen.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index c91d4e06c39d..87df1e2dd23d 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -20,6 +20,7 @@ import ScrollView from '@components/ScrollView'; import useShowWideRHPVersion from '@components/WideRHPContextProvider/useShowWideRHPVersion'; import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import {useCurrentReportIDState} from '@hooks/useCurrentReportID'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDocumentTitle from '@hooks/useDocumentTitle'; @@ -580,7 +581,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) { }); }, [reportWasDeleted, isFocused, deletedReportParentID, conciergeReportID, introSelected, currentUserAccountID, isSelfTourViewed, betas]); - const actionListValue = useActionListContextValue(); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const actionListValue = useActionListContextValue(archivedReportsIDSet); // This helps in tracking from the moment 'route' triggers useMemo until isLoadingInitialReportActions becomes true. It prevents blinking when loading reportActions from cache. useEffect(() => { From a5daacafac218d9662b1413640aa68ad4fccb537 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Wed, 1 Apr 2026 23:58:47 +0530 Subject: [PATCH 39/78] Fixes --- src/components/MoneyRequestConfirmationListFooter.tsx | 2 +- src/components/Search/index.tsx | 2 +- src/hooks/useAutoCreateTrackWorkspace.ts | 2 +- src/hooks/useSelectedTransactionsActions.ts | 2 +- src/hooks/useShowNotFoundPageInIOUStep.ts | 2 +- .../OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx | 2 +- src/pages/inbox/ReportRouteParamHandler.tsx | 2 +- src/pages/iou/request/step/IOURequestStepReport.tsx | 2 +- tests/unit/hooks/useSearchSections.test.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 6937ed4ae004..b27d24abfd49 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -7,7 +7,7 @@ import {View} from 'react-native'; import type {LayoutChangeEvent} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 45ebf794b295..a2aab61d71e4 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -11,7 +11,7 @@ import type {SelectionListHandle} from '@components/SelectionList/types'; import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; import {useWideRHPActions} from '@components/WideRHPContextProvider'; import useActionLoadingReportIDs from '@hooks/useActionLoadingReportIDs'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useMultipleSnapshots from '@hooks/useMultipleSnapshots'; diff --git a/src/hooks/useAutoCreateTrackWorkspace.ts b/src/hooks/useAutoCreateTrackWorkspace.ts index 6e9bd3e10eef..80ddad449e00 100644 --- a/src/hooks/useAutoCreateTrackWorkspace.ts +++ b/src/hooks/useAutoCreateTrackWorkspace.ts @@ -10,7 +10,7 @@ import {setOnboardingAdminsChatReportID, setOnboardingPolicyID} from '@userActio import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {OnboardingPurpose, Policy} from '@src/types/onyx'; -import useArchivedReportsIdSet from './useArchivedReportsIdSet'; +import useArchivedReportsIdSet from './useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useHasActiveAdminPolicies from './useHasActiveAdminPolicies'; import useLocalize from './useLocalize'; diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index b4a8f90251cc..6bb7fe106c44 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -35,7 +35,7 @@ import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import useAllTransactions from './useAllTransactions'; -import useArchivedReportsIdSet from './useArchivedReportsIdSet'; +import useArchivedReportsIdSet from './useArchivedReportsIDSet'; import {useCurrencyListActions} from './useCurrencyList'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useDeleteTransactions from './useDeleteTransactions'; diff --git a/src/hooks/useShowNotFoundPageInIOUStep.ts b/src/hooks/useShowNotFoundPageInIOUStep.ts index 26fe8720ece8..17ddd3968de4 100644 --- a/src/hooks/useShowNotFoundPageInIOUStep.ts +++ b/src/hooks/useShowNotFoundPageInIOUStep.ts @@ -8,7 +8,7 @@ import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {OnyxInputOrEntry, Report, ReportAction, ReportActions, Transaction} from '@src/types/onyx'; -import useArchivedReportsIdSet from './useArchivedReportsIdSet'; +import useArchivedReportsIdSet from './useArchivedReportsIDSet'; import useOnyx from './useOnyx'; /** diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 85b3e67b4bc8..b5ea6bb5a26d 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -9,7 +9,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import useAutoCreateTrackWorkspace from '@hooks/useAutoCreateTrackWorkspace'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; diff --git a/src/pages/inbox/ReportRouteParamHandler.tsx b/src/pages/inbox/ReportRouteParamHandler.tsx index d934c75eedef..3f8e12d13207 100644 --- a/src/pages/inbox/ReportRouteParamHandler.tsx +++ b/src/pages/inbox/ReportRouteParamHandler.tsx @@ -1,5 +1,5 @@ import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import usePermissions from '@hooks/usePermissions'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 4cba0e521f66..fb0ad475e7bd 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -4,7 +4,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {usePersonalDetails, useSession} from '@components/OnyxListItemProvider'; import {useSearchActionsContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; diff --git a/tests/unit/hooks/useSearchSections.test.ts b/tests/unit/hooks/useSearchSections.test.ts index 890f1157e0f2..438dc6663d77 100644 --- a/tests/unit/hooks/useSearchSections.test.ts +++ b/tests/unit/hooks/useSearchSections.test.ts @@ -48,7 +48,7 @@ jest.mock('@hooks/useActionLoadingReportIDs', () => ({ default: () => new Set(), })); -jest.mock('@hooks/useArchivedReportsIdSet', () => ({ +jest.mock('@hooks/useArchivedReportsIDSet', () => ({ // eslint-disable-next-line @typescript-eslint/naming-convention __esModule: true, default: () => new Set(), From 7d04345592d1b39f38e0e3d2163ddff6937e1515 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Thu, 2 Apr 2026 00:25:26 +0530 Subject: [PATCH 40/78] Resolve archived report set regressions and merge conflicts --- src/pages/ReportDetailsPage.tsx | 15 ++++++++++++++- tests/unit/ReportUtilsTest.ts | 8 ++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index b7661f197ec1..65be7bb5741a 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -25,6 +25,7 @@ import {useSearchActionsContext, useSearchStateContext} from '@components/Search import {SUPER_WIDE_RIGHT_MODALS} from '@components/WideRHPContextProvider/WIDE_RIGHT_MODALS'; import useActivePolicy from '@hooks/useActivePolicy'; import useAncestors from '@hooks/useAncestors'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDeleteTransactions from '@hooks/useDeleteTransactions'; @@ -175,6 +176,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail const parentReportAction = useParentReportAction(report); const hasOutstandingChildTask = useHasOutstandingChildTask(report); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); @@ -890,7 +892,17 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail const deleteTransaction = useCallback(() => { if (caseID === CASES.DEFAULT) { - deleteTask(report, parentReport, isReportArchived, currentUserPersonalDetails.accountID, hasOutstandingChildTask, parentReportAction, conciergeReportID, ancestors); + deleteTask( + report, + parentReport, + isReportArchived, + currentUserPersonalDetails.accountID, + hasOutstandingChildTask, + parentReportAction, + archivedReportsIDSet, + conciergeReportID, + ancestors, + ); return; } @@ -930,6 +942,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail currentUserPersonalDetails.accountID, hasOutstandingChildTask, parentReportAction, + archivedReportsIDSet, ancestors, moneyRequestReport, iouReport, diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 281426f4c514..9e76979b6b54 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -7129,8 +7129,8 @@ describe('ReportUtils', () => { }); it('should not return an archived report even if it was most recently accessed', () => { - const isReportArchived = (reportID?: string) => reportID === archivedReport.reportID; - const result = findLastAccessedReport(false, false, undefined, undefined, isReportArchived); + const archivedReportsIDSet = new Set([`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${archivedReport.reportID}`]); + const result = findLastAccessedReport(false, false, undefined, archivedReportsIDSet); // Even though the archived report has a more recent lastVisitTime, // the function should filter it out and return the normal report @@ -8044,8 +8044,8 @@ describe('ReportUtils', () => { statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - const isReportArchived = (reportID?: string) => reportID === report.reportID; - expect(isReportOutstanding(report, policy.id, isReportArchived)).toBe(false); + const archivedReportsIDSet = new Set([`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`]); + expect(isReportOutstanding(report, policy.id, archivedReportsIDSet)).toBe(false); }); }); From 33d61620f93371d2a8818fe0f8b8b0ef112b1dba Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Thu, 2 Apr 2026 02:15:14 +0530 Subject: [PATCH 41/78] Remove unused variables import from MoneyReportHeader --- src/components/MoneyReportHeader.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 988105aa0653..82034db0f4a7 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -132,7 +132,6 @@ import { } from '@libs/TransactionUtils'; import type {ExportType} from '@pages/inbox/report/ReportDetailsExportPage'; import {ActionListContext} from '@pages/inbox/ReportScreenContext'; -import variables from '@styles/variables'; import { approveMoneyRequest, canApproveIOU, From dbf7771a23a6dfcb521b0d2914e350dd58fa1170 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Thu, 2 Apr 2026 22:59:29 +0530 Subject: [PATCH 42/78] Fix unused ExportType import --- src/components/MoneyReportHeader.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 9a6c9185a7e4..3fcc5db81778 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -129,7 +129,6 @@ import { isPerDiemRequest, isTransactionPendingDelete, } from '@libs/TransactionUtils'; -import type {ExportType} from '@pages/inbox/report/ReportDetailsExportPage'; import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import { approveMoneyRequest, From 298e045378ea31d21ff54c0bd38cd765e1ca8957 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 10 Apr 2026 22:29:53 +0530 Subject: [PATCH 43/78] Remove unrelated archive changes --- src/DeepLinkHandler.tsx | 107 ------------------ src/Expensify.tsx | 56 ++++++++- .../ReportActionItem/MoneyRequestView.tsx | 5 +- .../TransactionPreviewContent.tsx | 6 +- src/components/Search/index.tsx | 5 - src/hooks/useShowNotFoundPageInIOUStep.ts | 4 +- src/libs/ReportUtils.ts | 21 ++-- 7 files changed, 67 insertions(+), 137 deletions(-) delete mode 100644 src/DeepLinkHandler.tsx diff --git a/src/DeepLinkHandler.tsx b/src/DeepLinkHandler.tsx deleted file mode 100644 index 636d707fb570..000000000000 --- a/src/DeepLinkHandler.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import {useEffect, useRef} from 'react'; -import type {NativeEventSubscription} from 'react-native'; -import {Linking} from 'react-native'; -import CONST from './CONST'; -import useArchivedReportsIDSet from './hooks/useArchivedReportsIDSet'; -import useIsAuthenticated from './hooks/useIsAuthenticated'; -import useOnyx from './hooks/useOnyx'; -import {openReportFromDeepLink} from './libs/actions/Link'; -import * as Report from './libs/actions/Report'; -import {hasAuthToken} from './libs/actions/Session'; -import Log from './libs/Log'; -import {endSpan} from './libs/telemetry/activeSpans'; -import ONYXKEYS from './ONYXKEYS'; -import type {Route} from './ROUTES'; -import isLoadingOnyxValue from './types/utils/isLoadingOnyxValue'; - -type DeepLinkHandlerProps = { - /** Callback to set the initial URL resolved from deep linking */ - onInitialUrl: (url: Route | null) => void; -}; - -/** - * Component that does not render anything but isolates the COLLECTION.REPORT Onyx subscription - * from the root Expensify component to prevent cascading re-renders of the - * entire navigation tree on every report change. - */ -function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { - const linkingChangeListener = useRef(null); - - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const [, sessionMetadata] = useOnyx(ONYXKEYS.SESSION); - const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); - const archivedReportsIDSet = useArchivedReportsIDSet(); - const [betas] = useOnyx(ONYXKEYS.BETAS); - const allReportsRef = useRef(allReports); - const onInitialURLRef = useRef(onInitialUrl); - const archivedReportsIDSetRef = useRef(archivedReportsIDSet); - const betasRef = useRef(betas); - const isAuthenticated = useIsAuthenticated(); - const isAuthenticatedRef = useRef(isAuthenticated); - - useEffect(() => { - allReportsRef.current = allReports; - }, [allReports]); - - useEffect(() => { - onInitialURLRef.current = onInitialUrl; - }, [onInitialUrl]); - - useEffect(() => { - archivedReportsIDSetRef.current = archivedReportsIDSet; - }, [archivedReportsIDSet]); - - useEffect(() => { - betasRef.current = betas; - }, [betas]); - - useEffect(() => { - isAuthenticatedRef.current = isAuthenticated; - }, [isAuthenticated]); - - useEffect(() => { - if (isLoadingOnyxValue(sessionMetadata)) { - return; - } - // If the app is opened from a deep link, get the reportID (if exists) from the deep link and navigate to the chat report - Linking.getInitialURL().then((url) => { - onInitialURLRef.current(url as Route); - - if (url) { - if (conciergeReportID === undefined) { - Log.info('[Deep link] conciergeReportID is undefined when processing initial URL', false, {url}); - } - if (introSelected === undefined) { - Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); - } - openReportFromDeepLink(url, allReportsRef.current, isAuthenticatedRef.current, conciergeReportID, introSelected, archivedReportsIDSetRef.current, betasRef.current); - } else { - Report.doneCheckingPublicRoom(); - } - - endSpan(CONST.TELEMETRY.SPAN_BOOTSPLASH.DEEP_LINK); - }); - - // Open chat report from a deep link (only mobile native) - linkingChangeListener.current = Linking.addEventListener('url', (state) => { - if (conciergeReportID === undefined) { - Log.info('[Deep link] conciergeReportID is undefined when processing URL change', false, {url: state.url}); - } - if (introSelected === undefined) { - Log.info('[Deep link] introSelected is undefined when processing URL change', false, {url: state.url}); - } - const isCurrentlyAuthenticated = hasAuthToken(); - openReportFromDeepLink(state.url, allReportsRef.current, isCurrentlyAuthenticated, conciergeReportID, introSelected, archivedReportsIDSetRef.current, betasRef.current); - }); - - return () => { - linkingChangeListener.current?.remove(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps -- we only want this effect to re-run when conciergeReportID changes - }, [sessionMetadata?.status, conciergeReportID, introSelected]); - - return null; -} - -export default DeepLinkHandler; diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 0163dfb2438e..a8feba63118f 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -2,22 +2,25 @@ import HybridAppModule from '@expensify/react-native-hybrid-app'; import type * as Sentry from '@sentry/react-native'; import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react'; import type {NativeEventSubscription} from 'react-native'; -import {AppState, Platform} from 'react-native'; +import {AppState, Linking, Platform} from 'react-native'; import Onyx from 'react-native-onyx'; import {useInitialURLActions} from './components/InitialURLContextProvider'; import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper'; import SplashScreenHider from './components/SplashScreenHider'; import CONFIG from './CONFIG'; import CONST from './CONST'; -import DeepLinkHandler from './DeepLinkHandler'; import DelegateAccessHandler from './DelegateAccessHandler'; import FullstoryInitHandler from './FullstoryInitHandler'; import GlobalModals from './GlobalModals'; +import useArchivedReportsIdSet from './hooks/useArchivedReportsIDSet'; import useDebugShortcut from './hooks/useDebugShortcut'; import useIsAuthenticated from './hooks/useIsAuthenticated'; import useLocalize from './hooks/useLocalize'; import useOnyx from './hooks/useOnyx'; import {updateLastRoute} from './libs/actions/App'; +import {openReportFromDeepLink} from './libs/actions/Link'; +import * as Report from './libs/actions/Report'; +import {hasAuthToken} from './libs/actions/Session'; import * as ActiveClientManager from './libs/ActiveClientManager'; import {isSafari} from './libs/Browser'; import Log from './libs/Log'; @@ -36,6 +39,7 @@ import PriorityModeHandler from './PriorityModeHandler'; import type {Route} from './ROUTES'; import {accountIDSelector} from './selectors/Session'; import {useSplashScreenActions, useSplashScreenState} from './SplashScreenStateContext'; +import isLoadingOnyxValue from './types/utils/isLoadingOnyxValue'; Onyx.registerLogger(({level, message, parameters}) => { if (level === 'alert') { @@ -50,6 +54,7 @@ Onyx.registerLogger(({level, message, parameters}) => { function Expensify() { const appStateChangeListener = useRef(null); + const linkingChangeListener = useRef(null); const [isNavigationReady, setIsNavigationReady] = useState(false); const [isOnyxMigrated, setIsOnyxMigrated] = useState(false); const {splashScreenState} = useSplashScreenState(); @@ -57,10 +62,16 @@ function Expensify() { const [hasAttemptedToOpenPublicRoom, setAttemptedToOpenPublicRoom] = useState(false); const {preferredLocale} = useLocalize(); const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: accountIDSelector}); + const [, sessionMetadata] = useOnyx(ONYXKEYS.SESSION); const [lastRoute] = useOnyx(ONYXKEYS.LAST_ROUTE); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [isCheckingPublicRoom = true] = useOnyx(ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, {initWithStoredValues: false}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); + const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); + const [betas] = useOnyx(ONYXKEYS.BETAS); const [updateRequired] = useOnyx(ONYXKEYS.RAM_ONLY_UPDATE_REQUIRED); const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH); + const archivedReportsIDSet = useArchivedReportsIdSet(); useDebugShortcut(); @@ -267,6 +278,46 @@ function Expensify() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isNavigationReady]); + useEffect(() => { + if (isLoadingOnyxValue(sessionMetadata)) { + return; + } + + Linking.getInitialURL().then((url) => { + setInitialUrl(url as Route); + + if (url) { + if (conciergeReportID === undefined) { + Log.info('[Deep link] conciergeReportID is undefined when processing initial URL', false, {url}); + } + if (introSelected === undefined) { + Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); + } + openReportFromDeepLink(url, allReports, isAuthenticated, conciergeReportID, introSelected, archivedReportsIDSet, betas); + } else { + Report.doneCheckingPublicRoom(); + } + + endSpan(CONST.TELEMETRY.SPAN_BOOTSPLASH.DEEP_LINK); + }); + + linkingChangeListener.current = Linking.addEventListener('url', (state) => { + if (conciergeReportID === undefined) { + Log.info('[Deep link] conciergeReportID is undefined when processing URL change', false, {url: state.url}); + } + if (introSelected === undefined) { + Log.info('[Deep link] introSelected is undefined when processing URL change', false, {url: state.url}); + } + const isCurrentlyAuthenticated = hasAuthToken(); + openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, archivedReportsIDSet, betas); + }); + + return () => { + linkingChangeListener.current?.remove(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps -- this bootstrap should only rerun when session metadata or concierge routing changes + }, [sessionMetadata?.status, conciergeReportID]); + // Display a blank page until the onyx migration completes if (!isOnyxMigrated) { return null; @@ -282,7 +333,6 @@ function Expensify() { - {hasAttemptedToOpenPublicRoom && ( >( @@ -125,7 +123,7 @@ function TransactionPreviewContent({ const filteredViolations = filterReceiptViolations(violations); const firstViolation = filteredViolations.at(0); const isIOUActionType = isMoneyRequestAction(action); - const canEdit = isIOUActionType && canEditMoneyRequest(action, transaction, isChatReportArchived, report, policy, archivedReportsIDSet); + const canEdit = isIOUActionType && canEditMoneyRequest(action, transaction, isChatReportArchived, report, policy); const companyCardPageURL = `${environmentURL}/${ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(report?.policyID)}`; const {personalCardsWithBrokenConnection} = useCardFeedErrors(); const connectionLink = getBrokenConnectionUrlToFixPersonalCard(personalCardsWithBrokenConnection, environmentURL); diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index a2aab61d71e4..0d8b917f62fb 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1060,11 +1060,6 @@ function Search({ return; } - if (isMobileSelectionModeEnabled) { - toggleTransaction(item); - return; - } - const isTransactionItem = isTransactionListItemType(item); const backTo = Navigation.getActiveRoute(); // If we're trying to open a transaction without a transaction thread, let's create the thread and navigate the user diff --git a/src/hooks/useShowNotFoundPageInIOUStep.ts b/src/hooks/useShowNotFoundPageInIOUStep.ts index 17ddd3968de4..9f58a84a705c 100644 --- a/src/hooks/useShowNotFoundPageInIOUStep.ts +++ b/src/hooks/useShowNotFoundPageInIOUStep.ts @@ -8,7 +8,6 @@ import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {OnyxInputOrEntry, Report, ReportAction, ReportActions, Transaction} from '@src/types/onyx'; -import useArchivedReportsIdSet from './useArchivedReportsIDSet'; import useOnyx from './useOnyx'; /** @@ -26,7 +25,6 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor const [session] = useOnyx(ONYXKEYS.SESSION); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(transaction?.reportID)}`); - const archivedReportsIDSet = useArchivedReportsIdSet(); const reportActionsReportID = useMemo(() => { let actionsReportID; @@ -64,7 +62,7 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor } else if (isSplitExpense) { shouldShowNotFoundPage = !canEditSplitExpense; } else { - shouldShowNotFoundPage = !isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, transaction, false, iouReport, policy, archivedReportsIDSet); + shouldShowNotFoundPage = !isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, transaction, false, iouReport, policy); } } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f437727b9e96..00b7b637088d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4708,7 +4708,6 @@ function canEditMoneyRequest( isChatReportArchived = false, report?: OnyxInputOrEntry, policy?: OnyxEntry, - archivedReportsIDSet?: ArchivedReportsIDSet, ): boolean { const isDeleted = isDeletedAction(reportAction); @@ -4746,9 +4745,6 @@ function canEditMoneyRequest( } const moneyRequestReport = report ?? getReportOrDraftReport(String(moneyRequestReportID)); - const isCurrentReportArchived = archivedReportsIDSet - ? isReportArchivedByID(archivedReportsIDSet, moneyRequestReport?.reportID) - : isArchivedReport(allReportNameValuePair?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${moneyRequestReport?.reportID}`]); const isSubmitted = isProcessingReport(moneyRequestReport); if (isIOUReport(moneyRequestReport)) { @@ -4760,7 +4756,7 @@ function canEditMoneyRequest( const isAdmin = reportPolicy?.role === CONST.POLICY.ROLE.ADMIN; const isManager = deprecatedCurrentUserAccountID === moneyRequestReport?.managerID; - if (isInvoiceReport(moneyRequestReport) && (isManager || isChatReportArchived || isCurrentReportArchived)) { + if (isInvoiceReport(moneyRequestReport) && (isManager || isChatReportArchived)) { return false; } @@ -4943,6 +4939,7 @@ function canEditFieldOfMoneyRequest({ transaction: OnyxEntry; report?: OnyxInputOrEntry; policy?: OnyxEntry; + // TODO: Make this required after all callers are migrated in https://github.com/Expensify/App/issues/66422. archivedReportsIDSet?: ArchivedReportsIDSet; }): boolean { // A list of fields that cannot be edited by anyone, once an expense has been settled @@ -4959,7 +4956,7 @@ function canEditFieldOfMoneyRequest({ CONST.EDIT_REQUEST_FIELD.BILLABLE, ]; - if (!isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, transaction, isChatReportArchived, report, policy, archivedReportsIDSet)) { + if (!isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction, transaction, isChatReportArchived, report, policy)) { return false; } @@ -11655,7 +11652,7 @@ function hasForwardedAction(reportID: string): boolean { return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); } -function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: string | undefined, archivedReportsIDSet?: ArchivedReportsIDSet, allowSubmitted = true): boolean { +function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: string | undefined, archivedReportsIDSet: ArchivedReportsIDSet | undefined, allowSubmitted = true): boolean { if ( !iouReport || isEmptyObject(iouReport) || @@ -11690,14 +11687,16 @@ function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: stri function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, - reports: OnyxCollection = allReports, - archivedReportsIDSet?: ArchivedReportsIDSet, + reports: OnyxCollection | undefined, + archivedReportsIDSet: ArchivedReportsIDSet | undefined, allowSubmitted = true, ): Array> { - if (!reports) { + const reportsToCheck = reports ?? allReports; + + if (!reportsToCheck) { return []; } - return Object.values(reports).filter( + return Object.values(reportsToCheck).filter( (report) => report?.pendingFields?.preview !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isReportOutstanding(report, policyID, archivedReportsIDSet, allowSubmitted) && From e57314c1b1bb1a44e61c3ab6bf966b0a9c9676aa Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:04:17 +0530 Subject: [PATCH 44/78] Fix ReportUtils optional params --- src/libs/ReportUtils.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 54b0ee76394a..75f62aab7352 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -11570,7 +11570,7 @@ function hasForwardedAction(reportID: string): boolean { return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); } -function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: string | undefined, archivedReportsIDSet: ArchivedReportsIDSet | undefined, allowSubmitted = true): boolean { +function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: string | undefined, archivedReportsIDSet?: ArchivedReportsIDSet, allowSubmitted = true): boolean { if ( !iouReport || isEmptyObject(iouReport) || @@ -11605,14 +11605,16 @@ function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: stri function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, - reports: OnyxCollection = deprecatedAllReports, - archivedReportsIDSet: ArchivedReportsIDSet | undefined, + reports?: OnyxCollection, + archivedReportsIDSet?: ArchivedReportsIDSet, allowSubmitted = true, ): Array> { - if (!reports) { + const reportsToCheck = reports ?? deprecatedAllReports; + + if (!reportsToCheck) { return []; } - return Object.values(reports).filter( + return Object.values(reportsToCheck).filter( (report) => report?.pendingFields?.preview !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isReportOutstanding(report, policyID, archivedReportsIDSet, allowSubmitted) && From 3a8d51fde28827bc1f39b28e114ccf260c8896ff Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 11 Apr 2026 00:20:24 +0530 Subject: [PATCH 45/78] Updates --- src/DeepLinkHandler.tsx | 82 ++++++++++++++++++++++++++++++++++++++++ src/Expensify.tsx | 58 ++-------------------------- src/libs/actions/Link.ts | 4 +- 3 files changed, 86 insertions(+), 58 deletions(-) create mode 100644 src/DeepLinkHandler.tsx diff --git a/src/DeepLinkHandler.tsx b/src/DeepLinkHandler.tsx new file mode 100644 index 000000000000..81b35023ba35 --- /dev/null +++ b/src/DeepLinkHandler.tsx @@ -0,0 +1,82 @@ +import {useEffect, useRef} from 'react'; +import type {NativeEventSubscription} from 'react-native'; +import {Linking} from 'react-native'; +import CONST from './CONST'; +import useIsAuthenticated from './hooks/useIsAuthenticated'; +import useOnyx from './hooks/useOnyx'; +import {openReportFromDeepLink} from './libs/actions/Link'; +import * as Report from './libs/actions/Report'; +import {hasAuthToken} from './libs/actions/Session'; +import Log from './libs/Log'; +import {endSpan} from './libs/telemetry/activeSpans'; +import ONYXKEYS from './ONYXKEYS'; +import type {Route} from './ROUTES'; +import {hasSeenTourSelector} from './selectors/Onboarding'; +import isLoadingOnyxValue from './types/utils/isLoadingOnyxValue'; + +type DeepLinkHandlerProps = { + /** Callback to set the initial URL resolved from deep linking */ + onInitialUrl: (url: Route | null) => void; +}; + +/** + * Component that does not render anything but isolates the COLLECTION.REPORT Onyx subscription + * from the root Expensify component to prevent cascading re-renders of the + * entire navigation tree on every report change. + */ +function DeepLinkHandler({onInitialUrl}: DeepLinkHandlerProps) { + const linkingChangeListener = useRef(null); + + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + const [, sessionMetadata] = useOnyx(ONYXKEYS.SESSION); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); + const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); + const [isSelfTourViewed, isSelfTourViewedMetadata] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); + const [betas] = useOnyx(ONYXKEYS.BETAS); + const isAuthenticated = useIsAuthenticated(); + + useEffect(() => { + if (isLoadingOnyxValue(sessionMetadata, isSelfTourViewedMetadata)) { + return; + } + // If the app is opened from a deep link, get the reportID (if exists) from the deep link and navigate to the chat report + Linking.getInitialURL().then((url) => { + onInitialUrl(url as Route); + + if (url) { + if (conciergeReportID === undefined) { + Log.info('[Deep link] conciergeReportID is undefined when processing initial URL', false, {url}); + } + if (introSelected === undefined) { + Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); + } + openReportFromDeepLink(url, allReports, isAuthenticated, conciergeReportID, introSelected, isSelfTourViewed, betas); + } else { + Report.doneCheckingPublicRoom(); + } + + endSpan(CONST.TELEMETRY.SPAN_BOOTSPLASH.DEEP_LINK); + }); + + // Open chat report from a deep link (only mobile native) + linkingChangeListener.current = Linking.addEventListener('url', (state) => { + if (conciergeReportID === undefined) { + Log.info('[Deep link] conciergeReportID is undefined when processing URL change', false, {url: state.url}); + } + if (introSelected === undefined) { + Log.info('[Deep link] introSelected is undefined when processing URL change', false, {url: state.url}); + } + const isCurrentlyAuthenticated = hasAuthToken(); + openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, isSelfTourViewed, betas); + }); + + return () => { + linkingChangeListener.current?.remove(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally excluding allReports, isAuthenticated, and onInitialUrl to avoid re-triggering deep link handling on every report update + }, [sessionMetadata?.status, conciergeReportID, introSelected, isSelfTourViewedMetadata, betas]); + + return null; +} + +export default DeepLinkHandler; diff --git a/src/Expensify.tsx b/src/Expensify.tsx index e81a4ad2eac6..9c5f021b57bb 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -2,25 +2,22 @@ import HybridAppModule from '@expensify/react-native-hybrid-app'; import type * as Sentry from '@sentry/react-native'; import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react'; import type {NativeEventSubscription} from 'react-native'; -import {AppState, Linking, Platform} from 'react-native'; +import {AppState, Platform} from 'react-native'; import Onyx from 'react-native-onyx'; import {useInitialURLActions} from './components/InitialURLContextProvider'; import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper'; import SplashScreenHider from './components/SplashScreenHider'; import CONFIG from './CONFIG'; import CONST from './CONST'; +import DeepLinkHandler from './DeepLinkHandler'; import DelegateAccessHandler from './DelegateAccessHandler'; import FullstoryInitHandler from './FullstoryInitHandler'; import GlobalModals from './GlobalModals'; -import useArchivedReportsIdSet from './hooks/useArchivedReportsIDSet'; import useDebugShortcut from './hooks/useDebugShortcut'; import useIsAuthenticated from './hooks/useIsAuthenticated'; import useLocalize from './hooks/useLocalize'; import useOnyx from './hooks/useOnyx'; import {updateLastRoute} from './libs/actions/App'; -import {openReportFromDeepLink} from './libs/actions/Link'; -import * as Report from './libs/actions/Report'; -import {hasAuthToken} from './libs/actions/Session'; import * as ActiveClientManager from './libs/ActiveClientManager'; import {isSafari} from './libs/Browser'; import Log from './libs/Log'; @@ -37,10 +34,8 @@ import Visibility from './libs/Visibility'; import ONYXKEYS from './ONYXKEYS'; import PriorityModeHandler from './PriorityModeHandler'; import type {Route} from './ROUTES'; -import {hasSeenTourSelector} from './selectors/Onboarding'; import {accountIDSelector} from './selectors/Session'; import {useSplashScreenActions, useSplashScreenState} from './SplashScreenStateContext'; -import isLoadingOnyxValue from './types/utils/isLoadingOnyxValue'; Onyx.registerLogger(({level, message, parameters}) => { if (level === 'alert') { @@ -55,7 +50,6 @@ Onyx.registerLogger(({level, message, parameters}) => { function Expensify() { const appStateChangeListener = useRef(null); - const linkingChangeListener = useRef(null); const [isNavigationReady, setIsNavigationReady] = useState(false); const [isOnyxMigrated, setIsOnyxMigrated] = useState(false); const {splashScreenState} = useSplashScreenState(); @@ -63,17 +57,10 @@ function Expensify() { const [hasAttemptedToOpenPublicRoom, setAttemptedToOpenPublicRoom] = useState(false); const {preferredLocale} = useLocalize(); const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: accountIDSelector}); - const [, sessionMetadata] = useOnyx(ONYXKEYS.SESSION); const [lastRoute] = useOnyx(ONYXKEYS.LAST_ROUTE); - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [isCheckingPublicRoom = true] = useOnyx(ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, {initWithStoredValues: false}); - const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); - const [isSelfTourViewed, isSelfTourViewedMetadata] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); - const [betas] = useOnyx(ONYXKEYS.BETAS); const [updateRequired] = useOnyx(ONYXKEYS.RAM_ONLY_UPDATE_REQUIRED); const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH); - const archivedReportsIDSet = useArchivedReportsIdSet(); useDebugShortcut(); @@ -280,46 +267,6 @@ function Expensify() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isNavigationReady]); - useEffect(() => { - if (isLoadingOnyxValue(sessionMetadata, isSelfTourViewedMetadata)) { - return; - } - - Linking.getInitialURL().then((url) => { - setInitialUrl(url as Route); - - if (url) { - if (conciergeReportID === undefined) { - Log.info('[Deep link] conciergeReportID is undefined when processing initial URL', false, {url}); - } - if (introSelected === undefined) { - Log.info('[Deep link] introSelected is undefined when processing initial URL', false, {url}); - } - openReportFromDeepLink(url, allReports, isAuthenticated, conciergeReportID, introSelected, isSelfTourViewed, archivedReportsIDSet, betas); - } else { - Report.doneCheckingPublicRoom(); - } - - endSpan(CONST.TELEMETRY.SPAN_BOOTSPLASH.DEEP_LINK); - }); - - linkingChangeListener.current = Linking.addEventListener('url', (state) => { - if (conciergeReportID === undefined) { - Log.info('[Deep link] conciergeReportID is undefined when processing URL change', false, {url: state.url}); - } - if (introSelected === undefined) { - Log.info('[Deep link] introSelected is undefined when processing URL change', false, {url: state.url}); - } - const isCurrentlyAuthenticated = hasAuthToken(); - openReportFromDeepLink(state.url, allReports, isCurrentlyAuthenticated, conciergeReportID, introSelected, isSelfTourViewed, archivedReportsIDSet, betas); - }); - - return () => { - linkingChangeListener.current?.remove(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps -- this bootstrap should only rerun when session metadata or concierge routing changes - }, [sessionMetadata?.status, conciergeReportID, isSelfTourViewedMetadata]); - // Display a blank page until the onyx migration completes if (!isOnyxMigrated) { return null; @@ -335,6 +282,7 @@ function Expensify() { + {hasAttemptedToOpenPublicRoom && ( , isSelfTourViewed: boolean | undefined, - archivedReportsIDSet: ArchivedReportsIDSet, betas: OnyxEntry, ) { const reportID = getReportIDFromLink(url); @@ -339,7 +337,7 @@ function openReportFromDeepLink( const report = reportParam ?? reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; // If the report does not exist, navigate to the last accessed report or Concierge chat if (reportID && (!report?.reportID || report.errorFields?.notFound)) { - const lastAccessedReportID = findLastAccessedReport(false, shouldOpenOnAdminRoom(), reportID, archivedReportsIDSet)?.reportID; + const lastAccessedReportID = findLastAccessedReport(false, shouldOpenOnAdminRoom(), reportID)?.reportID; if (lastAccessedReportID) { const lastAccessedReportRoute = ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID); Navigation.navigate(lastAccessedReportRoute, {forceReplace: Navigation.getTopmostReportId() === reportID}); From 5622a6249e6cfff4ae1f7f7af582c96c6dd0b535 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 11 Apr 2026 04:31:09 +0530 Subject: [PATCH 46/78] Pass archived report set via props --- src/components/MoneyReportHeader.tsx | 12 +++++++----- .../MoneyRequestReportActionsList.tsx | 8 +++++++- .../MoneyRequestReportView.tsx | 15 +++++++++++---- .../ReportActionItem/MoneyRequestView.tsx | 10 ++++++---- src/hooks/useActionListContextValue.ts | 5 ++--- .../Search/SearchMoneyRequestReportPage.tsx | 3 ++- .../TransactionDuplicate/Confirmation.tsx | 3 +++ .../TransactionMerge/ConfirmationPage.tsx | 3 +++ src/pages/inbox/ReportActionsList.tsx | 17 ++++++++++++++--- src/pages/inbox/ReportHeader.tsx | 6 ++++-- src/pages/inbox/ReportScreen.tsx | 8 ++++---- src/pages/inbox/ReportScreenContext.ts | 4 +--- .../inbox/report/PureReportActionItem.tsx | 19 ++++++++++++++++++- .../ReportActionCompose.tsx | 8 ++++++-- src/pages/inbox/report/ReportActionItem.tsx | 4 ++++ .../report/ReportActionItemContentCreated.tsx | 7 +++++++ .../report/ReportActionItemParentAction.tsx | 6 ++++++ src/pages/inbox/report/ReportActionsList.tsx | 7 +++++++ .../report/ReportActionsListItemRenderer.tsx | 7 +++++++ src/pages/inbox/report/ReportActionsView.tsx | 7 ++++++- src/pages/inbox/report/ReportFooter.tsx | 8 ++++++-- .../ReportActionCompose.perf-test.tsx | 5 ++++- .../perf-test/ReportActionsList.perf-test.tsx | 2 +- ...equestReportActionsListRejectModalTest.tsx | 5 ++++- tests/ui/ReportActionsViewTest.tsx | 7 ++++++- 25 files changed, 146 insertions(+), 40 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 700e575087d6..dbab3c805cb2 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -96,6 +96,7 @@ import { navigateToDetailsPage, shouldBlockSubmitDueToStrictPolicyRules, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import shouldPopoverUseScrollView from '@libs/shouldPopoverUseScrollView'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import { @@ -109,7 +110,6 @@ import { isPerDiemRequest, isTransactionPendingDelete, } from '@libs/TransactionUtils'; -import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import { approveMoneyRequest, canApproveIOU, @@ -161,21 +161,25 @@ type MoneyReportHeaderProps = { /** Method to trigger when pressing close button of the header */ onBackButtonPress: () => void; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; -function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackButtonPress}: MoneyReportHeaderProps) { +function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackButtonPress, archivedReportsIDSet}: MoneyReportHeaderProps) { return ( ); } -function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButton = false, onBackButtonPress}: MoneyReportHeaderProps) { +function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButton = false, onBackButtonPress, archivedReportsIDSet}: MoneyReportHeaderProps) { const [moneyRequestReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDProp}`); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getNonEmptyStringOnyxID(moneyRequestReport?.policyID)}`); const {reportActions: unfilteredReportActions} = usePaginatedReportActions(moneyRequestReport?.reportID); @@ -406,8 +410,6 @@ function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButt const isArchivedReport = useReportIsArchived(moneyRequestReport?.reportID); const isChatReportArchived = useReportIsArchived(chatReport?.reportID); - const {archivedReportsIDSet} = useContext(ActionListContext); - const canMoveSingleExpense = useMemo(() => { if (nonPendingDeleteTransactions.length !== 1) { return false; diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index d477a372e2e2..00e9c7195700 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -74,6 +74,7 @@ import { isHarvestCreatedExpenseReport, isUnread, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import shouldPopoverUseScrollView from '@libs/shouldPopoverUseScrollView'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; @@ -117,9 +118,12 @@ type MoneyRequestReportListProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; -function MoneyRequestReportActionsList({reportID: reportIDProp, onLayout}: MoneyRequestReportListProps) { +function MoneyRequestReportActionsList({reportID: reportIDProp, onLayout, archivedReportsIDSet}: MoneyRequestReportListProps) { const styles = useThemeStyles(); const {translate, getLocalDateFromDatetime} = useLocalize(); const {isOffline, lastOfflineAt, lastOnlineAt} = useNetworkWithOfflineStatus(); @@ -801,6 +805,7 @@ function MoneyRequestReportActionsList({reportID: reportIDProp, onLayout}: Money isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairs?.origin} reportNameValuePairsOriginalID={reportNameValuePairs?.originalID} + archivedReportsIDSet={archivedReportsIDSet} /> ); }, @@ -822,6 +827,7 @@ function MoneyRequestReportActionsList({reportID: reportIDProp, onLayout}: Money isReportArchived, reportNameValuePairs?.origin, reportNameValuePairs?.originalID, + archivedReportsIDSet, ], ); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index c749d8469ab0..d50655fe82f0 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -26,6 +26,7 @@ import navigationRef from '@libs/Navigation/navigationRef'; import {getFilteredReportActionsForReportView, getOneTransactionThreadReportID} from '@libs/ReportActionsUtils'; import {getReportOfflinePendingActionAndErrors, isReportTransactionThread} from '@libs/ReportUtils'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {cancelSpan} from '@libs/telemetry/activeSpans'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; @@ -58,6 +59,9 @@ type MoneyRequestReportViewProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; function goBackFromSearchMoneyRequest() { @@ -104,7 +108,7 @@ function InitialLoadingSkeleton({styles, onLayout, reasonAttributes}: {styles: T ); } -function MoneyRequestReportView({report, reportMetadata, shouldDisplayReportFooter, backToRoute, onLayout}: MoneyRequestReportViewProps) { +function MoneyRequestReportView({report, reportMetadata, shouldDisplayReportFooter, backToRoute, onLayout, archivedReportsIDSet}: MoneyRequestReportViewProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); @@ -181,6 +185,7 @@ function MoneyRequestReportView({report, reportMetadata, shouldDisplayReportFoot { if (!backToRoute) { goBackFromSearchMoneyRequest(); @@ -190,7 +195,7 @@ function MoneyRequestReportView({report, reportMetadata, shouldDisplayReportFoot }} /> ), - [backToRoute, isTransactionThreadView, report?.reportID], + [archivedReportsIDSet, backToRoute, isTransactionThreadView, report?.reportID], ); // We need to cancel telemetry span when user leaves the screen before full report data is loaded @@ -234,7 +239,7 @@ function MoneyRequestReportView({report, reportMetadata, shouldDisplayReportFoot - {shouldDisplayReportFooter ? : null} + {shouldDisplayReportFooter ? : null} ); } @@ -276,16 +281,18 @@ function MoneyRequestReportView({report, reportMetadata, shouldDisplayReportFoot ) : ( )} {shouldDisplayReportFooter ? ( <> - + ) : null} diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 3388228ea0c5..eaf9301a2491 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -1,5 +1,5 @@ import {Str} from 'expensify-common'; -import React, {useContext, useMemo, useState} from 'react'; +import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; @@ -80,6 +80,7 @@ import { isTrackExpenseReportNew, shouldEnableNegative, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {hasEnabledTags, shouldShowDependentTagList} from '@libs/TagsOptionsListUtils'; import { getBillable, @@ -116,7 +117,6 @@ import {isInvalidMerchantValue} from '@libs/ValidationUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import Navigation from '@navigation/Navigation'; import AnimatedEmptyStateBackground from '@pages/inbox/report/AnimatedEmptyStateBackground'; -import {ActionListContext} from '@pages/inbox/ReportScreenContext'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -149,6 +149,9 @@ type MoneyRequestViewProps = { /** Merge transaction ID to show in merge transaction flow */ mergeTransactionID?: string; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; const perDiemPoliciesSelector = (policies: OnyxCollection) => { @@ -171,6 +174,7 @@ function MoneyRequestView({ updatedTransaction, isFromReviewDuplicates = false, mergeTransactionID, + archivedReportsIDSet, }: MoneyRequestViewProps) { const icons = useMemoizedLazyExpensifyIcons(['DotIndicator', 'Checkmark', 'Suitcase']); const styles = useThemeStyles(); @@ -325,8 +329,6 @@ function MoneyRequestView({ const isChatReportArchived = useReportIsArchived(moneyRequestReport?.chatReportID); const pendingAction = transaction?.pendingAction; const shouldShowPaid = isSettled && transactionReimbursable && !pendingAction; - const {archivedReportsIDSet} = useContext(ActionListContext); - // Flags for allowing or disallowing editing an expense // Used for non-restricted fields such as: description, category, tag, billable, etc... const isReportArchived = useReportIsArchived(transactionThreadReport?.reportID); diff --git a/src/hooks/useActionListContextValue.ts b/src/hooks/useActionListContextValue.ts index fc176df77c10..ba73ff804cc0 100644 --- a/src/hooks/useActionListContextValue.ts +++ b/src/hooks/useActionListContextValue.ts @@ -1,14 +1,13 @@ import {useRef} from 'react'; import type {FlatList} from 'react-native'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import type {ActionListContextType, ScrollPosition} from '@pages/inbox/ReportScreenContext'; -function useActionListContextValue(archivedReportsIDSet: ArchivedReportsIDSet): ActionListContextType { +function useActionListContextValue(): ActionListContextType { const flatListRef = useRef(null); const scrollPositionRef = useRef({}); const scrollOffsetRef = useRef(0); - return {flatListRef, scrollPositionRef, scrollOffsetRef, archivedReportsIDSet}; + return {flatListRef, scrollPositionRef, scrollOffsetRef}; } export default useActionListContextValue; diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index df4fdfb6a734..6542fca8a8ec 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -142,7 +142,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute, isReportArchived); const archivedReportsIDSet = useArchivedReportsIDSet(); - const actionListValue = useActionListContextValue(archivedReportsIDSet); + const actionListValue = useActionListContextValue(); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); @@ -408,6 +408,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { key={report?.reportID} onLayout={handleSubmitToDestinationVisibleLayout} backToRoute={route.params.backTo} + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index abd77d80d6af..e47d93a3a883 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -13,6 +13,7 @@ import ScrollView from '@components/ScrollView'; import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import {useWideRHPState} from '@components/WideRHPContextProvider'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -40,6 +41,7 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; function Confirmation() { const styles = useThemeStyles(); const {translate} = useLocalize(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const route = useRoute>(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [reviewDuplicates, reviewDuplicatesResult] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); @@ -167,6 +169,7 @@ function Confirmation() { readonly updatedTransaction={newTransaction as OnyxEntry} isFromReviewDuplicates + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/TransactionMerge/ConfirmationPage.tsx b/src/pages/TransactionMerge/ConfirmationPage.tsx index 6bc398a43166..371a9b7ddf3b 100644 --- a/src/pages/TransactionMerge/ConfirmationPage.tsx +++ b/src/pages/TransactionMerge/ConfirmationPage.tsx @@ -10,6 +10,7 @@ import MoneyRequestView from '@components/ReportActionItem/MoneyRequestView'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useMergeTransactions from '@hooks/useMergeTransactions'; @@ -39,6 +40,7 @@ function ConfirmationPage({route}: ConfirmationPageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [isMergingExpenses, setIsMergingExpenses] = useState(false); + const archivedReportsIDSet = useArchivedReportsIDSet(); const {transactionID, isOnSearch, backTo} = route.params; const [mergeTransaction, mergeTransactionMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.MERGE_TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`); @@ -142,6 +144,7 @@ function ConfirmationPage({route}: ConfirmationPageProps) { readonly updatedTransaction={mergedTransactionData as unknown as OnyxEntry} mergeTransactionID={transactionID} + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/inbox/ReportActionsList.tsx b/src/pages/inbox/ReportActionsList.tsx index 9746b422f88c..e3545bd8675c 100644 --- a/src/pages/inbox/ReportActionsList.tsx +++ b/src/pages/inbox/ReportActionsList.tsx @@ -9,6 +9,7 @@ import useReportTransactionsCollection from '@hooks/useReportTransactionsCollect import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getAllNonDeletedTransactions, shouldDisplayReportTableView, shouldWaitForTransactions as shouldWaitForTransactionsUtil} from '@libs/MoneyRequestReportUtils'; import {isInvoiceReport, isMoneyRequestReport} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ReportActionsView from './report/ReportActionsView'; @@ -27,7 +28,7 @@ const defaultReportMetadata = { * or MoneyRequestReportActionsList. Only subscribes to what the branching * conditions need — heavy data derivation is pushed into each child. */ -function ReportActionsList() { +function ReportActionsList({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { const route = useRoute(); const routeParams = route.params as {reportID?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -50,10 +51,20 @@ function ReportActionsList() { } if (shouldDisplayMoneyRequestActionsList) { - return ; + return ( + + ); } - return ; + return ( + + ); } export default ReportActionsList; diff --git a/src/pages/inbox/ReportHeader.tsx b/src/pages/inbox/ReportHeader.tsx index 05bca59454ad..9cf66fa3ed6d 100644 --- a/src/pages/inbox/ReportHeader.tsx +++ b/src/pages/inbox/ReportHeader.tsx @@ -13,6 +13,7 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Navigation from '@libs/Navigation/Navigation'; import {getReportName} from '@libs/ReportNameUtils'; import {getReportOfflinePendingActionAndErrors, isInvoiceReport, isMoneyRequestReport, isReportTransactionThread} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; @@ -22,9 +23,9 @@ import HeaderView from './HeaderView'; /** * Owns header variant selection, back button logic, and OfflineWithFeedback wrapper. - * Subscribes to report type internally — ReportScreen passes nothing. + * Subscribes to report type internally. */ -function ReportHeader() { +function ReportHeader({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { const route = useRoute(); const routeParams = route.params as {reportID?: string; backTo?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -97,6 +98,7 @@ function ReportHeader() { ); diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index 278129123290..046acc9425d8 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -55,7 +55,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { CONST.TELEMETRY.SUBMIT_TO_DESTINATION_VISIBLE_TRIGGER.FOCUS, ); - const actionListValue = useActionListContextValue(archivedReportsIDSet); + const actionListValue = useActionListContextValue(); return ( @@ -75,7 +75,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { - + @@ -84,8 +84,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) { style={[styles.flex1, styles.justifyContentEnd, styles.overflowHidden]} testID="report-actions-view-wrapper" > - - + + diff --git a/src/pages/inbox/ReportScreenContext.ts b/src/pages/inbox/ReportScreenContext.ts index 83f6dd9d1ddc..50b1e3e707f9 100644 --- a/src/pages/inbox/ReportScreenContext.ts +++ b/src/pages/inbox/ReportScreenContext.ts @@ -2,7 +2,6 @@ import type {RefObject, SyntheticEvent} from 'react'; import {createContext} from 'react'; // eslint-disable-next-line no-restricted-imports import type {FlatList, GestureResponderEvent, Text, View} from 'react-native'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; type ReactionListAnchor = View | Text | HTMLDivElement | null; @@ -22,11 +21,10 @@ type ActionListContextType = { flatListRef: FlatListRefType; scrollPositionRef: RefObject; scrollOffsetRef: RefObject; - archivedReportsIDSet: ArchivedReportsIDSet; }; type ReactionListContextType = RefObject | null; -const ActionListContext = createContext({flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}, archivedReportsIDSet: new Set()}); +const ActionListContext = createContext({flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}}); const ReactionListContext = createContext(null); export {ActionListContext, ReactionListContext}; diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index ca19344d56b7..99784c0d4ef0 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -231,6 +231,7 @@ import { isTaskReport, shouldDisplayThreadReplies as shouldDisplayThreadRepliesUtils, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import SelectionScraper from '@libs/SelectionScraper'; import shouldRenderAddPaymentCard from '@libs/shouldRenderAppPaymentCard'; import {ReactionListContext} from '@pages/inbox/ReportScreenContext'; @@ -484,8 +485,13 @@ type PureReportActionItemProps = { /** The billing grace end period's shared NVP collection */ userBillingGracePeriodEnds: OnyxCollection; + + /** Set of archived report ID keys */ + archivedReportsIDSet?: ArchivedReportsIDSet; }; +const EMPTY_ARCHIVED_REPORTS_ID_SET: ArchivedReportsIDSet = new Set(); + // This is equivalent to returning a negative boolean in normal functions, but we can keep the element return type // If the child was rendered using RenderHTML and an empty html string, it has an empty prop called html // If we render an empty component/fragment, this does not apply @@ -554,6 +560,7 @@ function PureReportActionItem({ reportNameValuePairsOriginalID, reportMetadata, userBillingGracePeriodEnds, + archivedReportsIDSet = EMPTY_ARCHIVED_REPORTS_ID_SET, }: PureReportActionItemProps) { const isConciergeGreeting = action.reportActionID === CONST.CONCIERGE_GREETING_ACTION_ID; const shouldDisplayContextMenuValue = shouldDisplayContextMenu && !isConciergeGreeting; @@ -2014,9 +2021,19 @@ function PureReportActionItem({ transactionID={transactionID} draftMessage={draftMessage} shouldHideThreadDividerLine={shouldHideThreadDividerLine} + archivedReportsIDSet={archivedReportsIDSet} /> ); - }, [contextMenuStateValue, contextMenuActionsValue, parentReportAction, parentReport, draftMessage, shouldHideThreadDividerLine, parentReportActionForTransactionThread]); + }, [ + contextMenuStateValue, + contextMenuActionsValue, + parentReportAction, + parentReport, + draftMessage, + shouldHideThreadDividerLine, + parentReportActionForTransactionThread, + archivedReportsIDSet, + ]); if (action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && !isHarvestCreatedExpenseReport) { return createdActionContent; diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index 65d4ebcaee0c..ff9f20ac2813 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -80,6 +80,7 @@ import { isSettled, temporary_getMoneyRequestOptions, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {startSpan} from '@libs/telemetry/activeSpans'; import {getTransactionID, hasReceipt as hasReceiptTransactionUtils} from '@libs/TransactionUtils'; import {generateAccountID} from '@libs/UserUtils'; @@ -116,6 +117,9 @@ type SuggestionsRef = { type ReportActionComposeProps = { /** The ID of the report this composer is for */ reportID: string; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; function AgentZeroAwareTypingIndicator({reportID}: {reportID: string}) { @@ -148,7 +152,7 @@ function getRandomPlaceholder(translate: LocalizedTranslate): string { // eslint-disable-next-line import/no-mutable-exports let onSubmitAction = noop; -function ReportActionCompose({reportID}: ReportActionComposeProps) { +function ReportActionCompose({reportID, archivedReportsIDSet}: ReportActionComposeProps) { const styles = useThemeStyles(); const theme = useTheme(); const {translate, preferredLocale} = useLocalize(); @@ -228,7 +232,7 @@ function ReportActionCompose({reportID}: ReportActionComposeProps) { const [targetReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${effectiveTransactionThreadReportID ?? reportID}`); const reportAncestors = useAncestors(report); const targetReportAncestors = useAncestors(targetReport); - const {scrollOffsetRef, archivedReportsIDSet} = useContext(ActionListContext); + const {scrollOffsetRef} = useContext(ActionListContext); /** * Updates the Highlight state of the composer diff --git a/src/pages/inbox/report/ReportActionItem.tsx b/src/pages/inbox/report/ReportActionItem.tsx index 114e4491d4e7..1c23c72374b3 100644 --- a/src/pages/inbox/report/ReportActionItem.tsx +++ b/src/pages/inbox/report/ReportActionItem.tsx @@ -23,6 +23,7 @@ import { isClosedExpenseReportWithNoExpenses, isCurrentUserTheOnlyParticipant, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {clearAllRelatedReportActionErrors} from '@userActions/ClearReportActionErrors'; import { deleteReportActionDraft, @@ -73,6 +74,9 @@ type ReportActionItemProps = Omit< /** Did the user dismiss trying out NewDot? If true, it means they prefer using OldDot */ isTryNewDotNVPDismissed?: boolean; + + /** Set of archived report ID keys */ + archivedReportsIDSet?: ArchivedReportsIDSet; }; function ReportActionItem({ diff --git a/src/pages/inbox/report/ReportActionItemContentCreated.tsx b/src/pages/inbox/report/ReportActionItemContentCreated.tsx index aab07cc262f4..b1a691efc1b3 100644 --- a/src/pages/inbox/report/ReportActionItemContentCreated.tsx +++ b/src/pages/inbox/report/ReportActionItemContentCreated.tsx @@ -17,6 +17,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {isMessageDeleted, isReversedTransaction as isReversedTransactionReportActionsUtils, isTransactionThread} from '@libs/ReportActionsUtils'; import {isCanceledTaskReport, isExpenseReport, isInvoiceReport, isIOUReport, isTaskReport} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {getCurrency} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -48,6 +49,9 @@ type ReportActionItemContentCreatedProps = { /** Flag to show, hide the thread divider line */ shouldHideThreadDividerLine: boolean; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionItemContentCreated({ @@ -58,6 +62,7 @@ function ReportActionItemContentCreated({ transactionID, draftMessage, shouldHideThreadDividerLine, + archivedReportsIDSet, }: ReportActionItemContentCreatedProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -124,6 +129,7 @@ function ReportActionItemContentCreated({ parentReportID={report?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground + archivedReportsIDSet={archivedReportsIDSet} /> {renderThreadDivider} @@ -190,6 +196,7 @@ function ReportActionItemContentCreated({ parentReportID={transactionThreadReport?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground={false} + archivedReportsIDSet={archivedReportsIDSet} /> {renderThreadDivider} diff --git a/src/pages/inbox/report/ReportActionItemParentAction.tsx b/src/pages/inbox/report/ReportActionItemParentAction.tsx index 8d21b7af6445..a4e8614ec2df 100644 --- a/src/pages/inbox/report/ReportActionItemParentAction.tsx +++ b/src/pages/inbox/report/ReportActionItemParentAction.tsx @@ -20,6 +20,7 @@ import { navigateToLinkedReportAction, shouldExcludeAncestorReportAction, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Report, ReportAction, ReportActionReactions, ReportActions, ReportActionsDrafts, ReportNameValuePairs, Transaction} from '@src/types/onyx'; @@ -77,6 +78,9 @@ type ReportActionItemParentActionProps = { /** Whether the report is archived */ isReportArchived: boolean; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionItemParentAction({ @@ -95,6 +99,7 @@ function ReportActionItemParentAction({ userBillingFundID, isTryNewDotNVPDismissed = false, isReportArchived = false, + archivedReportsIDSet, }: ReportActionItemParentActionProps) { const styles = useThemeStyles(); const ancestors = useAncestors(report, shouldExcludeAncestorReportAction); @@ -260,6 +265,7 @@ function ReportActionItemParentAction({ linkedTransactionRouteError={linkedTransactionRouteError} userBillingFundID={userBillingFundID} isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} + archivedReportsIDSet={archivedReportsIDSet} /> ); diff --git a/src/pages/inbox/report/ReportActionsList.tsx b/src/pages/inbox/report/ReportActionsList.tsx index 32689dec4c96..2964df7b391f 100644 --- a/src/pages/inbox/report/ReportActionsList.tsx +++ b/src/pages/inbox/report/ReportActionsList.tsx @@ -59,6 +59,7 @@ import { isTaskReport, isUnread, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import Visibility from '@libs/Visibility'; import type {ReportsSplitNavigatorParamList} from '@navigation/types'; import ConciergeThinkingMessage from '@pages/home/report/ConciergeThinkingMessage'; @@ -133,6 +134,9 @@ type ReportActionsListProps = { /** Callback to show previous messages */ onShowPreviousMessages?: () => void; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; // In the component we are subscribing to the arrival of new actions. @@ -175,6 +179,7 @@ function ReportActionsList({ showHiddenHistory, hasPreviousMessages, onShowPreviousMessages, + archivedReportsIDSet, }: ReportActionsListProps) { const prevHasCreatedActionAdded = usePrevious(hasCreatedActionAdded); const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); @@ -726,6 +731,7 @@ function ReportActionsList({ isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairs?.origin} reportNameValuePairsOriginalID={reportNameValuePairs?.originalID} + archivedReportsIDSet={archivedReportsIDSet} /> ); @@ -750,6 +756,7 @@ function ReportActionsList({ isReportArchived, reportNameValuePairs?.origin, reportNameValuePairs?.originalID, + archivedReportsIDSet, reportActionsFromOnyx, isConciergeSidePanel, showHiddenHistory, diff --git a/src/pages/inbox/report/ReportActionsListItemRenderer.tsx b/src/pages/inbox/report/ReportActionsListItemRenderer.tsx index a6f38bec95b2..51dfae55ddbe 100644 --- a/src/pages/inbox/report/ReportActionsListItemRenderer.tsx +++ b/src/pages/inbox/report/ReportActionsListItemRenderer.tsx @@ -3,6 +3,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import useOnyx from '@hooks/useOnyx'; import {getOriginalMessage, isSentMoneyReportAction, isTransactionThread} from '@libs/ReportActionsUtils'; import {isChatThread} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Report, ReportAction} from '@src/types/onyx'; @@ -77,6 +78,9 @@ type ReportActionsListItemRendererProps = { /** Report name value pairs originalID */ reportNameValuePairsOriginalID?: string; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionsListItemRenderer({ @@ -103,6 +107,7 @@ function ReportActionsListItemRenderer({ isReportArchived = false, reportNameValuePairsOrigin, reportNameValuePairsOriginalID, + archivedReportsIDSet, }: ReportActionsListItemRendererProps) { const originalMessage = useMemo(() => getOriginalMessage(reportAction), [reportAction]); @@ -202,6 +207,7 @@ function ReportActionsListItemRenderer({ userBillingFundID={userBillingFundID} isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} isReportArchived={isReportArchived} + archivedReportsIDSet={archivedReportsIDSet} /> ); } @@ -230,6 +236,7 @@ function ReportActionsListItemRenderer({ isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairsOrigin} reportNameValuePairsOriginalID={reportNameValuePairsOriginalID} + archivedReportsIDSet={archivedReportsIDSet} /> ); } diff --git a/src/pages/inbox/report/ReportActionsView.tsx b/src/pages/inbox/report/ReportActionsView.tsx index 8f4ce82ebfd6..22e7ba415c06 100755 --- a/src/pages/inbox/report/ReportActionsView.tsx +++ b/src/pages/inbox/report/ReportActionsView.tsx @@ -49,6 +49,7 @@ import { isMoneyRequestReport, isReportTransactionThread as isReportTransactionThreadUtil, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -65,11 +66,14 @@ type ReportActionsViewProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; let listOldID = Math.round(Math.random() * 100); -function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) { +function ReportActionsView({reportID, onLayout, archivedReportsIDSet}: ReportActionsViewProps) { useCopySelectionHelper(); const {translate} = useLocalize(); usePendingConciergeResponse(reportID); @@ -420,6 +424,7 @@ function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) { showHiddenHistory={!showFullHistory} hasPreviousMessages={hasPreviousMessages} onShowPreviousMessages={handleShowPreviousMessages} + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/inbox/report/ReportFooter.tsx b/src/pages/inbox/report/ReportFooter.tsx index 10dbc816528c..f5e9e8473cf8 100644 --- a/src/pages/inbox/report/ReportFooter.tsx +++ b/src/pages/inbox/report/ReportFooter.tsx @@ -27,6 +27,7 @@ import { isPublicRoom, isSystemChat as isSystemChatUtil, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isLoadingInitialReportActionsSelector} from '@src/selectors/ReportMetaData'; @@ -40,7 +41,7 @@ const policyRoleSelector = (policy: OnyxEntry) => policy?.role * Footer component that decides between the composer and * archived/anonymous/blocked/system chat/admins-only footer. */ -function ReportFooter() { +function ReportFooter({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { const route = useRoute(); const routeParams = route.params as {reportID?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -91,7 +92,10 @@ function ReportFooter() { return ( - + ); diff --git a/tests/perf-test/ReportActionCompose.perf-test.tsx b/tests/perf-test/ReportActionCompose.perf-test.tsx index c1d514e5554e..8e6a7a05ad1b 100644 --- a/tests/perf-test/ReportActionCompose.perf-test.tsx +++ b/tests/perf-test/ReportActionCompose.perf-test.tsx @@ -88,7 +88,10 @@ beforeEach(() => { function ReportActionComposeWrapper() { return ( - + ()} + /> ); } diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index 9023e28896f8..b1dde7824cde 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -73,7 +73,7 @@ const mockOnLayout = jest.fn(); const mockOnScroll = jest.fn(); const mockLoadChats = jest.fn(); const mockReactionListRef = {current: null}; -const mockActionListContext = {flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}, archivedReportsIDSet: new Set()}; +const mockActionListContext = {flatListRef: null, scrollPositionRef: {current: {}}, scrollOffsetRef: {current: 0}}; const TEST_USER_ACCOUNT_ID = 1; const TEST_USER_LOGIN = 'test@test.com'; diff --git a/tests/ui/MoneyRequestReportActionsListRejectModalTest.tsx b/tests/ui/MoneyRequestReportActionsListRejectModalTest.tsx index 3ce3f714eecf..777cd8e6ed2e 100644 --- a/tests/ui/MoneyRequestReportActionsListRejectModalTest.tsx +++ b/tests/ui/MoneyRequestReportActionsListRejectModalTest.tsx @@ -194,7 +194,10 @@ const renderComponent = () => { - + ()} + /> , diff --git a/tests/ui/ReportActionsViewTest.tsx b/tests/ui/ReportActionsViewTest.tsx index a54817dec5fe..489f673d0b5c 100644 --- a/tests/ui/ReportActionsViewTest.tsx +++ b/tests/ui/ReportActionsViewTest.tsx @@ -147,7 +147,12 @@ const mockReportActions: OnyxTypes.ReportAction[] = [ const renderReportActionsView = (props: {reportID?: string} = {}) => { const reportID = props.reportID ?? mockReport.reportID; - return render(); + return render( + ()} + />, + ); }; describe('ReportActionsView', () => { From 5f2c0d20a314f979113d284735104be592ac4ef3 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 11 Apr 2026 09:15:56 +0530 Subject: [PATCH 47/78] Fix archived report set test props --- tests/perf-test/ReportActionsList.perf-test.tsx | 2 ++ tests/ui/MoneyRequestViewTest.tsx | 2 ++ tests/ui/ReportActionComposeTest.tsx | 1 + 3 files changed, 5 insertions(+) diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index b1dde7824cde..d2f9fe2f10a3 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -84,6 +84,7 @@ const signUpWithTestUser = () => { const report = createRandomReport(1, undefined); const parentReportAction = createRandomReportAction(1); +const archivedReportsIDSet = new Set(); beforeEach(() => { // Initialize the network key for OfflineWithFeedback @@ -115,6 +116,7 @@ function ReportActionsListWrapper() { loadOlderChats={mockLoadChats} loadNewerChats={mockLoadChats} transactionThreadReport={report} + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/tests/ui/MoneyRequestViewTest.tsx b/tests/ui/MoneyRequestViewTest.tsx index 3b525b2b27c6..93ec988ba8e0 100644 --- a/tests/ui/MoneyRequestViewTest.tsx +++ b/tests/ui/MoneyRequestViewTest.tsx @@ -64,6 +64,7 @@ const policyID = 'policy_mrv_test'; const expenseReportID = 'expense_mrv_123'; const parentReportActionID = 'parent_action_mrv'; const transactionID = 'txn_mrv_test'; +const archivedReportsIDSet = new Set(); const renderMoneyRequestView = (threadReport: ReturnType, policy?: Record) => render( @@ -84,6 +85,7 @@ const renderMoneyRequestView = (threadReport: ReturnType , ); diff --git a/tests/ui/ReportActionComposeTest.tsx b/tests/ui/ReportActionComposeTest.tsx index 9e2e0edc835e..d6a310e61072 100644 --- a/tests/ui/ReportActionComposeTest.tsx +++ b/tests/ui/ReportActionComposeTest.tsx @@ -57,6 +57,7 @@ TestHelper.setupGlobalFetchMock(); const defaultReport = LHNTestUtils.getFakeReport(); const defaultProps: ReportActionComposeProps = { reportID: defaultReport.reportID, + archivedReportsIDSet: new Set(), }; const renderReportActionCompose = (props?: Partial) => { From 0802cc8b518d5d859f5b27b9db29a83b9a2f4189 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 02:04:24 +0530 Subject: [PATCH 48/78] Address PR review comments --- .../MoneyRequestReportActionsList.tsx | 10 +++------- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 5 +---- src/components/Search/index.tsx | 8 ++++---- src/pages/inbox/ReportActionsList.tsx | 2 +- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index 63ccb2ada13e..f652e7e50f43 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -8,6 +8,7 @@ import {DeviceEventEmitter, InteractionManager, View} from 'react-native'; import FlatListWithScrollKey from '@components/FlatList/FlatListWithScrollKey'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import ScrollView from '@components/ScrollView'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLoadReportActions from '@hooks/useLoadReportActions'; import useLocalize from '@hooks/useLocalize'; @@ -44,7 +45,6 @@ import { wasMessageReceivedWhileOffline, } from '@libs/ReportActionsUtils'; import {canUserPerformWriteAction, chatIncludesChronosWithID, getOriginalReportID, getReportLastVisibleActionCreated, isHarvestCreatedExpenseReport, isUnread} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import Visibility from '@libs/Visibility'; @@ -67,8 +67,6 @@ import ReportActionsListLoadingSkeleton from './ReportActionsListLoadingSkeleton import SearchMoneyRequestReportEmptyState from './SearchMoneyRequestReportEmptyState'; import SelectionToolbar from './SelectionToolbar'; -const EMPTY_ARCHIVED_REPORTS_ID_SET = new Set(); - /** * In this view we are not handling the special single transaction case, we're just handling the report */ @@ -84,15 +82,13 @@ const BACKFILL_MIN_ACTIONS_THRESHOLD = 50; type MoneyRequestReportListProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; - - /** Set of archived report ID keys */ - archivedReportsIDSet?: ArchivedReportsIDSet; }; -function MoneyRequestReportActionsList({onLayout, archivedReportsIDSet = EMPTY_ARCHIVED_REPORTS_ID_SET}: MoneyRequestReportListProps) { +function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps) { const styles = useThemeStyles(); const {translate, getLocalDateFromDatetime} = useLocalize(); const {isOffline, lastOfflineAt, lastOnlineAt} = useNetworkWithOfflineStatus(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const reportScrollManager = useReportScrollManager(); const lastMessageTime = useRef(null); const didLayout = useRef(false); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 87f5da4fcfdd..25916f2dd4ef 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -278,10 +278,7 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport )} {shouldDisplayMoneyRequestActionsList ? ( - + ) : ( , currentUserLogin: string, currentUserAccountID: number, - outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, - archivedReportsIDSet?: ArchivedReportsIDSet, + outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined, + archivedReportsIDSet: ArchivedReportsIDSet, allowNegativeAmount = true, ): [string, SelectedTransactionInfo] { const {canHoldRequest, canUnholdRequest} = canHoldUnholdReportAction(item.report, item.reportAction, item.holdReportAction, item, item.policy, currentUserAccountID); @@ -194,8 +194,8 @@ function prepareTransactionsList( selectedTransactions: SelectedTransactions, currentUserLogin: string, currentUserAccountID: number, - outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, - archivedReportsIDSet?: ArchivedReportsIDSet, + outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined, + archivedReportsIDSet: ArchivedReportsIDSet, ) { if (selectedTransactions[item.keyForList]?.isSelected) { const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions; diff --git a/src/pages/inbox/ReportActionsList.tsx b/src/pages/inbox/ReportActionsList.tsx index cf4aac5d4852..3cfbf8d5a3fe 100644 --- a/src/pages/inbox/ReportActionsList.tsx +++ b/src/pages/inbox/ReportActionsList.tsx @@ -50,7 +50,7 @@ function ReportActionsList({archivedReportsIDSet}: {archivedReportsIDSet: Archiv } if (shouldDisplayMoneyRequestActionsList) { - return ; + return ; } return ( From de378f7f186e6f8acf570f6296d803b57f2b4e03 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 02:23:22 +0530 Subject: [PATCH 49/78] Normalize archived reports hook naming --- src/components/Search/index.tsx | 4 ++-- src/hooks/useArchivedReportsIDSet.ts | 4 ++-- src/hooks/useAutoCreateTrackWorkspace.ts | 4 ++-- src/hooks/useDeleteTransactions.ts | 4 ++-- src/hooks/useSelectedTransactionsActions.ts | 4 ++-- .../KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx | 4 ++-- .../AppNavigator/Navigators/ReportsSplitNavigator.tsx | 4 ++-- .../BaseOnboardingPersonalDetails.tsx | 4 ++-- .../BaseOnboardingWorkspaceInvite.tsx | 4 ++-- src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx | 4 ++-- src/pages/inbox/ReportRouteParamHandler.tsx | 4 ++-- src/pages/iou/request/step/IOURequestStepReport.tsx | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 2508aabc7154..73f869017136 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -11,7 +11,7 @@ import type {SelectionListHandle} from '@components/SelectionList/types'; import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; import {useWideRHPActions} from '@components/WideRHPContextProvider'; import useActionLoadingReportIDs from '@hooks/useActionLoadingReportIDs'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useMultipleSnapshots from '@hooks/useMultipleSnapshots'; @@ -333,7 +333,7 @@ function Search({ const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, { selector: selectFilteredReportActions, diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts index cc695e2a4c30..d03a3b7c92a6 100644 --- a/src/hooks/useArchivedReportsIDSet.ts +++ b/src/hooks/useArchivedReportsIDSet.ts @@ -28,10 +28,10 @@ const archivedReportIdsSelector = (reportNameValuePairs: OnyxCollection { diff --git a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx index 41581c12d6ae..2bd8f429e6f4 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx @@ -1,5 +1,5 @@ import React, {useState} from 'react'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import usePermissions from '@hooks/usePermissions'; import createSplitNavigator from '@libs/Navigation/AppNavigator/createSplitNavigator'; import FreezeWrapper from '@libs/Navigation/AppNavigator/FreezeWrapper'; @@ -26,7 +26,7 @@ const Split = createSplitNavigator(); function ReportsSplitNavigator({route}: PlatformStackScreenProps) { const {isBetaEnabled} = usePermissions(); const splitNavigatorScreenOptions = useSplitNavigatorScreenOptions(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const isOpenOnAdminRoom = shouldOpenOnAdminRoom(); const [initialReportID] = useState(() => { diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index b5ea6bb5a26d..e2c7e7122b4d 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -9,7 +9,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useAutoCreateTrackWorkspace from '@hooks/useAutoCreateTrackWorkspace'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; @@ -45,7 +45,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [betas] = useOnyx(ONYXKEYS.BETAS); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [onboardingValues] = useOnyx(ONYXKEYS.NVP_ONBOARDING); const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); diff --git a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx index 9543b3c1712d..ed8fe7442110 100644 --- a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx +++ b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx @@ -10,7 +10,7 @@ import SelectionListWithSections from '@components/SelectionList/SelectionListWi import type {Section} from '@components/SelectionList/SelectionListWithSections/types'; import Text from '@components/Text'; import useAllPolicyExpenseChatReportActions from '@hooks/useAllPolicyExpenseChatReportActions'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnboardingMessages from '@hooks/useOnboardingMessages'; @@ -66,7 +66,7 @@ function BaseOnboardingWorkspaceInvite({shouldUseNativeStyles}: BaseOnboardingWo const session = useSession(); const {isBetaEnabled} = usePermissions(); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const filteredReportActions = useAllPolicyExpenseChatReportActions(); const ineligibleInvitees = getIneligibleInvitees(policy?.employeeList); diff --git a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx index 51b744a0939e..cd789f6fb63b 100644 --- a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx +++ b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx @@ -8,7 +8,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; import Text from '@components/Text'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -59,7 +59,7 @@ function BaseOnboardingWorkspaces({route, shouldUseNativeStyles}: BaseOnboarding const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const [betas] = useOnyx(ONYXKEYS.BETAS); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const isValidated = isCurrentUserValidated(loginList, session?.email); diff --git a/src/pages/inbox/ReportRouteParamHandler.tsx b/src/pages/inbox/ReportRouteParamHandler.tsx index 3f8e12d13207..578a251d7621 100644 --- a/src/pages/inbox/ReportRouteParamHandler.tsx +++ b/src/pages/inbox/ReportRouteParamHandler.tsx @@ -1,5 +1,5 @@ import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import usePermissions from '@hooks/usePermissions'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -22,7 +22,7 @@ function ReportRouteParamHandler() { const route = useRoute(); const navigation = useNavigation(); const {isBetaEnabled} = usePermissions(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); useFocusEffect(() => { // Don't update if there is a reportID in the params already diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 0c3bbe2c57a7..9c2e58461e74 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -5,7 +5,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {usePersonalDetails, useSession} from '@components/OnyxListItemProvider'; import {useSearchActionsContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useOnyx from '@hooks/useOnyx'; import useOptimisticDraftTransactions from '@hooks/useOptimisticDraftTransactions'; @@ -48,7 +48,7 @@ const getIOUActionsSelector = (actions: OnyxEntry): ReportAction[ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {backTo, action, iouType, transactionID, reportID: reportIDFromRoute, reportActionID} = route.params; const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const archivedReportsIDSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); const participantReportID = transaction?.participants?.at(0)?.reportID; From 386c9a7482788a5a4961287aa9eaee11bbb06b9f Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 02:34:30 +0530 Subject: [PATCH 50/78] Tighten archived report set parameters --- src/libs/ReportUtils.ts | 30 +++++++++++++++++-- .../ReportActionCompose/ComposerProvider.tsx | 2 +- .../ReportActionCompose.tsx | 6 ++-- src/pages/inbox/report/ReportActionsList.tsx | 6 ++-- .../perf-test/ReportActionsList.perf-test.tsx | 1 + 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 74e1530ee126..6386e18aad8f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2354,6 +2354,23 @@ function isArchivedReport(reportNameValuePairs?: OnyxInputOrEntry): ArchivedReportsIDSet { + const archivedReportsIDSet = new Set(); + for (const [key, value] of Object.entries(reportNameValuePairs ?? {})) { + if (isArchivedReport(value)) { + archivedReportsIDSet.add(key); + } + } + return archivedReportsIDSet; +} + /** * Whether the report was created during harvesting */ @@ -4955,6 +4972,7 @@ function canEditFieldOfMoneyRequest({ // Unreported transaction from OldDot can have the reportID as an empty string const isUnreportedExpense = !transaction?.reportID || transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; + const archivedReportsIDSetForOutstandingReports = archivedReportsIDSet ?? buildArchivedReportsIDSet(allReportNameValuePair); if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE) { // The distance rate can be modified only on the distance expense reports @@ -4990,7 +5008,7 @@ function canEditFieldOfMoneyRequest({ moneyRequestReport?.policyID, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[moneyRequestReport?.policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, - archivedReportsIDSet, + archivedReportsIDSetForOutstandingReports, ).length > 0 ); } @@ -5010,7 +5028,12 @@ function canEditFieldOfMoneyRequest({ // Check if there are multiple outstanding reports across policies let outstandingReportsCount = 0; for (const currentPolicy of policiesArray) { - const reports = getOutstandingReportsForUser(currentPolicy.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, archivedReportsIDSet); + const reports = getOutstandingReportsForUser( + currentPolicy.id, + moneyRequestReport?.ownerAccountID, + outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, + archivedReportsIDSetForOutstandingReports, + ); outstandingReportsCount += reports.length; // Short-circuit once we find more than 1 @@ -11505,7 +11528,7 @@ function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, reports: OnyxCollection = deprecatedAllReports, - archivedReportsIDSet?: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet, allowSubmitted = true, ): Array> { if (!reports) { @@ -13499,6 +13522,7 @@ export { isAnnounceRoom, isArchivedNonExpenseReport, isArchivedReport, + isReportArchivedByID, isClosedReport, isCanceledTaskReport, isChatReport, diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx index ad49a598eb01..69bfc14b06d8 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx @@ -23,7 +23,7 @@ const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); type ComposerProviderProps = { reportID: string; - archivedReportsIDSet?: ArchivedReportsIDSet; + archivedReportsIDSet: ArchivedReportsIDSet; children: React.ReactNode; }; diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index f63c179abfdd..ef4face996cd 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -23,11 +23,9 @@ import ComposerProvider from './ComposerProvider'; import ComposerSendButton from './ComposerSendButton'; import type {ComposerRef} from './ComposerWithSuggestions/ComposerWithSuggestions'; -const EMPTY_ARCHIVED_REPORTS_ID_SET = new Set(); - type ReportActionComposeProps = { reportID: string; - archivedReportsIDSet?: ArchivedReportsIDSet; + archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionComposeInner({reportID}: Pick) { @@ -71,7 +69,7 @@ function ReportActionComposeInner({reportID}: Pick(); - type ReportActionsListProps = { /** The report currently being looked at */ report: OnyxTypes.Report; @@ -136,7 +134,7 @@ type ReportActionsListProps = { onShowPreviousMessages?: () => void; /** Set of archived report ID keys */ - archivedReportsIDSet?: ArchivedReportsIDSet; + archivedReportsIDSet: ArchivedReportsIDSet; }; // In the component we are subscribing to the arrival of new actions. @@ -176,7 +174,7 @@ function ReportActionsList({ showHiddenHistory, hasPreviousMessages, onShowPreviousMessages, - archivedReportsIDSet = EMPTY_ARCHIVED_REPORTS_ID_SET, + archivedReportsIDSet, }: ReportActionsListProps) { const prevHasCreatedActionAdded = usePrevious(hasCreatedActionAdded); const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index 276b0c3446b3..5c5a9378456a 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -120,6 +120,7 @@ function ReportActionsListWrapper() { loadOlderChats={mockLoadChats} loadNewerChats={mockLoadChats} transactionThreadReport={report} + archivedReportsIDSet={new Set()} /> From 87c02a7e8ea8dbf6a9f444c837ee1c2be066b6e6 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 02:38:15 +0530 Subject: [PATCH 51/78] Revert "Tighten archived report set parameters" This reverts commit 386c9a7482788a5a4961287aa9eaee11bbb06b9f. --- src/libs/ReportUtils.ts | 30 ++----------------- .../ReportActionCompose/ComposerProvider.tsx | 2 +- .../ReportActionCompose.tsx | 6 ++-- src/pages/inbox/report/ReportActionsList.tsx | 6 ++-- .../perf-test/ReportActionsList.perf-test.tsx | 1 - 5 files changed, 12 insertions(+), 33 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6386e18aad8f..74e1530ee126 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2354,23 +2354,6 @@ function isArchivedReport(reportNameValuePairs?: OnyxInputOrEntry): ArchivedReportsIDSet { - const archivedReportsIDSet = new Set(); - for (const [key, value] of Object.entries(reportNameValuePairs ?? {})) { - if (isArchivedReport(value)) { - archivedReportsIDSet.add(key); - } - } - return archivedReportsIDSet; -} - /** * Whether the report was created during harvesting */ @@ -4972,7 +4955,6 @@ function canEditFieldOfMoneyRequest({ // Unreported transaction from OldDot can have the reportID as an empty string const isUnreportedExpense = !transaction?.reportID || transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; - const archivedReportsIDSetForOutstandingReports = archivedReportsIDSet ?? buildArchivedReportsIDSet(allReportNameValuePair); if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE) { // The distance rate can be modified only on the distance expense reports @@ -5008,7 +4990,7 @@ function canEditFieldOfMoneyRequest({ moneyRequestReport?.policyID, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[moneyRequestReport?.policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, - archivedReportsIDSetForOutstandingReports, + archivedReportsIDSet, ).length > 0 ); } @@ -5028,12 +5010,7 @@ function canEditFieldOfMoneyRequest({ // Check if there are multiple outstanding reports across policies let outstandingReportsCount = 0; for (const currentPolicy of policiesArray) { - const reports = getOutstandingReportsForUser( - currentPolicy.id, - moneyRequestReport?.ownerAccountID, - outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, - archivedReportsIDSetForOutstandingReports, - ); + const reports = getOutstandingReportsForUser(currentPolicy.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, archivedReportsIDSet); outstandingReportsCount += reports.length; // Short-circuit once we find more than 1 @@ -11528,7 +11505,7 @@ function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, reports: OnyxCollection = deprecatedAllReports, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIDSet?: ArchivedReportsIDSet, allowSubmitted = true, ): Array> { if (!reports) { @@ -13522,7 +13499,6 @@ export { isAnnounceRoom, isArchivedNonExpenseReport, isArchivedReport, - isReportArchivedByID, isClosedReport, isCanceledTaskReport, isChatReport, diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx index 69bfc14b06d8..ad49a598eb01 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx @@ -23,7 +23,7 @@ const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); type ComposerProviderProps = { reportID: string; - archivedReportsIDSet: ArchivedReportsIDSet; + archivedReportsIDSet?: ArchivedReportsIDSet; children: React.ReactNode; }; diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index ef4face996cd..f63c179abfdd 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -23,9 +23,11 @@ import ComposerProvider from './ComposerProvider'; import ComposerSendButton from './ComposerSendButton'; import type {ComposerRef} from './ComposerWithSuggestions/ComposerWithSuggestions'; +const EMPTY_ARCHIVED_REPORTS_ID_SET = new Set(); + type ReportActionComposeProps = { reportID: string; - archivedReportsIDSet: ArchivedReportsIDSet; + archivedReportsIDSet?: ArchivedReportsIDSet; }; function ReportActionComposeInner({reportID}: Pick) { @@ -69,7 +71,7 @@ function ReportActionComposeInner({reportID}: Pick(); + type ReportActionsListProps = { /** The report currently being looked at */ report: OnyxTypes.Report; @@ -134,7 +136,7 @@ type ReportActionsListProps = { onShowPreviousMessages?: () => void; /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; + archivedReportsIDSet?: ArchivedReportsIDSet; }; // In the component we are subscribing to the arrival of new actions. @@ -174,7 +176,7 @@ function ReportActionsList({ showHiddenHistory, hasPreviousMessages, onShowPreviousMessages, - archivedReportsIDSet, + archivedReportsIDSet = EMPTY_ARCHIVED_REPORTS_ID_SET, }: ReportActionsListProps) { const prevHasCreatedActionAdded = usePrevious(hasCreatedActionAdded); const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index 5c5a9378456a..276b0c3446b3 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -120,7 +120,6 @@ function ReportActionsListWrapper() { loadOlderChats={mockLoadChats} loadNewerChats={mockLoadChats} transactionThreadReport={report} - archivedReportsIDSet={new Set()} /> From 594479b9a6cc2d3a3374be5f5ae3b0fc3fe4ab95 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 02:38:17 +0530 Subject: [PATCH 52/78] Revert "Normalize archived reports hook naming" This reverts commit de378f7f186e6f8acf570f6296d803b57f2b4e03. --- src/components/Search/index.tsx | 4 ++-- src/hooks/useArchivedReportsIDSet.ts | 4 ++-- src/hooks/useAutoCreateTrackWorkspace.ts | 4 ++-- src/hooks/useDeleteTransactions.ts | 4 ++-- src/hooks/useSelectedTransactionsActions.ts | 4 ++-- .../KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx | 4 ++-- .../AppNavigator/Navigators/ReportsSplitNavigator.tsx | 4 ++-- .../BaseOnboardingPersonalDetails.tsx | 4 ++-- .../BaseOnboardingWorkspaceInvite.tsx | 4 ++-- src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx | 4 ++-- src/pages/inbox/ReportRouteParamHandler.tsx | 4 ++-- src/pages/iou/request/step/IOURequestStepReport.tsx | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 73f869017136..2508aabc7154 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -11,7 +11,7 @@ import type {SelectionListHandle} from '@components/SelectionList/types'; import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; import {useWideRHPActions} from '@components/WideRHPContextProvider'; import useActionLoadingReportIDs from '@hooks/useActionLoadingReportIDs'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useMultipleSnapshots from '@hooks/useMultipleSnapshots'; @@ -333,7 +333,7 @@ function Search({ const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIdSet(); const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, { selector: selectFilteredReportActions, diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts index d03a3b7c92a6..cc695e2a4c30 100644 --- a/src/hooks/useArchivedReportsIDSet.ts +++ b/src/hooks/useArchivedReportsIDSet.ts @@ -28,10 +28,10 @@ const archivedReportIdsSelector = (reportNameValuePairs: OnyxCollection { diff --git a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx index 2bd8f429e6f4..41581c12d6ae 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx @@ -1,5 +1,5 @@ import React, {useState} from 'react'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import usePermissions from '@hooks/usePermissions'; import createSplitNavigator from '@libs/Navigation/AppNavigator/createSplitNavigator'; import FreezeWrapper from '@libs/Navigation/AppNavigator/FreezeWrapper'; @@ -26,7 +26,7 @@ const Split = createSplitNavigator(); function ReportsSplitNavigator({route}: PlatformStackScreenProps) { const {isBetaEnabled} = usePermissions(); const splitNavigatorScreenOptions = useSplitNavigatorScreenOptions(); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIdSet(); const isOpenOnAdminRoom = shouldOpenOnAdminRoom(); const [initialReportID] = useState(() => { diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index e2c7e7122b4d..b5ea6bb5a26d 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -9,7 +9,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import useAutoCreateTrackWorkspace from '@hooks/useAutoCreateTrackWorkspace'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; @@ -45,7 +45,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [betas] = useOnyx(ONYXKEYS.BETAS); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIdSet(); const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [onboardingValues] = useOnyx(ONYXKEYS.NVP_ONBOARDING); const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); diff --git a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx index ed8fe7442110..9543b3c1712d 100644 --- a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx +++ b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx @@ -10,7 +10,7 @@ import SelectionListWithSections from '@components/SelectionList/SelectionListWi import type {Section} from '@components/SelectionList/SelectionListWithSections/types'; import Text from '@components/Text'; import useAllPolicyExpenseChatReportActions from '@hooks/useAllPolicyExpenseChatReportActions'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnboardingMessages from '@hooks/useOnboardingMessages'; @@ -66,7 +66,7 @@ function BaseOnboardingWorkspaceInvite({shouldUseNativeStyles}: BaseOnboardingWo const session = useSession(); const {isBetaEnabled} = usePermissions(); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIdSet(); const filteredReportActions = useAllPolicyExpenseChatReportActions(); const ineligibleInvitees = getIneligibleInvitees(policy?.employeeList); diff --git a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx index cd789f6fb63b..51b744a0939e 100644 --- a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx +++ b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx @@ -8,7 +8,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; import Text from '@components/Text'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -59,7 +59,7 @@ function BaseOnboardingWorkspaces({route, shouldUseNativeStyles}: BaseOnboarding const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const [betas] = useOnyx(ONYXKEYS.BETAS); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIdSet(); const isValidated = isCurrentUserValidated(loginList, session?.email); diff --git a/src/pages/inbox/ReportRouteParamHandler.tsx b/src/pages/inbox/ReportRouteParamHandler.tsx index 578a251d7621..3f8e12d13207 100644 --- a/src/pages/inbox/ReportRouteParamHandler.tsx +++ b/src/pages/inbox/ReportRouteParamHandler.tsx @@ -1,5 +1,5 @@ import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import usePermissions from '@hooks/usePermissions'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -22,7 +22,7 @@ function ReportRouteParamHandler() { const route = useRoute(); const navigation = useNavigation(); const {isBetaEnabled} = usePermissions(); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIdSet(); useFocusEffect(() => { // Don't update if there is a reportID in the params already diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 9c2e58461e74..0c3bbe2c57a7 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -5,7 +5,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {usePersonalDetails, useSession} from '@components/OnyxListItemProvider'; import {useSearchActionsContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useOnyx from '@hooks/useOnyx'; import useOptimisticDraftTransactions from '@hooks/useOptimisticDraftTransactions'; @@ -48,7 +48,7 @@ const getIOUActionsSelector = (actions: OnyxEntry): ReportAction[ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {backTo, action, iouType, transactionID, reportID: reportIDFromRoute, reportActionID} = route.params; const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const archivedReportsIDSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIdSet(); const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); const participantReportID = transaction?.participants?.at(0)?.reportID; From 5cdfc1fd8ebb3aa6b4a6fcf1d1e9752f30a0d8ca Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 02:38:22 +0530 Subject: [PATCH 53/78] Revert "Address PR review comments" This reverts commit 0802cc8b518d5d859f5b27b9db29a83b9a2f4189. --- .../MoneyRequestReportActionsList.tsx | 10 +++++++--- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 5 ++++- src/components/Search/index.tsx | 8 ++++---- src/pages/inbox/ReportActionsList.tsx | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index f652e7e50f43..63ccb2ada13e 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -8,7 +8,6 @@ import {DeviceEventEmitter, InteractionManager, View} from 'react-native'; import FlatListWithScrollKey from '@components/FlatList/FlatListWithScrollKey'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import ScrollView from '@components/ScrollView'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLoadReportActions from '@hooks/useLoadReportActions'; import useLocalize from '@hooks/useLocalize'; @@ -45,6 +44,7 @@ import { wasMessageReceivedWhileOffline, } from '@libs/ReportActionsUtils'; import {canUserPerformWriteAction, chatIncludesChronosWithID, getOriginalReportID, getReportLastVisibleActionCreated, isHarvestCreatedExpenseReport, isUnread} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import Visibility from '@libs/Visibility'; @@ -67,6 +67,8 @@ import ReportActionsListLoadingSkeleton from './ReportActionsListLoadingSkeleton import SearchMoneyRequestReportEmptyState from './SearchMoneyRequestReportEmptyState'; import SelectionToolbar from './SelectionToolbar'; +const EMPTY_ARCHIVED_REPORTS_ID_SET = new Set(); + /** * In this view we are not handling the special single transaction case, we're just handling the report */ @@ -82,13 +84,15 @@ const BACKFILL_MIN_ACTIONS_THRESHOLD = 50; type MoneyRequestReportListProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; + + /** Set of archived report ID keys */ + archivedReportsIDSet?: ArchivedReportsIDSet; }; -function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps) { +function MoneyRequestReportActionsList({onLayout, archivedReportsIDSet = EMPTY_ARCHIVED_REPORTS_ID_SET}: MoneyRequestReportListProps) { const styles = useThemeStyles(); const {translate, getLocalDateFromDatetime} = useLocalize(); const {isOffline, lastOfflineAt, lastOnlineAt} = useNetworkWithOfflineStatus(); - const archivedReportsIDSet = useArchivedReportsIDSet(); const reportScrollManager = useReportScrollManager(); const lastMessageTime = useRef(null); const didLayout = useRef(false); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 25916f2dd4ef..87f5da4fcfdd 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -278,7 +278,10 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport )} {shouldDisplayMoneyRequestActionsList ? ( - + ) : ( , currentUserLogin: string, currentUserAccountID: number, - outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined, - archivedReportsIDSet: ArchivedReportsIDSet, + outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, + archivedReportsIDSet?: ArchivedReportsIDSet, allowNegativeAmount = true, ): [string, SelectedTransactionInfo] { const {canHoldRequest, canUnholdRequest} = canHoldUnholdReportAction(item.report, item.reportAction, item.holdReportAction, item, item.policy, currentUserAccountID); @@ -194,8 +194,8 @@ function prepareTransactionsList( selectedTransactions: SelectedTransactions, currentUserLogin: string, currentUserAccountID: number, - outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined, - archivedReportsIDSet: ArchivedReportsIDSet, + outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, + archivedReportsIDSet?: ArchivedReportsIDSet, ) { if (selectedTransactions[item.keyForList]?.isSelected) { const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions; diff --git a/src/pages/inbox/ReportActionsList.tsx b/src/pages/inbox/ReportActionsList.tsx index 3cfbf8d5a3fe..cf4aac5d4852 100644 --- a/src/pages/inbox/ReportActionsList.tsx +++ b/src/pages/inbox/ReportActionsList.tsx @@ -50,7 +50,7 @@ function ReportActionsList({archivedReportsIDSet}: {archivedReportsIDSet: Archiv } if (shouldDisplayMoneyRequestActionsList) { - return ; + return ; } return ( From 1dd67f596072179b2d39d3ff0fa0e4523b4b36b3 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 02:48:49 +0530 Subject: [PATCH 54/78] Narrow archived report connect removal --- .../sections/ReportField.tsx | 7 +- .../MoneyRequestHeaderSecondaryActions.tsx | 16 +- .../MoneyRequestReportActionsList.tsx | 10 +- .../MoneyRequestReportView.tsx | 19 +-- .../ReportActionItem/MoneyRequestView.tsx | 7 +- src/components/Search/index.tsx | 54 +------ src/hooks/useArchivedReportsIDSet.ts | 37 ----- src/hooks/useArchivedReportsIdSet.ts | 23 +++ src/hooks/useAutoCreateTrackWorkspace.ts | 2 +- src/hooks/useDeleteTransactions.ts | 5 +- src/hooks/useOutstandingReports.ts | 7 +- src/hooks/useSearchSections.ts | 4 +- src/hooks/useSelectedTransactionsActions.ts | 10 +- .../MarkAllMessagesAsReadHandler.tsx | 2 +- .../Navigators/ReportsSplitNavigator.tsx | 2 +- src/libs/ReportSecondaryActionUtils.ts | 50 ++---- src/libs/ReportUtils.ts | 26 ++-- src/libs/actions/Report/index.ts | 8 +- src/libs/actions/Task.ts | 8 +- src/libs/navigateAfterOnboarding.ts | 12 +- .../BaseOnboardingInterestedFeatures.tsx | 4 +- .../BaseOnboardingPersonalDetails.tsx | 2 +- .../BaseOnboardingWorkspaceInvite.tsx | 2 +- .../BaseOnboardingWorkspaceOptional.tsx | 4 +- .../BaseOnboardingWorkspaces.tsx | 2 +- src/pages/ReportDetailsPage.tsx | 3 - .../Search/SearchMoneyRequestReportPage.tsx | 3 - .../TransactionDuplicate/Confirmation.tsx | 3 - .../TransactionMerge/ConfirmationPage.tsx | 3 - src/pages/inbox/ReportActionsList.tsx | 12 +- src/pages/inbox/ReportHeader.tsx | 6 +- src/pages/inbox/ReportRouteParamHandler.tsx | 2 +- src/pages/inbox/ReportScreen.tsx | 8 +- .../inbox/report/PureReportActionItem.tsx | 19 +-- .../ReportActionCompose/ComposerProvider.tsx | 2 - .../ReportActionCompose.tsx | 13 +- .../useShouldAddOrReplaceReceipt.ts | 4 +- .../report/ReportActionItemContentCreated.tsx | 7 - .../report/ReportActionItemParentAction.tsx | 6 - src/pages/inbox/report/ReportActionsList.tsx | 9 -- .../report/ReportActionsListItemRenderer.tsx | 7 - src/pages/inbox/report/ReportActionsView.tsx | 7 +- src/pages/inbox/report/ReportFooter.tsx | 8 +- .../iou/request/step/IOURequestStepReport.tsx | 4 +- .../TaskShareDestinationSelectorModal.tsx | 8 +- tests/actions/ReportTest.ts | 1 + tests/actions/TaskTest.ts | 19 ++- .../ReportActionCompose.perf-test.tsx | 5 +- tests/ui/MoneyRequestViewTest.tsx | 2 - tests/ui/ReportActionComposeTest.tsx | 1 - tests/ui/ReportActionsViewTest.tsx | 7 +- tests/unit/ReportSecondaryActionUtilsTest.ts | 144 ++++-------------- tests/unit/ReportUtilsTest.ts | 7 +- .../hooks/useArchivedReportsIdSet.test.ts | 14 +- tests/unit/hooks/useSearchSections.test.ts | 2 +- tests/unit/navigateAfterOnboardingTest.ts | 8 +- 56 files changed, 184 insertions(+), 483 deletions(-) delete mode 100644 src/hooks/useArchivedReportsIDSet.ts create mode 100644 src/hooks/useArchivedReportsIdSet.ts diff --git a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx index 2990429fdc1f..c6756d307077 100644 --- a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx +++ b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx @@ -1,7 +1,6 @@ import React from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useOutstandingReports from '@hooks/useOutstandingReports'; @@ -51,7 +50,7 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a const {translate, localeCompare} = useLocalize(); const reportAttributes = useReportAttributes(); - const archivedReportsIDSet = useArchivedReportsIDSet(); + const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID); // Per-key report subscriptions instead of full COLLECTION.REPORT @@ -68,7 +67,7 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a * Also we need to check if transaction report exists in outstanding reports in order to show a correct report name. */ const policyID = selectedParticipants?.at(0)?.policyID; - const shouldUseTransactionReport = (!!transactionReportEntry && isReportOutstanding(transactionReportEntry, policyID, archivedReportsIDSet, false)) || isUnreported; + const shouldUseTransactionReport = (!!transactionReportEntry && isReportOutstanding(transactionReportEntry, policyID, undefined, false)) || isUnreported; const ownerAccountID = selectedParticipants?.at(0)?.ownerAccountID; @@ -76,7 +75,7 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a policyID, ownerAccountID, outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, - archivedReportsIDSet, + reportNameValuePairs, false, ).sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? '')); diff --git a/src/components/MoneyRequestHeaderSecondaryActions.tsx b/src/components/MoneyRequestHeaderSecondaryActions.tsx index e57970d94fce..57708b8d1568 100644 --- a/src/components/MoneyRequestHeaderSecondaryActions.tsx +++ b/src/components/MoneyRequestHeaderSecondaryActions.tsx @@ -269,18 +269,18 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money if (!transaction || !parentReportAction || !parentReport) { return []; } - return getSecondaryTransactionThreadActions({ - currentUserLogin: currentUserLogin ?? '', - currentUserAccountID: accountID, + return getSecondaryTransactionThreadActions( + currentUserLogin ?? '', + accountID, parentReport, - reportTransaction: transaction, - reportAction: parentReportAction, + transaction, + parentReportAction, originalTransaction, policy, - transactionThreadReport: report, + report, outstandingReportsByPolicyID, - isChatReportArchived: isChatIOUReportArchived, - }); + isChatIOUReportArchived, + ); })(); const secondaryActionsImplementation: Partial< diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index 63ccb2ada13e..34c7fad51de2 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -44,7 +44,6 @@ import { wasMessageReceivedWhileOffline, } from '@libs/ReportActionsUtils'; import {canUserPerformWriteAction, chatIncludesChronosWithID, getOriginalReportID, getReportLastVisibleActionCreated, isHarvestCreatedExpenseReport, isUnread} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import Visibility from '@libs/Visibility'; @@ -67,8 +66,6 @@ import ReportActionsListLoadingSkeleton from './ReportActionsListLoadingSkeleton import SearchMoneyRequestReportEmptyState from './SearchMoneyRequestReportEmptyState'; import SelectionToolbar from './SelectionToolbar'; -const EMPTY_ARCHIVED_REPORTS_ID_SET = new Set(); - /** * In this view we are not handling the special single transaction case, we're just handling the report */ @@ -84,12 +81,9 @@ const BACKFILL_MIN_ACTIONS_THRESHOLD = 50; type MoneyRequestReportListProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; - - /** Set of archived report ID keys */ - archivedReportsIDSet?: ArchivedReportsIDSet; }; -function MoneyRequestReportActionsList({onLayout, archivedReportsIDSet = EMPTY_ARCHIVED_REPORTS_ID_SET}: MoneyRequestReportListProps) { +function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps) { const styles = useThemeStyles(); const {translate, getLocalDateFromDatetime} = useLocalize(); const {isOffline, lastOfflineAt, lastOnlineAt} = useNetworkWithOfflineStatus(); @@ -591,7 +585,6 @@ function MoneyRequestReportActionsList({onLayout, archivedReportsIDSet = EMPTY_A isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairs?.origin} reportNameValuePairsOriginalID={reportNameValuePairs?.originalID} - archivedReportsIDSet={archivedReportsIDSet} /> ); }, @@ -611,7 +604,6 @@ function MoneyRequestReportActionsList({onLayout, archivedReportsIDSet = EMPTY_A isReportArchived, reportNameValuePairs?.origin, reportNameValuePairs?.originalID, - archivedReportsIDSet, ], ); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 87f5da4fcfdd..6f40fae7d5b0 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -26,7 +26,6 @@ import navigationRef from '@libs/Navigation/navigationRef'; import {getFilteredReportActionsForReportView, getOneTransactionThreadReportID} from '@libs/ReportActionsUtils'; import {getReportOfflinePendingActionAndErrors, isReportTransactionThread} from '@libs/ReportUtils'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {cancelSpan} from '@libs/telemetry/activeSpans'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; @@ -59,9 +58,6 @@ type MoneyRequestReportViewProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; function goBackFromSearchMoneyRequest() { @@ -108,7 +104,7 @@ function InitialLoadingSkeleton({styles, onLayout, reasonAttributes}: {styles: T ); } -function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReportFooter, backToRoute, onLayout, archivedReportsIDSet}: MoneyRequestReportViewProps) { +function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReportFooter, backToRoute, onLayout}: MoneyRequestReportViewProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); @@ -192,10 +188,9 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport } Navigation.goBack(backToRoute); }} - archivedReportsIDSet={archivedReportsIDSet} /> ), - [archivedReportsIDSet, backToRoute, isTransactionThreadView, report?.reportID], + [backToRoute, isTransactionThreadView, report?.reportID], ); // We need to cancel telemetry span when user leaves the screen before full report data is loaded @@ -239,7 +234,7 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport - {shouldDisplayReportFooter ? : null} + {shouldDisplayReportFooter ? : null} ); } @@ -278,20 +273,16 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport )} {shouldDisplayMoneyRequestActionsList ? ( - + ) : ( )} {shouldDisplayReportFooter ? ( <> - + ) : null} diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index d6b0fb382150..3df940219f55 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -80,7 +80,6 @@ import { isTrackExpenseReportNew, shouldEnableNegative, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {hasEnabledTags, shouldShowDependentTagList} from '@libs/TagsOptionsListUtils'; import { getBillable, @@ -148,9 +147,6 @@ type MoneyRequestViewProps = { /** Merge transaction ID to show in merge transaction flow */ mergeTransactionID?: string; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; const perDiemPoliciesSelector = (policies: OnyxCollection) => { @@ -173,7 +169,6 @@ function MoneyRequestView({ updatedTransaction, isFromReviewDuplicates = false, mergeTransactionID, - archivedReportsIDSet, }: MoneyRequestViewProps) { const icons = useMemoizedLazyExpensifyIcons(['DotIndicator', 'Checkmark', 'Suitcase']); const styles = useThemeStyles(); @@ -327,6 +322,7 @@ function MoneyRequestView({ const isChatReportArchived = useReportIsArchived(moneyRequestReport?.chatReportID); const pendingAction = transaction?.pendingAction; const shouldShowPaid = isSettled && transactionReimbursable && !pendingAction; + // Flags for allowing or disallowing editing an expense // Used for non-restricted fields such as: description, category, tag, billable, etc... const isReportArchived = useReportIsArchived(transactionThreadReport?.reportID); @@ -397,7 +393,6 @@ function MoneyRequestView({ transaction, report: moneyRequestReport, policy, - archivedReportsIDSet, }) && (!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy) || (isExpenseUnreported && !!perDiemOriginalPolicy)); diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 46549ffb75c0..fda2ec413e7a 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -11,7 +11,7 @@ import type {SelectionListHandle} from '@components/SelectionList/types'; import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; import {useWideRHPActions} from '@components/WideRHPContextProvider'; import useActionLoadingReportIDs from '@hooks/useActionLoadingReportIDs'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useMultipleSnapshots from '@hooks/useMultipleSnapshots'; @@ -37,7 +37,6 @@ import {isCreatedTaskReportAction} from '@libs/ReportActionsUtils'; import {isSplitAction} from '@libs/ReportSecondaryActionUtils'; import {canEditFieldOfMoneyRequest, canHoldUnholdReportAction, canRejectReportAction, isOneTransactionReport, selectFilteredReportActions} from '@libs/ReportUtils'; import {buildCannedSearchQuery, buildSearchQueryString, isDefaultExpensesQuery} from '@libs/SearchQueryUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import { createAndOpenSearchTransactionThread, doesSearchItemMatchSort, @@ -121,7 +120,6 @@ function mapTransactionItemToSelectedEntry( currentUserLogin: string, currentUserAccountID: number, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, - archivedReportsIDSet?: ArchivedReportsIDSet, allowNegativeAmount = true, ): [string, SelectedTransactionInfo] { const {canHoldRequest, canUnholdRequest} = canHoldUnholdReportAction(item.report, item.reportAction, item.holdReportAction, item, item.policy, currentUserAccountID); @@ -146,7 +144,6 @@ function mapTransactionItemToSelectedEntry( transaction: item, report: item.report, policy: item.policy, - archivedReportsIDSet, }), action: item.action, groupCurrency: item.groupCurrency, @@ -195,7 +192,6 @@ function prepareTransactionsList( currentUserLogin: string, currentUserAccountID: number, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, - archivedReportsIDSet?: ArchivedReportsIDSet, ) { if (selectedTransactions[item.keyForList]?.isSelected) { const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions; @@ -210,7 +206,6 @@ function prepareTransactionsList( currentUserLogin, currentUserAccountID, outstandingReportsByPolicyID, - archivedReportsIDSet, false, ); @@ -812,7 +807,6 @@ function Search({ transaction: transactionItem, report: transactionItem.report, policy: transactionItem.policy, - archivedReportsIDSet: archivedReportsIdSet, }), isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID]?.isSelected || isExpenseReportType, @@ -869,7 +863,6 @@ function Search({ transaction: transactionItem, report: transactionItem.report, policy: transactionItem.policy, - archivedReportsIDSet: archivedReportsIdSet, }), isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID].isSelected, @@ -1004,7 +997,6 @@ function Search({ email ?? '', accountID, outstandingReportsByPolicyID, - archivedReportsIdSet, ); setSelectedTransactions(updatedTransactions, filteredData); updateSelectAllMatchingItemsState(updatedTransactions); @@ -1070,33 +1062,14 @@ function Search({ const originalItemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`] ?? transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry( - transactionItem, - itemTransaction, - originalItemTransaction, - email ?? '', - accountID, - outstandingReportsByPolicyID, - archivedReportsIdSet, - ); + return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', accountID, outstandingReportsByPolicyID); }), ), }; setSelectedTransactions(updatedTransactions, filteredData); updateSelectAllMatchingItemsState(updatedTransactions); }, - [ - selectedTransactions, - setSelectedTransactions, - filteredData, - updateSelectAllMatchingItemsState, - transactions, - email, - accountID, - outstandingReportsByPolicyID, - searchResults?.data, - archivedReportsIdSet, - ], + [selectedTransactions, setSelectedTransactions, filteredData, updateSelectAllMatchingItemsState, transactions, email, accountID, outstandingReportsByPolicyID, searchResults?.data], ); const onSelectRow = useCallback( @@ -1412,15 +1385,7 @@ function Search({ .map((transactionItem) => { const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry( - transactionItem, - itemTransaction, - originalItemTransaction, - email ?? '', - accountID, - outstandingReportsByPolicyID, - archivedReportsIdSet, - ); + return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', accountID, outstandingReportsByPolicyID); }); }); updatedTransactions = Object.fromEntries(allSelections); @@ -1432,15 +1397,7 @@ function Search({ .map((transactionItem) => { const itemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry( - transactionItem, - itemTransaction, - originalItemTransaction, - email ?? '', - accountID, - outstandingReportsByPolicyID, - archivedReportsIdSet, - ); + return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', accountID, outstandingReportsByPolicyID); }), ); } @@ -1459,7 +1416,6 @@ function Search({ accountID, outstandingReportsByPolicyID, searchResults?.data, - archivedReportsIdSet, ]); const onLayout = useCallback(() => { diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts deleted file mode 100644 index cc695e2a4c30..000000000000 --- a/src/hooks/useArchivedReportsIDSet.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type {OnyxCollection} from 'react-native-onyx'; -import {isArchivedReport} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {ReportNameValuePairs} from '@src/types/onyx'; -import useOnyx from './useOnyx'; - -/** - * Selector that extracts archived report IDs as a sorted array. - * Onyx performs shallow comparison on the returned array to prevent - * unnecessary re-renders without expensive deep comparison of Sets. - */ -const archivedReportIdsSelector = (reportNameValuePairs: OnyxCollection): string[] => { - if (!reportNameValuePairs) { - return []; - } - - const ids: string[] = []; - for (const [key, value] of Object.entries(reportNameValuePairs)) { - if (isArchivedReport(value)) { - ids.push(key); - } - } - return ids; -}; - -/** - * Hook that returns a Set of archived report IDs - */ -function useArchivedReportsIdSet(): ArchivedReportsIDSet { - const [archivedReportIds = CONST.EMPTY_ARRAY] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {selector: archivedReportIdsSelector}); - - return new Set(archivedReportIds); -} - -export default useArchivedReportsIdSet; diff --git a/src/hooks/useArchivedReportsIdSet.ts b/src/hooks/useArchivedReportsIdSet.ts new file mode 100644 index 000000000000..20dc07998fa0 --- /dev/null +++ b/src/hooks/useArchivedReportsIdSet.ts @@ -0,0 +1,23 @@ +import {isArchivedReport} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; +import useOnyx from './useOnyx'; + +/** + * Hook that returns a Set of archived report IDs + */ +function useArchivedReportsIdSet(): ArchivedReportsIDSet { + const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); + const ids: string[] = []; + + if (reportNameValuePairs) { + for (const [key, value] of Object.entries(reportNameValuePairs)) { + if (isArchivedReport(value)) { + ids.push(key); + } + } + } + return new Set(ids); +} + +export default useArchivedReportsIdSet; diff --git a/src/hooks/useAutoCreateTrackWorkspace.ts b/src/hooks/useAutoCreateTrackWorkspace.ts index 1bfcaf9439f9..b51c91982368 100644 --- a/src/hooks/useAutoCreateTrackWorkspace.ts +++ b/src/hooks/useAutoCreateTrackWorkspace.ts @@ -13,7 +13,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {lastWorkspaceNumberSelector} from '@src/selectors/Policy'; import type {OnboardingPurpose, OnboardingRHPVariant, Policy} from '@src/types/onyx'; -import useArchivedReportsIdSet from './useArchivedReportsIDSet'; +import useArchivedReportsIdSet from './useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useHasActiveAdminPolicies from './useHasActiveAdminPolicies'; import useLocalize from './useLocalize'; diff --git a/src/hooks/useDeleteTransactions.ts b/src/hooks/useDeleteTransactions.ts index 9f4500f6cea8..7a45bac7b41f 100644 --- a/src/hooks/useDeleteTransactions.ts +++ b/src/hooks/useDeleteTransactions.ts @@ -9,13 +9,12 @@ import {updateSplitTransactions} from '@libs/actions/IOU/Split'; import {initSplitExpenseItemData} from '@libs/actions/IOU/SplitExpenseItems'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; -import {isReportArchivedByID} from '@libs/ReportUtils'; import {getActiveGroupSearchHashes} from '@libs/SearchUIUtils'; import {getChildTransactions, getOriginalTransactionWithSplitInfo} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Report, ReportAction, Transaction, TransactionViolations} from '@src/types/onyx'; -import useArchivedReportsIdSet from './useArchivedReportsIDSet'; +import useArchivedReportsIdSet from './useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useNetwork from './useNetwork'; import useOnyx from './useOnyx'; @@ -192,7 +191,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`]; const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`]; const chatIOUReportID = chatReport?.reportID; - const isChatIOUReportArchived = isReportArchivedByID(archivedReportsIdSet, chatIOUReportID); + const isChatIOUReportArchived = archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${chatIOUReportID}`); deleteMoneyRequest({ transactionID, reportAction: action, diff --git a/src/hooks/useOutstandingReports.ts b/src/hooks/useOutstandingReports.ts index 43b4ea29d6b0..b23b255bf3b7 100644 --- a/src/hooks/useOutstandingReports.ts +++ b/src/hooks/useOutstandingReports.ts @@ -4,7 +4,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useMappedPolicies from './useMappedPolicies'; import useOnyx from './useOnyx'; @@ -14,7 +13,7 @@ export default function useOutstandingReports(selectedReportID: string | undefin const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); const [allPoliciesID] = useMappedPolicies(policyIdMapper); - const archivedReportsIDSet = useArchivedReportsIDSet(); + const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); // Early return if no reports are available to prevent useless loop @@ -29,11 +28,11 @@ export default function useOutstandingReports(selectedReportID: string | undefin continue; } - const reports = getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID[policyID] ?? {}, archivedReportsIDSet, isEditing); + const reports = getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID[policyID] ?? {}, reportNameValuePairs, isEditing); result.push(...reports); } return result; } - return getOutstandingReportsForUser(selectedPolicyID, ownerAccountID, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, archivedReportsIDSet, isEditing); + return getOutstandingReportsForUser(selectedPolicyID, ownerAccountID, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, reportNameValuePairs, isEditing); } diff --git a/src/hooks/useSearchSections.ts b/src/hooks/useSearchSections.ts index f671ea7065bc..b7fa54da3460 100644 --- a/src/hooks/useSearchSections.ts +++ b/src/hooks/useSearchSections.ts @@ -6,7 +6,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Report} from '@src/types/onyx'; import type LastSearchParams from '@src/types/onyx/ReportNavigation'; import useActionLoadingReportIDs from './useActionLoadingReportIDs'; -import useArchivedReportsIDSet from './useArchivedReportsIDSet'; +import useArchivedReportsIdSet from './useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useLocalize from './useLocalize'; import useOnyx from './useOnyx'; @@ -48,7 +48,7 @@ function useSearchSections(): UseSearchSectionsResult { const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [allReportMetadata] = useOnyx(ONYXKEYS.COLLECTION.REPORT_METADATA); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIdSet(); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const {type, status, sortBy, sortOrder, groupBy} = lastSearchQuery?.queryJSON ?? {}; diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index dabfe400e1a2..fae8c629d348 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -36,7 +36,6 @@ import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import useAllTransactions from './useAllTransactions'; -import useArchivedReportsIdSet from './useArchivedReportsIDSet'; import useConfirmModal from './useConfirmModal'; import {useCurrencyListActions} from './useCurrencyList'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; @@ -82,7 +81,6 @@ function useSelectedTransactionsActions({ const {selectedTransactionIDs, currentSearchHash, selectedTransactions: selectedTransactionsMeta} = useSearchStateContext(); const {clearSelectedTransactions} = useSearchActionsContext(); const allTransactions = useAllTransactions(); - const archivedReportsIDSet = useArchivedReportsIdSet(); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [allReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); @@ -417,13 +415,7 @@ function useSelectedTransactionsActions({ } const iouReportAction = getIOUActionForTransactionID(reportActions, transaction.transactionID); - const canMoveExpense = canEditFieldOfMoneyRequest({ - reportAction: iouReportAction, - fieldToEdit: CONST.EDIT_REQUEST_FIELD.REPORT, - outstandingReportsByPolicyID, - transaction, - archivedReportsIDSet, - }); + const canMoveExpense = canEditFieldOfMoneyRequest({reportAction: iouReportAction, fieldToEdit: CONST.EDIT_REQUEST_FIELD.REPORT, outstandingReportsByPolicyID, transaction}); return canMoveExpense; }); diff --git a/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx b/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx index 3bcb6d2ae987..13b70d5bfee4 100644 --- a/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx +++ b/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx @@ -1,5 +1,5 @@ import {useEffect, useRef} from 'react'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import markAllMessagesAsRead from '@libs/actions/Report/MarkAllMessageAsRead'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import CONST from '@src/CONST'; diff --git a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx index 41581c12d6ae..2fa371a64f86 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx @@ -1,5 +1,5 @@ import React, {useState} from 'react'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import usePermissions from '@hooks/usePermissions'; import createSplitNavigator from '@libs/Navigation/AppNavigator/createSplitNavigator'; import FreezeWrapper from '@libs/Navigation/AppNavigator/FreezeWrapper'; diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index a444001c9be9..26aabad2a1fd 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -77,7 +77,6 @@ import { isSettled, isWorkspaceEligibleForReportChange, } from './ReportUtils'; -import type {ArchivedReportsIDSet} from './SearchUIUtils'; import { allHavePendingRTERViolation, getOriginalTransactionWithSplitInfo, @@ -852,7 +851,6 @@ function getSecondaryReportActions({ policies, outstandingReportsByPolicyID, isChatReportArchived = false, - archivedReportsIDSet, }: { currentUserLogin: string; currentUserAccountID: number; @@ -870,7 +868,6 @@ function getSecondaryReportActions({ outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; canUseNewDotSplits?: boolean; isChatReportArchived?: boolean; - archivedReportsIDSet?: ArchivedReportsIDSet; }): Array> { const options: Array> = []; @@ -1001,7 +998,6 @@ function getSecondaryReportActions({ isChatReportArchived, outstandingReportsByPolicyID, transaction, - archivedReportsIDSet, }); const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(report, isChatReportArchived); @@ -1052,31 +1048,18 @@ function getSecondaryExportReportActions( return options; } -function getSecondaryTransactionThreadActions({ - currentUserLogin, - currentUserAccountID, - parentReport, - reportTransaction, - reportAction, - originalTransaction, - policy, - transactionThreadReport, - outstandingReportsByPolicyID, - isChatReportArchived, - archivedReportsIDSet, -}: { - currentUserLogin: string; - currentUserAccountID: number; - parentReport: Report; - reportTransaction: Transaction; - reportAction: ReportAction | undefined; - originalTransaction: OnyxEntry; - policy: OnyxEntry; - transactionThreadReport?: OnyxEntry; - outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; - isChatReportArchived?: boolean; - archivedReportsIDSet?: ArchivedReportsIDSet; -}): Array> { +function getSecondaryTransactionThreadActions( + currentUserLogin: string, + currentUserAccountID: number, + parentReport: Report, + reportTransaction: Transaction, + reportAction: ReportAction | undefined, + originalTransaction: OnyxEntry, + policy: OnyxEntry, + transactionThreadReport?: OnyxEntry, + outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, + isChatReportArchived?: boolean, +): Array> { const options: Array> = []; if (!!reportAction && isHoldActionForTransaction(parentReport, reportTransaction, reportAction, policy, currentUserAccountID)) { @@ -1106,14 +1089,7 @@ function getSecondaryTransactionThreadActions({ if ( reportTransaction?.transactionID && reportAction && - canEditFieldOfMoneyRequest({ - reportAction, - fieldToEdit: CONST.EDIT_REQUEST_FIELD.REPORT, - isChatReportArchived, - outstandingReportsByPolicyID, - transaction: reportTransaction, - archivedReportsIDSet, - }) && + canEditFieldOfMoneyRequest({reportAction, fieldToEdit: CONST.EDIT_REQUEST_FIELD.REPORT, isChatReportArchived, outstandingReportsByPolicyID, transaction: reportTransaction}) && canUserPerformWriteActionReportUtils(parentReport, isChatReportArchived) ) { options.push(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 74e1530ee126..10932d5d9aa6 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1208,7 +1208,7 @@ Onyx.connectWithoutView({ }); let allReportNameValuePair: OnyxCollection; -Onyx.connect({ +Onyx.connectWithoutView({ key: ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, waitForCollectionCallback: true, callback: (value) => { @@ -4865,7 +4865,6 @@ function canEditFieldOfMoneyRequest({ transaction, report, policy, - archivedReportsIDSet, }: { reportAction: OnyxInputOrEntry; fieldToEdit: ValueOf; @@ -4875,7 +4874,6 @@ function canEditFieldOfMoneyRequest({ transaction: OnyxEntry; report?: OnyxInputOrEntry; policy?: OnyxEntry; - archivedReportsIDSet?: ArchivedReportsIDSet; }): boolean { // A list of fields that cannot be edited by anyone, once an expense has been settled const restrictedFields: string[] = [ @@ -4974,7 +4972,7 @@ function canEditFieldOfMoneyRequest({ return true; } - if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportsIDSet)) { + if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)) { return false; } @@ -4990,7 +4988,6 @@ function canEditFieldOfMoneyRequest({ moneyRequestReport?.policyID, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[moneyRequestReport?.policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, - archivedReportsIDSet, ).length > 0 ); } @@ -5003,14 +5000,14 @@ function canEditFieldOfMoneyRequest({ } // Check the cheaper condition first - if ((isOwner || isAdmin || isManager) && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportsIDSet)) { + if ((isOwner || isAdmin || isManager) && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)) { return true; } // Check if there are multiple outstanding reports across policies let outstandingReportsCount = 0; for (const currentPolicy of policiesArray) { - const reports = getOutstandingReportsForUser(currentPolicy.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, archivedReportsIDSet); + const reports = getOutstandingReportsForUser(currentPolicy.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}); outstandingReportsCount += reports.length; // Short-circuit once we find more than 1 @@ -11469,7 +11466,12 @@ function hasForwardedAction(reportID: string): boolean { return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); } -function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: string | undefined, archivedReportsIDSet?: ArchivedReportsIDSet, allowSubmitted = true): boolean { +function isReportOutstanding( + iouReport: OnyxInputOrEntry, + policyID: string | undefined, + reportNameValuePairs: OnyxCollection = allReportNameValuePair, + allowSubmitted = true, +): boolean { if ( !iouReport || isEmptyObject(iouReport) || @@ -11481,8 +11483,8 @@ function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: stri ) { return false; } - const reportNameValuePair = archivedReportsIDSet ? undefined : allReportNameValuePair?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport.reportID}`]; - if ((archivedReportsIDSet && isReportArchivedByID(archivedReportsIDSet, iouReport.reportID)) || isArchivedReport(reportNameValuePair)) { + const reportNameValuePair = reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport.reportID}`]; + if (isArchivedReport(reportNameValuePair)) { return false; } const currentRoute = navigationRef.getCurrentRoute(); @@ -11505,7 +11507,7 @@ function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, reports: OnyxCollection = deprecatedAllReports, - archivedReportsIDSet?: ArchivedReportsIDSet, + reportNameValuePairs: OnyxCollection = allReportNameValuePair, allowSubmitted = true, ): Array> { if (!reports) { @@ -11514,7 +11516,7 @@ function getOutstandingReportsForUser( return Object.values(reports).filter( (report) => report?.pendingFields?.preview !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - isReportOutstanding(report, policyID, archivedReportsIDSet, allowSubmitted) && + isReportOutstanding(report, policyID, reportNameValuePairs, allowSubmitted) && report?.ownerAccountID === reportOwnerAccountID, ); } diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 88f93f404a7f..fad817a7cfde 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -168,7 +168,6 @@ import { prepareOnboardingOnyxData, } from '@libs/ReportUtils'; import {buildOptimisticSnapshotData, getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import {getAmount, getCurrency, hasValidModifiedAmount, isOnHold, recalculateUnreportedTransactionDetails, shouldClearConvertedAmount} from '@libs/TransactionUtils'; import addTrailingForwardSlash from '@libs/UrlUtils'; @@ -4451,9 +4450,8 @@ function navigateToMostRecentReport( currentUserAccountID: number, introSelected: OnyxEntry, betas: OnyxEntry, - archivedReportsIDSet?: ArchivedReportsIDSet, ) { - const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, archivedReportsIDSet)?.reportID; + const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID)?.reportID; if (lastAccessedReportID) { // Check if route exists for super wide RHP vs regular full screen report @@ -4478,8 +4476,8 @@ function navigateToMostRecentReport( } } -function getMostRecentReportID(currentReport: OnyxEntry, conciergeReportID: string | undefined, archivedReportsIDSet?: ArchivedReportsIDSet) { - const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, archivedReportsIDSet)?.reportID; +function getMostRecentReportID(currentReport: OnyxEntry, conciergeReportID: string | undefined) { + const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID)?.reportID; return lastAccessedReportID ?? conciergeReportID; } diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 31232f3fbb7b..22cc60fc890d 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -18,7 +18,6 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import {getReportName} from '@libs/ReportNameUtils'; import * as ReportUtils from '@libs/ReportUtils'; import {buildOptimisticSnapshotData} from '@libs/SearchQueryUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -1155,7 +1154,7 @@ function getShareDestination( * @param report - The task report being deleted * @returns The URL to navigate to */ -function getNavigationUrlOnTaskDelete(report: OnyxEntry, conciergeReportID: string | undefined, archivedReportsIDSet: ArchivedReportsIDSet): string | undefined { +function getNavigationUrlOnTaskDelete(report: OnyxEntry, conciergeReportID: string | undefined): string | undefined { if (!report) { return undefined; } @@ -1170,7 +1169,7 @@ function getNavigationUrlOnTaskDelete(report: OnyxEntry, conci } // If no parent report, try to navigate to most recent report - const mostRecentReportID = getMostRecentReportID(report, conciergeReportID, archivedReportsIDSet); + const mostRecentReportID = getMostRecentReportID(report, conciergeReportID); if (mostRecentReportID) { return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID); } @@ -1188,7 +1187,6 @@ function deleteTask( currentUserAccountID: number, hasOutstandingChildTask: boolean, parentReportAction: OnyxEntry, - archivedReportsIDSet: ArchivedReportsIDSet, conciergeReportID: string | undefined, delegateEmail: string | undefined, ancestors: ReportUtils.Ancestor[] = [], @@ -1316,7 +1314,7 @@ function deleteTask( API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData}); notifyNewAction(report.reportID, undefined, true); - const urlToNavigateBack = getNavigationUrlOnTaskDelete(report, conciergeReportID, archivedReportsIDSet); + const urlToNavigateBack = getNavigationUrlOnTaskDelete(report, conciergeReportID); if (urlToNavigateBack) { Navigation.goBack(); return urlToNavigateBack; diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts index 6b24aca1fea8..509f7928c7c1 100644 --- a/src/libs/navigateAfterOnboarding.ts +++ b/src/libs/navigateAfterOnboarding.ts @@ -28,7 +28,7 @@ function getReportIDAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIdSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -43,7 +43,7 @@ function getReportIDAfterOnboarding( return undefined; } - const lastAccessedReport = findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom() && !shouldPreventOpenAdminRoom, undefined, archivedReportsIDSet); + const lastAccessedReport = findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom() && !shouldPreventOpenAdminRoom, undefined, archivedReportsIdSet); const lastAccessedReportID = lastAccessedReport?.reportID; // When the user goes through the onboarding flow, a workspace can be created if the user selects specific options. The user should be taken to the #admins room for that workspace because it is the most natural place for them to start their experience in the app. @@ -59,7 +59,7 @@ function navigateAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIdSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -86,7 +86,7 @@ function navigateAfterOnboarding( isSmallScreenWidth, canUseDefaultRooms, conciergeReportID, - archivedReportsIDSet, + archivedReportsIdSet, onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, @@ -103,7 +103,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIdSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -115,7 +115,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth, canUseDefaultRooms, conciergeReportID, - archivedReportsIDSet, + archivedReportsIdSet, onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, diff --git a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx index 6edf2213eeff..7b3fa91fa36b 100644 --- a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx +++ b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx @@ -13,7 +13,7 @@ import ScrollView from '@components/ScrollView'; import Section from '@components/Section'; import isSidePanelReportSupported from '@components/SidePanel/isSidePanelReportSupported'; import Text from '@components/Text'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHasActiveAdminPolicies from '@hooks/useHasActiveAdminPolicies'; import useLastWorkspaceNumber from '@hooks/useLastWorkspaceNumber'; @@ -65,7 +65,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin const {isBetaEnabled} = usePermissions(); const [session] = useOnyx(ONYXKEYS.SESSION); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIdSet(); const hasActiveAdminPolicies = useHasActiveAdminPolicies(); const lastWorkspaceNumber = useLastWorkspaceNumber(); diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index b5ea6bb5a26d..85b3e67b4bc8 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -9,7 +9,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useAutoCreateTrackWorkspace from '@hooks/useAutoCreateTrackWorkspace'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; diff --git a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx index 9543b3c1712d..42af8bc1ecba 100644 --- a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx +++ b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx @@ -10,7 +10,7 @@ import SelectionListWithSections from '@components/SelectionList/SelectionListWi import type {Section} from '@components/SelectionList/SelectionListWithSections/types'; import Text from '@components/Text'; import useAllPolicyExpenseChatReportActions from '@hooks/useAllPolicyExpenseChatReportActions'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnboardingMessages from '@hooks/useOnboardingMessages'; diff --git a/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx b/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx index 40c0afa2da72..568b8d9a4606 100644 --- a/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx +++ b/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx @@ -8,7 +8,7 @@ import RenderHTML from '@components/RenderHTML'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHasActiveAdminPolicies from '@hooks/useHasActiveAdminPolicies'; import useHasTeam2025Pricing from '@hooks/useHasTeam2025Pricing'; @@ -53,7 +53,7 @@ function BaseOnboardingWorkspaceOptional({shouldUseNativeStyles}: BaseOnboarding const [onboardingPolicyID] = useOnyx(ONYXKEYS.ONBOARDING_POLICY_ID); const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID); const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIdSet(); const {onboardingMessages} = useOnboardingMessages(); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [betas] = useOnyx(ONYXKEYS.BETAS); diff --git a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx index 51b744a0939e..b91ad2e1a154 100644 --- a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx +++ b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx @@ -8,7 +8,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; import Text from '@components/Text'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 3a38d83e402d..eca440a146c1 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -28,7 +28,6 @@ import {useSearchActionsContext} from '@components/Search/SearchContext'; import {SUPER_WIDE_RIGHT_MODALS} from '@components/WideRHPContextProvider/WIDE_RIGHT_MODALS'; import useActivePolicy from '@hooks/useActivePolicy'; import useAncestors from '@hooks/useAncestors'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDeleteTransactions from '@hooks/useDeleteTransactions'; @@ -181,7 +180,6 @@ function ReportDetailsPage({policy, report, route, reportMetadata, reportLoading const parentReportAction = useParentReportAction(report); const hasOutstandingChildTask = useHasOutstandingChildTask(report); - const archivedReportsIDSet = useArchivedReportsIDSet(); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); @@ -913,7 +911,6 @@ function ReportDetailsPage({policy, report, route, reportMetadata, reportLoading currentUserPersonalDetails.accountID, hasOutstandingChildTask, parentReportAction, - archivedReportsIDSet, conciergeReportID, delegateEmail, ancestors, diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index 9632d589513c..d467087a44c0 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -12,7 +12,6 @@ import {useSearchStateContext} from '@components/Search/SearchContext'; import useShowSuperWideRHPVersion from '@components/WideRHPContextProvider/useShowSuperWideRHPVersion'; import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDocumentTitle from '@hooks/useDocumentTitle'; import useIsReportReadyToDisplay from '@hooks/useIsReportReadyToDisplay'; @@ -147,7 +146,6 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute, isReportArchived); - const archivedReportsIDSet = useArchivedReportsIDSet(); const actionListValue = useActionListContextValue(); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`); @@ -413,7 +411,6 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { key={report?.reportID} onLayout={handleSubmitToDestinationVisibleLayout} backToRoute={route.params.backTo} - archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index cfc3c90cd534..7eff4be2dfc7 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -13,7 +13,6 @@ import ScrollView from '@components/ScrollView'; import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import {useWideRHPState} from '@components/WideRHPContextProvider'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -41,7 +40,6 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; function Confirmation() { const styles = useThemeStyles(); const {translate} = useLocalize(); - const archivedReportsIDSet = useArchivedReportsIDSet(); const route = useRoute>(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [reviewDuplicates, reviewDuplicatesResult] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); @@ -168,7 +166,6 @@ function Confirmation() { readonly updatedTransaction={newTransaction as OnyxEntry} isFromReviewDuplicates - archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/TransactionMerge/ConfirmationPage.tsx b/src/pages/TransactionMerge/ConfirmationPage.tsx index 371a9b7ddf3b..6bc398a43166 100644 --- a/src/pages/TransactionMerge/ConfirmationPage.tsx +++ b/src/pages/TransactionMerge/ConfirmationPage.tsx @@ -10,7 +10,6 @@ import MoneyRequestView from '@components/ReportActionItem/MoneyRequestView'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useMergeTransactions from '@hooks/useMergeTransactions'; @@ -40,7 +39,6 @@ function ConfirmationPage({route}: ConfirmationPageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [isMergingExpenses, setIsMergingExpenses] = useState(false); - const archivedReportsIDSet = useArchivedReportsIDSet(); const {transactionID, isOnSearch, backTo} = route.params; const [mergeTransaction, mergeTransactionMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.MERGE_TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`); @@ -144,7 +142,6 @@ function ConfirmationPage({route}: ConfirmationPageProps) { readonly updatedTransaction={mergedTransactionData as unknown as OnyxEntry} mergeTransactionID={transactionID} - archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/inbox/ReportActionsList.tsx b/src/pages/inbox/ReportActionsList.tsx index cf4aac5d4852..38b9741ec5fd 100644 --- a/src/pages/inbox/ReportActionsList.tsx +++ b/src/pages/inbox/ReportActionsList.tsx @@ -9,7 +9,6 @@ import useReportTransactionsCollection from '@hooks/useReportTransactionsCollect import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getAllNonDeletedTransactions, shouldDisplayReportTableView, shouldWaitForTransactions as shouldWaitForTransactionsUtil} from '@libs/MoneyRequestReportUtils'; import {isInvoiceReport, isMoneyRequestReport} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ReportActionsView from './report/ReportActionsView'; @@ -27,7 +26,7 @@ const defaultReportLoadingState = { * or MoneyRequestReportActionsList. Only subscribes to what the branching * conditions need — heavy data derivation is pushed into each child. */ -function ReportActionsList({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { +function ReportActionsList() { const route = useRoute(); const routeParams = route.params as {reportID?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -50,15 +49,10 @@ function ReportActionsList({archivedReportsIDSet}: {archivedReportsIDSet: Archiv } if (shouldDisplayMoneyRequestActionsList) { - return ; + return ; } - return ( - - ); + return ; } export default ReportActionsList; diff --git a/src/pages/inbox/ReportHeader.tsx b/src/pages/inbox/ReportHeader.tsx index 5fe4f54df3b7..76d67178ab85 100644 --- a/src/pages/inbox/ReportHeader.tsx +++ b/src/pages/inbox/ReportHeader.tsx @@ -13,7 +13,6 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Navigation from '@libs/Navigation/Navigation'; import {getReportName} from '@libs/ReportNameUtils'; import {getReportOfflinePendingActionAndErrors, isInvoiceReport, isMoneyRequestReport, isReportTransactionThread} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; @@ -23,9 +22,9 @@ import HeaderView from './HeaderView'; /** * Owns header variant selection, back button logic, and OfflineWithFeedback wrapper. - * Subscribes to report type internally. + * Subscribes to report type internally — ReportScreen passes nothing. */ -function ReportHeader({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { +function ReportHeader() { const route = useRoute(); const routeParams = route.params as {reportID?: string; backTo?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -98,7 +97,6 @@ function ReportHeader({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedRep ); diff --git a/src/pages/inbox/ReportRouteParamHandler.tsx b/src/pages/inbox/ReportRouteParamHandler.tsx index 3f8e12d13207..d934c75eedef 100644 --- a/src/pages/inbox/ReportRouteParamHandler.tsx +++ b/src/pages/inbox/ReportRouteParamHandler.tsx @@ -1,5 +1,5 @@ import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import usePermissions from '@hooks/usePermissions'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index 44719c36be3b..6790e427478a 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -7,7 +7,6 @@ import {InteractionManager, View} from 'react-native'; import ScreenWrapper from '@components/ScreenWrapper'; import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import {useCurrentReportIDState} from '@hooks/useCurrentReportID'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSubmitToDestinationVisible from '@hooks/useSubmitToDestinationVisible'; @@ -54,7 +53,6 @@ function ReportScreen({route, navigation}: ReportScreenProps) { const viewportOffsetTop = useViewportOffsetTop(); const isTopMostReportId = currentReportIDValue === reportIDFromRoute; const screenWrapperStyle: ViewStyle[] = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}]; - const archivedReportsIDSet = useArchivedReportsIDSet(); // During dismiss_modal_and_open_report, defer heavy non-content components // (composer, invisible handlers) so the first render is lighter. @@ -142,7 +140,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { {!shouldDeferNonEssentials && } - + {!shouldDeferNonEssentials && } {!shouldDeferNonEssentials && } @@ -151,8 +149,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) { style={[styles.flex1, styles.justifyContentEnd, styles.overflowHidden]} testID="report-actions-view-wrapper" > - - {shouldDeferNonEssentials ? : } + + {shouldDeferNonEssentials ? : } diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index c67531a061a3..2e14824165c4 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -101,7 +101,6 @@ import { isTaskReport, shouldDisplayThreadReplies as shouldDisplayThreadRepliesUtils, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import SelectionScraper from '@libs/SelectionScraper'; import {ReactionListContext} from '@pages/inbox/ReportScreenContext'; import AttachmentModalContext from '@pages/media/AttachmentModalScreen/AttachmentModalContext'; @@ -145,8 +144,6 @@ import SearchActionHeader from './SearchActionHeader'; import TripSummary from './TripSummary'; import WhisperBanner from './WhisperBanner'; -const EMPTY_ARCHIVED_REPORTS_ID_SET = new Set(); - type PureReportActionItemProps = { /** The personal policy ID */ personalPolicyID: string | undefined; @@ -287,9 +284,6 @@ type PureReportActionItemProps = { /** Report metadata for the report */ reportMetadata?: OnyxEntry; - - /** Set of archived report ID keys */ - archivedReportsIDSet?: ArchivedReportsIDSet; }; // This is equivalent to returning a negative boolean in normal functions, but we can keep the element return type @@ -339,7 +333,6 @@ function PureReportActionItem({ reportNameValuePairsOrigin, reportNameValuePairsOriginalID, reportMetadata, - archivedReportsIDSet = EMPTY_ARCHIVED_REPORTS_ID_SET, }: PureReportActionItemProps) { const isConciergeGreeting = action.reportActionID === CONST.CONCIERGE_GREETING_ACTION_ID; const shouldDisplayContextMenuValue = shouldDisplayContextMenu && !isConciergeGreeting; @@ -1078,19 +1071,9 @@ function PureReportActionItem({ transactionID={transactionID} draftMessage={draftMessage} shouldHideThreadDividerLine={shouldHideThreadDividerLine} - archivedReportsIDSet={archivedReportsIDSet} /> ); - }, [ - contextMenuStateValue, - contextMenuActionsValue, - parentReportAction, - parentReport, - draftMessage, - shouldHideThreadDividerLine, - archivedReportsIDSet, - parentReportActionForTransactionThread, - ]); + }, [contextMenuStateValue, contextMenuActionsValue, parentReportAction, parentReport, draftMessage, shouldHideThreadDividerLine, parentReportActionForTransactionThread]); if (action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && !isHarvestCreatedExpenseReport) { return createdActionContent; diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx index ad49a598eb01..32f182db3989 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx @@ -8,7 +8,6 @@ import useHandleExceedMaxTaskTitleLength from '@hooks/useHandleExceedMaxTaskTitl import useOnyx from '@hooks/useOnyx'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; import {chatIncludesConcierge} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {setIsComposerFullSize} from '@userActions/Report'; import {isBlockedFromConcierge as isBlockedFromConciergeUserAction} from '@userActions/User'; import CONST from '@src/CONST'; @@ -23,7 +22,6 @@ const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); type ComposerProviderProps = { reportID: string; - archivedReportsIDSet?: ArchivedReportsIDSet; children: React.ReactNode; }; diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index f63c179abfdd..706901cf2e12 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -6,7 +6,6 @@ import useOnyx from '@hooks/useOnyx'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import {getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import AgentZeroAwareTypingIndicator from './AgentZeroAwareTypingIndicator'; import ComposerActionMenu from './ComposerActionMenu'; @@ -23,14 +22,11 @@ import ComposerProvider from './ComposerProvider'; import ComposerSendButton from './ComposerSendButton'; import type {ComposerRef} from './ComposerWithSuggestions/ComposerWithSuggestions'; -const EMPTY_ARCHIVED_REPORTS_ID_SET = new Set(); - type ReportActionComposeProps = { reportID: string; - archivedReportsIDSet?: ArchivedReportsIDSet; }; -function ReportActionComposeInner({reportID}: Pick) { +function ReportActionComposeInner({reportID}: ReportActionComposeProps) { const styles = useThemeStyles(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); @@ -71,12 +67,9 @@ function ReportActionComposeInner({reportID}: Pick + ); diff --git a/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts b/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts index d5bdbf2c6e0a..4a454111f10a 100644 --- a/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts +++ b/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts @@ -1,4 +1,3 @@ -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; @@ -15,7 +14,6 @@ import type {ReportAction} from '@src/types/onyx'; function useShouldAddOrReplaceReceipt(reportID: string) { const {isOffline} = useNetwork(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); - const archivedReportsIDSet = useArchivedReportsIDSet(); const isReportArchived = useReportIsArchived(report?.reportID); const allReportTransactions = useReportTransactionsCollection(reportID); const [rawReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`); @@ -36,7 +34,7 @@ function useShouldAddOrReplaceReceipt(reportID: string) { const canUserPerformWriteAction = !!canUserPerformWriteActionReportUtils(report, isReportArchived); const canEditReceipt = canUserPerformWriteAction && - canEditFieldOfMoneyRequest({reportAction: effectiveParentReportAction, fieldToEdit: CONST.EDIT_REQUEST_FIELD.RECEIPT, transaction, archivedReportsIDSet}) && + canEditFieldOfMoneyRequest({reportAction: effectiveParentReportAction, fieldToEdit: CONST.EDIT_REQUEST_FIELD.RECEIPT, transaction}) && !transaction?.receipt?.isTestDriveReceipt; const shouldAddOrReplaceReceipt = (isTransactionThreadView || isSingleTransactionView) && canEditReceipt; diff --git a/src/pages/inbox/report/ReportActionItemContentCreated.tsx b/src/pages/inbox/report/ReportActionItemContentCreated.tsx index b1a691efc1b3..aab07cc262f4 100644 --- a/src/pages/inbox/report/ReportActionItemContentCreated.tsx +++ b/src/pages/inbox/report/ReportActionItemContentCreated.tsx @@ -17,7 +17,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {isMessageDeleted, isReversedTransaction as isReversedTransactionReportActionsUtils, isTransactionThread} from '@libs/ReportActionsUtils'; import {isCanceledTaskReport, isExpenseReport, isInvoiceReport, isIOUReport, isTaskReport} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {getCurrency} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -49,9 +48,6 @@ type ReportActionItemContentCreatedProps = { /** Flag to show, hide the thread divider line */ shouldHideThreadDividerLine: boolean; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionItemContentCreated({ @@ -62,7 +58,6 @@ function ReportActionItemContentCreated({ transactionID, draftMessage, shouldHideThreadDividerLine, - archivedReportsIDSet, }: ReportActionItemContentCreatedProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -129,7 +124,6 @@ function ReportActionItemContentCreated({ parentReportID={report?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground - archivedReportsIDSet={archivedReportsIDSet} /> {renderThreadDivider} @@ -196,7 +190,6 @@ function ReportActionItemContentCreated({ parentReportID={transactionThreadReport?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground={false} - archivedReportsIDSet={archivedReportsIDSet} /> {renderThreadDivider} diff --git a/src/pages/inbox/report/ReportActionItemParentAction.tsx b/src/pages/inbox/report/ReportActionItemParentAction.tsx index da0482d4cac3..dfd306bf5704 100644 --- a/src/pages/inbox/report/ReportActionItemParentAction.tsx +++ b/src/pages/inbox/report/ReportActionItemParentAction.tsx @@ -20,7 +20,6 @@ import { navigateToLinkedReportAction, shouldExcludeAncestorReportAction, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Report, ReportAction, ReportActions, ReportActionsDrafts, ReportNameValuePairs, Transaction} from '@src/types/onyx'; @@ -72,9 +71,6 @@ type ReportActionItemParentActionProps = { /** Whether the report is archived */ isReportArchived: boolean; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionItemParentAction({ @@ -91,7 +87,6 @@ function ReportActionItemParentAction({ userBillingFundID, isTryNewDotNVPDismissed = false, isReportArchived = false, - archivedReportsIDSet, }: ReportActionItemParentActionProps) { const styles = useThemeStyles(); const ancestors = useAncestors(report, shouldExcludeAncestorReportAction); @@ -236,7 +231,6 @@ function ReportActionItemParentAction({ linkedTransactionRouteError={linkedTransactionRouteError} userBillingFundID={userBillingFundID} isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} - archivedReportsIDSet={archivedReportsIDSet} /> ); diff --git a/src/pages/inbox/report/ReportActionsList.tsx b/src/pages/inbox/report/ReportActionsList.tsx index 113556c5c407..d2b4ec25b642 100644 --- a/src/pages/inbox/report/ReportActionsList.tsx +++ b/src/pages/inbox/report/ReportActionsList.tsx @@ -60,7 +60,6 @@ import { isTaskReport, isUnread, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import Visibility from '@libs/Visibility'; import type {ReportsSplitNavigatorParamList} from '@navigation/types'; import ConciergeThinkingMessage from '@pages/home/report/ConciergeThinkingMessage'; @@ -81,8 +80,6 @@ import {getUnreadMarkerReportAction} from './shouldDisplayNewMarkerOnReportActio import StaticReportActionsPreview from './StaticReportActionsPreview'; import useReportUnreadMessageScrollTracking from './useReportUnreadMessageScrollTracking'; -const EMPTY_ARCHIVED_REPORTS_ID_SET = new Set(); - type ReportActionsListProps = { /** The report currently being looked at */ report: OnyxTypes.Report; @@ -134,9 +131,6 @@ type ReportActionsListProps = { /** Callback to show previous messages */ onShowPreviousMessages?: () => void; - - /** Set of archived report ID keys */ - archivedReportsIDSet?: ArchivedReportsIDSet; }; // In the component we are subscribing to the arrival of new actions. @@ -176,7 +170,6 @@ function ReportActionsList({ showHiddenHistory, hasPreviousMessages, onShowPreviousMessages, - archivedReportsIDSet = EMPTY_ARCHIVED_REPORTS_ID_SET, }: ReportActionsListProps) { const prevHasCreatedActionAdded = usePrevious(hasCreatedActionAdded); const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); @@ -744,7 +737,6 @@ function ReportActionsList({ isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairs?.origin} reportNameValuePairsOriginalID={reportNameValuePairs?.originalID} - archivedReportsIDSet={archivedReportsIDSet} /> {showPreviousMessagesButton && ( @@ -783,7 +775,6 @@ function ReportActionsList({ isReportArchived, reportNameValuePairs?.origin, reportNameValuePairs?.originalID, - archivedReportsIDSet, reportActionsFromOnyx, isConciergeSidePanel, showHiddenHistory, diff --git a/src/pages/inbox/report/ReportActionsListItemRenderer.tsx b/src/pages/inbox/report/ReportActionsListItemRenderer.tsx index f2b9219bec97..a08b7ba8281b 100644 --- a/src/pages/inbox/report/ReportActionsListItemRenderer.tsx +++ b/src/pages/inbox/report/ReportActionsListItemRenderer.tsx @@ -3,7 +3,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import useOnyx from '@hooks/useOnyx'; import {getOriginalMessage, isSentMoneyReportAction, isTransactionThread} from '@libs/ReportActionsUtils'; import {isChatThread} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Report, ReportAction} from '@src/types/onyx'; @@ -72,9 +71,6 @@ type ReportActionsListItemRendererProps = { /** Report name value pairs originalID */ reportNameValuePairsOriginalID?: string; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionsListItemRenderer({ @@ -99,7 +95,6 @@ function ReportActionsListItemRenderer({ isReportArchived = false, reportNameValuePairsOrigin, reportNameValuePairsOriginalID, - archivedReportsIDSet, }: ReportActionsListItemRendererProps) { const originalMessage = useMemo(() => getOriginalMessage(reportAction), [reportAction]); @@ -196,7 +191,6 @@ function ReportActionsListItemRenderer({ userBillingFundID={userBillingFundID} isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} isReportArchived={isReportArchived} - archivedReportsIDSet={archivedReportsIDSet} /> ); } @@ -222,7 +216,6 @@ function ReportActionsListItemRenderer({ isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairsOrigin} reportNameValuePairsOriginalID={reportNameValuePairsOriginalID} - archivedReportsIDSet={archivedReportsIDSet} /> ); } diff --git a/src/pages/inbox/report/ReportActionsView.tsx b/src/pages/inbox/report/ReportActionsView.tsx index 806415e44bf7..01209272079c 100755 --- a/src/pages/inbox/report/ReportActionsView.tsx +++ b/src/pages/inbox/report/ReportActionsView.tsx @@ -46,7 +46,6 @@ import { isMoneyRequestReport, isReportTransactionThread as isReportTransactionThreadUtil, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -63,14 +62,11 @@ type ReportActionsViewProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; let listOldID = Math.round(Math.random() * 100); -function ReportActionsView({reportID, onLayout, archivedReportsIDSet}: ReportActionsViewProps) { +function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) { useCopySelectionHelper(); const {translate} = useLocalize(); usePendingConciergeResponse(reportID); @@ -373,7 +369,6 @@ function ReportActionsView({reportID, onLayout, archivedReportsIDSet}: ReportAct showHiddenHistory={!showFullHistory} hasPreviousMessages={hasPreviousMessages} onShowPreviousMessages={handleShowPreviousMessages} - archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/inbox/report/ReportFooter.tsx b/src/pages/inbox/report/ReportFooter.tsx index b753ad19ed72..adb8efdfa270 100644 --- a/src/pages/inbox/report/ReportFooter.tsx +++ b/src/pages/inbox/report/ReportFooter.tsx @@ -27,7 +27,6 @@ import { isPublicRoom, isSystemChat as isSystemChatUtil, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isLoadingInitialReportActionsSelector} from '@src/selectors/ReportMetaData'; @@ -41,7 +40,7 @@ const policyRoleSelector = (policy: OnyxEntry) => policy?.role * Footer component that decides between the composer and * archived/anonymous/blocked/system chat/admins-only footer. */ -function ReportFooter({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { +function ReportFooter() { const route = useRoute(); const routeParams = route.params as {reportID?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -92,10 +91,7 @@ function ReportFooter({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedRep return ( - + ); diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 0c3bbe2c57a7..f1926c645a30 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -5,7 +5,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import {usePersonalDetails, useSession} from '@components/OnyxListItemProvider'; import {useSearchActionsContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIDSet'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useOnyx from '@hooks/useOnyx'; import useOptimisticDraftTransactions from '@hooks/useOptimisticDraftTransactions'; @@ -48,12 +47,11 @@ const getIOUActionsSelector = (actions: OnyxEntry): ReportAction[ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {backTo, action, iouType, transactionID, reportID: reportIDFromRoute, reportActionID} = route.params; const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const archivedReportsIDSet = useArchivedReportsIdSet(); const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); const participantReportID = transaction?.participants?.at(0)?.reportID; const participantReport = Object.values(allReports ?? {}).find((report) => report?.reportID === participantReportID); - const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID, archivedReportsIDSet)) || isUnreported; + const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID)) || isUnreported; const outstandingReportID = isPolicyExpenseChat(participantReport) ? participantReport?.iouReportID : participantReportID; const selectedReportID = shouldUseTransactionReport ? transactionReport?.reportID : outstandingReportID; const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index 32b0c7064d80..f2087ca731b8 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -4,7 +4,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -64,15 +64,15 @@ function TaskShareDestinationSelectorModal() { onSingleSelect: selectReportHandler, }); - const archivedReportsIDSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIdSet(); const filteredOptions = useMemo(() => { - const filteredReports = reportFilter(availableOptions.recentReports as Array>, archivedReportsIDSet); + const filteredReports = reportFilter(availableOptions.recentReports as Array>, archivedReportsIdSet); return { ...availableOptions, recentReports: filteredReports ?? [], }; - }, [availableOptions, archivedReportsIDSet]); + }, [availableOptions, archivedReportsIdSet]); const data = useMemo( () => diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 8712cbd98feb..c6cf26b29e6f 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -5596,6 +5596,7 @@ describe('actions/Report', () => { // Shared test constants for leave functions const TEST_CONCIERGE_REPORT_ID = '999'; const TEST_CURRENT_USER_ACCOUNT_ID = 1; + describe('leaveGroupChat', () => { const GROUP_CHAT_REPORT_ID = '1001'; diff --git a/tests/actions/TaskTest.ts b/tests/actions/TaskTest.ts index f3a38a0a0ed1..7773e883b32b 100644 --- a/tests/actions/TaskTest.ts +++ b/tests/actions/TaskTest.ts @@ -1230,7 +1230,6 @@ describe('actions/Task', () => { const mockCurrentUserAccountID = 123; const DELEGATE_EMAIL = 'delegate@example.com'; const DELEGATE_ACCOUNT_ID = 999; - const archivedReportsIDSet = new Set(); beforeEach(async () => { jest.clearAllMocks(); @@ -1264,7 +1263,7 @@ describe('actions/Task', () => { }); it('should return early when report is undefined', () => { - deleteTask(undefined, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123', undefined); + deleteTask(undefined, undefined, false, mockCurrentUserAccountID, false, undefined, 'concierge_123', undefined); // eslint-disable-next-line rulesdir/no-multiple-api-calls expect(API.write).not.toHaveBeenCalled(); @@ -1305,7 +1304,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(true); - deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, parentReportAction, archivedReportsIDSet, 'concierge_123', undefined); + deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, parentReportAction, 'concierge_123', undefined); // eslint-disable-next-line rulesdir/no-multiple-api-calls expect(API.write).toHaveBeenCalledWith( @@ -1349,7 +1348,7 @@ describe('actions/Task', () => { // No visible actions means the task report should be deleted doesReportHaveVisibleActionsSpy.mockReturnValue(false); - const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123', undefined); + const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, 'concierge_123', undefined); expect(result).toBe(`r/${parentReportID}`); expect(Navigation.goBack).toHaveBeenCalled(); @@ -1377,10 +1376,10 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(conciergeReportID); - const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, conciergeReportID, undefined); + const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, conciergeReportID, undefined); expect(result).toBe(`r/${conciergeReportID}`); - expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID, archivedReportsIDSet); + expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID); expect(Navigation.goBack).toHaveBeenCalled(); }); @@ -1412,7 +1411,7 @@ describe('actions/Task', () => { // Has visible actions, so should not navigate away doesReportHaveVisibleActionsSpy.mockReturnValue(true); - const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123', undefined); + const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, 'concierge_123', undefined); expect(result).toBeUndefined(); expect(Navigation.goBack).not.toHaveBeenCalled(); @@ -1439,7 +1438,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(undefined); - const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, undefined, undefined); + const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, undefined, undefined); // API.write should still be called // eslint-disable-next-line rulesdir/no-multiple-api-calls @@ -1466,7 +1465,7 @@ describe('actions/Task', () => { }); await waitForBatchedUpdatesWithAct(); - deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, undefined, DELEGATE_EMAIL); + deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, undefined, DELEGATE_EMAIL); // eslint-disable-next-line rulesdir/no-multiple-api-calls -- Inspecting mock call args to verify optimistic data structure const calls = (API.write as jest.Mock).mock.calls; @@ -1497,7 +1496,7 @@ describe('actions/Task', () => { }); await waitForBatchedUpdatesWithAct(); - deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, undefined, undefined); + deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, undefined, undefined); // eslint-disable-next-line rulesdir/no-multiple-api-calls -- Inspecting mock call args to verify optimistic data structure const calls = (API.write as jest.Mock).mock.calls; diff --git a/tests/perf-test/ReportActionCompose.perf-test.tsx b/tests/perf-test/ReportActionCompose.perf-test.tsx index 113a7f0d0b5f..b4cf986ceb14 100644 --- a/tests/perf-test/ReportActionCompose.perf-test.tsx +++ b/tests/perf-test/ReportActionCompose.perf-test.tsx @@ -89,10 +89,7 @@ beforeEach(() => { function ReportActionComposeWrapper() { return ( - ()} - /> + ); } diff --git a/tests/ui/MoneyRequestViewTest.tsx b/tests/ui/MoneyRequestViewTest.tsx index 5e861508c942..74c06d8cb8e7 100644 --- a/tests/ui/MoneyRequestViewTest.tsx +++ b/tests/ui/MoneyRequestViewTest.tsx @@ -64,7 +64,6 @@ const policyID = 'policy_mrv_test'; const expenseReportID = 'expense_mrv_123'; const parentReportActionID = 'parent_action_mrv'; const transactionID = 'txn_mrv_test'; -const archivedReportsIDSet = new Set(); const renderMoneyRequestView = (threadReport: ReturnType, policy?: Record) => render( @@ -85,7 +84,6 @@ const renderMoneyRequestView = (threadReport: ReturnType , ); diff --git a/tests/ui/ReportActionComposeTest.tsx b/tests/ui/ReportActionComposeTest.tsx index 4b662f834f49..bba2658a7a4c 100644 --- a/tests/ui/ReportActionComposeTest.tsx +++ b/tests/ui/ReportActionComposeTest.tsx @@ -54,7 +54,6 @@ TestHelper.setupGlobalFetchMock(); const defaultReport = LHNTestUtils.getFakeReport(); const defaultProps: ReportActionComposeProps = { reportID: defaultReport.reportID, - archivedReportsIDSet: new Set(), }; const renderReportActionCompose = (props?: Partial) => { diff --git a/tests/ui/ReportActionsViewTest.tsx b/tests/ui/ReportActionsViewTest.tsx index 84b19116583e..c4980df1f1a2 100644 --- a/tests/ui/ReportActionsViewTest.tsx +++ b/tests/ui/ReportActionsViewTest.tsx @@ -146,12 +146,7 @@ const mockReportActions: OnyxTypes.ReportAction[] = [ const renderReportActionsView = (props: {reportID?: string} = {}) => { const reportID = props.reportID ?? mockReport.reportID; - return render( - ()} - />, - ); + return render(); }; describe('ReportActionsView', () => { diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index ae3bc8be2fc1..257cc9cc3ebf 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -1732,15 +1732,7 @@ describe('getSecondaryAction', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, - parentReport: report, - reportTransaction: {} as Transaction, - reportAction: undefined, - originalTransaction: {} as Transaction, - policy, - }); + const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, {} as Transaction, undefined, {} as Transaction, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.DELETE)).toBe(true); }); @@ -2794,17 +2786,7 @@ describe('getSecondaryTransactionThreadActions', () => { const policy = {} as unknown as Policy; const result = [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.VIEW_DETAILS]; - expect( - getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, - parentReport: report, - reportTransaction: {} as Transaction, - reportAction: undefined, - originalTransaction: {} as Transaction, - policy, - }), - ).toEqual(result); + expect(getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, {} as Transaction, undefined, {} as Transaction, policy)).toEqual(result); }); it('includes HOLD option', () => { @@ -2824,15 +2806,7 @@ describe('getSecondaryTransactionThreadActions', () => { jest.spyOn(ReportUtils, 'canHoldUnholdReportAction').mockReturnValueOnce({canHoldRequest: true, canUnholdRequest: true}); jest.spyOn(ReportUtils, 'isAwaitingFirstLevelApproval').mockReturnValueOnce(true); - const result = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, - parentReport: report, - reportTransaction: transaction, - reportAction: actionR14932, - originalTransaction: {} as Transaction, - policy, - }); + const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.HOLD)).toBe(true); }); @@ -2849,30 +2823,12 @@ describe('getSecondaryTransactionThreadActions', () => { } as unknown as Transaction; jest.spyOn(ReportUtils, 'isHoldCreator').mockReturnValue(false); - const result = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, - parentReport: report, - reportTransaction: transaction, - reportAction: undefined, - originalTransaction: {} as Transaction, - policy, - transactionThreadReport, - }); + const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, undefined, {} as Transaction, policy, transactionThreadReport); expect(result).toContain(CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD); // Do not show if admin is the holder jest.spyOn(ReportUtils, 'isHoldCreator').mockReturnValue(true); - const result2 = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, - parentReport: report, - reportTransaction: transaction, - reportAction: undefined, - originalTransaction: {} as Transaction, - policy, - transactionThreadReport, - }); + const result2 = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, undefined, {} as Transaction, policy, transactionThreadReport); expect(result2).not.toContain(CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD); }); @@ -2889,15 +2845,7 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, - parentReport: report, - reportTransaction: {} as Transaction, - reportAction: undefined, - originalTransaction: {} as Transaction, - policy, - }); + const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, {} as Transaction, undefined, {} as Transaction, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.DELETE)).toBe(true); }); @@ -2985,15 +2933,7 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, - parentReport: report, - reportTransaction: transaction, - reportAction: actionR14932, - originalTransaction: {} as Transaction, - policy, - }); + const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(true); }); @@ -3029,15 +2969,7 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, - parentReport: report, - reportTransaction: transaction, - reportAction: actionR14932, - originalTransaction: {} as Transaction, - policy, - }); + const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3074,15 +3006,7 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, - parentReport: report, - reportTransaction: transaction, - reportAction: actionR14932, - originalTransaction: {} as Transaction, - policy, - }); + const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3123,15 +3047,7 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, - parentReport: report, - reportTransaction: transaction, - reportAction: actionR14932, - originalTransaction: {} as Transaction, - policy, - }); + const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3150,16 +3066,18 @@ describe('getSecondaryTransactionThreadActions', () => { jest.spyOn(ReportUtils, 'canEditFieldOfMoneyRequest').mockReturnValue(true); jest.spyOn(ReportUtils, 'canUserPerformWriteAction').mockReturnValue(true); - const result = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + const result = getSecondaryTransactionThreadActions( + EMPLOYEE_EMAIL, + EMPLOYEE_ACCOUNT_ID, parentReport, - reportTransaction: transaction, - reportAction: actionR14932, - originalTransaction: {} as Transaction, - policy, - isChatReportArchived: false, - }); + transaction, + actionR14932, + {} as Transaction, + policy, + undefined, + undefined, + false, + ); expect(result).toContain(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); }); @@ -3178,16 +3096,18 @@ describe('getSecondaryTransactionThreadActions', () => { jest.spyOn(ReportUtils, 'canEditFieldOfMoneyRequest').mockReturnValue(false); jest.spyOn(ReportUtils, 'canUserPerformWriteAction').mockReturnValue(true); - const result = getSecondaryTransactionThreadActions({ - currentUserLogin: EMPLOYEE_EMAIL, - currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + const result = getSecondaryTransactionThreadActions( + EMPLOYEE_EMAIL, + EMPLOYEE_ACCOUNT_ID, parentReport, - reportTransaction: transaction, - reportAction: actionR14932, - originalTransaction: {} as Transaction, - policy, - isChatReportArchived: false, - }); + transaction, + actionR14932, + {} as Transaction, + policy, + undefined, + undefined, + false, + ); expect(result).not.toContain(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); }); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 6a52545919b1..3c5f78a8dc33 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -7616,8 +7616,7 @@ describe('ReportUtils', () => { }); it('should not return an archived report even if it was most recently accessed', () => { - const archivedReportsIDSet = new Set([`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${archivedReport.reportID}`]); - const result = findLastAccessedReport(false, false, undefined, archivedReportsIDSet); + const result = findLastAccessedReport(false); // Even though the archived report has a more recent lastVisitTime, // the function should filter it out and return the normal report @@ -8531,8 +8530,8 @@ describe('ReportUtils', () => { statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - const archivedReportsIDSet = new Set([`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`]); - expect(isReportOutstanding(report, policy.id, archivedReportsIDSet)).toBe(false); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`, {private_isArchived: DateUtils.getDBTime()}); + expect(isReportOutstanding(report, policy.id)).toBe(false); }); }); diff --git a/tests/unit/hooks/useArchivedReportsIdSet.test.ts b/tests/unit/hooks/useArchivedReportsIdSet.test.ts index d096207645f9..50ae22e69319 100644 --- a/tests/unit/hooks/useArchivedReportsIdSet.test.ts +++ b/tests/unit/hooks/useArchivedReportsIdSet.test.ts @@ -1,9 +1,9 @@ import {renderHook, waitFor} from '@testing-library/react-native'; import Onyx from 'react-native-onyx'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; +import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; import ONYXKEYS from '@src/ONYXKEYS'; -describe('useArchivedReportsIDSet', () => { +describe('useArchivedReportsIdSet', () => { beforeAll(() => { Onyx.init({keys: ONYXKEYS}); }); @@ -13,7 +13,7 @@ describe('useArchivedReportsIDSet', () => { }); it('should return an empty Set when no report name value pairs exist', async () => { - const {result} = renderHook(() => useArchivedReportsIDSet()); + const {result} = renderHook(() => useArchivedReportsIdSet()); await waitFor(() => { expect(result.current).toBeInstanceOf(Set); @@ -25,7 +25,7 @@ describe('useArchivedReportsIDSet', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}123`, {private_isArchived: 'archived'}); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}456`, {private_isArchived: ''}); - const {result} = renderHook(() => useArchivedReportsIDSet()); + const {result} = renderHook(() => useArchivedReportsIdSet()); await waitFor(() => { expect(result.current.size).toBe(1); @@ -39,7 +39,7 @@ describe('useArchivedReportsIDSet', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}200`, {private_isArchived: 'archived'}); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}300`, {private_isArchived: ''}); - const {result} = renderHook(() => useArchivedReportsIDSet()); + const {result} = renderHook(() => useArchivedReportsIdSet()); await waitFor(() => { expect(result.current.size).toBe(2); @@ -52,7 +52,7 @@ describe('useArchivedReportsIDSet', () => { it('should update when a report becomes archived', async () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}123`, {private_isArchived: ''}); - const {result} = renderHook(() => useArchivedReportsIDSet()); + const {result} = renderHook(() => useArchivedReportsIdSet()); await waitFor(() => { expect(result.current.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}123`)).toBe(false); @@ -69,7 +69,7 @@ describe('useArchivedReportsIDSet', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}123`, {private_isArchived: 'archived'}); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}456`, null); - const {result} = renderHook(() => useArchivedReportsIDSet()); + const {result} = renderHook(() => useArchivedReportsIdSet()); await waitFor(() => { expect(result.current.size).toBe(1); diff --git a/tests/unit/hooks/useSearchSections.test.ts b/tests/unit/hooks/useSearchSections.test.ts index 438dc6663d77..890f1157e0f2 100644 --- a/tests/unit/hooks/useSearchSections.test.ts +++ b/tests/unit/hooks/useSearchSections.test.ts @@ -48,7 +48,7 @@ jest.mock('@hooks/useActionLoadingReportIDs', () => ({ default: () => new Set(), })); -jest.mock('@hooks/useArchivedReportsIDSet', () => ({ +jest.mock('@hooks/useArchivedReportsIdSet', () => ({ // eslint-disable-next-line @typescript-eslint/naming-convention __esModule: true, default: () => new Set(), diff --git a/tests/unit/navigateAfterOnboardingTest.ts b/tests/unit/navigateAfterOnboardingTest.ts index 380b85a7cc59..1d4d2a4fe498 100644 --- a/tests/unit/navigateAfterOnboardingTest.ts +++ b/tests/unit/navigateAfterOnboardingTest.ts @@ -122,14 +122,14 @@ describe('navigateAfterOnboarding', () => { expect(navigate).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(REPORT_ID)); }); - it('should pass archivedReportsIDSet when looking up last accessed report', () => { - const archivedReportsIDSet = new Set(['report_1']); + it('should pass archivedReportsIdSet when looking up last accessed report', () => { + const archivedReportsIdSet = new Set(['report_1']); mockFindLastAccessedReport.mockReturnValue(undefined); mockShouldOpenOnAdminRoom.mockReturnValue(false); - navigateAfterOnboarding(true, true, '', archivedReportsIDSet, ONBOARDING_POLICY_ID, ONBOARDING_ADMINS_CHAT_REPORT_ID); + navigateAfterOnboarding(true, true, '', archivedReportsIdSet, ONBOARDING_POLICY_ID, ONBOARDING_ADMINS_CHAT_REPORT_ID); - expect(mockFindLastAccessedReport).toHaveBeenCalledWith(false, false, undefined, archivedReportsIDSet); + expect(mockFindLastAccessedReport).toHaveBeenCalledWith(false, false, undefined, archivedReportsIdSet); }); it('should navigate to Concierge room if user uses a test email', () => { From 58d528fc41727acae2f59e1514de902ea404db8d Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 02:53:04 +0530 Subject: [PATCH 55/78] Restore full archived report migration --- .../sections/ReportField.tsx | 7 +- .../MoneyRequestHeaderSecondaryActions.tsx | 16 +- .../MoneyRequestReportActionsList.tsx | 4 + .../MoneyRequestReportView.tsx | 14 +- .../ReportActionItem/MoneyRequestView.tsx | 7 +- src/components/Search/index.tsx | 60 +++++++- src/hooks/useArchivedReportsIDSet.ts | 37 +++++ src/hooks/useArchivedReportsIdSet.ts | 23 --- src/hooks/useAutoCreateTrackWorkspace.ts | 4 +- src/hooks/useDeleteTransactions.ts | 7 +- src/hooks/useOutstandingReports.ts | 7 +- src/hooks/useSearchSections.ts | 4 +- src/hooks/useSelectedTransactionsActions.ts | 10 +- .../MarkAllMessagesAsReadHandler.tsx | 4 +- .../Navigators/ReportsSplitNavigator.tsx | 4 +- src/libs/ReportSecondaryActionUtils.ts | 50 ++++-- src/libs/ReportUtils.ts | 50 ++++-- src/libs/actions/Report/index.ts | 8 +- src/libs/actions/Task.ts | 8 +- src/libs/navigateAfterOnboarding.ts | 12 +- .../BaseOnboardingInterestedFeatures.tsx | 4 +- .../BaseOnboardingPersonalDetails.tsx | 4 +- .../BaseOnboardingWorkspaceInvite.tsx | 4 +- .../BaseOnboardingWorkspaceOptional.tsx | 4 +- .../BaseOnboardingWorkspaces.tsx | 4 +- src/pages/ReportDetailsPage.tsx | 3 + .../Search/SearchMoneyRequestReportPage.tsx | 3 + .../TransactionDuplicate/Confirmation.tsx | 3 + .../TransactionMerge/ConfirmationPage.tsx | 3 + src/pages/inbox/ReportActionsList.tsx | 10 +- src/pages/inbox/ReportHeader.tsx | 6 +- src/pages/inbox/ReportRouteParamHandler.tsx | 4 +- src/pages/inbox/ReportScreen.tsx | 8 +- .../inbox/report/PureReportActionItem.tsx | 19 ++- .../ReportActionCompose/ComposerProvider.tsx | 2 + .../ReportActionCompose.tsx | 11 +- .../useShouldAddOrReplaceReceipt.ts | 4 +- .../report/ReportActionItemContentCreated.tsx | 7 + .../report/ReportActionItemParentAction.tsx | 6 + src/pages/inbox/report/ReportActionsList.tsx | 7 + .../report/ReportActionsListItemRenderer.tsx | 7 + src/pages/inbox/report/ReportActionsView.tsx | 7 +- src/pages/inbox/report/ReportFooter.tsx | 8 +- .../iou/request/step/IOURequestStepReport.tsx | 4 +- .../TaskShareDestinationSelectorModal.tsx | 8 +- tests/actions/ReportTest.ts | 1 - tests/actions/TaskTest.ts | 19 +-- .../ReportActionCompose.perf-test.tsx | 5 +- .../perf-test/ReportActionsList.perf-test.tsx | 1 + tests/ui/MoneyRequestViewTest.tsx | 2 + tests/ui/ReportActionComposeTest.tsx | 1 + tests/ui/ReportActionsViewTest.tsx | 7 +- tests/unit/ReportSecondaryActionUtilsTest.ts | 144 ++++++++++++++---- tests/unit/ReportUtilsTest.ts | 7 +- .../hooks/useArchivedReportsIdSet.test.ts | 14 +- tests/unit/hooks/useSearchSections.test.ts | 2 +- tests/unit/navigateAfterOnboardingTest.ts | 8 +- 57 files changed, 505 insertions(+), 192 deletions(-) create mode 100644 src/hooks/useArchivedReportsIDSet.ts delete mode 100644 src/hooks/useArchivedReportsIdSet.ts diff --git a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx index c6756d307077..2990429fdc1f 100644 --- a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx +++ b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useOutstandingReports from '@hooks/useOutstandingReports'; @@ -50,7 +51,7 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a const {translate, localeCompare} = useLocalize(); const reportAttributes = useReportAttributes(); - const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID); // Per-key report subscriptions instead of full COLLECTION.REPORT @@ -67,7 +68,7 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a * Also we need to check if transaction report exists in outstanding reports in order to show a correct report name. */ const policyID = selectedParticipants?.at(0)?.policyID; - const shouldUseTransactionReport = (!!transactionReportEntry && isReportOutstanding(transactionReportEntry, policyID, undefined, false)) || isUnreported; + const shouldUseTransactionReport = (!!transactionReportEntry && isReportOutstanding(transactionReportEntry, policyID, archivedReportsIDSet, false)) || isUnreported; const ownerAccountID = selectedParticipants?.at(0)?.ownerAccountID; @@ -75,7 +76,7 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a policyID, ownerAccountID, outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, - reportNameValuePairs, + archivedReportsIDSet, false, ).sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? '')); diff --git a/src/components/MoneyRequestHeaderSecondaryActions.tsx b/src/components/MoneyRequestHeaderSecondaryActions.tsx index 57708b8d1568..e57970d94fce 100644 --- a/src/components/MoneyRequestHeaderSecondaryActions.tsx +++ b/src/components/MoneyRequestHeaderSecondaryActions.tsx @@ -269,18 +269,18 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money if (!transaction || !parentReportAction || !parentReport) { return []; } - return getSecondaryTransactionThreadActions( - currentUserLogin ?? '', - accountID, + return getSecondaryTransactionThreadActions({ + currentUserLogin: currentUserLogin ?? '', + currentUserAccountID: accountID, parentReport, - transaction, - parentReportAction, + reportTransaction: transaction, + reportAction: parentReportAction, originalTransaction, policy, - report, + transactionThreadReport: report, outstandingReportsByPolicyID, - isChatIOUReportArchived, - ); + isChatReportArchived: isChatIOUReportArchived, + }); })(); const secondaryActionsImplementation: Partial< diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index 34c7fad51de2..f652e7e50f43 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -8,6 +8,7 @@ import {DeviceEventEmitter, InteractionManager, View} from 'react-native'; import FlatListWithScrollKey from '@components/FlatList/FlatListWithScrollKey'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import ScrollView from '@components/ScrollView'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLoadReportActions from '@hooks/useLoadReportActions'; import useLocalize from '@hooks/useLocalize'; @@ -87,6 +88,7 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps) const styles = useThemeStyles(); const {translate, getLocalDateFromDatetime} = useLocalize(); const {isOffline, lastOfflineAt, lastOnlineAt} = useNetworkWithOfflineStatus(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const reportScrollManager = useReportScrollManager(); const lastMessageTime = useRef(null); const didLayout = useRef(false); @@ -585,6 +587,7 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps) isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairs?.origin} reportNameValuePairsOriginalID={reportNameValuePairs?.originalID} + archivedReportsIDSet={archivedReportsIDSet} /> ); }, @@ -604,6 +607,7 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps) isReportArchived, reportNameValuePairs?.origin, reportNameValuePairs?.originalID, + archivedReportsIDSet, ], ); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 6f40fae7d5b0..25916f2dd4ef 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -26,6 +26,7 @@ import navigationRef from '@libs/Navigation/navigationRef'; import {getFilteredReportActionsForReportView, getOneTransactionThreadReportID} from '@libs/ReportActionsUtils'; import {getReportOfflinePendingActionAndErrors, isReportTransactionThread} from '@libs/ReportUtils'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {cancelSpan} from '@libs/telemetry/activeSpans'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; @@ -58,6 +59,9 @@ type MoneyRequestReportViewProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; function goBackFromSearchMoneyRequest() { @@ -104,7 +108,7 @@ function InitialLoadingSkeleton({styles, onLayout, reasonAttributes}: {styles: T ); } -function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReportFooter, backToRoute, onLayout}: MoneyRequestReportViewProps) { +function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReportFooter, backToRoute, onLayout, archivedReportsIDSet}: MoneyRequestReportViewProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); @@ -188,9 +192,10 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport } Navigation.goBack(backToRoute); }} + archivedReportsIDSet={archivedReportsIDSet} /> ), - [backToRoute, isTransactionThreadView, report?.reportID], + [archivedReportsIDSet, backToRoute, isTransactionThreadView, report?.reportID], ); // We need to cancel telemetry span when user leaves the screen before full report data is loaded @@ -234,7 +239,7 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport - {shouldDisplayReportFooter ? : null} + {shouldDisplayReportFooter ? : null} ); } @@ -278,11 +283,12 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport )} {shouldDisplayReportFooter ? ( <> - + ) : null} diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 3df940219f55..d6b0fb382150 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -80,6 +80,7 @@ import { isTrackExpenseReportNew, shouldEnableNegative, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {hasEnabledTags, shouldShowDependentTagList} from '@libs/TagsOptionsListUtils'; import { getBillable, @@ -147,6 +148,9 @@ type MoneyRequestViewProps = { /** Merge transaction ID to show in merge transaction flow */ mergeTransactionID?: string; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; const perDiemPoliciesSelector = (policies: OnyxCollection) => { @@ -169,6 +173,7 @@ function MoneyRequestView({ updatedTransaction, isFromReviewDuplicates = false, mergeTransactionID, + archivedReportsIDSet, }: MoneyRequestViewProps) { const icons = useMemoizedLazyExpensifyIcons(['DotIndicator', 'Checkmark', 'Suitcase']); const styles = useThemeStyles(); @@ -322,7 +327,6 @@ function MoneyRequestView({ const isChatReportArchived = useReportIsArchived(moneyRequestReport?.chatReportID); const pendingAction = transaction?.pendingAction; const shouldShowPaid = isSettled && transactionReimbursable && !pendingAction; - // Flags for allowing or disallowing editing an expense // Used for non-restricted fields such as: description, category, tag, billable, etc... const isReportArchived = useReportIsArchived(transactionThreadReport?.reportID); @@ -393,6 +397,7 @@ function MoneyRequestView({ transaction, report: moneyRequestReport, policy, + archivedReportsIDSet, }) && (!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy) || (isExpenseUnreported && !!perDiemOriginalPolicy)); diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index fda2ec413e7a..73f869017136 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -11,7 +11,7 @@ import type {SelectionListHandle} from '@components/SelectionList/types'; import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton'; import {useWideRHPActions} from '@components/WideRHPContextProvider'; import useActionLoadingReportIDs from '@hooks/useActionLoadingReportIDs'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useMultipleSnapshots from '@hooks/useMultipleSnapshots'; @@ -37,6 +37,7 @@ import {isCreatedTaskReportAction} from '@libs/ReportActionsUtils'; import {isSplitAction} from '@libs/ReportSecondaryActionUtils'; import {canEditFieldOfMoneyRequest, canHoldUnholdReportAction, canRejectReportAction, isOneTransactionReport, selectFilteredReportActions} from '@libs/ReportUtils'; import {buildCannedSearchQuery, buildSearchQueryString, isDefaultExpensesQuery} from '@libs/SearchQueryUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import { createAndOpenSearchTransactionThread, doesSearchItemMatchSort, @@ -119,7 +120,8 @@ function mapTransactionItemToSelectedEntry( originalItemTransaction: OnyxEntry, currentUserLogin: string, currentUserAccountID: number, - outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, + outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined, + archivedReportsIDSet: ArchivedReportsIDSet, allowNegativeAmount = true, ): [string, SelectedTransactionInfo] { const {canHoldRequest, canUnholdRequest} = canHoldUnholdReportAction(item.report, item.reportAction, item.holdReportAction, item, item.policy, currentUserAccountID); @@ -144,6 +146,7 @@ function mapTransactionItemToSelectedEntry( transaction: item, report: item.report, policy: item.policy, + archivedReportsIDSet, }), action: item.action, groupCurrency: item.groupCurrency, @@ -191,7 +194,8 @@ function prepareTransactionsList( selectedTransactions: SelectedTransactions, currentUserLogin: string, currentUserAccountID: number, - outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, + outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined, + archivedReportsIDSet: ArchivedReportsIDSet, ) { if (selectedTransactions[item.keyForList]?.isSelected) { const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions; @@ -206,6 +210,7 @@ function prepareTransactionsList( currentUserLogin, currentUserAccountID, outstandingReportsByPolicyID, + archivedReportsIDSet, false, ); @@ -328,7 +333,7 @@ function Search({ const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, { selector: selectFilteredReportActions, @@ -807,6 +812,7 @@ function Search({ transaction: transactionItem, report: transactionItem.report, policy: transactionItem.policy, + archivedReportsIDSet: archivedReportsIdSet, }), isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID]?.isSelected || isExpenseReportType, @@ -863,6 +869,7 @@ function Search({ transaction: transactionItem, report: transactionItem.report, policy: transactionItem.policy, + archivedReportsIDSet: archivedReportsIdSet, }), isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID].isSelected, @@ -997,6 +1004,7 @@ function Search({ email ?? '', accountID, outstandingReportsByPolicyID, + archivedReportsIdSet, ); setSelectedTransactions(updatedTransactions, filteredData); updateSelectAllMatchingItemsState(updatedTransactions); @@ -1062,14 +1070,33 @@ function Search({ const originalItemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`] ?? transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', accountID, outstandingReportsByPolicyID); + return mapTransactionItemToSelectedEntry( + transactionItem, + itemTransaction, + originalItemTransaction, + email ?? '', + accountID, + outstandingReportsByPolicyID, + archivedReportsIdSet, + ); }), ), }; setSelectedTransactions(updatedTransactions, filteredData); updateSelectAllMatchingItemsState(updatedTransactions); }, - [selectedTransactions, setSelectedTransactions, filteredData, updateSelectAllMatchingItemsState, transactions, email, accountID, outstandingReportsByPolicyID, searchResults?.data], + [ + selectedTransactions, + setSelectedTransactions, + filteredData, + updateSelectAllMatchingItemsState, + transactions, + email, + accountID, + outstandingReportsByPolicyID, + searchResults?.data, + archivedReportsIdSet, + ], ); const onSelectRow = useCallback( @@ -1385,7 +1412,15 @@ function Search({ .map((transactionItem) => { const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', accountID, outstandingReportsByPolicyID); + return mapTransactionItemToSelectedEntry( + transactionItem, + itemTransaction, + originalItemTransaction, + email ?? '', + accountID, + outstandingReportsByPolicyID, + archivedReportsIdSet, + ); }); }); updatedTransactions = Object.fromEntries(allSelections); @@ -1397,7 +1432,15 @@ function Search({ .map((transactionItem) => { const itemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; - return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, email ?? '', accountID, outstandingReportsByPolicyID); + return mapTransactionItemToSelectedEntry( + transactionItem, + itemTransaction, + originalItemTransaction, + email ?? '', + accountID, + outstandingReportsByPolicyID, + archivedReportsIdSet, + ); }), ); } @@ -1416,6 +1459,7 @@ function Search({ accountID, outstandingReportsByPolicyID, searchResults?.data, + archivedReportsIdSet, ]); const onLayout = useCallback(() => { diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts new file mode 100644 index 000000000000..d03a3b7c92a6 --- /dev/null +++ b/src/hooks/useArchivedReportsIDSet.ts @@ -0,0 +1,37 @@ +import type {OnyxCollection} from 'react-native-onyx'; +import {isArchivedReport} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {ReportNameValuePairs} from '@src/types/onyx'; +import useOnyx from './useOnyx'; + +/** + * Selector that extracts archived report IDs as a sorted array. + * Onyx performs shallow comparison on the returned array to prevent + * unnecessary re-renders without expensive deep comparison of Sets. + */ +const archivedReportIdsSelector = (reportNameValuePairs: OnyxCollection): string[] => { + if (!reportNameValuePairs) { + return []; + } + + const ids: string[] = []; + for (const [key, value] of Object.entries(reportNameValuePairs)) { + if (isArchivedReport(value)) { + ids.push(key); + } + } + return ids; +}; + +/** + * Hook that returns a Set of archived report IDs + */ +function useArchivedReportsIDSet(): ArchivedReportsIDSet { + const [archivedReportIds = CONST.EMPTY_ARRAY] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {selector: archivedReportIdsSelector}); + + return new Set(archivedReportIds); +} + +export default useArchivedReportsIDSet; diff --git a/src/hooks/useArchivedReportsIdSet.ts b/src/hooks/useArchivedReportsIdSet.ts deleted file mode 100644 index 20dc07998fa0..000000000000 --- a/src/hooks/useArchivedReportsIdSet.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {isArchivedReport} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; -import ONYXKEYS from '@src/ONYXKEYS'; -import useOnyx from './useOnyx'; - -/** - * Hook that returns a Set of archived report IDs - */ -function useArchivedReportsIdSet(): ArchivedReportsIDSet { - const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); - const ids: string[] = []; - - if (reportNameValuePairs) { - for (const [key, value] of Object.entries(reportNameValuePairs)) { - if (isArchivedReport(value)) { - ids.push(key); - } - } - } - return new Set(ids); -} - -export default useArchivedReportsIdSet; diff --git a/src/hooks/useAutoCreateTrackWorkspace.ts b/src/hooks/useAutoCreateTrackWorkspace.ts index b51c91982368..061cd5422a23 100644 --- a/src/hooks/useAutoCreateTrackWorkspace.ts +++ b/src/hooks/useAutoCreateTrackWorkspace.ts @@ -13,7 +13,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {lastWorkspaceNumberSelector} from '@src/selectors/Policy'; import type {OnboardingPurpose, OnboardingRHPVariant, Policy} from '@src/types/onyx'; -import useArchivedReportsIdSet from './useArchivedReportsIdSet'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useHasActiveAdminPolicies from './useHasActiveAdminPolicies'; import useLocalize from './useLocalize'; @@ -52,7 +52,7 @@ function useAutoCreateTrackWorkspace() { const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [onboardingValues] = useOnyx(ONYXKEYS.NVP_ONBOARDING); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const {isBetaEnabled} = usePermissions(); const {translate, formatPhoneNumber} = useLocalize(); const {isRestrictedPolicyCreation} = usePreferredPolicy(); diff --git a/src/hooks/useDeleteTransactions.ts b/src/hooks/useDeleteTransactions.ts index 7a45bac7b41f..e409c11d93b7 100644 --- a/src/hooks/useDeleteTransactions.ts +++ b/src/hooks/useDeleteTransactions.ts @@ -9,12 +9,13 @@ import {updateSplitTransactions} from '@libs/actions/IOU/Split'; import {initSplitExpenseItemData} from '@libs/actions/IOU/SplitExpenseItems'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; +import {isReportArchivedByID} from '@libs/ReportUtils'; import {getActiveGroupSearchHashes} from '@libs/SearchUIUtils'; import {getChildTransactions, getOriginalTransactionWithSplitInfo} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Report, ReportAction, Transaction, TransactionViolations} from '@src/types/onyx'; -import useArchivedReportsIdSet from './useArchivedReportsIdSet'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useNetwork from './useNetwork'; import useOnyx from './useOnyx'; @@ -49,7 +50,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac const [allPolicyTags] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS, {selector: passthroughPolicyTagListSelector}); const [betas] = useOnyx(ONYXKEYS.BETAS); const {isBetaEnabled} = usePermissions(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const {isOffline} = useNetwork(); @@ -191,7 +192,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`]; const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`]; const chatIOUReportID = chatReport?.reportID; - const isChatIOUReportArchived = archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${chatIOUReportID}`); + const isChatIOUReportArchived = isReportArchivedByID(archivedReportsIdSet, chatIOUReportID); deleteMoneyRequest({ transactionID, reportAction: action, diff --git a/src/hooks/useOutstandingReports.ts b/src/hooks/useOutstandingReports.ts index b23b255bf3b7..43b4ea29d6b0 100644 --- a/src/hooks/useOutstandingReports.ts +++ b/src/hooks/useOutstandingReports.ts @@ -4,6 +4,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useMappedPolicies from './useMappedPolicies'; import useOnyx from './useOnyx'; @@ -13,7 +14,7 @@ export default function useOutstandingReports(selectedReportID: string | undefin const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); const [allPoliciesID] = useMappedPolicies(policyIdMapper); - const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); // Early return if no reports are available to prevent useless loop @@ -28,11 +29,11 @@ export default function useOutstandingReports(selectedReportID: string | undefin continue; } - const reports = getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID[policyID] ?? {}, reportNameValuePairs, isEditing); + const reports = getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID[policyID] ?? {}, archivedReportsIDSet, isEditing); result.push(...reports); } return result; } - return getOutstandingReportsForUser(selectedPolicyID, ownerAccountID, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, reportNameValuePairs, isEditing); + return getOutstandingReportsForUser(selectedPolicyID, ownerAccountID, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, archivedReportsIDSet, isEditing); } diff --git a/src/hooks/useSearchSections.ts b/src/hooks/useSearchSections.ts index b7fa54da3460..f671ea7065bc 100644 --- a/src/hooks/useSearchSections.ts +++ b/src/hooks/useSearchSections.ts @@ -6,7 +6,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Report} from '@src/types/onyx'; import type LastSearchParams from '@src/types/onyx/ReportNavigation'; import useActionLoadingReportIDs from './useActionLoadingReportIDs'; -import useArchivedReportsIdSet from './useArchivedReportsIdSet'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; import useLocalize from './useLocalize'; import useOnyx from './useOnyx'; @@ -48,7 +48,7 @@ function useSearchSections(): UseSearchSectionsResult { const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [allReportMetadata] = useOnyx(ONYXKEYS.COLLECTION.REPORT_METADATA); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const {type, status, sortBy, sortOrder, groupBy} = lastSearchQuery?.queryJSON ?? {}; diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index fae8c629d348..25be98798544 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -36,6 +36,7 @@ import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type {Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import useAllTransactions from './useAllTransactions'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useConfirmModal from './useConfirmModal'; import {useCurrencyListActions} from './useCurrencyList'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; @@ -81,6 +82,7 @@ function useSelectedTransactionsActions({ const {selectedTransactionIDs, currentSearchHash, selectedTransactions: selectedTransactionsMeta} = useSearchStateContext(); const {clearSelectedTransactions} = useSearchActionsContext(); const allTransactions = useAllTransactions(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [allReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); @@ -415,7 +417,13 @@ function useSelectedTransactionsActions({ } const iouReportAction = getIOUActionForTransactionID(reportActions, transaction.transactionID); - const canMoveExpense = canEditFieldOfMoneyRequest({reportAction: iouReportAction, fieldToEdit: CONST.EDIT_REQUEST_FIELD.REPORT, outstandingReportsByPolicyID, transaction}); + const canMoveExpense = canEditFieldOfMoneyRequest({ + reportAction: iouReportAction, + fieldToEdit: CONST.EDIT_REQUEST_FIELD.REPORT, + outstandingReportsByPolicyID, + transaction, + archivedReportsIDSet, + }); return canMoveExpense; }); diff --git a/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx b/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx index 13b70d5bfee4..63a6c3581628 100644 --- a/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx +++ b/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx @@ -1,11 +1,11 @@ import {useEffect, useRef} from 'react'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import markAllMessagesAsRead from '@libs/actions/Report/MarkAllMessageAsRead'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import CONST from '@src/CONST'; function MarkAllMessagesAsReadHandler() { - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const archivedReportsIdSetRef = useRef(archivedReportsIdSet); useEffect(() => { diff --git a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx index 2fa371a64f86..2bd8f429e6f4 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx @@ -1,5 +1,5 @@ import React, {useState} from 'react'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import usePermissions from '@hooks/usePermissions'; import createSplitNavigator from '@libs/Navigation/AppNavigator/createSplitNavigator'; import FreezeWrapper from '@libs/Navigation/AppNavigator/FreezeWrapper'; @@ -26,7 +26,7 @@ const Split = createSplitNavigator(); function ReportsSplitNavigator({route}: PlatformStackScreenProps) { const {isBetaEnabled} = usePermissions(); const splitNavigatorScreenOptions = useSplitNavigatorScreenOptions(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const isOpenOnAdminRoom = shouldOpenOnAdminRoom(); const [initialReportID] = useState(() => { diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index 26aabad2a1fd..a444001c9be9 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -77,6 +77,7 @@ import { isSettled, isWorkspaceEligibleForReportChange, } from './ReportUtils'; +import type {ArchivedReportsIDSet} from './SearchUIUtils'; import { allHavePendingRTERViolation, getOriginalTransactionWithSplitInfo, @@ -851,6 +852,7 @@ function getSecondaryReportActions({ policies, outstandingReportsByPolicyID, isChatReportArchived = false, + archivedReportsIDSet, }: { currentUserLogin: string; currentUserAccountID: number; @@ -868,6 +870,7 @@ function getSecondaryReportActions({ outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; canUseNewDotSplits?: boolean; isChatReportArchived?: boolean; + archivedReportsIDSet?: ArchivedReportsIDSet; }): Array> { const options: Array> = []; @@ -998,6 +1001,7 @@ function getSecondaryReportActions({ isChatReportArchived, outstandingReportsByPolicyID, transaction, + archivedReportsIDSet, }); const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(report, isChatReportArchived); @@ -1048,18 +1052,31 @@ function getSecondaryExportReportActions( return options; } -function getSecondaryTransactionThreadActions( - currentUserLogin: string, - currentUserAccountID: number, - parentReport: Report, - reportTransaction: Transaction, - reportAction: ReportAction | undefined, - originalTransaction: OnyxEntry, - policy: OnyxEntry, - transactionThreadReport?: OnyxEntry, - outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, - isChatReportArchived?: boolean, -): Array> { +function getSecondaryTransactionThreadActions({ + currentUserLogin, + currentUserAccountID, + parentReport, + reportTransaction, + reportAction, + originalTransaction, + policy, + transactionThreadReport, + outstandingReportsByPolicyID, + isChatReportArchived, + archivedReportsIDSet, +}: { + currentUserLogin: string; + currentUserAccountID: number; + parentReport: Report; + reportTransaction: Transaction; + reportAction: ReportAction | undefined; + originalTransaction: OnyxEntry; + policy: OnyxEntry; + transactionThreadReport?: OnyxEntry; + outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; + isChatReportArchived?: boolean; + archivedReportsIDSet?: ArchivedReportsIDSet; +}): Array> { const options: Array> = []; if (!!reportAction && isHoldActionForTransaction(parentReport, reportTransaction, reportAction, policy, currentUserAccountID)) { @@ -1089,7 +1106,14 @@ function getSecondaryTransactionThreadActions( if ( reportTransaction?.transactionID && reportAction && - canEditFieldOfMoneyRequest({reportAction, fieldToEdit: CONST.EDIT_REQUEST_FIELD.REPORT, isChatReportArchived, outstandingReportsByPolicyID, transaction: reportTransaction}) && + canEditFieldOfMoneyRequest({ + reportAction, + fieldToEdit: CONST.EDIT_REQUEST_FIELD.REPORT, + isChatReportArchived, + outstandingReportsByPolicyID, + transaction: reportTransaction, + archivedReportsIDSet, + }) && canUserPerformWriteActionReportUtils(parentReport, isChatReportArchived) ) { options.push(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 10932d5d9aa6..6386e18aad8f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1208,7 +1208,7 @@ Onyx.connectWithoutView({ }); let allReportNameValuePair: OnyxCollection; -Onyx.connectWithoutView({ +Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, waitForCollectionCallback: true, callback: (value) => { @@ -2354,6 +2354,23 @@ function isArchivedReport(reportNameValuePairs?: OnyxInputOrEntry): ArchivedReportsIDSet { + const archivedReportsIDSet = new Set(); + for (const [key, value] of Object.entries(reportNameValuePairs ?? {})) { + if (isArchivedReport(value)) { + archivedReportsIDSet.add(key); + } + } + return archivedReportsIDSet; +} + /** * Whether the report was created during harvesting */ @@ -4865,6 +4882,7 @@ function canEditFieldOfMoneyRequest({ transaction, report, policy, + archivedReportsIDSet, }: { reportAction: OnyxInputOrEntry; fieldToEdit: ValueOf; @@ -4874,6 +4892,7 @@ function canEditFieldOfMoneyRequest({ transaction: OnyxEntry; report?: OnyxInputOrEntry; policy?: OnyxEntry; + archivedReportsIDSet?: ArchivedReportsIDSet; }): boolean { // A list of fields that cannot be edited by anyone, once an expense has been settled const restrictedFields: string[] = [ @@ -4953,6 +4972,7 @@ function canEditFieldOfMoneyRequest({ // Unreported transaction from OldDot can have the reportID as an empty string const isUnreportedExpense = !transaction?.reportID || transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; + const archivedReportsIDSetForOutstandingReports = archivedReportsIDSet ?? buildArchivedReportsIDSet(allReportNameValuePair); if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE) { // The distance rate can be modified only on the distance expense reports @@ -4972,7 +4992,7 @@ function canEditFieldOfMoneyRequest({ return true; } - if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)) { + if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportsIDSet)) { return false; } @@ -4988,6 +5008,7 @@ function canEditFieldOfMoneyRequest({ moneyRequestReport?.policyID, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[moneyRequestReport?.policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, + archivedReportsIDSetForOutstandingReports, ).length > 0 ); } @@ -5000,14 +5021,19 @@ function canEditFieldOfMoneyRequest({ } // Check the cheaper condition first - if ((isOwner || isAdmin || isManager) && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)) { + if ((isOwner || isAdmin || isManager) && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportsIDSet)) { return true; } // Check if there are multiple outstanding reports across policies let outstandingReportsCount = 0; for (const currentPolicy of policiesArray) { - const reports = getOutstandingReportsForUser(currentPolicy.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}); + const reports = getOutstandingReportsForUser( + currentPolicy.id, + moneyRequestReport?.ownerAccountID, + outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, + archivedReportsIDSetForOutstandingReports, + ); outstandingReportsCount += reports.length; // Short-circuit once we find more than 1 @@ -11466,12 +11492,7 @@ function hasForwardedAction(reportID: string): boolean { return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); } -function isReportOutstanding( - iouReport: OnyxInputOrEntry, - policyID: string | undefined, - reportNameValuePairs: OnyxCollection = allReportNameValuePair, - allowSubmitted = true, -): boolean { +function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: string | undefined, archivedReportsIDSet?: ArchivedReportsIDSet, allowSubmitted = true): boolean { if ( !iouReport || isEmptyObject(iouReport) || @@ -11483,8 +11504,8 @@ function isReportOutstanding( ) { return false; } - const reportNameValuePair = reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport.reportID}`]; - if (isArchivedReport(reportNameValuePair)) { + const reportNameValuePair = archivedReportsIDSet ? undefined : allReportNameValuePair?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport.reportID}`]; + if ((archivedReportsIDSet && isReportArchivedByID(archivedReportsIDSet, iouReport.reportID)) || isArchivedReport(reportNameValuePair)) { return false; } const currentRoute = navigationRef.getCurrentRoute(); @@ -11507,7 +11528,7 @@ function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, reports: OnyxCollection = deprecatedAllReports, - reportNameValuePairs: OnyxCollection = allReportNameValuePair, + archivedReportsIDSet: ArchivedReportsIDSet, allowSubmitted = true, ): Array> { if (!reports) { @@ -11516,7 +11537,7 @@ function getOutstandingReportsForUser( return Object.values(reports).filter( (report) => report?.pendingFields?.preview !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - isReportOutstanding(report, policyID, reportNameValuePairs, allowSubmitted) && + isReportOutstanding(report, policyID, archivedReportsIDSet, allowSubmitted) && report?.ownerAccountID === reportOwnerAccountID, ); } @@ -13501,6 +13522,7 @@ export { isAnnounceRoom, isArchivedNonExpenseReport, isArchivedReport, + isReportArchivedByID, isClosedReport, isCanceledTaskReport, isChatReport, diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index fad817a7cfde..88f93f404a7f 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -168,6 +168,7 @@ import { prepareOnboardingOnyxData, } from '@libs/ReportUtils'; import {buildOptimisticSnapshotData, getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import {getAmount, getCurrency, hasValidModifiedAmount, isOnHold, recalculateUnreportedTransactionDetails, shouldClearConvertedAmount} from '@libs/TransactionUtils'; import addTrailingForwardSlash from '@libs/UrlUtils'; @@ -4450,8 +4451,9 @@ function navigateToMostRecentReport( currentUserAccountID: number, introSelected: OnyxEntry, betas: OnyxEntry, + archivedReportsIDSet?: ArchivedReportsIDSet, ) { - const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID)?.reportID; + const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, archivedReportsIDSet)?.reportID; if (lastAccessedReportID) { // Check if route exists for super wide RHP vs regular full screen report @@ -4476,8 +4478,8 @@ function navigateToMostRecentReport( } } -function getMostRecentReportID(currentReport: OnyxEntry, conciergeReportID: string | undefined) { - const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID)?.reportID; +function getMostRecentReportID(currentReport: OnyxEntry, conciergeReportID: string | undefined, archivedReportsIDSet?: ArchivedReportsIDSet) { + const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, archivedReportsIDSet)?.reportID; return lastAccessedReportID ?? conciergeReportID; } diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 22cc60fc890d..31232f3fbb7b 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -18,6 +18,7 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import {getReportName} from '@libs/ReportNameUtils'; import * as ReportUtils from '@libs/ReportUtils'; import {buildOptimisticSnapshotData} from '@libs/SearchQueryUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -1154,7 +1155,7 @@ function getShareDestination( * @param report - The task report being deleted * @returns The URL to navigate to */ -function getNavigationUrlOnTaskDelete(report: OnyxEntry, conciergeReportID: string | undefined): string | undefined { +function getNavigationUrlOnTaskDelete(report: OnyxEntry, conciergeReportID: string | undefined, archivedReportsIDSet: ArchivedReportsIDSet): string | undefined { if (!report) { return undefined; } @@ -1169,7 +1170,7 @@ function getNavigationUrlOnTaskDelete(report: OnyxEntry, conci } // If no parent report, try to navigate to most recent report - const mostRecentReportID = getMostRecentReportID(report, conciergeReportID); + const mostRecentReportID = getMostRecentReportID(report, conciergeReportID, archivedReportsIDSet); if (mostRecentReportID) { return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID); } @@ -1187,6 +1188,7 @@ function deleteTask( currentUserAccountID: number, hasOutstandingChildTask: boolean, parentReportAction: OnyxEntry, + archivedReportsIDSet: ArchivedReportsIDSet, conciergeReportID: string | undefined, delegateEmail: string | undefined, ancestors: ReportUtils.Ancestor[] = [], @@ -1314,7 +1316,7 @@ function deleteTask( API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData}); notifyNewAction(report.reportID, undefined, true); - const urlToNavigateBack = getNavigationUrlOnTaskDelete(report, conciergeReportID); + const urlToNavigateBack = getNavigationUrlOnTaskDelete(report, conciergeReportID, archivedReportsIDSet); if (urlToNavigateBack) { Navigation.goBack(); return urlToNavigateBack; diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts index 509f7928c7c1..6b24aca1fea8 100644 --- a/src/libs/navigateAfterOnboarding.ts +++ b/src/libs/navigateAfterOnboarding.ts @@ -28,7 +28,7 @@ function getReportIDAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIdSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -43,7 +43,7 @@ function getReportIDAfterOnboarding( return undefined; } - const lastAccessedReport = findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom() && !shouldPreventOpenAdminRoom, undefined, archivedReportsIdSet); + const lastAccessedReport = findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom() && !shouldPreventOpenAdminRoom, undefined, archivedReportsIDSet); const lastAccessedReportID = lastAccessedReport?.reportID; // When the user goes through the onboarding flow, a workspace can be created if the user selects specific options. The user should be taken to the #admins room for that workspace because it is the most natural place for them to start their experience in the app. @@ -59,7 +59,7 @@ function navigateAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIdSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -86,7 +86,7 @@ function navigateAfterOnboarding( isSmallScreenWidth, canUseDefaultRooms, conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, @@ -103,7 +103,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIdSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -115,7 +115,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth, canUseDefaultRooms, conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, diff --git a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx index 7b3fa91fa36b..6edf2213eeff 100644 --- a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx +++ b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx @@ -13,7 +13,7 @@ import ScrollView from '@components/ScrollView'; import Section from '@components/Section'; import isSidePanelReportSupported from '@components/SidePanel/isSidePanelReportSupported'; import Text from '@components/Text'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHasActiveAdminPolicies from '@hooks/useHasActiveAdminPolicies'; import useLastWorkspaceNumber from '@hooks/useLastWorkspaceNumber'; @@ -65,7 +65,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin const {isBetaEnabled} = usePermissions(); const [session] = useOnyx(ONYXKEYS.SESSION); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const hasActiveAdminPolicies = useHasActiveAdminPolicies(); const lastWorkspaceNumber = useLastWorkspaceNumber(); diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 85b3e67b4bc8..e2c7e7122b4d 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -9,7 +9,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useAutoCreateTrackWorkspace from '@hooks/useAutoCreateTrackWorkspace'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; @@ -45,7 +45,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [betas] = useOnyx(ONYXKEYS.BETAS); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [onboardingValues] = useOnyx(ONYXKEYS.NVP_ONBOARDING); const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); diff --git a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx index 42af8bc1ecba..ed8fe7442110 100644 --- a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx +++ b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx @@ -10,7 +10,7 @@ import SelectionListWithSections from '@components/SelectionList/SelectionListWi import type {Section} from '@components/SelectionList/SelectionListWithSections/types'; import Text from '@components/Text'; import useAllPolicyExpenseChatReportActions from '@hooks/useAllPolicyExpenseChatReportActions'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnboardingMessages from '@hooks/useOnboardingMessages'; @@ -66,7 +66,7 @@ function BaseOnboardingWorkspaceInvite({shouldUseNativeStyles}: BaseOnboardingWo const session = useSession(); const {isBetaEnabled} = usePermissions(); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const filteredReportActions = useAllPolicyExpenseChatReportActions(); const ineligibleInvitees = getIneligibleInvitees(policy?.employeeList); diff --git a/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx b/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx index 568b8d9a4606..40c0afa2da72 100644 --- a/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx +++ b/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx @@ -8,7 +8,7 @@ import RenderHTML from '@components/RenderHTML'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useHasActiveAdminPolicies from '@hooks/useHasActiveAdminPolicies'; import useHasTeam2025Pricing from '@hooks/useHasTeam2025Pricing'; @@ -53,7 +53,7 @@ function BaseOnboardingWorkspaceOptional({shouldUseNativeStyles}: BaseOnboarding const [onboardingPolicyID] = useOnyx(ONYXKEYS.ONBOARDING_POLICY_ID); const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID); const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const {onboardingMessages} = useOnboardingMessages(); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [betas] = useOnyx(ONYXKEYS.BETAS); diff --git a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx index b91ad2e1a154..cd789f6fb63b 100644 --- a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx +++ b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx @@ -8,7 +8,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; import Text from '@components/Text'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -59,7 +59,7 @@ function BaseOnboardingWorkspaces({route, shouldUseNativeStyles}: BaseOnboarding const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const [betas] = useOnyx(ONYXKEYS.BETAS); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const isValidated = isCurrentUserValidated(loginList, session?.email); diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index eca440a146c1..3a38d83e402d 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -28,6 +28,7 @@ import {useSearchActionsContext} from '@components/Search/SearchContext'; import {SUPER_WIDE_RIGHT_MODALS} from '@components/WideRHPContextProvider/WIDE_RIGHT_MODALS'; import useActivePolicy from '@hooks/useActivePolicy'; import useAncestors from '@hooks/useAncestors'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDeleteTransactions from '@hooks/useDeleteTransactions'; @@ -180,6 +181,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata, reportLoading const parentReportAction = useParentReportAction(report); const hasOutstandingChildTask = useHasOutstandingChildTask(report); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); @@ -911,6 +913,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata, reportLoading currentUserPersonalDetails.accountID, hasOutstandingChildTask, parentReportAction, + archivedReportsIDSet, conciergeReportID, delegateEmail, ancestors, diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index d467087a44c0..9632d589513c 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -12,6 +12,7 @@ import {useSearchStateContext} from '@components/Search/SearchContext'; import useShowSuperWideRHPVersion from '@components/WideRHPContextProvider/useShowSuperWideRHPVersion'; import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDocumentTitle from '@hooks/useDocumentTitle'; import useIsReportReadyToDisplay from '@hooks/useIsReportReadyToDisplay'; @@ -146,6 +147,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute, isReportArchived); + const archivedReportsIDSet = useArchivedReportsIDSet(); const actionListValue = useActionListContextValue(); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`); @@ -411,6 +413,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { key={report?.reportID} onLayout={handleSubmitToDestinationVisibleLayout} backToRoute={route.params.backTo} + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index 7eff4be2dfc7..cfc3c90cd534 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -13,6 +13,7 @@ import ScrollView from '@components/ScrollView'; import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import {useWideRHPState} from '@components/WideRHPContextProvider'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -40,6 +41,7 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; function Confirmation() { const styles = useThemeStyles(); const {translate} = useLocalize(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const route = useRoute>(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [reviewDuplicates, reviewDuplicatesResult] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); @@ -166,6 +168,7 @@ function Confirmation() { readonly updatedTransaction={newTransaction as OnyxEntry} isFromReviewDuplicates + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/TransactionMerge/ConfirmationPage.tsx b/src/pages/TransactionMerge/ConfirmationPage.tsx index 6bc398a43166..371a9b7ddf3b 100644 --- a/src/pages/TransactionMerge/ConfirmationPage.tsx +++ b/src/pages/TransactionMerge/ConfirmationPage.tsx @@ -10,6 +10,7 @@ import MoneyRequestView from '@components/ReportActionItem/MoneyRequestView'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useMergeTransactions from '@hooks/useMergeTransactions'; @@ -39,6 +40,7 @@ function ConfirmationPage({route}: ConfirmationPageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [isMergingExpenses, setIsMergingExpenses] = useState(false); + const archivedReportsIDSet = useArchivedReportsIDSet(); const {transactionID, isOnSearch, backTo} = route.params; const [mergeTransaction, mergeTransactionMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.MERGE_TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`); @@ -142,6 +144,7 @@ function ConfirmationPage({route}: ConfirmationPageProps) { readonly updatedTransaction={mergedTransactionData as unknown as OnyxEntry} mergeTransactionID={transactionID} + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/inbox/ReportActionsList.tsx b/src/pages/inbox/ReportActionsList.tsx index 38b9741ec5fd..3cfbf8d5a3fe 100644 --- a/src/pages/inbox/ReportActionsList.tsx +++ b/src/pages/inbox/ReportActionsList.tsx @@ -9,6 +9,7 @@ import useReportTransactionsCollection from '@hooks/useReportTransactionsCollect import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getAllNonDeletedTransactions, shouldDisplayReportTableView, shouldWaitForTransactions as shouldWaitForTransactionsUtil} from '@libs/MoneyRequestReportUtils'; import {isInvoiceReport, isMoneyRequestReport} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ReportActionsView from './report/ReportActionsView'; @@ -26,7 +27,7 @@ const defaultReportLoadingState = { * or MoneyRequestReportActionsList. Only subscribes to what the branching * conditions need — heavy data derivation is pushed into each child. */ -function ReportActionsList() { +function ReportActionsList({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { const route = useRoute(); const routeParams = route.params as {reportID?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -52,7 +53,12 @@ function ReportActionsList() { return ; } - return ; + return ( + + ); } export default ReportActionsList; diff --git a/src/pages/inbox/ReportHeader.tsx b/src/pages/inbox/ReportHeader.tsx index 76d67178ab85..5fe4f54df3b7 100644 --- a/src/pages/inbox/ReportHeader.tsx +++ b/src/pages/inbox/ReportHeader.tsx @@ -13,6 +13,7 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Navigation from '@libs/Navigation/Navigation'; import {getReportName} from '@libs/ReportNameUtils'; import {getReportOfflinePendingActionAndErrors, isInvoiceReport, isMoneyRequestReport, isReportTransactionThread} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; @@ -22,9 +23,9 @@ import HeaderView from './HeaderView'; /** * Owns header variant selection, back button logic, and OfflineWithFeedback wrapper. - * Subscribes to report type internally — ReportScreen passes nothing. + * Subscribes to report type internally. */ -function ReportHeader() { +function ReportHeader({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { const route = useRoute(); const routeParams = route.params as {reportID?: string; backTo?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -97,6 +98,7 @@ function ReportHeader() { ); diff --git a/src/pages/inbox/ReportRouteParamHandler.tsx b/src/pages/inbox/ReportRouteParamHandler.tsx index d934c75eedef..578a251d7621 100644 --- a/src/pages/inbox/ReportRouteParamHandler.tsx +++ b/src/pages/inbox/ReportRouteParamHandler.tsx @@ -1,5 +1,5 @@ import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import usePermissions from '@hooks/usePermissions'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -22,7 +22,7 @@ function ReportRouteParamHandler() { const route = useRoute(); const navigation = useNavigation(); const {isBetaEnabled} = usePermissions(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); useFocusEffect(() => { // Don't update if there is a reportID in the params already diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index 6790e427478a..44719c36be3b 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -7,6 +7,7 @@ import {InteractionManager, View} from 'react-native'; import ScreenWrapper from '@components/ScreenWrapper'; import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import {useCurrentReportIDState} from '@hooks/useCurrentReportID'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSubmitToDestinationVisible from '@hooks/useSubmitToDestinationVisible'; @@ -53,6 +54,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { const viewportOffsetTop = useViewportOffsetTop(); const isTopMostReportId = currentReportIDValue === reportIDFromRoute; const screenWrapperStyle: ViewStyle[] = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}]; + const archivedReportsIDSet = useArchivedReportsIDSet(); // During dismiss_modal_and_open_report, defer heavy non-content components // (composer, invisible handlers) so the first render is lighter. @@ -140,7 +142,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { {!shouldDeferNonEssentials && } - + {!shouldDeferNonEssentials && } {!shouldDeferNonEssentials && } @@ -149,8 +151,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) { style={[styles.flex1, styles.justifyContentEnd, styles.overflowHidden]} testID="report-actions-view-wrapper" > - - {shouldDeferNonEssentials ? : } + + {shouldDeferNonEssentials ? : } diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index 2e14824165c4..c67531a061a3 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -101,6 +101,7 @@ import { isTaskReport, shouldDisplayThreadReplies as shouldDisplayThreadRepliesUtils, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import SelectionScraper from '@libs/SelectionScraper'; import {ReactionListContext} from '@pages/inbox/ReportScreenContext'; import AttachmentModalContext from '@pages/media/AttachmentModalScreen/AttachmentModalContext'; @@ -144,6 +145,8 @@ import SearchActionHeader from './SearchActionHeader'; import TripSummary from './TripSummary'; import WhisperBanner from './WhisperBanner'; +const EMPTY_ARCHIVED_REPORTS_ID_SET = new Set(); + type PureReportActionItemProps = { /** The personal policy ID */ personalPolicyID: string | undefined; @@ -284,6 +287,9 @@ type PureReportActionItemProps = { /** Report metadata for the report */ reportMetadata?: OnyxEntry; + + /** Set of archived report ID keys */ + archivedReportsIDSet?: ArchivedReportsIDSet; }; // This is equivalent to returning a negative boolean in normal functions, but we can keep the element return type @@ -333,6 +339,7 @@ function PureReportActionItem({ reportNameValuePairsOrigin, reportNameValuePairsOriginalID, reportMetadata, + archivedReportsIDSet = EMPTY_ARCHIVED_REPORTS_ID_SET, }: PureReportActionItemProps) { const isConciergeGreeting = action.reportActionID === CONST.CONCIERGE_GREETING_ACTION_ID; const shouldDisplayContextMenuValue = shouldDisplayContextMenu && !isConciergeGreeting; @@ -1071,9 +1078,19 @@ function PureReportActionItem({ transactionID={transactionID} draftMessage={draftMessage} shouldHideThreadDividerLine={shouldHideThreadDividerLine} + archivedReportsIDSet={archivedReportsIDSet} /> ); - }, [contextMenuStateValue, contextMenuActionsValue, parentReportAction, parentReport, draftMessage, shouldHideThreadDividerLine, parentReportActionForTransactionThread]); + }, [ + contextMenuStateValue, + contextMenuActionsValue, + parentReportAction, + parentReport, + draftMessage, + shouldHideThreadDividerLine, + archivedReportsIDSet, + parentReportActionForTransactionThread, + ]); if (action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && !isHarvestCreatedExpenseReport) { return createdActionContent; diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx index 32f182db3989..69bfc14b06d8 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx @@ -8,6 +8,7 @@ import useHandleExceedMaxTaskTitleLength from '@hooks/useHandleExceedMaxTaskTitl import useOnyx from '@hooks/useOnyx'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; import {chatIncludesConcierge} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {setIsComposerFullSize} from '@userActions/Report'; import {isBlockedFromConcierge as isBlockedFromConciergeUserAction} from '@userActions/User'; import CONST from '@src/CONST'; @@ -22,6 +23,7 @@ const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); type ComposerProviderProps = { reportID: string; + archivedReportsIDSet: ArchivedReportsIDSet; children: React.ReactNode; }; diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index 706901cf2e12..ef4face996cd 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -6,6 +6,7 @@ import useOnyx from '@hooks/useOnyx'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import {getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import AgentZeroAwareTypingIndicator from './AgentZeroAwareTypingIndicator'; import ComposerActionMenu from './ComposerActionMenu'; @@ -24,9 +25,10 @@ import type {ComposerRef} from './ComposerWithSuggestions/ComposerWithSuggestion type ReportActionComposeProps = { reportID: string; + archivedReportsIDSet: ArchivedReportsIDSet; }; -function ReportActionComposeInner({reportID}: ReportActionComposeProps) { +function ReportActionComposeInner({reportID}: Pick) { const styles = useThemeStyles(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); @@ -67,9 +69,12 @@ function ReportActionComposeInner({reportID}: ReportActionComposeProps) { ); } -function ReportActionCompose({reportID}: ReportActionComposeProps) { +function ReportActionCompose({reportID, archivedReportsIDSet}: ReportActionComposeProps) { return ( - + ); diff --git a/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts b/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts index 4a454111f10a..d5bdbf2c6e0a 100644 --- a/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts +++ b/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts @@ -1,3 +1,4 @@ +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; @@ -14,6 +15,7 @@ import type {ReportAction} from '@src/types/onyx'; function useShouldAddOrReplaceReceipt(reportID: string) { const {isOffline} = useNetwork(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); + const archivedReportsIDSet = useArchivedReportsIDSet(); const isReportArchived = useReportIsArchived(report?.reportID); const allReportTransactions = useReportTransactionsCollection(reportID); const [rawReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`); @@ -34,7 +36,7 @@ function useShouldAddOrReplaceReceipt(reportID: string) { const canUserPerformWriteAction = !!canUserPerformWriteActionReportUtils(report, isReportArchived); const canEditReceipt = canUserPerformWriteAction && - canEditFieldOfMoneyRequest({reportAction: effectiveParentReportAction, fieldToEdit: CONST.EDIT_REQUEST_FIELD.RECEIPT, transaction}) && + canEditFieldOfMoneyRequest({reportAction: effectiveParentReportAction, fieldToEdit: CONST.EDIT_REQUEST_FIELD.RECEIPT, transaction, archivedReportsIDSet}) && !transaction?.receipt?.isTestDriveReceipt; const shouldAddOrReplaceReceipt = (isTransactionThreadView || isSingleTransactionView) && canEditReceipt; diff --git a/src/pages/inbox/report/ReportActionItemContentCreated.tsx b/src/pages/inbox/report/ReportActionItemContentCreated.tsx index aab07cc262f4..b1a691efc1b3 100644 --- a/src/pages/inbox/report/ReportActionItemContentCreated.tsx +++ b/src/pages/inbox/report/ReportActionItemContentCreated.tsx @@ -17,6 +17,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {isMessageDeleted, isReversedTransaction as isReversedTransactionReportActionsUtils, isTransactionThread} from '@libs/ReportActionsUtils'; import {isCanceledTaskReport, isExpenseReport, isInvoiceReport, isIOUReport, isTaskReport} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {getCurrency} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -48,6 +49,9 @@ type ReportActionItemContentCreatedProps = { /** Flag to show, hide the thread divider line */ shouldHideThreadDividerLine: boolean; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionItemContentCreated({ @@ -58,6 +62,7 @@ function ReportActionItemContentCreated({ transactionID, draftMessage, shouldHideThreadDividerLine, + archivedReportsIDSet, }: ReportActionItemContentCreatedProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -124,6 +129,7 @@ function ReportActionItemContentCreated({ parentReportID={report?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground + archivedReportsIDSet={archivedReportsIDSet} /> {renderThreadDivider} @@ -190,6 +196,7 @@ function ReportActionItemContentCreated({ parentReportID={transactionThreadReport?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground={false} + archivedReportsIDSet={archivedReportsIDSet} /> {renderThreadDivider} diff --git a/src/pages/inbox/report/ReportActionItemParentAction.tsx b/src/pages/inbox/report/ReportActionItemParentAction.tsx index dfd306bf5704..da0482d4cac3 100644 --- a/src/pages/inbox/report/ReportActionItemParentAction.tsx +++ b/src/pages/inbox/report/ReportActionItemParentAction.tsx @@ -20,6 +20,7 @@ import { navigateToLinkedReportAction, shouldExcludeAncestorReportAction, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Report, ReportAction, ReportActions, ReportActionsDrafts, ReportNameValuePairs, Transaction} from '@src/types/onyx'; @@ -71,6 +72,9 @@ type ReportActionItemParentActionProps = { /** Whether the report is archived */ isReportArchived: boolean; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionItemParentAction({ @@ -87,6 +91,7 @@ function ReportActionItemParentAction({ userBillingFundID, isTryNewDotNVPDismissed = false, isReportArchived = false, + archivedReportsIDSet, }: ReportActionItemParentActionProps) { const styles = useThemeStyles(); const ancestors = useAncestors(report, shouldExcludeAncestorReportAction); @@ -231,6 +236,7 @@ function ReportActionItemParentAction({ linkedTransactionRouteError={linkedTransactionRouteError} userBillingFundID={userBillingFundID} isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} + archivedReportsIDSet={archivedReportsIDSet} /> ); diff --git a/src/pages/inbox/report/ReportActionsList.tsx b/src/pages/inbox/report/ReportActionsList.tsx index d2b4ec25b642..6741792d8ebf 100644 --- a/src/pages/inbox/report/ReportActionsList.tsx +++ b/src/pages/inbox/report/ReportActionsList.tsx @@ -60,6 +60,7 @@ import { isTaskReport, isUnread, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import Visibility from '@libs/Visibility'; import type {ReportsSplitNavigatorParamList} from '@navigation/types'; import ConciergeThinkingMessage from '@pages/home/report/ConciergeThinkingMessage'; @@ -131,6 +132,9 @@ type ReportActionsListProps = { /** Callback to show previous messages */ onShowPreviousMessages?: () => void; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; // In the component we are subscribing to the arrival of new actions. @@ -170,6 +174,7 @@ function ReportActionsList({ showHiddenHistory, hasPreviousMessages, onShowPreviousMessages, + archivedReportsIDSet, }: ReportActionsListProps) { const prevHasCreatedActionAdded = usePrevious(hasCreatedActionAdded); const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); @@ -737,6 +742,7 @@ function ReportActionsList({ isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairs?.origin} reportNameValuePairsOriginalID={reportNameValuePairs?.originalID} + archivedReportsIDSet={archivedReportsIDSet} /> {showPreviousMessagesButton && ( @@ -775,6 +781,7 @@ function ReportActionsList({ isReportArchived, reportNameValuePairs?.origin, reportNameValuePairs?.originalID, + archivedReportsIDSet, reportActionsFromOnyx, isConciergeSidePanel, showHiddenHistory, diff --git a/src/pages/inbox/report/ReportActionsListItemRenderer.tsx b/src/pages/inbox/report/ReportActionsListItemRenderer.tsx index a08b7ba8281b..f2b9219bec97 100644 --- a/src/pages/inbox/report/ReportActionsListItemRenderer.tsx +++ b/src/pages/inbox/report/ReportActionsListItemRenderer.tsx @@ -3,6 +3,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import useOnyx from '@hooks/useOnyx'; import {getOriginalMessage, isSentMoneyReportAction, isTransactionThread} from '@libs/ReportActionsUtils'; import {isChatThread} from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Report, ReportAction} from '@src/types/onyx'; @@ -71,6 +72,9 @@ type ReportActionsListItemRendererProps = { /** Report name value pairs originalID */ reportNameValuePairsOriginalID?: string; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionsListItemRenderer({ @@ -95,6 +99,7 @@ function ReportActionsListItemRenderer({ isReportArchived = false, reportNameValuePairsOrigin, reportNameValuePairsOriginalID, + archivedReportsIDSet, }: ReportActionsListItemRendererProps) { const originalMessage = useMemo(() => getOriginalMessage(reportAction), [reportAction]); @@ -191,6 +196,7 @@ function ReportActionsListItemRenderer({ userBillingFundID={userBillingFundID} isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} isReportArchived={isReportArchived} + archivedReportsIDSet={archivedReportsIDSet} /> ); } @@ -216,6 +222,7 @@ function ReportActionsListItemRenderer({ isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairsOrigin} reportNameValuePairsOriginalID={reportNameValuePairsOriginalID} + archivedReportsIDSet={archivedReportsIDSet} /> ); } diff --git a/src/pages/inbox/report/ReportActionsView.tsx b/src/pages/inbox/report/ReportActionsView.tsx index 01209272079c..806415e44bf7 100755 --- a/src/pages/inbox/report/ReportActionsView.tsx +++ b/src/pages/inbox/report/ReportActionsView.tsx @@ -46,6 +46,7 @@ import { isMoneyRequestReport, isReportTransactionThread as isReportTransactionThreadUtil, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -62,11 +63,14 @@ type ReportActionsViewProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; + + /** Set of archived report ID keys */ + archivedReportsIDSet: ArchivedReportsIDSet; }; let listOldID = Math.round(Math.random() * 100); -function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) { +function ReportActionsView({reportID, onLayout, archivedReportsIDSet}: ReportActionsViewProps) { useCopySelectionHelper(); const {translate} = useLocalize(); usePendingConciergeResponse(reportID); @@ -369,6 +373,7 @@ function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) { showHiddenHistory={!showFullHistory} hasPreviousMessages={hasPreviousMessages} onShowPreviousMessages={handleShowPreviousMessages} + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/inbox/report/ReportFooter.tsx b/src/pages/inbox/report/ReportFooter.tsx index adb8efdfa270..b753ad19ed72 100644 --- a/src/pages/inbox/report/ReportFooter.tsx +++ b/src/pages/inbox/report/ReportFooter.tsx @@ -27,6 +27,7 @@ import { isPublicRoom, isSystemChat as isSystemChatUtil, } from '@libs/ReportUtils'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isLoadingInitialReportActionsSelector} from '@src/selectors/ReportMetaData'; @@ -40,7 +41,7 @@ const policyRoleSelector = (policy: OnyxEntry) => policy?.role * Footer component that decides between the composer and * archived/anonymous/blocked/system chat/admins-only footer. */ -function ReportFooter() { +function ReportFooter({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { const route = useRoute(); const routeParams = route.params as {reportID?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -91,7 +92,10 @@ function ReportFooter() { return ( - + ); diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index f1926c645a30..9c2e58461e74 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -5,6 +5,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {usePersonalDetails, useSession} from '@components/OnyxListItemProvider'; import {useSearchActionsContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useOnyx from '@hooks/useOnyx'; import useOptimisticDraftTransactions from '@hooks/useOptimisticDraftTransactions'; @@ -47,11 +48,12 @@ const getIOUActionsSelector = (actions: OnyxEntry): ReportAction[ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {backTo, action, iouType, transactionID, reportID: reportIDFromRoute, reportActionID} = route.params; const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + const archivedReportsIDSet = useArchivedReportsIDSet(); const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); const participantReportID = transaction?.participants?.at(0)?.reportID; const participantReport = Object.values(allReports ?? {}).find((report) => report?.reportID === participantReportID); - const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID)) || isUnreported; + const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID, archivedReportsIDSet)) || isUnreported; const outstandingReportID = isPolicyExpenseChat(participantReport) ? participantReport?.iouReportID : participantReportID; const selectedReportID = shouldUseTransactionReport ? transactionReport?.reportID : outstandingReportID; const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index f2087ca731b8..32b0c7064d80 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -4,7 +4,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import UserListItem from '@components/SelectionList/ListItem/UserListItem'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -64,15 +64,15 @@ function TaskShareDestinationSelectorModal() { onSingleSelect: selectReportHandler, }); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const filteredOptions = useMemo(() => { - const filteredReports = reportFilter(availableOptions.recentReports as Array>, archivedReportsIdSet); + const filteredReports = reportFilter(availableOptions.recentReports as Array>, archivedReportsIDSet); return { ...availableOptions, recentReports: filteredReports ?? [], }; - }, [availableOptions, archivedReportsIdSet]); + }, [availableOptions, archivedReportsIDSet]); const data = useMemo( () => diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index c6cf26b29e6f..8712cbd98feb 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -5596,7 +5596,6 @@ describe('actions/Report', () => { // Shared test constants for leave functions const TEST_CONCIERGE_REPORT_ID = '999'; const TEST_CURRENT_USER_ACCOUNT_ID = 1; - describe('leaveGroupChat', () => { const GROUP_CHAT_REPORT_ID = '1001'; diff --git a/tests/actions/TaskTest.ts b/tests/actions/TaskTest.ts index 7773e883b32b..f3a38a0a0ed1 100644 --- a/tests/actions/TaskTest.ts +++ b/tests/actions/TaskTest.ts @@ -1230,6 +1230,7 @@ describe('actions/Task', () => { const mockCurrentUserAccountID = 123; const DELEGATE_EMAIL = 'delegate@example.com'; const DELEGATE_ACCOUNT_ID = 999; + const archivedReportsIDSet = new Set(); beforeEach(async () => { jest.clearAllMocks(); @@ -1263,7 +1264,7 @@ describe('actions/Task', () => { }); it('should return early when report is undefined', () => { - deleteTask(undefined, undefined, false, mockCurrentUserAccountID, false, undefined, 'concierge_123', undefined); + deleteTask(undefined, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123', undefined); // eslint-disable-next-line rulesdir/no-multiple-api-calls expect(API.write).not.toHaveBeenCalled(); @@ -1304,7 +1305,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(true); - deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, parentReportAction, 'concierge_123', undefined); + deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, parentReportAction, archivedReportsIDSet, 'concierge_123', undefined); // eslint-disable-next-line rulesdir/no-multiple-api-calls expect(API.write).toHaveBeenCalledWith( @@ -1348,7 +1349,7 @@ describe('actions/Task', () => { // No visible actions means the task report should be deleted doesReportHaveVisibleActionsSpy.mockReturnValue(false); - const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, 'concierge_123', undefined); + const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123', undefined); expect(result).toBe(`r/${parentReportID}`); expect(Navigation.goBack).toHaveBeenCalled(); @@ -1376,10 +1377,10 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(conciergeReportID); - const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, conciergeReportID, undefined); + const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, conciergeReportID, undefined); expect(result).toBe(`r/${conciergeReportID}`); - expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID); + expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID, archivedReportsIDSet); expect(Navigation.goBack).toHaveBeenCalled(); }); @@ -1411,7 +1412,7 @@ describe('actions/Task', () => { // Has visible actions, so should not navigate away doesReportHaveVisibleActionsSpy.mockReturnValue(true); - const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, 'concierge_123', undefined); + const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123', undefined); expect(result).toBeUndefined(); expect(Navigation.goBack).not.toHaveBeenCalled(); @@ -1438,7 +1439,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(undefined); - const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, undefined, undefined); + const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, undefined, undefined); // API.write should still be called // eslint-disable-next-line rulesdir/no-multiple-api-calls @@ -1465,7 +1466,7 @@ describe('actions/Task', () => { }); await waitForBatchedUpdatesWithAct(); - deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, undefined, DELEGATE_EMAIL); + deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, undefined, DELEGATE_EMAIL); // eslint-disable-next-line rulesdir/no-multiple-api-calls -- Inspecting mock call args to verify optimistic data structure const calls = (API.write as jest.Mock).mock.calls; @@ -1496,7 +1497,7 @@ describe('actions/Task', () => { }); await waitForBatchedUpdatesWithAct(); - deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, undefined, undefined); + deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, undefined, undefined); // eslint-disable-next-line rulesdir/no-multiple-api-calls -- Inspecting mock call args to verify optimistic data structure const calls = (API.write as jest.Mock).mock.calls; diff --git a/tests/perf-test/ReportActionCompose.perf-test.tsx b/tests/perf-test/ReportActionCompose.perf-test.tsx index b4cf986ceb14..113a7f0d0b5f 100644 --- a/tests/perf-test/ReportActionCompose.perf-test.tsx +++ b/tests/perf-test/ReportActionCompose.perf-test.tsx @@ -89,7 +89,10 @@ beforeEach(() => { function ReportActionComposeWrapper() { return ( - + ()} + /> ); } diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index 276b0c3446b3..5c5a9378456a 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -120,6 +120,7 @@ function ReportActionsListWrapper() { loadOlderChats={mockLoadChats} loadNewerChats={mockLoadChats} transactionThreadReport={report} + archivedReportsIDSet={new Set()} /> diff --git a/tests/ui/MoneyRequestViewTest.tsx b/tests/ui/MoneyRequestViewTest.tsx index 74c06d8cb8e7..5e861508c942 100644 --- a/tests/ui/MoneyRequestViewTest.tsx +++ b/tests/ui/MoneyRequestViewTest.tsx @@ -64,6 +64,7 @@ const policyID = 'policy_mrv_test'; const expenseReportID = 'expense_mrv_123'; const parentReportActionID = 'parent_action_mrv'; const transactionID = 'txn_mrv_test'; +const archivedReportsIDSet = new Set(); const renderMoneyRequestView = (threadReport: ReturnType, policy?: Record) => render( @@ -84,6 +85,7 @@ const renderMoneyRequestView = (threadReport: ReturnType , ); diff --git a/tests/ui/ReportActionComposeTest.tsx b/tests/ui/ReportActionComposeTest.tsx index bba2658a7a4c..4b662f834f49 100644 --- a/tests/ui/ReportActionComposeTest.tsx +++ b/tests/ui/ReportActionComposeTest.tsx @@ -54,6 +54,7 @@ TestHelper.setupGlobalFetchMock(); const defaultReport = LHNTestUtils.getFakeReport(); const defaultProps: ReportActionComposeProps = { reportID: defaultReport.reportID, + archivedReportsIDSet: new Set(), }; const renderReportActionCompose = (props?: Partial) => { diff --git a/tests/ui/ReportActionsViewTest.tsx b/tests/ui/ReportActionsViewTest.tsx index c4980df1f1a2..84b19116583e 100644 --- a/tests/ui/ReportActionsViewTest.tsx +++ b/tests/ui/ReportActionsViewTest.tsx @@ -146,7 +146,12 @@ const mockReportActions: OnyxTypes.ReportAction[] = [ const renderReportActionsView = (props: {reportID?: string} = {}) => { const reportID = props.reportID ?? mockReport.reportID; - return render(); + return render( + ()} + />, + ); }; describe('ReportActionsView', () => { diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index 257cc9cc3ebf..ae3bc8be2fc1 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -1732,7 +1732,15 @@ describe('getSecondaryAction', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, {} as Transaction, undefined, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: {} as Transaction, + reportAction: undefined, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.DELETE)).toBe(true); }); @@ -2786,7 +2794,17 @@ describe('getSecondaryTransactionThreadActions', () => { const policy = {} as unknown as Policy; const result = [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.VIEW_DETAILS]; - expect(getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, {} as Transaction, undefined, {} as Transaction, policy)).toEqual(result); + expect( + getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: {} as Transaction, + reportAction: undefined, + originalTransaction: {} as Transaction, + policy, + }), + ).toEqual(result); }); it('includes HOLD option', () => { @@ -2806,7 +2824,15 @@ describe('getSecondaryTransactionThreadActions', () => { jest.spyOn(ReportUtils, 'canHoldUnholdReportAction').mockReturnValueOnce({canHoldRequest: true, canUnholdRequest: true}); jest.spyOn(ReportUtils, 'isAwaitingFirstLevelApproval').mockReturnValueOnce(true); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.HOLD)).toBe(true); }); @@ -2823,12 +2849,30 @@ describe('getSecondaryTransactionThreadActions', () => { } as unknown as Transaction; jest.spyOn(ReportUtils, 'isHoldCreator').mockReturnValue(false); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, undefined, {} as Transaction, policy, transactionThreadReport); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: undefined, + originalTransaction: {} as Transaction, + policy, + transactionThreadReport, + }); expect(result).toContain(CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD); // Do not show if admin is the holder jest.spyOn(ReportUtils, 'isHoldCreator').mockReturnValue(true); - const result2 = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, undefined, {} as Transaction, policy, transactionThreadReport); + const result2 = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: undefined, + originalTransaction: {} as Transaction, + policy, + transactionThreadReport, + }); expect(result2).not.toContain(CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD); }); @@ -2845,7 +2889,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, {} as Transaction, undefined, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: {} as Transaction, + reportAction: undefined, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.DELETE)).toBe(true); }); @@ -2933,7 +2985,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(true); }); @@ -2969,7 +3029,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3006,7 +3074,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3047,7 +3123,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, actionR14932, {} as Transaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3066,18 +3150,16 @@ describe('getSecondaryTransactionThreadActions', () => { jest.spyOn(ReportUtils, 'canEditFieldOfMoneyRequest').mockReturnValue(true); jest.spyOn(ReportUtils, 'canUserPerformWriteAction').mockReturnValue(true); - const result = getSecondaryTransactionThreadActions( - EMPLOYEE_EMAIL, - EMPLOYEE_ACCOUNT_ID, + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, parentReport, - transaction, - actionR14932, - {} as Transaction, - policy, - undefined, - undefined, - false, - ); + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + isChatReportArchived: false, + }); expect(result).toContain(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); }); @@ -3096,18 +3178,16 @@ describe('getSecondaryTransactionThreadActions', () => { jest.spyOn(ReportUtils, 'canEditFieldOfMoneyRequest').mockReturnValue(false); jest.spyOn(ReportUtils, 'canUserPerformWriteAction').mockReturnValue(true); - const result = getSecondaryTransactionThreadActions( - EMPLOYEE_EMAIL, - EMPLOYEE_ACCOUNT_ID, + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, parentReport, - transaction, - actionR14932, - {} as Transaction, - policy, - undefined, - undefined, - false, - ); + reportTransaction: transaction, + reportAction: actionR14932, + originalTransaction: {} as Transaction, + policy, + isChatReportArchived: false, + }); expect(result).not.toContain(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); }); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 3c5f78a8dc33..6a52545919b1 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -7616,7 +7616,8 @@ describe('ReportUtils', () => { }); it('should not return an archived report even if it was most recently accessed', () => { - const result = findLastAccessedReport(false); + const archivedReportsIDSet = new Set([`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${archivedReport.reportID}`]); + const result = findLastAccessedReport(false, false, undefined, archivedReportsIDSet); // Even though the archived report has a more recent lastVisitTime, // the function should filter it out and return the normal report @@ -8530,8 +8531,8 @@ describe('ReportUtils', () => { statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`, {private_isArchived: DateUtils.getDBTime()}); - expect(isReportOutstanding(report, policy.id)).toBe(false); + const archivedReportsIDSet = new Set([`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`]); + expect(isReportOutstanding(report, policy.id, archivedReportsIDSet)).toBe(false); }); }); diff --git a/tests/unit/hooks/useArchivedReportsIdSet.test.ts b/tests/unit/hooks/useArchivedReportsIdSet.test.ts index 50ae22e69319..d096207645f9 100644 --- a/tests/unit/hooks/useArchivedReportsIdSet.test.ts +++ b/tests/unit/hooks/useArchivedReportsIdSet.test.ts @@ -1,9 +1,9 @@ import {renderHook, waitFor} from '@testing-library/react-native'; import Onyx from 'react-native-onyx'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import ONYXKEYS from '@src/ONYXKEYS'; -describe('useArchivedReportsIdSet', () => { +describe('useArchivedReportsIDSet', () => { beforeAll(() => { Onyx.init({keys: ONYXKEYS}); }); @@ -13,7 +13,7 @@ describe('useArchivedReportsIdSet', () => { }); it('should return an empty Set when no report name value pairs exist', async () => { - const {result} = renderHook(() => useArchivedReportsIdSet()); + const {result} = renderHook(() => useArchivedReportsIDSet()); await waitFor(() => { expect(result.current).toBeInstanceOf(Set); @@ -25,7 +25,7 @@ describe('useArchivedReportsIdSet', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}123`, {private_isArchived: 'archived'}); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}456`, {private_isArchived: ''}); - const {result} = renderHook(() => useArchivedReportsIdSet()); + const {result} = renderHook(() => useArchivedReportsIDSet()); await waitFor(() => { expect(result.current.size).toBe(1); @@ -39,7 +39,7 @@ describe('useArchivedReportsIdSet', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}200`, {private_isArchived: 'archived'}); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}300`, {private_isArchived: ''}); - const {result} = renderHook(() => useArchivedReportsIdSet()); + const {result} = renderHook(() => useArchivedReportsIDSet()); await waitFor(() => { expect(result.current.size).toBe(2); @@ -52,7 +52,7 @@ describe('useArchivedReportsIdSet', () => { it('should update when a report becomes archived', async () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}123`, {private_isArchived: ''}); - const {result} = renderHook(() => useArchivedReportsIdSet()); + const {result} = renderHook(() => useArchivedReportsIDSet()); await waitFor(() => { expect(result.current.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}123`)).toBe(false); @@ -69,7 +69,7 @@ describe('useArchivedReportsIdSet', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}123`, {private_isArchived: 'archived'}); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}456`, null); - const {result} = renderHook(() => useArchivedReportsIdSet()); + const {result} = renderHook(() => useArchivedReportsIDSet()); await waitFor(() => { expect(result.current.size).toBe(1); diff --git a/tests/unit/hooks/useSearchSections.test.ts b/tests/unit/hooks/useSearchSections.test.ts index 890f1157e0f2..438dc6663d77 100644 --- a/tests/unit/hooks/useSearchSections.test.ts +++ b/tests/unit/hooks/useSearchSections.test.ts @@ -48,7 +48,7 @@ jest.mock('@hooks/useActionLoadingReportIDs', () => ({ default: () => new Set(), })); -jest.mock('@hooks/useArchivedReportsIdSet', () => ({ +jest.mock('@hooks/useArchivedReportsIDSet', () => ({ // eslint-disable-next-line @typescript-eslint/naming-convention __esModule: true, default: () => new Set(), diff --git a/tests/unit/navigateAfterOnboardingTest.ts b/tests/unit/navigateAfterOnboardingTest.ts index 1d4d2a4fe498..380b85a7cc59 100644 --- a/tests/unit/navigateAfterOnboardingTest.ts +++ b/tests/unit/navigateAfterOnboardingTest.ts @@ -122,14 +122,14 @@ describe('navigateAfterOnboarding', () => { expect(navigate).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(REPORT_ID)); }); - it('should pass archivedReportsIdSet when looking up last accessed report', () => { - const archivedReportsIdSet = new Set(['report_1']); + it('should pass archivedReportsIDSet when looking up last accessed report', () => { + const archivedReportsIDSet = new Set(['report_1']); mockFindLastAccessedReport.mockReturnValue(undefined); mockShouldOpenOnAdminRoom.mockReturnValue(false); - navigateAfterOnboarding(true, true, '', archivedReportsIdSet, ONBOARDING_POLICY_ID, ONBOARDING_ADMINS_CHAT_REPORT_ID); + navigateAfterOnboarding(true, true, '', archivedReportsIDSet, ONBOARDING_POLICY_ID, ONBOARDING_ADMINS_CHAT_REPORT_ID); - expect(mockFindLastAccessedReport).toHaveBeenCalledWith(false, false, undefined, archivedReportsIdSet); + expect(mockFindLastAccessedReport).toHaveBeenCalledWith(false, false, undefined, archivedReportsIDSet); }); it('should navigate to Concierge room if user uses a test email', () => { From 2c03fefe19154a63b8ae57e7d10ae7a5284181d4 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 02:59:27 +0530 Subject: [PATCH 56/78] Remove unused archived report prop plumbing --- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 4 ++-- src/pages/inbox/ReportScreen.tsx | 2 +- .../report/ReportActionCompose/ComposerProvider.tsx | 2 -- .../ReportActionCompose/ReportActionCompose.tsx | 11 +++-------- src/pages/inbox/report/ReportFooter.tsx | 8 ++------ tests/actions/ReportTest.ts | 1 + tests/perf-test/ReportActionCompose.perf-test.tsx | 5 +---- tests/ui/ReportActionComposeTest.tsx | 1 - 8 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 25916f2dd4ef..ee1c3d1ac6f1 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -239,7 +239,7 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport - {shouldDisplayReportFooter ? : null} + {shouldDisplayReportFooter ? : null} ); } @@ -288,7 +288,7 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport )} {shouldDisplayReportFooter ? ( <> - + ) : null} diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index 44719c36be3b..5513c0453fe6 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -152,7 +152,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { testID="report-actions-view-wrapper" > - {shouldDeferNonEssentials ? : } + {shouldDeferNonEssentials ? : } diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx index 69bfc14b06d8..32f182db3989 100644 --- a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx @@ -8,7 +8,6 @@ import useHandleExceedMaxTaskTitleLength from '@hooks/useHandleExceedMaxTaskTitl import useOnyx from '@hooks/useOnyx'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; import {chatIncludesConcierge} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {setIsComposerFullSize} from '@userActions/Report'; import {isBlockedFromConcierge as isBlockedFromConciergeUserAction} from '@userActions/User'; import CONST from '@src/CONST'; @@ -23,7 +22,6 @@ const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); type ComposerProviderProps = { reportID: string; - archivedReportsIDSet: ArchivedReportsIDSet; children: React.ReactNode; }; diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx index ef4face996cd..706901cf2e12 100644 --- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx @@ -6,7 +6,6 @@ import useOnyx from '@hooks/useOnyx'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import {getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import AgentZeroAwareTypingIndicator from './AgentZeroAwareTypingIndicator'; import ComposerActionMenu from './ComposerActionMenu'; @@ -25,10 +24,9 @@ import type {ComposerRef} from './ComposerWithSuggestions/ComposerWithSuggestion type ReportActionComposeProps = { reportID: string; - archivedReportsIDSet: ArchivedReportsIDSet; }; -function ReportActionComposeInner({reportID}: Pick) { +function ReportActionComposeInner({reportID}: ReportActionComposeProps) { const styles = useThemeStyles(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); @@ -69,12 +67,9 @@ function ReportActionComposeInner({reportID}: Pick + ); diff --git a/src/pages/inbox/report/ReportFooter.tsx b/src/pages/inbox/report/ReportFooter.tsx index b753ad19ed72..adb8efdfa270 100644 --- a/src/pages/inbox/report/ReportFooter.tsx +++ b/src/pages/inbox/report/ReportFooter.tsx @@ -27,7 +27,6 @@ import { isPublicRoom, isSystemChat as isSystemChatUtil, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isLoadingInitialReportActionsSelector} from '@src/selectors/ReportMetaData'; @@ -41,7 +40,7 @@ const policyRoleSelector = (policy: OnyxEntry) => policy?.role * Footer component that decides between the composer and * archived/anonymous/blocked/system chat/admins-only footer. */ -function ReportFooter({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { +function ReportFooter() { const route = useRoute(); const routeParams = route.params as {reportID?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -92,10 +91,7 @@ function ReportFooter({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedRep return ( - + ); diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 8712cbd98feb..c6cf26b29e6f 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -5596,6 +5596,7 @@ describe('actions/Report', () => { // Shared test constants for leave functions const TEST_CONCIERGE_REPORT_ID = '999'; const TEST_CURRENT_USER_ACCOUNT_ID = 1; + describe('leaveGroupChat', () => { const GROUP_CHAT_REPORT_ID = '1001'; diff --git a/tests/perf-test/ReportActionCompose.perf-test.tsx b/tests/perf-test/ReportActionCompose.perf-test.tsx index 113a7f0d0b5f..b4cf986ceb14 100644 --- a/tests/perf-test/ReportActionCompose.perf-test.tsx +++ b/tests/perf-test/ReportActionCompose.perf-test.tsx @@ -89,10 +89,7 @@ beforeEach(() => { function ReportActionComposeWrapper() { return ( - ()} - /> + ); } diff --git a/tests/ui/ReportActionComposeTest.tsx b/tests/ui/ReportActionComposeTest.tsx index 4b662f834f49..bba2658a7a4c 100644 --- a/tests/ui/ReportActionComposeTest.tsx +++ b/tests/ui/ReportActionComposeTest.tsx @@ -54,7 +54,6 @@ TestHelper.setupGlobalFetchMock(); const defaultReport = LHNTestUtils.getFakeReport(); const defaultProps: ReportActionComposeProps = { reportID: defaultReport.reportID, - archivedReportsIDSet: new Set(), }; const renderReportActionCompose = (props?: Partial) => { From 9e6af31bf8a1a0ba360dda50759f7c270556f1de Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 03:10:10 +0530 Subject: [PATCH 57/78] Fix TS --- src/components/MoneyReportHeader.tsx | 7 ++++++- tests/actions/TaskTest.ts | 12 ++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index c801a98ca216..125937348677 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -14,6 +14,7 @@ import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportsSplitNavigatorParamList, RightModalNavigatorParamList} from '@libs/Navigation/types'; +import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; @@ -35,9 +36,12 @@ type MoneyReportHeaderProps = { /** Method to trigger when pressing close button of the header */ onBackButtonPress: () => void; + + /** Set of archived report ID keys, used by secondary actions to check archive status */ + archivedReportsIDSet?: ArchivedReportsIDSet; }; -function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackButtonPress}: MoneyReportHeaderProps) { +function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackButtonPress, archivedReportsIDSet}: MoneyReportHeaderProps) { return ( @@ -45,6 +49,7 @@ function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackBut reportID={reportID} shouldDisplayBackButton={shouldDisplayBackButton} onBackButtonPress={onBackButtonPress} + archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/tests/actions/TaskTest.ts b/tests/actions/TaskTest.ts index f3a38a0a0ed1..a83c7d5c2e32 100644 --- a/tests/actions/TaskTest.ts +++ b/tests/actions/TaskTest.ts @@ -1002,14 +1002,14 @@ describe('actions/Task', () => { }); it('should return undefined when report is undefined', () => { - expect(getNavigationUrlOnTaskDelete(undefined, 'concierge_123')).toBeUndefined(); + expect(getNavigationUrlOnTaskDelete(undefined, 'concierge_123', new Set())).toBeUndefined(); }); it('should return undefined when report has visible actions (should not delete)', () => { const taskReport = getFakeReport(); doesReportHaveVisibleActionsSpy.mockReturnValue(true); - expect(getNavigationUrlOnTaskDelete(taskReport, 'concierge_123')).toBeUndefined(); + expect(getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', new Set())).toBeUndefined(); }); it('should return parent report route when report has parentReportID and no visible actions', () => { @@ -1017,7 +1017,7 @@ describe('actions/Task', () => { const taskReport = {...getFakeReport(), parentReportID}; doesReportHaveVisibleActionsSpy.mockReturnValue(false); - const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123'); + const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', new Set()); expect(result).toBe(`r/${parentReportID}`); }); @@ -1027,7 +1027,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(mostRecentReportID); - const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123'); + const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', new Set()); expect(result).toBe(`r/${mostRecentReportID}`); expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, 'concierge_123'); }); @@ -1038,7 +1038,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(conciergeReportID); - const result = getNavigationUrlOnTaskDelete(taskReport, conciergeReportID); + const result = getNavigationUrlOnTaskDelete(taskReport, conciergeReportID, new Set()); expect(result).toBe(`r/${conciergeReportID}`); expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID); }); @@ -1048,7 +1048,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(undefined); - expect(getNavigationUrlOnTaskDelete(taskReport, undefined)).toBeUndefined(); + expect(getNavigationUrlOnTaskDelete(taskReport, undefined, new Set())).toBeUndefined(); }); }); From f99187645b105f5940727559ac2342511e26cd88 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 1 May 2026 03:40:32 +0530 Subject: [PATCH 58/78] Fixes --- src/components/MoneyReportHeader.tsx | 5 +++-- .../sections/ReportField.tsx | 2 +- src/hooks/useOutstandingReports.ts | 4 ++-- src/libs/ReportUtils.ts | 6 +++--- tests/actions/TaskTest.ts | 10 ++++++---- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 125937348677..5029720a90df 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -41,6 +41,8 @@ type MoneyReportHeaderProps = { archivedReportsIDSet?: ArchivedReportsIDSet; }; +type MoneyReportHeaderContentProps = Omit; + function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackButtonPress, archivedReportsIDSet}: MoneyReportHeaderProps) { return ( @@ -49,14 +51,13 @@ function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackBut reportID={reportID} shouldDisplayBackButton={shouldDisplayBackButton} onBackButtonPress={onBackButtonPress} - archivedReportsIDSet={archivedReportsIDSet} /> ); } -function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButton = false, onBackButtonPress}: MoneyReportHeaderProps) { +function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButton = false, onBackButtonPress}: MoneyReportHeaderContentProps) { const {clearSelectedTransactions} = useSearchActionsContext(); const [moneyRequestReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDProp}`); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getNonEmptyStringOnyxID(moneyRequestReport?.policyID)}`); diff --git a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx index 2990429fdc1f..4f5d8eb14c26 100644 --- a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx +++ b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx @@ -75,8 +75,8 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a const availableOutstandingReports = getOutstandingReportsForUser( policyID, ownerAccountID, - outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, archivedReportsIDSet, + outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, false, ).sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? '')); diff --git a/src/hooks/useOutstandingReports.ts b/src/hooks/useOutstandingReports.ts index 43b4ea29d6b0..348daaf597a4 100644 --- a/src/hooks/useOutstandingReports.ts +++ b/src/hooks/useOutstandingReports.ts @@ -29,11 +29,11 @@ export default function useOutstandingReports(selectedReportID: string | undefin continue; } - const reports = getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsByPolicyID[policyID] ?? {}, archivedReportsIDSet, isEditing); + const reports = getOutstandingReportsForUser(policyID, ownerAccountID, archivedReportsIDSet, outstandingReportsByPolicyID[policyID] ?? {}, isEditing); result.push(...reports); } return result; } - return getOutstandingReportsForUser(selectedPolicyID, ownerAccountID, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, archivedReportsIDSet, isEditing); + return getOutstandingReportsForUser(selectedPolicyID, ownerAccountID, archivedReportsIDSet, outstandingReportsByPolicyID?.[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, isEditing); } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6386e18aad8f..246eb77e4746 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5007,8 +5007,8 @@ function canEditFieldOfMoneyRequest({ getOutstandingReportsForUser( moneyRequestReport?.policyID, moneyRequestReport?.ownerAccountID, - outstandingReportsByPolicyID?.[moneyRequestReport?.policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, archivedReportsIDSetForOutstandingReports, + outstandingReportsByPolicyID?.[moneyRequestReport?.policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, ).length > 0 ); } @@ -5031,8 +5031,8 @@ function canEditFieldOfMoneyRequest({ const reports = getOutstandingReportsForUser( currentPolicy.id, moneyRequestReport?.ownerAccountID, - outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, archivedReportsIDSetForOutstandingReports, + outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, ); outstandingReportsCount += reports.length; @@ -11527,8 +11527,8 @@ function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: stri function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, - reports: OnyxCollection = deprecatedAllReports, archivedReportsIDSet: ArchivedReportsIDSet, + reports: OnyxCollection = deprecatedAllReports, allowSubmitted = true, ): Array> { if (!reports) { diff --git a/tests/actions/TaskTest.ts b/tests/actions/TaskTest.ts index a83c7d5c2e32..474c8f4006ba 100644 --- a/tests/actions/TaskTest.ts +++ b/tests/actions/TaskTest.ts @@ -1027,9 +1027,10 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(mostRecentReportID); - const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', new Set()); + const emptySet = new Set(); + const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', emptySet); expect(result).toBe(`r/${mostRecentReportID}`); - expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, 'concierge_123'); + expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, 'concierge_123', emptySet); }); it('should pass conciergeReportID to getMostRecentReportID as fallback', () => { @@ -1038,9 +1039,10 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(conciergeReportID); - const result = getNavigationUrlOnTaskDelete(taskReport, conciergeReportID, new Set()); + const emptySet = new Set(); + const result = getNavigationUrlOnTaskDelete(taskReport, conciergeReportID, emptySet); expect(result).toBe(`r/${conciergeReportID}`); - expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID); + expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID, emptySet); }); it('should return undefined when no parentReportID, no most recent report, and conciergeReportID is undefined', () => { From a71b129c0a251b34b8f49d2b421929ec23fe9695 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 9 May 2026 04:07:37 +0530 Subject: [PATCH 59/78] Split archived report UI plumbing into follow-up branch --- src/components/MoneyReportHeader.tsx | 10 +---- .../sections/ReportField.tsx | 7 ++-- .../MoneyRequestHeaderSecondaryActions.tsx | 16 ++++---- .../MoneyRequestReportActionsList.tsx | 4 -- .../MoneyRequestReportView.tsx | 10 +---- .../ReportActionItem/MoneyRequestView.tsx | 7 +--- src/libs/ReportUtils.ts | 15 ++++++-- src/libs/actions/Report/index.ts | 8 ++-- src/libs/actions/Task.ts | 8 ++-- src/libs/navigateAfterOnboarding.ts | 12 +++--- src/pages/ReportDetailsPage.tsx | 3 -- .../Search/SearchMoneyRequestReportPage.tsx | 3 -- .../TransactionDuplicate/Confirmation.tsx | 3 -- .../TransactionMerge/ConfirmationPage.tsx | 3 -- src/pages/inbox/ReportActionsList.tsx | 10 +---- src/pages/inbox/ReportHeader.tsx | 6 +-- src/pages/inbox/ReportScreen.tsx | 6 +-- .../inbox/report/PureReportActionItem.tsx | 13 +------ .../useShouldAddOrReplaceReceipt.ts | 4 +- .../report/ReportActionItemContentCreated.tsx | 7 ---- .../report/ReportActionItemParentAction.tsx | 6 --- src/pages/inbox/report/ReportActionsList.tsx | 7 ---- .../report/ReportActionsListItemRenderer.tsx | 7 ---- src/pages/inbox/report/ReportActionsView.tsx | 7 +--- .../iou/request/step/IOURequestStepReport.tsx | 4 +- .../TaskShareDestinationSelectorModal.tsx | 6 +-- tests/actions/TaskTest.ts | 37 +++++++++---------- .../perf-test/ReportActionsList.perf-test.tsx | 1 - tests/ui/MoneyRequestViewTest.tsx | 2 - tests/ui/ReportActionsViewTest.tsx | 7 +--- tests/unit/navigateAfterOnboardingTest.ts | 8 ++-- 31 files changed, 76 insertions(+), 171 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index ad8b01bef627..8074d99f1c17 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -14,7 +14,6 @@ import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportsSplitNavigatorParamList, RightModalNavigatorParamList} from '@libs/Navigation/types'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; @@ -36,14 +35,9 @@ type MoneyReportHeaderProps = { /** Method to trigger when pressing close button of the header */ onBackButtonPress: () => void; - - /** Set of archived report ID keys, used by secondary actions to check archive status */ - archivedReportsIDSet?: ArchivedReportsIDSet; }; -type MoneyReportHeaderContentProps = Omit; - -function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackButtonPress, archivedReportsIDSet}: MoneyReportHeaderProps) { +function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackButtonPress}: MoneyReportHeaderProps) { return ( @@ -57,7 +51,7 @@ function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackBut ); } -function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButton = false, onBackButtonPress}: MoneyReportHeaderContentProps) { +function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButton = false, onBackButtonPress}: MoneyReportHeaderProps) { const {clearSelectedTransactions} = useSearchActionsContext(); const [moneyRequestReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDProp}`); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getNonEmptyStringOnyxID(moneyRequestReport?.policyID)}`); diff --git a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx index 4f5d8eb14c26..c6756d307077 100644 --- a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx +++ b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx @@ -1,7 +1,6 @@ import React from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useOutstandingReports from '@hooks/useOutstandingReports'; @@ -51,7 +50,7 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a const {translate, localeCompare} = useLocalize(); const reportAttributes = useReportAttributes(); - const archivedReportsIDSet = useArchivedReportsIDSet(); + const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID); // Per-key report subscriptions instead of full COLLECTION.REPORT @@ -68,15 +67,15 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a * Also we need to check if transaction report exists in outstanding reports in order to show a correct report name. */ const policyID = selectedParticipants?.at(0)?.policyID; - const shouldUseTransactionReport = (!!transactionReportEntry && isReportOutstanding(transactionReportEntry, policyID, archivedReportsIDSet, false)) || isUnreported; + const shouldUseTransactionReport = (!!transactionReportEntry && isReportOutstanding(transactionReportEntry, policyID, undefined, false)) || isUnreported; const ownerAccountID = selectedParticipants?.at(0)?.ownerAccountID; const availableOutstandingReports = getOutstandingReportsForUser( policyID, ownerAccountID, - archivedReportsIDSet, outstandingReportsByPolicyID?.[policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, + reportNameValuePairs, false, ).sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? '')); diff --git a/src/components/MoneyRequestHeaderSecondaryActions.tsx b/src/components/MoneyRequestHeaderSecondaryActions.tsx index a4b37c411ce3..1526b9f84b35 100644 --- a/src/components/MoneyRequestHeaderSecondaryActions.tsx +++ b/src/components/MoneyRequestHeaderSecondaryActions.tsx @@ -270,18 +270,18 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money if (!transaction || !parentReportAction || !parentReport) { return []; } - return getSecondaryTransactionThreadActions({ - currentUserLogin: currentUserLogin ?? '', - currentUserAccountID: accountID, + return getSecondaryTransactionThreadActions( + currentUserLogin ?? '', + accountID, parentReport, - reportTransaction: transaction, - reportAction: parentReportAction, + transaction, + parentReportAction, originalTransaction, policy, - transactionThreadReport: report, + report, outstandingReportsByPolicyID, - isChatReportArchived: isChatIOUReportArchived, - }); + isChatIOUReportArchived, + ); })(); const secondaryActionsImplementation: Partial< diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index bed0cacd5e64..b2d3bc97489a 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -8,7 +8,6 @@ import {DeviceEventEmitter, InteractionManager, View} from 'react-native'; import FlatListWithScrollKey from '@components/FlatList/FlatListWithScrollKey'; import {usePersonalDetails} from '@components/OnyxListItemProvider'; import ScrollView from '@components/ScrollView'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLoadReportActions from '@hooks/useLoadReportActions'; import useLocalize from '@hooks/useLocalize'; @@ -88,7 +87,6 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps) const styles = useThemeStyles(); const {translate, getLocalDateFromDatetime} = useLocalize(); const {isOffline, lastOfflineAt, lastOnlineAt} = useNetworkWithOfflineStatus(); - const archivedReportsIDSet = useArchivedReportsIDSet(); const reportScrollManager = useReportScrollManager(); const lastMessageTime = useRef(null); const didLayout = useRef(false); @@ -584,7 +582,6 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps) isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairs?.origin} reportNameValuePairsOriginalID={reportNameValuePairs?.originalID} - archivedReportsIDSet={archivedReportsIDSet} /> ); }, @@ -604,7 +601,6 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps) isReportArchived, reportNameValuePairs?.origin, reportNameValuePairs?.originalID, - archivedReportsIDSet, ], ); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 906fa2b71509..9f6b32fb3e84 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -27,7 +27,6 @@ import navigationRef from '@libs/Navigation/navigationRef'; import {getFilteredReportActionsForReportView, getOneTransactionThreadReportID} from '@libs/ReportActionsUtils'; import {getReportOfflinePendingActionAndErrors, isReportTransactionThread} from '@libs/ReportUtils'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {cancelSpan} from '@libs/telemetry/activeSpans'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; @@ -60,9 +59,6 @@ type MoneyRequestReportViewProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; function goBackFromSearchMoneyRequest() { @@ -109,7 +105,7 @@ function InitialLoadingSkeleton({styles, onLayout, reasonAttributes}: {styles: T ); } -function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReportFooter, backToRoute, onLayout, archivedReportsIDSet}: MoneyRequestReportViewProps) { +function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReportFooter, backToRoute, onLayout}: MoneyRequestReportViewProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); @@ -192,10 +188,9 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport } Navigation.goBack(backToRoute); }} - archivedReportsIDSet={archivedReportsIDSet} /> ), - [archivedReportsIDSet, backToRoute, isTransactionThreadView, report?.reportID], + [backToRoute, isTransactionThreadView, report?.reportID], ); // We need to cancel telemetry span when user leaves the screen before full report data is loaded @@ -283,7 +278,6 @@ function MoneyRequestReportView({report, reportLoadingState, shouldDisplayReport )} {shouldDisplayReportFooter ? ( diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 4ab5bb47aeee..961b246d9757 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -80,7 +80,6 @@ import { isTrackExpenseReportNew, shouldEnableNegative, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {hasEnabledTags, shouldShowDependentTagList} from '@libs/TagsOptionsListUtils'; import { getAttendeesListDisplayString, @@ -149,9 +148,6 @@ type MoneyRequestViewProps = { /** Merge transaction ID to show in merge transaction flow */ mergeTransactionID?: string; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; const perDiemPoliciesSelector = (policies: OnyxCollection) => { @@ -174,7 +170,6 @@ function MoneyRequestView({ updatedTransaction, isFromReviewDuplicates = false, mergeTransactionID, - archivedReportsIDSet, }: MoneyRequestViewProps) { const icons = useMemoizedLazyExpensifyIcons(['DotIndicator', 'Checkmark', 'Suitcase']); const styles = useThemeStyles(); @@ -329,6 +324,7 @@ function MoneyRequestView({ const isChatReportArchived = useReportIsArchived(moneyRequestReport?.chatReportID); const pendingAction = transaction?.pendingAction; const shouldShowPaid = isSettled && transactionReimbursable && !pendingAction; + // Flags for allowing or disallowing editing an expense // Used for non-restricted fields such as: description, category, tag, billable, etc... const isReportArchived = useReportIsArchived(transactionThreadReport?.reportID); @@ -399,7 +395,6 @@ function MoneyRequestView({ transaction, report: moneyRequestReport, policy, - archivedReportsIDSet, }) && (!isPerDiemRequest || canSubmitPerDiemExpenseFromWorkspace(policy) || (isExpenseUnreported && !!perDiemOriginalPolicy)); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8de51534a3e5..b41ceec68aea 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -11532,17 +11532,26 @@ function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: stri function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, - archivedReportsIDSet: ArchivedReportsIDSet, - reports: OnyxCollection = deprecatedAllReports, + archivedReportsIDSetOrReports?: ArchivedReportsIDSet | OnyxCollection, + reportsOrReportNameValuePairs?: OnyxCollection | OnyxCollection | boolean, allowSubmitted = true, ): Array> { + const isUsingArchivedReportsIDSet = archivedReportsIDSetOrReports instanceof Set; + const reportNameValuePairs = + typeof reportsOrReportNameValuePairs === 'boolean' ? allReportNameValuePair : ((reportsOrReportNameValuePairs as OnyxCollection) ?? allReportNameValuePair); + const archivedReportsIDSet = isUsingArchivedReportsIDSet ? archivedReportsIDSetOrReports : buildArchivedReportsIDSet(reportNameValuePairs); + const reports = isUsingArchivedReportsIDSet + ? ((typeof reportsOrReportNameValuePairs === 'boolean' ? undefined : reportsOrReportNameValuePairs) as OnyxCollection) || deprecatedAllReports + : (archivedReportsIDSetOrReports as OnyxCollection) || deprecatedAllReports; + const shouldAllowSubmitted = typeof reportsOrReportNameValuePairs === 'boolean' ? reportsOrReportNameValuePairs : allowSubmitted; + if (!reports) { return []; } return Object.values(reports).filter( (report) => report?.pendingFields?.preview !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - isReportOutstanding(report, policyID, archivedReportsIDSet, allowSubmitted) && + isReportOutstanding(report, policyID, archivedReportsIDSet, shouldAllowSubmitted) && report?.ownerAccountID === reportOwnerAccountID, ); } diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index a132453e3d24..142a9a3c61d8 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -172,7 +172,6 @@ import { prepareOnboardingOnyxData, } from '@libs/ReportUtils'; import {buildOptimisticSnapshotData, getCurrentSearchQueryJSON} from '@libs/SearchQueryUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import {getAmount, getCurrency, hasValidModifiedAmount, isOnHold, recalculateUnreportedTransactionDetails, shouldClearConvertedAmount} from '@libs/TransactionUtils'; import addTrailingForwardSlash from '@libs/UrlUtils'; @@ -4560,9 +4559,8 @@ function navigateToMostRecentReport( currentUserAccountID: number, introSelected: OnyxEntry, betas: OnyxEntry, - archivedReportsIDSet?: ArchivedReportsIDSet, ) { - const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, archivedReportsIDSet)?.reportID; + const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID)?.reportID; if (lastAccessedReportID) { // Check if route exists for super wide RHP vs regular full screen report @@ -4587,8 +4585,8 @@ function navigateToMostRecentReport( } } -function getMostRecentReportID(currentReport: OnyxEntry, conciergeReportID: string | undefined, archivedReportsIDSet?: ArchivedReportsIDSet) { - const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID, archivedReportsIDSet)?.reportID; +function getMostRecentReportID(currentReport: OnyxEntry, conciergeReportID: string | undefined) { + const lastAccessedReportID = findLastAccessedReport(false, false, currentReport?.reportID)?.reportID; return lastAccessedReportID ?? conciergeReportID; } diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index c4ce66ee66ee..271667b89115 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -18,7 +18,6 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import {getReportName} from '@libs/ReportNameUtils'; import * as ReportUtils from '@libs/ReportUtils'; import {buildOptimisticSnapshotData} from '@libs/SearchQueryUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -1154,7 +1153,7 @@ function getShareDestination( * @param report - The task report being deleted * @returns The URL to navigate to */ -function getNavigationUrlOnTaskDelete(report: OnyxEntry, conciergeReportID: string | undefined, archivedReportsIDSet: ArchivedReportsIDSet): string | undefined { +function getNavigationUrlOnTaskDelete(report: OnyxEntry, conciergeReportID: string | undefined): string | undefined { if (!report) { return undefined; } @@ -1169,7 +1168,7 @@ function getNavigationUrlOnTaskDelete(report: OnyxEntry, conci } // If no parent report, try to navigate to most recent report - const mostRecentReportID = getMostRecentReportID(report, conciergeReportID, archivedReportsIDSet); + const mostRecentReportID = getMostRecentReportID(report, conciergeReportID); if (mostRecentReportID) { return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID); } @@ -1187,7 +1186,6 @@ function deleteTask( currentUserAccountID: number, hasOutstandingChildTask: boolean, parentReportAction: OnyxEntry, - archivedReportsIDSet: ArchivedReportsIDSet, conciergeReportID: string | undefined, delegateEmail: string | undefined, ancestors: ReportUtils.Ancestor[] = [], @@ -1315,7 +1313,7 @@ function deleteTask( API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData}); notifyNewAction(report.reportID, undefined, true); - const urlToNavigateBack = getNavigationUrlOnTaskDelete(report, conciergeReportID, archivedReportsIDSet); + const urlToNavigateBack = getNavigationUrlOnTaskDelete(report, conciergeReportID); if (urlToNavigateBack) { Navigation.goBack(); return urlToNavigateBack; diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts index 6b24aca1fea8..509f7928c7c1 100644 --- a/src/libs/navigateAfterOnboarding.ts +++ b/src/libs/navigateAfterOnboarding.ts @@ -28,7 +28,7 @@ function getReportIDAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIdSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -43,7 +43,7 @@ function getReportIDAfterOnboarding( return undefined; } - const lastAccessedReport = findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom() && !shouldPreventOpenAdminRoom, undefined, archivedReportsIDSet); + const lastAccessedReport = findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom() && !shouldPreventOpenAdminRoom, undefined, archivedReportsIdSet); const lastAccessedReportID = lastAccessedReport?.reportID; // When the user goes through the onboarding flow, a workspace can be created if the user selects specific options. The user should be taken to the #admins room for that workspace because it is the most natural place for them to start their experience in the app. @@ -59,7 +59,7 @@ function navigateAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIdSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -86,7 +86,7 @@ function navigateAfterOnboarding( isSmallScreenWidth, canUseDefaultRooms, conciergeReportID, - archivedReportsIDSet, + archivedReportsIdSet, onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, @@ -103,7 +103,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIdSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -115,7 +115,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth, canUseDefaultRooms, conciergeReportID, - archivedReportsIDSet, + archivedReportsIdSet, onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index a978cf4f50ec..a8bcd8b20792 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -28,7 +28,6 @@ import {useSearchActionsContext} from '@components/Search/SearchContext'; import {SUPER_WIDE_RIGHT_MODALS} from '@components/WideRHPContextProvider/WIDE_RIGHT_MODALS'; import useActivePolicy from '@hooks/useActivePolicy'; import useAncestors from '@hooks/useAncestors'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDeleteTransactions from '@hooks/useDeleteTransactions'; @@ -181,7 +180,6 @@ function ReportDetailsPage({policy, report, route, reportMetadata, reportLoading const parentReportAction = useParentReportAction(report); const hasOutstandingChildTask = useHasOutstandingChildTask(report); - const archivedReportsIDSet = useArchivedReportsIDSet(); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); @@ -918,7 +916,6 @@ function ReportDetailsPage({policy, report, route, reportMetadata, reportLoading currentUserPersonalDetails.accountID, hasOutstandingChildTask, parentReportAction, - archivedReportsIDSet, conciergeReportID, delegateEmail, ancestors, diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index 7565f1e19e24..538496f27f3f 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -12,7 +12,6 @@ import {useSearchStateContext} from '@components/Search/SearchContext'; import useShowSuperWideRHPVersion from '@components/WideRHPContextProvider/useShowSuperWideRHPVersion'; import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDismissOnMoneyRequestReportRemoval from '@hooks/useDismissOnMoneyRequestReportRemoval'; import useDocumentTitle from '@hooks/useDocumentTitle'; @@ -121,7 +120,6 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute, isReportArchived); - const archivedReportsIDSet = useArchivedReportsIDSet(); const actionListValue = useActionListContextValue(); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`); @@ -388,7 +386,6 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { key={report?.reportID} onLayout={handleSubmitToDestinationVisibleLayout} backToRoute={route.params.backTo} - archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index 92a72a71382e..22fddddcf2ca 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -13,7 +13,6 @@ import ScrollView from '@components/ScrollView'; import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import {useWideRHPState} from '@components/WideRHPContextProvider'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -41,7 +40,6 @@ import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; function Confirmation() { const styles = useThemeStyles(); const {translate} = useLocalize(); - const archivedReportsIDSet = useArchivedReportsIDSet(); const route = useRoute>(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [reviewDuplicates, reviewDuplicatesResult] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES); @@ -188,7 +186,6 @@ function Confirmation() { readonly updatedTransaction={newTransaction as OnyxEntry} isFromReviewDuplicates - archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/TransactionMerge/ConfirmationPage.tsx b/src/pages/TransactionMerge/ConfirmationPage.tsx index 371a9b7ddf3b..6bc398a43166 100644 --- a/src/pages/TransactionMerge/ConfirmationPage.tsx +++ b/src/pages/TransactionMerge/ConfirmationPage.tsx @@ -10,7 +10,6 @@ import MoneyRequestView from '@components/ReportActionItem/MoneyRequestView'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useMergeTransactions from '@hooks/useMergeTransactions'; @@ -40,7 +39,6 @@ function ConfirmationPage({route}: ConfirmationPageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [isMergingExpenses, setIsMergingExpenses] = useState(false); - const archivedReportsIDSet = useArchivedReportsIDSet(); const {transactionID, isOnSearch, backTo} = route.params; const [mergeTransaction, mergeTransactionMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.MERGE_TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`); @@ -144,7 +142,6 @@ function ConfirmationPage({route}: ConfirmationPageProps) { readonly updatedTransaction={mergedTransactionData as unknown as OnyxEntry} mergeTransactionID={transactionID} - archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/inbox/ReportActionsList.tsx b/src/pages/inbox/ReportActionsList.tsx index 3cfbf8d5a3fe..38b9741ec5fd 100644 --- a/src/pages/inbox/ReportActionsList.tsx +++ b/src/pages/inbox/ReportActionsList.tsx @@ -9,7 +9,6 @@ import useReportTransactionsCollection from '@hooks/useReportTransactionsCollect import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getAllNonDeletedTransactions, shouldDisplayReportTableView, shouldWaitForTransactions as shouldWaitForTransactionsUtil} from '@libs/MoneyRequestReportUtils'; import {isInvoiceReport, isMoneyRequestReport} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ReportActionsView from './report/ReportActionsView'; @@ -27,7 +26,7 @@ const defaultReportLoadingState = { * or MoneyRequestReportActionsList. Only subscribes to what the branching * conditions need — heavy data derivation is pushed into each child. */ -function ReportActionsList({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { +function ReportActionsList() { const route = useRoute(); const routeParams = route.params as {reportID?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -53,12 +52,7 @@ function ReportActionsList({archivedReportsIDSet}: {archivedReportsIDSet: Archiv return ; } - return ( - - ); + return ; } export default ReportActionsList; diff --git a/src/pages/inbox/ReportHeader.tsx b/src/pages/inbox/ReportHeader.tsx index 5fe4f54df3b7..76d67178ab85 100644 --- a/src/pages/inbox/ReportHeader.tsx +++ b/src/pages/inbox/ReportHeader.tsx @@ -13,7 +13,6 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Navigation from '@libs/Navigation/Navigation'; import {getReportName} from '@libs/ReportNameUtils'; import {getReportOfflinePendingActionAndErrors, isInvoiceReport, isMoneyRequestReport, isReportTransactionThread} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; @@ -23,9 +22,9 @@ import HeaderView from './HeaderView'; /** * Owns header variant selection, back button logic, and OfflineWithFeedback wrapper. - * Subscribes to report type internally. + * Subscribes to report type internally — ReportScreen passes nothing. */ -function ReportHeader({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedReportsIDSet}) { +function ReportHeader() { const route = useRoute(); const routeParams = route.params as {reportID?: string; backTo?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); @@ -98,7 +97,6 @@ function ReportHeader({archivedReportsIDSet}: {archivedReportsIDSet: ArchivedRep ); diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx index 1dc5b9e69e87..acf78a3dbb45 100644 --- a/src/pages/inbox/ReportScreen.tsx +++ b/src/pages/inbox/ReportScreen.tsx @@ -7,7 +7,6 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ScreenWrapper from '@components/ScreenWrapper'; import WideRHPOverlayWrapper from '@components/WideRHPOverlayWrapper'; import useActionListContextValue from '@hooks/useActionListContextValue'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import {useCurrentReportIDState} from '@hooks/useCurrentReportID'; import useOnyx from '@hooks/useOnyx'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -57,7 +56,6 @@ function ReportScreen({route, navigation}: ReportScreenProps) { const viewportOffsetTop = useViewportOffsetTop(); const isTopMostReportId = currentReportIDValue === reportIDFromRoute; const screenWrapperStyle: ViewStyle[] = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}]; - const archivedReportsIDSet = useArchivedReportsIDSet(); const shouldDeferNonEssentials = useDeferNonEssentials(reportIDFromRoute); @@ -108,7 +106,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { {!shouldDeferNonEssentials && } - + {!shouldDeferNonEssentials && } - + {shouldDeferNonEssentials ? : } diff --git a/src/pages/inbox/report/PureReportActionItem.tsx b/src/pages/inbox/report/PureReportActionItem.tsx index 4f281975a2a5..d3fc4b08fd14 100644 --- a/src/pages/inbox/report/PureReportActionItem.tsx +++ b/src/pages/inbox/report/PureReportActionItem.tsx @@ -99,7 +99,6 @@ import { isTaskReport, shouldDisplayThreadReplies as shouldDisplayThreadRepliesUtils, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import SelectionScraper from '@libs/SelectionScraper'; import {ReactionListContext} from '@pages/inbox/ReportScreenContext'; import AttachmentModalContext from '@pages/media/AttachmentModalScreen/AttachmentModalContext'; @@ -144,8 +143,6 @@ import SearchActionHeader from './SearchActionHeader'; import TripSummary from './TripSummary'; import WhisperBanner from './WhisperBanner'; -const EMPTY_ARCHIVED_REPORTS_ID_SET = new Set(); - type PureReportActionItemProps = { /** Report for this action */ report: OnyxEntry; @@ -242,9 +239,6 @@ type PureReportActionItemProps = { /** Report name value pairs originalID */ reportNameValuePairsOriginalID?: string; - - /** Set of archived report ID keys */ - archivedReportsIDSet?: ArchivedReportsIDSet; }; // This is equivalent to returning a negative boolean in normal functions, but we can keep the element return type @@ -285,7 +279,6 @@ function PureReportActionItem({ isTryNewDotNVPDismissed = false, reportNameValuePairsOrigin, reportNameValuePairsOriginalID, - archivedReportsIDSet = EMPTY_ARCHIVED_REPORTS_ID_SET, }: PureReportActionItemProps) { const isConciergeGreeting = action.reportActionID === CONST.CONCIERGE_GREETING_ACTION_ID; const shouldDisplayContextMenuValue = shouldDisplayContextMenu && !isConciergeGreeting; @@ -1007,10 +1000,9 @@ function PureReportActionItem({ transactionID={transactionID} draftMessage={draftMessage} shouldHideThreadDividerLine={shouldHideThreadDividerLine} - archivedReportsIDSet={archivedReportsIDSet} /> ); - }, [contextMenuStateValue, contextMenuActionsValue, parentReportAction, draftMessage, shouldHideThreadDividerLine, archivedReportsIDSet, parentReportActionForTransactionThread]); + }, [contextMenuStateValue, contextMenuActionsValue, parentReportAction, draftMessage, shouldHideThreadDividerLine, parentReportActionForTransactionThread]); if (action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && !isHarvestCreatedExpenseReport) { return createdActionContent; @@ -1200,7 +1192,6 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => { prevProps.userBillingFundID === nextProps.userBillingFundID && prevProps.shouldHighlight === nextProps.shouldHighlight && prevProps.reportNameValuePairsOrigin === nextProps.reportNameValuePairsOrigin && - prevProps.reportNameValuePairsOriginalID === nextProps.reportNameValuePairsOriginalID && - prevProps.archivedReportsIDSet === nextProps.archivedReportsIDSet + prevProps.reportNameValuePairsOriginalID === nextProps.reportNameValuePairsOriginalID ); }); diff --git a/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts b/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts index d5bdbf2c6e0a..4a454111f10a 100644 --- a/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts +++ b/src/pages/inbox/report/ReportActionCompose/useShouldAddOrReplaceReceipt.ts @@ -1,4 +1,3 @@ -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; @@ -15,7 +14,6 @@ import type {ReportAction} from '@src/types/onyx'; function useShouldAddOrReplaceReceipt(reportID: string) { const {isOffline} = useNetwork(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); - const archivedReportsIDSet = useArchivedReportsIDSet(); const isReportArchived = useReportIsArchived(report?.reportID); const allReportTransactions = useReportTransactionsCollection(reportID); const [rawReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`); @@ -36,7 +34,7 @@ function useShouldAddOrReplaceReceipt(reportID: string) { const canUserPerformWriteAction = !!canUserPerformWriteActionReportUtils(report, isReportArchived); const canEditReceipt = canUserPerformWriteAction && - canEditFieldOfMoneyRequest({reportAction: effectiveParentReportAction, fieldToEdit: CONST.EDIT_REQUEST_FIELD.RECEIPT, transaction, archivedReportsIDSet}) && + canEditFieldOfMoneyRequest({reportAction: effectiveParentReportAction, fieldToEdit: CONST.EDIT_REQUEST_FIELD.RECEIPT, transaction}) && !transaction?.receipt?.isTestDriveReceipt; const shouldAddOrReplaceReceipt = (isTransactionThreadView || isSingleTransactionView) && canEditReceipt; diff --git a/src/pages/inbox/report/ReportActionItemContentCreated.tsx b/src/pages/inbox/report/ReportActionItemContentCreated.tsx index e7c7d07e3abb..9fcdcf63cde3 100644 --- a/src/pages/inbox/report/ReportActionItemContentCreated.tsx +++ b/src/pages/inbox/report/ReportActionItemContentCreated.tsx @@ -17,7 +17,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {isMessageDeleted, isReversedTransaction as isReversedTransactionReportActionsUtils, isTransactionThread} from '@libs/ReportActionsUtils'; import {isCanceledTaskReport, isExpenseReport, isInvoiceReport, isIOUReport, isTaskReport} from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {getCurrency} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -46,9 +45,6 @@ type ReportActionItemContentCreatedProps = { /** Flag to show, hide the thread divider line */ shouldHideThreadDividerLine: boolean; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionItemContentCreated({ @@ -58,7 +54,6 @@ function ReportActionItemContentCreated({ transactionID, draftMessage, shouldHideThreadDividerLine, - archivedReportsIDSet, }: ReportActionItemContentCreatedProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -126,7 +121,6 @@ function ReportActionItemContentCreated({ parentReportID={report?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground - archivedReportsIDSet={archivedReportsIDSet} /> {renderThreadDivider} @@ -193,7 +187,6 @@ function ReportActionItemContentCreated({ parentReportID={transactionThreadReport?.parentReportID} expensePolicy={policy} shouldShowAnimatedBackground={false} - archivedReportsIDSet={archivedReportsIDSet} /> {renderThreadDivider} diff --git a/src/pages/inbox/report/ReportActionItemParentAction.tsx b/src/pages/inbox/report/ReportActionItemParentAction.tsx index da0482d4cac3..dfd306bf5704 100644 --- a/src/pages/inbox/report/ReportActionItemParentAction.tsx +++ b/src/pages/inbox/report/ReportActionItemParentAction.tsx @@ -20,7 +20,6 @@ import { navigateToLinkedReportAction, shouldExcludeAncestorReportAction, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Report, ReportAction, ReportActions, ReportActionsDrafts, ReportNameValuePairs, Transaction} from '@src/types/onyx'; @@ -72,9 +71,6 @@ type ReportActionItemParentActionProps = { /** Whether the report is archived */ isReportArchived: boolean; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; function ReportActionItemParentAction({ @@ -91,7 +87,6 @@ function ReportActionItemParentAction({ userBillingFundID, isTryNewDotNVPDismissed = false, isReportArchived = false, - archivedReportsIDSet, }: ReportActionItemParentActionProps) { const styles = useThemeStyles(); const ancestors = useAncestors(report, shouldExcludeAncestorReportAction); @@ -236,7 +231,6 @@ function ReportActionItemParentAction({ linkedTransactionRouteError={linkedTransactionRouteError} userBillingFundID={userBillingFundID} isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} - archivedReportsIDSet={archivedReportsIDSet} /> ); diff --git a/src/pages/inbox/report/ReportActionsList.tsx b/src/pages/inbox/report/ReportActionsList.tsx index f58388d659f1..7062af6b5cc5 100644 --- a/src/pages/inbox/report/ReportActionsList.tsx +++ b/src/pages/inbox/report/ReportActionsList.tsx @@ -60,7 +60,6 @@ import { isTaskReport, isUnread, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import Visibility from '@libs/Visibility'; import type {ReportsSplitNavigatorParamList} from '@navigation/types'; import {useConciergeDraft, useConciergeDraftActions} from '@pages/inbox/ConciergeDraftContext'; @@ -130,9 +129,6 @@ type ReportActionsListProps = { /** Callback to show previous messages */ onShowPreviousMessages?: () => void; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; // In the component we are subscribing to the arrival of new actions. @@ -171,7 +167,6 @@ function ReportActionsList({ showHiddenHistory, hasPreviousMessages, onShowPreviousMessages, - archivedReportsIDSet, }: ReportActionsListProps) { const prevHasCreatedActionAdded = usePrevious(hasCreatedActionAdded); const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); @@ -780,7 +775,6 @@ function ReportActionsList({ isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairs?.origin} reportNameValuePairsOriginalID={reportNameValuePairs?.originalID} - archivedReportsIDSet={archivedReportsIDSet} /> getOriginalMessage(reportAction), [reportAction]); @@ -196,7 +191,6 @@ function ReportActionsListItemRenderer({ userBillingFundID={userBillingFundID} isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} isReportArchived={isReportArchived} - archivedReportsIDSet={archivedReportsIDSet} /> ); } @@ -222,7 +216,6 @@ function ReportActionsListItemRenderer({ isTryNewDotNVPDismissed={isTryNewDotNVPDismissed} reportNameValuePairsOrigin={reportNameValuePairsOrigin} reportNameValuePairsOriginalID={reportNameValuePairsOriginalID} - archivedReportsIDSet={archivedReportsIDSet} /> ); } diff --git a/src/pages/inbox/report/ReportActionsView.tsx b/src/pages/inbox/report/ReportActionsView.tsx index 05d7a166ceb8..0d9005acf714 100755 --- a/src/pages/inbox/report/ReportActionsView.tsx +++ b/src/pages/inbox/report/ReportActionsView.tsx @@ -46,7 +46,6 @@ import { isMoneyRequestReport, isReportTransactionThread as isReportTransactionThreadUtil, } from '@libs/ReportUtils'; -import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -63,14 +62,11 @@ type ReportActionsViewProps = { /** Callback executed on layout */ onLayout?: (event: LayoutChangeEvent) => void; - - /** Set of archived report ID keys */ - archivedReportsIDSet: ArchivedReportsIDSet; }; let listOldID = Math.round(Math.random() * 100); -function ReportActionsView({reportID, onLayout, archivedReportsIDSet}: ReportActionsViewProps) { +function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) { useCopySelectionHelper(); const {translate} = useLocalize(); usePendingConciergeResponse(reportID); @@ -375,7 +371,6 @@ function ReportActionsView({reportID, onLayout, archivedReportsIDSet}: ReportAct showHiddenHistory={!showFullHistory} hasPreviousMessages={hasPreviousMessages} onShowPreviousMessages={handleShowPreviousMessages} - archivedReportsIDSet={archivedReportsIDSet} /> diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 12fcb17a280a..ba087d224041 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -5,7 +5,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import {usePersonalDetails, useSession} from '@components/OnyxListItemProvider'; import {useSearchActionsContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConditionalCreateEmptyReportConfirmation from '@hooks/useConditionalCreateEmptyReportConfirmation'; import useOnyx from '@hooks/useOnyx'; import useOptimisticDraftTransactions from '@hooks/useOptimisticDraftTransactions'; @@ -49,12 +48,11 @@ const getIOUActionsSelector = (actions: OnyxEntry): ReportAction[ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {backTo, action, iouType, transactionID, reportID: reportIDFromRoute, reportActionID} = route.params; const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const archivedReportsIDSet = useArchivedReportsIDSet(); const isUnreported = transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); const participantReportID = transaction?.participants?.at(0)?.reportID; const participantReport = Object.values(allReports ?? {}).find((report) => report?.reportID === participantReportID); - const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID, archivedReportsIDSet)) || isUnreported; + const shouldUseTransactionReport = (!!transactionReport && isReportOutstanding(transactionReport, transactionReport?.policyID)) || isUnreported; const outstandingReportID = isPolicyExpenseChat(participantReport) ? participantReport?.iouReportID : participantReportID; const selectedReportID = shouldUseTransactionReport ? transactionReport?.reportID : outstandingReportID; const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index 32b0c7064d80..c91b6d45efe1 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -64,15 +64,15 @@ function TaskShareDestinationSelectorModal() { onSingleSelect: selectReportHandler, }); - const archivedReportsIDSet = useArchivedReportsIDSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const filteredOptions = useMemo(() => { - const filteredReports = reportFilter(availableOptions.recentReports as Array>, archivedReportsIDSet); + const filteredReports = reportFilter(availableOptions.recentReports as Array>, archivedReportsIdSet); return { ...availableOptions, recentReports: filteredReports ?? [], }; - }, [availableOptions, archivedReportsIDSet]); + }, [availableOptions, archivedReportsIdSet]); const data = useMemo( () => diff --git a/tests/actions/TaskTest.ts b/tests/actions/TaskTest.ts index 474c8f4006ba..7773e883b32b 100644 --- a/tests/actions/TaskTest.ts +++ b/tests/actions/TaskTest.ts @@ -1002,14 +1002,14 @@ describe('actions/Task', () => { }); it('should return undefined when report is undefined', () => { - expect(getNavigationUrlOnTaskDelete(undefined, 'concierge_123', new Set())).toBeUndefined(); + expect(getNavigationUrlOnTaskDelete(undefined, 'concierge_123')).toBeUndefined(); }); it('should return undefined when report has visible actions (should not delete)', () => { const taskReport = getFakeReport(); doesReportHaveVisibleActionsSpy.mockReturnValue(true); - expect(getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', new Set())).toBeUndefined(); + expect(getNavigationUrlOnTaskDelete(taskReport, 'concierge_123')).toBeUndefined(); }); it('should return parent report route when report has parentReportID and no visible actions', () => { @@ -1017,7 +1017,7 @@ describe('actions/Task', () => { const taskReport = {...getFakeReport(), parentReportID}; doesReportHaveVisibleActionsSpy.mockReturnValue(false); - const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', new Set()); + const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123'); expect(result).toBe(`r/${parentReportID}`); }); @@ -1027,10 +1027,9 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(mostRecentReportID); - const emptySet = new Set(); - const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123', emptySet); + const result = getNavigationUrlOnTaskDelete(taskReport, 'concierge_123'); expect(result).toBe(`r/${mostRecentReportID}`); - expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, 'concierge_123', emptySet); + expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, 'concierge_123'); }); it('should pass conciergeReportID to getMostRecentReportID as fallback', () => { @@ -1039,10 +1038,9 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(conciergeReportID); - const emptySet = new Set(); - const result = getNavigationUrlOnTaskDelete(taskReport, conciergeReportID, emptySet); + const result = getNavigationUrlOnTaskDelete(taskReport, conciergeReportID); expect(result).toBe(`r/${conciergeReportID}`); - expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID, emptySet); + expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID); }); it('should return undefined when no parentReportID, no most recent report, and conciergeReportID is undefined', () => { @@ -1050,7 +1048,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(undefined); - expect(getNavigationUrlOnTaskDelete(taskReport, undefined, new Set())).toBeUndefined(); + expect(getNavigationUrlOnTaskDelete(taskReport, undefined)).toBeUndefined(); }); }); @@ -1232,7 +1230,6 @@ describe('actions/Task', () => { const mockCurrentUserAccountID = 123; const DELEGATE_EMAIL = 'delegate@example.com'; const DELEGATE_ACCOUNT_ID = 999; - const archivedReportsIDSet = new Set(); beforeEach(async () => { jest.clearAllMocks(); @@ -1266,7 +1263,7 @@ describe('actions/Task', () => { }); it('should return early when report is undefined', () => { - deleteTask(undefined, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123', undefined); + deleteTask(undefined, undefined, false, mockCurrentUserAccountID, false, undefined, 'concierge_123', undefined); // eslint-disable-next-line rulesdir/no-multiple-api-calls expect(API.write).not.toHaveBeenCalled(); @@ -1307,7 +1304,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(true); - deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, parentReportAction, archivedReportsIDSet, 'concierge_123', undefined); + deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, parentReportAction, 'concierge_123', undefined); // eslint-disable-next-line rulesdir/no-multiple-api-calls expect(API.write).toHaveBeenCalledWith( @@ -1351,7 +1348,7 @@ describe('actions/Task', () => { // No visible actions means the task report should be deleted doesReportHaveVisibleActionsSpy.mockReturnValue(false); - const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123', undefined); + const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, 'concierge_123', undefined); expect(result).toBe(`r/${parentReportID}`); expect(Navigation.goBack).toHaveBeenCalled(); @@ -1379,10 +1376,10 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(conciergeReportID); - const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, conciergeReportID, undefined); + const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, conciergeReportID, undefined); expect(result).toBe(`r/${conciergeReportID}`); - expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID, archivedReportsIDSet); + expect(getMostRecentReportIDSpy).toHaveBeenCalledWith(taskReport, conciergeReportID); expect(Navigation.goBack).toHaveBeenCalled(); }); @@ -1414,7 +1411,7 @@ describe('actions/Task', () => { // Has visible actions, so should not navigate away doesReportHaveVisibleActionsSpy.mockReturnValue(true); - const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, 'concierge_123', undefined); + const result = deleteTask(taskReport, parentReport, false, mockCurrentUserAccountID, false, undefined, 'concierge_123', undefined); expect(result).toBeUndefined(); expect(Navigation.goBack).not.toHaveBeenCalled(); @@ -1441,7 +1438,7 @@ describe('actions/Task', () => { doesReportHaveVisibleActionsSpy.mockReturnValue(false); getMostRecentReportIDSpy.mockReturnValue(undefined); - const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, undefined, undefined); + const result = deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, undefined, undefined); // API.write should still be called // eslint-disable-next-line rulesdir/no-multiple-api-calls @@ -1468,7 +1465,7 @@ describe('actions/Task', () => { }); await waitForBatchedUpdatesWithAct(); - deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, undefined, DELEGATE_EMAIL); + deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, undefined, DELEGATE_EMAIL); // eslint-disable-next-line rulesdir/no-multiple-api-calls -- Inspecting mock call args to verify optimistic data structure const calls = (API.write as jest.Mock).mock.calls; @@ -1499,7 +1496,7 @@ describe('actions/Task', () => { }); await waitForBatchedUpdatesWithAct(); - deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, archivedReportsIDSet, undefined, undefined); + deleteTask(taskReport, undefined, false, mockCurrentUserAccountID, false, undefined, undefined, undefined); // eslint-disable-next-line rulesdir/no-multiple-api-calls -- Inspecting mock call args to verify optimistic data structure const calls = (API.write as jest.Mock).mock.calls; diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index 5c5a9378456a..276b0c3446b3 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -120,7 +120,6 @@ function ReportActionsListWrapper() { loadOlderChats={mockLoadChats} loadNewerChats={mockLoadChats} transactionThreadReport={report} - archivedReportsIDSet={new Set()} /> diff --git a/tests/ui/MoneyRequestViewTest.tsx b/tests/ui/MoneyRequestViewTest.tsx index 5e861508c942..74c06d8cb8e7 100644 --- a/tests/ui/MoneyRequestViewTest.tsx +++ b/tests/ui/MoneyRequestViewTest.tsx @@ -64,7 +64,6 @@ const policyID = 'policy_mrv_test'; const expenseReportID = 'expense_mrv_123'; const parentReportActionID = 'parent_action_mrv'; const transactionID = 'txn_mrv_test'; -const archivedReportsIDSet = new Set(); const renderMoneyRequestView = (threadReport: ReturnType, policy?: Record) => render( @@ -85,7 +84,6 @@ const renderMoneyRequestView = (threadReport: ReturnType , ); diff --git a/tests/ui/ReportActionsViewTest.tsx b/tests/ui/ReportActionsViewTest.tsx index 84b19116583e..c4980df1f1a2 100644 --- a/tests/ui/ReportActionsViewTest.tsx +++ b/tests/ui/ReportActionsViewTest.tsx @@ -146,12 +146,7 @@ const mockReportActions: OnyxTypes.ReportAction[] = [ const renderReportActionsView = (props: {reportID?: string} = {}) => { const reportID = props.reportID ?? mockReport.reportID; - return render( - ()} - />, - ); + return render(); }; describe('ReportActionsView', () => { diff --git a/tests/unit/navigateAfterOnboardingTest.ts b/tests/unit/navigateAfterOnboardingTest.ts index 4b10cc03e8b1..2ed25846302f 100644 --- a/tests/unit/navigateAfterOnboardingTest.ts +++ b/tests/unit/navigateAfterOnboardingTest.ts @@ -121,14 +121,14 @@ describe('navigateAfterOnboarding', () => { expect(navigate).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(REPORT_ID)); }); - it('should pass archivedReportsIDSet when looking up last accessed report', () => { - const archivedReportsIDSet = new Set(['report_1']); + it('should pass archivedReportsIdSet when looking up last accessed report', () => { + const archivedReportsIdSet = new Set(['report_1']); mockFindLastAccessedReport.mockReturnValue(undefined); mockShouldOpenOnAdminRoom.mockReturnValue(false); - navigateAfterOnboarding(true, true, '', archivedReportsIDSet, ONBOARDING_POLICY_ID, ONBOARDING_ADMINS_CHAT_REPORT_ID); + navigateAfterOnboarding(true, true, '', archivedReportsIdSet, ONBOARDING_POLICY_ID, ONBOARDING_ADMINS_CHAT_REPORT_ID); - expect(mockFindLastAccessedReport).toHaveBeenCalledWith(false, false, undefined, archivedReportsIDSet); + expect(mockFindLastAccessedReport).toHaveBeenCalledWith(false, false, undefined, archivedReportsIdSet); }); it('should navigate to Concierge room if user uses a test email', () => { From 618e1b5f3b748b4ae9cae4669e33dbd731b124ca Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 9 May 2026 04:14:03 +0530 Subject: [PATCH 60/78] Fix transaction thread secondary actions call --- .../MoneyRequestHeaderSecondaryActions.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyRequestHeaderSecondaryActions.tsx b/src/components/MoneyRequestHeaderSecondaryActions.tsx index 1526b9f84b35..a4b37c411ce3 100644 --- a/src/components/MoneyRequestHeaderSecondaryActions.tsx +++ b/src/components/MoneyRequestHeaderSecondaryActions.tsx @@ -270,18 +270,18 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money if (!transaction || !parentReportAction || !parentReport) { return []; } - return getSecondaryTransactionThreadActions( - currentUserLogin ?? '', - accountID, + return getSecondaryTransactionThreadActions({ + currentUserLogin: currentUserLogin ?? '', + currentUserAccountID: accountID, parentReport, - transaction, - parentReportAction, + reportTransaction: transaction, + reportAction: parentReportAction, originalTransaction, policy, - report, + transactionThreadReport: report, outstandingReportsByPolicyID, - isChatIOUReportArchived, - ); + isChatReportArchived: isChatIOUReportArchived, + }); })(); const secondaryActionsImplementation: Partial< From cd25ffa2f236e4bc9857b25680c765e99c3d70b4 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Sat, 9 May 2026 17:58:27 +0530 Subject: [PATCH 61/78] Fix nullish fallback lint in report utils --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b41ceec68aea..9277e338b976 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -11541,8 +11541,8 @@ function getOutstandingReportsForUser( typeof reportsOrReportNameValuePairs === 'boolean' ? allReportNameValuePair : ((reportsOrReportNameValuePairs as OnyxCollection) ?? allReportNameValuePair); const archivedReportsIDSet = isUsingArchivedReportsIDSet ? archivedReportsIDSetOrReports : buildArchivedReportsIDSet(reportNameValuePairs); const reports = isUsingArchivedReportsIDSet - ? ((typeof reportsOrReportNameValuePairs === 'boolean' ? undefined : reportsOrReportNameValuePairs) as OnyxCollection) || deprecatedAllReports - : (archivedReportsIDSetOrReports as OnyxCollection) || deprecatedAllReports; + ? (((typeof reportsOrReportNameValuePairs === 'boolean' ? undefined : reportsOrReportNameValuePairs) as OnyxCollection) ?? deprecatedAllReports) + : ((archivedReportsIDSetOrReports as OnyxCollection) ?? deprecatedAllReports); const shouldAllowSubmitted = typeof reportsOrReportNameValuePairs === 'boolean' ? reportsOrReportNameValuePairs : allowSubmitted; if (!reports) { From 09b1e1b7571aa4913a3833b250cabcc563b58da8 Mon Sep 17 00:00:00 2001 From: Shridhar Goel <35566748+ShridharGoel@users.noreply.github.com> Date: Mon, 11 May 2026 20:22:13 +0530 Subject: [PATCH 62/78] Fix lint --- tests/unit/hooks/useSearchSections.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/hooks/useSearchSections.test.ts b/tests/unit/hooks/useSearchSections.test.ts index 206999b62d56..8db152e9fdeb 100644 --- a/tests/unit/hooks/useSearchSections.test.ts +++ b/tests/unit/hooks/useSearchSections.test.ts @@ -45,7 +45,6 @@ jest.mock('@hooks/useActionLoadingReportIDs', () => ({ })); jest.mock('@hooks/useArchivedReportsIDSet', () => ({ - // eslint-disable-next-line @typescript-eslint/naming-convention __esModule: true, default: () => new Set(), })); From 616e966cd6f9b66d999e7d5d44c7b2e5c73c5796 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 15 May 2026 03:36:13 +0530 Subject: [PATCH 63/78] Address archived report review feedback --- src/components/Search/index.tsx | 14 +++++++------- src/hooks/useArchivedReportsIDSet.ts | 28 +++------------------------- src/libs/ReportUtils.ts | 11 ++++++++++- 3 files changed, 20 insertions(+), 33 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index d3709057df06..64c82f99388c 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -126,8 +126,8 @@ function mapTransactionItemToSelectedEntry( originalItemTransaction: OnyxEntry, currentUserLogin: string, currentUserAccountID: number, - outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined, archivedReportsIDSet: ArchivedReportsIDSet, + outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, allowNegativeAmount = true, ): [string, SelectedTransactionInfo] { const {canHoldRequest, canUnholdRequest} = canHoldUnholdReportAction(item.report, item.reportAction, item.holdReportAction, item, item.policy, currentUserAccountID); @@ -200,8 +200,8 @@ function prepareTransactionsList( selectedTransactions: SelectedTransactions, currentUserLogin: string, currentUserAccountID: number, - outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined, archivedReportsIDSet: ArchivedReportsIDSet, + outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, ) { if (selectedTransactions[item.keyForList]?.isSelected) { const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions; @@ -215,8 +215,8 @@ function prepareTransactionsList( originalItemTransaction, currentUserLogin, currentUserAccountID, - outstandingReportsByPolicyID, archivedReportsIDSet, + outstandingReportsByPolicyID, false, ); @@ -1048,8 +1048,8 @@ function Search({ selectedTransactions, email ?? '', accountID, - outstandingReportsByPolicyID, archivedReportsIdSet, + outstandingReportsByPolicyID, ); setSelectedTransactions(updatedTransactions, filteredData); updateSelectAllMatchingItemsState(updatedTransactions); @@ -1121,8 +1121,8 @@ function Search({ originalItemTransaction, email ?? '', accountID, - outstandingReportsByPolicyID, archivedReportsIdSet, + outstandingReportsByPolicyID, ); }), ), @@ -1476,8 +1476,8 @@ function Search({ originalItemTransaction, email ?? '', accountID, - outstandingReportsByPolicyID, archivedReportsIdSet, + outstandingReportsByPolicyID, ); }); }); @@ -1496,8 +1496,8 @@ function Search({ originalItemTransaction, email ?? '', accountID, - outstandingReportsByPolicyID, archivedReportsIdSet, + outstandingReportsByPolicyID, ); }), ); diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts index d03a3b7c92a6..5b3ba137bf98 100644 --- a/src/hooks/useArchivedReportsIDSet.ts +++ b/src/hooks/useArchivedReportsIDSet.ts @@ -1,37 +1,15 @@ -import type {OnyxCollection} from 'react-native-onyx'; -import {isArchivedReport} from '@libs/ReportUtils'; +import {buildArchivedReportsIDSet} from '@libs/ReportUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ReportNameValuePairs} from '@src/types/onyx'; import useOnyx from './useOnyx'; -/** - * Selector that extracts archived report IDs as a sorted array. - * Onyx performs shallow comparison on the returned array to prevent - * unnecessary re-renders without expensive deep comparison of Sets. - */ -const archivedReportIdsSelector = (reportNameValuePairs: OnyxCollection): string[] => { - if (!reportNameValuePairs) { - return []; - } - - const ids: string[] = []; - for (const [key, value] of Object.entries(reportNameValuePairs)) { - if (isArchivedReport(value)) { - ids.push(key); - } - } - return ids; -}; - /** * Hook that returns a Set of archived report IDs */ function useArchivedReportsIDSet(): ArchivedReportsIDSet { - const [archivedReportIds = CONST.EMPTY_ARRAY] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {selector: archivedReportIdsSelector}); + const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); - return new Set(archivedReportIds); + return buildArchivedReportsIDSet(reportNameValuePairs); } export default useArchivedReportsIDSet; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9277e338b976..c6f1ed175a35 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4887,6 +4887,7 @@ function canEditFieldOfMoneyRequest({ transaction: OnyxEntry; report?: OnyxInputOrEntry; policy?: OnyxEntry; + // Temporarily optional while archived report checks are migrated in smaller PRs. Remove this fallback as part of https://github.com/Expensify/App/issues/66422. archivedReportsIDSet?: ArchivedReportsIDSet; }): boolean { // A list of fields that cannot be edited by anyone, once an expense has been settled @@ -11497,7 +11498,13 @@ function hasForwardedAction(reportID: string): boolean { return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); } -function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: string | undefined, archivedReportsIDSet?: ArchivedReportsIDSet, allowSubmitted = true): boolean { +function isReportOutstanding( + iouReport: OnyxInputOrEntry, + policyID: string | undefined, + // Temporarily optional while archived report checks are migrated in smaller PRs. Remove this fallback as part of https://github.com/Expensify/App/issues/66422. + archivedReportsIDSet?: ArchivedReportsIDSet, + allowSubmitted = true, +): boolean { if ( !iouReport || isEmptyObject(iouReport) || @@ -11532,6 +11539,7 @@ function isReportOutstanding(iouReport: OnyxInputOrEntry, policyID: stri function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, + // Temporarily supports the old reports argument while archived report checks are migrated in smaller PRs. Remove this fallback as part of https://github.com/Expensify/App/issues/66422. archivedReportsIDSetOrReports?: ArchivedReportsIDSet | OnyxCollection, reportsOrReportNameValuePairs?: OnyxCollection | OnyxCollection | boolean, allowSubmitted = true, @@ -13399,6 +13407,7 @@ export { buildOptimisticReportLevelRejectCommentAction, buildOptimisticMarkedAsResolvedReportAction, buildParticipantsFromAccountIDs, + buildArchivedReportsIDSet, buildOptimisticChangeApproverReportAction, buildTransactionThread, canAccessReport, From cfdf13b3d50ae7a463dcae53e482284b4d08dacb Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 22 May 2026 04:56:58 +0530 Subject: [PATCH 64/78] Pass archived report set to transaction actions --- src/components/MoneyRequestHeaderSecondaryActions.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/MoneyRequestHeaderSecondaryActions.tsx b/src/components/MoneyRequestHeaderSecondaryActions.tsx index a4b37c411ce3..3a3f4fc6cf98 100644 --- a/src/components/MoneyRequestHeaderSecondaryActions.tsx +++ b/src/components/MoneyRequestHeaderSecondaryActions.tsx @@ -6,6 +6,7 @@ import React, {useRef, useState} from 'react'; // eslint-disable-next-line no-restricted-imports import {InteractionManager} from 'react-native'; import type {ValueOf} from 'type-fest'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConfirmModal from '@hooks/useConfirmModal'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -112,6 +113,7 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money const {wideRHPRouteKeys} = useWideRHPState(); const isNarrowButton = !useShouldDisplayButtonsInSeparateLine() || (wideRHPRouteKeys.length > 0 && !isSmallScreenWidth); const {isOffline} = useNetwork(); + const archivedReportsIDSet = useArchivedReportsIDSet(); // Per-key Onyx subscriptions const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); @@ -281,6 +283,7 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money transactionThreadReport: report, outstandingReportsByPolicyID, isChatReportArchived: isChatIOUReportArchived, + archivedReportsIDSet, }); })(); From 21d18ae40c0d126caab010ed6a3508a941c923ac Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 22 May 2026 05:09:40 +0530 Subject: [PATCH 65/78] Fix transaction action test call signature --- tests/unit/ReportSecondaryActionUtilsTest.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index da665adc06bb..4569caec0edd 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -3116,7 +3116,15 @@ describe('getSecondaryTransactionThreadActions', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${POLICY_ID}`, policy); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report); - const result = getSecondaryTransactionThreadActions(EMPLOYEE_EMAIL, EMPLOYEE_ACCOUNT_ID, report, transaction, undefined, originalTransaction, policy); + const result = getSecondaryTransactionThreadActions({ + currentUserLogin: EMPLOYEE_EMAIL, + currentUserAccountID: EMPLOYEE_ACCOUNT_ID, + parentReport: report, + reportTransaction: transaction, + reportAction: undefined, + originalTransaction, + policy, + }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.DELETE)).toBe(true); From bbbe8708d149774d08aa87d3d93da91b7bb8f221 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 22 May 2026 10:44:33 +0530 Subject: [PATCH 66/78] Remove unused onboarding beta subscriptions --- .../OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx | 1 - src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 5d95111ca950..4b63e8e1acd9 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -46,7 +46,6 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); - const [betas] = useOnyx(ONYXKEYS.BETAS); const archivedReportsIdSet = useArchivedReportsIDSet(); const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [onboardingValues] = useOnyx(ONYXKEYS.NVP_ONBOARDING); diff --git a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx index e79756c6af26..d4a91348b109 100644 --- a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx +++ b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx @@ -59,7 +59,6 @@ function BaseOnboardingWorkspaces({route, shouldUseNativeStyles}: BaseOnboarding const [session] = useOnyx(ONYXKEYS.SESSION); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); - const [betas] = useOnyx(ONYXKEYS.BETAS); const archivedReportsIdSet = useArchivedReportsIDSet(); const isValidated = isCurrentUserValidated(loginList, session?.email); From c47418724bd3482bbc2ee56472b76b93bd383081 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Wed, 27 May 2026 00:06:11 +0530 Subject: [PATCH 67/78] Clarify outstanding reports archived check --- .../sections/ReportField.tsx | 7 ++++--- src/libs/ReportUtils.ts | 18 ++++++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx index 17136e970ffb..edb0187e4b1a 100644 --- a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx +++ b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useOutstandingReports from '@hooks/useOutstandingReports'; @@ -54,7 +55,7 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a const reportAttributes = useReportAttributes(); const policyID = selectedParticipants?.at(0)?.policyID; - const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [outstandingReportsForPolicy] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID, {selector: createOutstandingReportsForPolicySelector(policyID)}, [policyID]); // Per-key report subscriptions instead of full COLLECTION.REPORT @@ -70,11 +71,11 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a * We need to check if the transaction report exists first in order to prevent the outstanding reports from being used. * Also we need to check if transaction report exists in outstanding reports in order to show a correct report name. */ - const shouldUseTransactionReport = (!!transactionReportEntry && isReportOutstanding(transactionReportEntry, policyID, undefined, false)) || isUnreported; + const shouldUseTransactionReport = (!!transactionReportEntry && isReportOutstanding(transactionReportEntry, policyID, archivedReportsIDSet, false)) || isUnreported; const ownerAccountID = selectedParticipants?.at(0)?.ownerAccountID; - const availableOutstandingReports = getOutstandingReportsForUser(policyID, ownerAccountID, outstandingReportsForPolicy ?? {}, reportNameValuePairs, false).sort((a, b) => + const availableOutstandingReports = getOutstandingReportsForUser(policyID, ownerAccountID, archivedReportsIDSet, outstandingReportsForPolicy ?? {}, false).sort((a, b) => localeCompare(a?.reportName?.toLowerCase() ?? '', b?.reportName?.toLowerCase() ?? ''), ); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a2b250b6ae44..3d92a37b387a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -11525,25 +11525,19 @@ function isReportOutstanding( * Get outstanding expense reports for a given policy ID * @param policyID - The policy ID to filter reports by * @param reportOwnerAccountID - The accountID of the report owner + * @param archivedReportsIDSet - Set of archived report IDs * @param reports - Collection of reports to filter + * @param allowSubmitted - Whether submitted reports are allowed * @returns Array of outstanding expense reports */ function getOutstandingReportsForUser( policyID: string | undefined, reportOwnerAccountID: number | undefined, - // Temporarily supports the old reports argument while archived report checks are migrated in smaller PRs. Remove this fallback as part of https://github.com/Expensify/App/issues/66422. - archivedReportsIDSetOrReports?: ArchivedReportsIDSet | OnyxCollection, - reportsOrReportNameValuePairs?: OnyxCollection | OnyxCollection | boolean, + archivedReportsIDSet?: ArchivedReportsIDSet, + reports: OnyxCollection = deprecatedAllReports, allowSubmitted = true, ): Array> { - const isUsingArchivedReportsIDSet = archivedReportsIDSetOrReports instanceof Set; - const reportNameValuePairs = - typeof reportsOrReportNameValuePairs === 'boolean' ? allReportNameValuePair : ((reportsOrReportNameValuePairs as OnyxCollection) ?? allReportNameValuePair); - const archivedReportsIDSet = isUsingArchivedReportsIDSet ? archivedReportsIDSetOrReports : buildArchivedReportsIDSet(reportNameValuePairs); - const reports = isUsingArchivedReportsIDSet - ? (((typeof reportsOrReportNameValuePairs === 'boolean' ? undefined : reportsOrReportNameValuePairs) as OnyxCollection) ?? deprecatedAllReports) - : ((archivedReportsIDSetOrReports as OnyxCollection) ?? deprecatedAllReports); - const shouldAllowSubmitted = typeof reportsOrReportNameValuePairs === 'boolean' ? reportsOrReportNameValuePairs : allowSubmitted; + const archivedReportIDs = archivedReportsIDSet ?? buildArchivedReportsIDSet(allReportNameValuePair); if (!reports) { return []; @@ -11551,7 +11545,7 @@ function getOutstandingReportsForUser( return Object.values(reports).filter( (report) => report?.pendingFields?.preview !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - isReportOutstanding(report, policyID, archivedReportsIDSet, shouldAllowSubmitted) && + isReportOutstanding(report, policyID, archivedReportIDs, allowSubmitted) && report?.ownerAccountID === reportOwnerAccountID, ); } From 6252bdc588e48839df4557aeb666f83552206fe9 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Wed, 27 May 2026 00:36:21 +0530 Subject: [PATCH 68/78] Fix archived reports hook import --- src/components/MigratedUserWelcomeModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index 7a1d82a9aaa2..a2f605011e43 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -1,6 +1,6 @@ import React, {useMemo} from 'react'; import {View} from 'react-native'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useIsPaidPolicyAdmin from '@hooks/useIsPaidPolicyAdmin'; import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -31,7 +31,7 @@ function MigratedUserWelcomeModal() { const StyleUtils = useStyleUtils(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const {isBetaEnabled} = usePermissions(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIdSet = useArchivedReportsIDSet(); const isReduceMotionEnabled = Accessibility.useReducedMotion(); const illustrations = useMemoizedLazyIllustrations(['ChatBubbles', 'ConciergeBot', 'PlanetWithMobileApp', 'MagnifyingGlassReceipt']); const isCurrentUserPolicyAdmin = useIsPaidPolicyAdmin(); From e4695b1ad676e8cd5d846da4dbb1bdfdfc8df2c1 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Wed, 27 May 2026 18:02:23 +0530 Subject: [PATCH 69/78] Address archived reports review feedback --- src/components/MigratedUserWelcomeModal.tsx | 4 +- src/components/Search/index.tsx | 39 +++++++------------ src/hooks/useAutoCreateTrackWorkspace.ts | 6 +-- src/hooks/useDeleteTransactions.ts | 6 +-- src/hooks/useSearchSections.ts | 4 +- .../MarkAllMessagesAsReadHandler.tsx | 10 ++--- .../Navigators/ReportsSplitNavigator.tsx | 4 +- src/libs/ReportSecondaryActionUtils.ts | 3 +- src/libs/ReportUtils.ts | 25 +++++------- .../actions/Report/MarkAllMessageAsRead.tsx | 4 +- src/libs/navigateAfterOnboarding.ts | 12 +++--- .../BaseOnboardingInterestedFeatures.tsx | 6 +-- .../BaseOnboardingPersonalDetails.tsx | 6 +-- .../BaseOnboardingWorkspaceInvite.tsx | 4 +- .../BaseOnboardingWorkspaceOptional.tsx | 6 +-- .../BaseOnboardingWorkspaces.tsx | 4 +- src/pages/inbox/ReportRouteParamHandler.tsx | 4 +- .../TaskShareDestinationSelectorModal.tsx | 6 +-- tests/unit/ReportSecondaryActionUtilsTest.ts | 13 +++++++ ...est.ts => useArchivedReportsIDSet.test.ts} | 0 tests/unit/navigateAfterOnboardingTest.ts | 8 ++-- 21 files changed, 84 insertions(+), 90 deletions(-) rename tests/unit/hooks/{useArchivedReportsIdSet.test.ts => useArchivedReportsIDSet.test.ts} (100%) diff --git a/src/components/MigratedUserWelcomeModal.tsx b/src/components/MigratedUserWelcomeModal.tsx index a2f605011e43..2da92685ab27 100644 --- a/src/components/MigratedUserWelcomeModal.tsx +++ b/src/components/MigratedUserWelcomeModal.tsx @@ -31,7 +31,7 @@ function MigratedUserWelcomeModal() { const StyleUtils = useStyleUtils(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const {isBetaEnabled} = usePermissions(); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const isReduceMotionEnabled = Accessibility.useReducedMotion(); const illustrations = useMemoizedLazyIllustrations(['ChatBubbles', 'ConciergeBot', 'PlanetWithMobileApp', 'MagnifyingGlassReceipt']); const isCurrentUserPolicyAdmin = useIsPaidPolicyAdmin(); @@ -66,7 +66,7 @@ function MigratedUserWelcomeModal() { const onClose = () => { Log.hmmm('[MigratedUserWelcomeModal] onClose called, dismissing product training'); dismissProductTraining(CONST.MIGRATED_USER_WELCOME_MODAL); - const lastAccessedReportID = findLastAccessedReport(!isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), shouldOpenOnAdminRoom(), undefined, archivedReportsIdSet)?.reportID; + const lastAccessedReportID = findLastAccessedReport(!isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), shouldOpenOnAdminRoom(), undefined, archivedReportsIDSet)?.reportID; if (lastAccessedReportID) { Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID)); } diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 994a982f59d9..b5a313068ddc 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -288,7 +288,7 @@ function Search({ const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, { selector: selectFilteredReportActions, @@ -522,7 +522,7 @@ function Search({ groupBy: validGroupBy, reportActions: exportReportActions, currentSearch: currentSearchKey, - archivedReportsIDList: archivedReportsIdSet, + archivedReportsIDList: archivedReportsIDSet, queryJSON, isActionLoadingSet, cardFeeds, @@ -554,7 +554,7 @@ function Search({ searchDataWithOptimisticTransaction, searchResults, type, - archivedReportsIdSet, + archivedReportsIDSet, translate, formatPhoneNumber, accountID, @@ -807,7 +807,7 @@ function Search({ transaction: transactionItem, report: transactionItem.report, policy: transactionItem.policy, - archivedReportsIDSet: archivedReportsIdSet, + archivedReportsIDSet, }), isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID]?.isSelected || isExpenseReportType, @@ -864,7 +864,7 @@ function Search({ transaction: transactionItem, report: transactionItem.report, policy: transactionItem.policy, - archivedReportsIDSet: archivedReportsIdSet, + archivedReportsIDSet, }), isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID].isSelected, @@ -1019,7 +1019,7 @@ function Search({ selectedTransactions, email ?? '', accountID, - archivedReportsIdSet, + archivedReportsIDSet, outstandingReportsByPolicyID, ); setSelectedTransactions(updatedTransactions); @@ -1081,18 +1081,15 @@ function Search({ currentTransactions .filter((t) => !isTransactionPendingDelete(t)) .map((transactionItem) => { - const itemTransaction = (searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] ?? - transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`]) as OnyxEntry; - const originalItemTransaction = - searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`] ?? - transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; + const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; + const originalItemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; return mapTransactionItemToSelectedEntry( transactionItem, itemTransaction, originalItemTransaction, email ?? '', accountID, - archivedReportsIdSet, + archivedReportsIDSet, outstandingReportsByPolicyID, ); }), @@ -1101,17 +1098,7 @@ function Search({ setSelectedTransactions(updatedTransactions); updateSelectAllMatchingItemsState(updatedTransactions); }, - [ - selectedTransactions, - setSelectedTransactions, - updateSelectAllMatchingItemsState, - transactions, - email, - accountID, - outstandingReportsByPolicyID, - searchResults?.data, - archivedReportsIdSet, - ], + [selectedTransactions, setSelectedTransactions, updateSelectAllMatchingItemsState, transactions, email, accountID, outstandingReportsByPolicyID, archivedReportsIDSet], ); const onSelectRowInMobileSelectionMode = (item: SearchListItem) => { @@ -1420,7 +1407,7 @@ function Search({ originalItemTransaction, email ?? '', accountID, - archivedReportsIdSet, + archivedReportsIDSet, outstandingReportsByPolicyID, ); }); @@ -1440,7 +1427,7 @@ function Search({ originalItemTransaction, email ?? '', accountID, - archivedReportsIdSet, + archivedReportsIDSet, outstandingReportsByPolicyID, ); }), @@ -1460,7 +1447,7 @@ function Search({ accountID, outstandingReportsByPolicyID, searchResults?.data, - archivedReportsIdSet, + archivedReportsIDSet, ]); const onLayoutBase = useCallback(() => { diff --git a/src/hooks/useAutoCreateTrackWorkspace.ts b/src/hooks/useAutoCreateTrackWorkspace.ts index d6e2f9f4472d..d8f706220a37 100644 --- a/src/hooks/useAutoCreateTrackWorkspace.ts +++ b/src/hooks/useAutoCreateTrackWorkspace.ts @@ -50,7 +50,7 @@ function useAutoCreateTrackWorkspace() { const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [onboardingValues] = useOnyx(ONYXKEYS.NVP_ONBOARDING); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const {isBetaEnabled} = usePermissions(); const mergedAccountConciergeReportID = !onboardingValues?.shouldRedirectToClassicAfterMerge && onboardingValues?.shouldValidate ? conciergeChatReportID : undefined; @@ -120,7 +120,7 @@ function useAutoCreateTrackWorkspace() { shouldUseNarrowLayout, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeChatReportID, - archivedReportsIdSet, + archivedReportsIDSet, newPolicyID, mergedAccountConciergeReportID, false, @@ -148,7 +148,7 @@ function useAutoCreateTrackWorkspace() { shouldUseNarrowLayout, isBetaEnabled, conciergeChatReportID, - archivedReportsIdSet, + archivedReportsIDSet, mergedAccountConciergeReportID, ], ); diff --git a/src/hooks/useDeleteTransactions.ts b/src/hooks/useDeleteTransactions.ts index 63466a6d67a1..96e5ff24e830 100644 --- a/src/hooks/useDeleteTransactions.ts +++ b/src/hooks/useDeleteTransactions.ts @@ -77,7 +77,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac const [allPolicyTags] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS, {selector: passthroughPolicyTagListSelector}); const [betas] = useOnyx(ONYXKEYS.BETAS); const {isBetaEnabled} = usePermissions(); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const {isOffline} = useNetwork(); @@ -267,7 +267,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`]; const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${action?.childReportID}`]; const chatIOUReportID = chatReport?.reportID; - const isChatIOUReportArchived = isReportArchivedByID(archivedReportsIdSet, chatIOUReportID); + const isChatIOUReportArchived = isReportArchivedByID(archivedReportsIDSet, chatIOUReportID); deleteMoneyRequest({ transactionID, reportAction: action, @@ -301,7 +301,7 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac allReports, allSnapshots, allTransactions, - archivedReportsIdSet, + archivedReportsIDSet, currentUserPersonalDetails, currentSearchQueryJSON, currentSearchResults?.data, diff --git a/src/hooks/useSearchSections.ts b/src/hooks/useSearchSections.ts index edbb1fb2d8f4..ef673d132ddb 100644 --- a/src/hooks/useSearchSections.ts +++ b/src/hooks/useSearchSections.ts @@ -35,7 +35,7 @@ function useSearchSections(): UseSearchSectionsResult { const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [allReportMetadata] = useOnyx(ONYXKEYS.COLLECTION.REPORT_METADATA); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const reportAttributesDerivedValue = useReportAttributes(); @@ -59,7 +59,7 @@ function useSearchSections(): UseSearchSectionsResult { groupBy, reportActions: exportReportActions, currentSearch: searchKey, - archivedReportsIDList: archivedReportsIdSet, + archivedReportsIDList: archivedReportsIDSet, isActionLoadingSet, cardFeeds, cardList: nonPersonalAndWorkspaceCards, diff --git a/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx b/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx index 63a6c3581628..3f3ae0675b82 100644 --- a/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx +++ b/src/libs/Navigation/AppNavigator/KeyboardShortcutsHandler/MarkAllMessagesAsReadHandler.tsx @@ -5,18 +5,18 @@ import KeyboardShortcut from '@libs/KeyboardShortcut'; import CONST from '@src/CONST'; function MarkAllMessagesAsReadHandler() { - const archivedReportsIdSet = useArchivedReportsIDSet(); - const archivedReportsIdSetRef = useRef(archivedReportsIdSet); + const archivedReportsIDSet = useArchivedReportsIDSet(); + const archivedReportsIDSetRef = useRef(archivedReportsIDSet); useEffect(() => { - archivedReportsIdSetRef.current = archivedReportsIdSet; - }, [archivedReportsIdSet]); + archivedReportsIDSetRef.current = archivedReportsIDSet; + }, [archivedReportsIDSet]); useEffect(() => { const shortcutConfig = CONST.KEYBOARD_SHORTCUTS.MARK_ALL_MESSAGES_AS_READ; const unsubscribe = KeyboardShortcut.subscribe( shortcutConfig.shortcutKey, - () => markAllMessagesAsRead(archivedReportsIdSetRef.current), + () => markAllMessagesAsRead(archivedReportsIDSetRef.current), shortcutConfig.descriptionKey, shortcutConfig.modifiers, true, diff --git a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx index 2bd8f429e6f4..5e07e6d54362 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/ReportsSplitNavigator.tsx @@ -26,7 +26,7 @@ const Split = createSplitNavigator(); function ReportsSplitNavigator({route}: PlatformStackScreenProps) { const {isBetaEnabled} = usePermissions(); const splitNavigatorScreenOptions = useSplitNavigatorScreenOptions(); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const isOpenOnAdminRoom = shouldOpenOnAdminRoom(); const [initialReportID] = useState(() => { @@ -50,7 +50,7 @@ function ReportsSplitNavigator({route}: PlatformStackScreenProps> { const options: Array> = []; @@ -1126,7 +1127,7 @@ function getSecondaryTransactionThreadActions({ transactionThreadReport?: OnyxEntry; outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; isChatReportArchived?: boolean; - archivedReportsIDSet?: ArchivedReportsIDSet; + archivedReportsIDSet: ArchivedReportsIDSet; }): Array> { const options: Array> = []; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6dd084f6620a..8399990ca966 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2251,7 +2251,7 @@ function getMostRecentlyVisitedReport(reports: Array>, lastVis * This function is used to find the last accessed report and we don't need to subscribe the data in the UI. * So please use `Onyx.connectWithoutView()` to get the necessary data when we remove the `Onyx.connect()` */ -function findLastAccessedReport(ignoreDomainRooms: boolean, openOnAdminRoom = false, excludeReportID?: string, archivedReportsIdSet?: ArchivedReportsIDSet): OnyxEntry { +function findLastAccessedReport(ignoreDomainRooms: boolean, openOnAdminRoom = false, excludeReportID?: string, archivedReportsIDSet?: ArchivedReportsIDSet): OnyxEntry { let reportsValues = Object.values(deprecatedAllReports ?? {}); if (openOnAdminRoom) { @@ -2288,7 +2288,7 @@ function findLastAccessedReport(ignoreDomainRooms: boolean, openOnAdminRoom = fa reportsValues = reportsValues.filter((report) => { const reportNameValuePairsKey = `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`; - const isArchived = archivedReportsIdSet ? archivedReportsIdSet.has(reportNameValuePairsKey) : isArchivedReport(allReportNameValuePair?.[reportNameValuePairsKey]); + const isArchived = archivedReportsIDSet ? archivedReportsIDSet.has(reportNameValuePairsKey) : isArchivedReport(allReportNameValuePair?.[reportNameValuePairsKey]); return !isSystemChat(report) && !isArchived; }) ?? []; @@ -4972,7 +4972,7 @@ function canEditFieldOfMoneyRequest({ // Unreported transaction from OldDot can have the reportID as an empty string const isUnreportedExpense = !transaction?.reportID || transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; - const archivedReportsIDSetForOutstandingReports = archivedReportsIDSet ?? buildArchivedReportsIDSet(allReportNameValuePair); + const archivedReportIDs = archivedReportsIDSet ?? buildArchivedReportsIDSet(allReportNameValuePair); if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE) { // The distance rate can be modified only on the distance expense reports @@ -4992,7 +4992,7 @@ function canEditFieldOfMoneyRequest({ return true; } - if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportsIDSet)) { + if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportIDs)) { return false; } @@ -5007,7 +5007,7 @@ function canEditFieldOfMoneyRequest({ getOutstandingReportsForUser( moneyRequestReport?.policyID, moneyRequestReport?.ownerAccountID, - archivedReportsIDSetForOutstandingReports, + archivedReportIDs, outstandingReportsByPolicyID?.[moneyRequestReport?.policyID ?? CONST.DEFAULT_NUMBER_ID] ?? {}, ).length > 0 ); @@ -5021,19 +5021,14 @@ function canEditFieldOfMoneyRequest({ } // Check the cheaper condition first - if ((isOwner || isAdmin || isManager) && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportsIDSet)) { + if ((isOwner || isAdmin || isManager) && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportIDs)) { return true; } // Check if there are multiple outstanding reports across policies let outstandingReportsCount = 0; for (const currentPolicy of policiesArray) { - const reports = getOutstandingReportsForUser( - currentPolicy.id, - moneyRequestReport?.ownerAccountID, - archivedReportsIDSetForOutstandingReports, - outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}, - ); + const reports = getOutstandingReportsForUser(currentPolicy.id, moneyRequestReport?.ownerAccountID, archivedReportIDs, outstandingReportsByPolicyID?.[currentPolicy?.id] ?? {}); outstandingReportsCount += reports.length; // Short-circuit once we find more than 1 @@ -11512,8 +11507,8 @@ function isReportOutstanding( ) { return false; } - const reportNameValuePair = archivedReportsIDSet ? undefined : allReportNameValuePair?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport.reportID}`]; - if ((archivedReportsIDSet && isReportArchivedByID(archivedReportsIDSet, iouReport.reportID)) || isArchivedReport(reportNameValuePair)) { + const reportNameValuePair = allReportNameValuePair?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport.reportID}`]; + if (archivedReportsIDSet ? isReportArchivedByID(archivedReportsIDSet, iouReport.reportID) : isArchivedReport(reportNameValuePair)) { return false; } const currentRoute = navigationRef.getCurrentRoute(); @@ -11529,9 +11524,7 @@ function isReportOutstanding( * Get outstanding expense reports for a given policy ID * @param policyID - The policy ID to filter reports by * @param reportOwnerAccountID - The accountID of the report owner - * @param archivedReportsIDSet - Set of archived report IDs * @param reports - Collection of reports to filter - * @param allowSubmitted - Whether submitted reports are allowed * @returns Array of outstanding expense reports */ function getOutstandingReportsForUser( diff --git a/src/libs/actions/Report/MarkAllMessageAsRead.tsx b/src/libs/actions/Report/MarkAllMessageAsRead.tsx index 0c31360c9453..b7d0a014aa9a 100644 --- a/src/libs/actions/Report/MarkAllMessageAsRead.tsx +++ b/src/libs/actions/Report/MarkAllMessageAsRead.tsx @@ -27,7 +27,7 @@ Onyx.connectWithoutView({ callback: (value) => (allReports = value), }); -function markAllMessagesAsRead(archivedReportsIdSet: ArchivedReportsIDSet) { +function markAllMessagesAsRead(archivedReportsIDSet: ArchivedReportsIDSet) { if (isAnonymousUser()) { return; } @@ -48,7 +48,7 @@ function markAllMessagesAsRead(archivedReportsIdSet: ArchivedReportsIDSet) { const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`]; const oneTransactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]); const oneTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneTransactionThreadReportID}`]; - const isArchivedReport = archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`); + const isArchivedReport = archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`); if (!isUnread(report, oneTransactionThreadReport, isArchivedReport)) { continue; } diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts index 8a5443e93d12..4c76ae21790c 100644 --- a/src/libs/navigateAfterOnboarding.ts +++ b/src/libs/navigateAfterOnboarding.ts @@ -31,7 +31,7 @@ function getReportIDAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIdSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -46,7 +46,7 @@ function getReportIDAfterOnboarding( return undefined; } - const lastAccessedReport = findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom() && !shouldPreventOpenAdminRoom, undefined, archivedReportsIdSet); + const lastAccessedReport = findLastAccessedReport(!canUseDefaultRooms, shouldOpenOnAdminRoom() && !shouldPreventOpenAdminRoom, undefined, archivedReportsIDSet); const lastAccessedReportID = lastAccessedReport?.reportID; // When the user goes through the onboarding flow, a workspace can be created if the user selects specific options. The user should be taken to the #admins room for that workspace because it is the most natural place for them to start their experience in the app. @@ -62,7 +62,7 @@ function navigateAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIdSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -89,7 +89,7 @@ function navigateAfterOnboarding( isSmallScreenWidth, canUseDefaultRooms, conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, @@ -106,7 +106,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIdSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -118,7 +118,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth, canUseDefaultRooms, conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, onboardingAdminsChatReportID, shouldPreventOpenAdminRoom, diff --git a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx index e0029c475651..2dbd5c0cf89e 100644 --- a/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx +++ b/src/pages/OnboardingInterestedFeatures/BaseOnboardingInterestedFeatures.tsx @@ -66,7 +66,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin const {isBetaEnabled} = usePermissions(); const [session] = useOnyx(ONYXKEYS.SESSION); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const activePolicy = useActivePolicy(); const hasActiveAdminPolicies = useHasActiveAdminPolicies(); const lastWorkspaceNumber = useLastWorkspaceNumber(); @@ -253,7 +253,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin isSmallScreenWidth, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, policyID, adminsChatReportID, // Onboarding tasks would show in Concierge instead of admins room for testing accounts, we should open where onboarding tasks are located @@ -268,7 +268,7 @@ function BaseOnboardingInterestedFeatures({shouldUseNativeStyles}: BaseOnboardin }, [ isBetaEnabled, isSmallScreenWidth, - archivedReportsIdSet, + archivedReportsIDSet, onboardingAdminsChatReportID, onboardingCompanySize, onboardingMessages, diff --git a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx index 4b63e8e1acd9..bc1fcfd6e9c2 100644 --- a/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx +++ b/src/pages/OnboardingPersonalDetails/BaseOnboardingPersonalDetails.tsx @@ -46,7 +46,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID); const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [onboardingValues] = useOnyx(ONYXKEYS.NVP_ONBOARDING); const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); @@ -102,7 +102,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat isSmallScreenWidth, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeChatReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, mergedAccountConciergeReportID, false, @@ -114,7 +114,7 @@ function BaseOnboardingPersonalDetails({currentUserPersonalDetails, shouldUseNat onboardingMessages, onboardingPolicyID, isBetaEnabled, - archivedReportsIdSet, + archivedReportsIDSet, isSmallScreenWidth, mergedAccountConciergeReportID, conciergeChatReportID, diff --git a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx index 98703c35017f..08e1332f44f4 100644 --- a/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx +++ b/src/pages/OnboardingWorkspaceInvite/BaseOnboardingWorkspaceInvite.tsx @@ -65,7 +65,7 @@ function BaseOnboardingWorkspaceInvite({shouldUseNativeStyles}: BaseOnboardingWo const session = useSession(); const {isBetaEnabled} = usePermissions(); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const filteredReportActions = useAllPolicyExpenseChatReportActions(); const ineligibleInvitees = getIneligibleInvitees(policy?.employeeList); @@ -144,7 +144,7 @@ function BaseOnboardingWorkspaceInvite({shouldUseNativeStyles}: BaseOnboardingWo isSmallScreenWidth, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, onboardingPolicyID, onboardingAdminsChatReportID, // Onboarding tasks would show in Concierge instead of admins room for testing accounts, we should open where onboarding tasks are located diff --git a/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx b/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx index ce436a55c34c..d363dec03fa0 100644 --- a/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx +++ b/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx @@ -55,7 +55,7 @@ function BaseOnboardingWorkspaceOptional({shouldUseNativeStyles}: BaseOnboarding const [onboardingPolicyID] = useOnyx(ONYXKEYS.ONBOARDING_POLICY_ID); const [onboardingAdminsChatReportID] = useOnyx(ONYXKEYS.ONBOARDING_ADMINS_CHAT_REPORT_ID); const [conciergeChatReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const {onboardingMessages} = useOnboardingMessages(); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [session] = useOnyx(ONYXKEYS.SESSION); @@ -128,7 +128,7 @@ function BaseOnboardingWorkspaceOptional({shouldUseNativeStyles}: BaseOnboarding isSmallScreenWidth, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeChatReportID, - archivedReportsIdSet, + archivedReportsIDSet, resolvedPolicyID, mergedAccountConciergeReportID, false, @@ -141,7 +141,7 @@ function BaseOnboardingWorkspaceOptional({shouldUseNativeStyles}: BaseOnboarding onboardingAdminsChatReportID, onboardingMessages, onboardingPolicyID, - archivedReportsIdSet, + archivedReportsIDSet, isSmallScreenWidth, isBetaEnabled, mergedAccountConciergeReportID, diff --git a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx index d4a91348b109..b4c56755288c 100644 --- a/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx +++ b/src/pages/OnboardingWorkspaces/BaseOnboardingWorkspaces.tsx @@ -59,7 +59,7 @@ function BaseOnboardingWorkspaces({route, shouldUseNativeStyles}: BaseOnboarding const [session] = useOnyx(ONYXKEYS.SESSION); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const isValidated = isCurrentUserValidated(loginList, session?.email); @@ -107,7 +107,7 @@ function BaseOnboardingWorkspaces({route, shouldUseNativeStyles}: BaseOnboarding isSmallScreenWidth, isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), conciergeReportID, - archivedReportsIdSet, + archivedReportsIDSet, policy.automaticJoiningEnabled ? policy.policyID : undefined, undefined, false, diff --git a/src/pages/inbox/ReportRouteParamHandler.tsx b/src/pages/inbox/ReportRouteParamHandler.tsx index 578a251d7621..cd6531d3834e 100644 --- a/src/pages/inbox/ReportRouteParamHandler.tsx +++ b/src/pages/inbox/ReportRouteParamHandler.tsx @@ -22,7 +22,7 @@ function ReportRouteParamHandler() { const route = useRoute(); const navigation = useNavigation(); const {isBetaEnabled} = usePermissions(); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); useFocusEffect(() => { // Don't update if there is a reportID in the params already @@ -39,7 +39,7 @@ function ReportRouteParamHandler() { !isBetaEnabled(CONST.BETAS.DEFAULT_ROOMS), 'openOnAdminRoom' in route.params && !!route.params.openOnAdminRoom, undefined, - archivedReportsIdSet, + archivedReportsIDSet, )?.reportID; // It's possible that reports aren't fully loaded yet diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index c91b6d45efe1..32b0c7064d80 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -64,15 +64,15 @@ function TaskShareDestinationSelectorModal() { onSingleSelect: selectReportHandler, }); - const archivedReportsIdSet = useArchivedReportsIDSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const filteredOptions = useMemo(() => { - const filteredReports = reportFilter(availableOptions.recentReports as Array>, archivedReportsIdSet); + const filteredReports = reportFilter(availableOptions.recentReports as Array>, archivedReportsIDSet); return { ...availableOptions, recentReports: filteredReports ?? [], }; - }, [availableOptions, archivedReportsIdSet]); + }, [availableOptions, archivedReportsIDSet]); const data = useMemo( () => diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index b9e55c7ff725..dd3cc70c0ae0 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -2132,6 +2132,7 @@ describe('getSecondaryAction', () => { reportAction: undefined, originalTransaction: {} as Transaction, policy, + archivedReportsIDSet: new Set(), }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.DELETE)).toBe(true); @@ -3195,6 +3196,7 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: undefined, originalTransaction: {} as Transaction, policy, + archivedReportsIDSet: new Set(), }), ).toEqual(result); }); @@ -3224,6 +3226,7 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, + archivedReportsIDSet: new Set(), }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.HOLD)).toBe(true); }); @@ -3250,6 +3253,7 @@ describe('getSecondaryTransactionThreadActions', () => { originalTransaction: {} as Transaction, policy, transactionThreadReport, + archivedReportsIDSet: new Set(), }); expect(result).toContain(CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD); @@ -3264,6 +3268,7 @@ describe('getSecondaryTransactionThreadActions', () => { originalTransaction: {} as Transaction, policy, transactionThreadReport, + archivedReportsIDSet: new Set(), }); expect(result2).not.toContain(CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD); }); @@ -3289,6 +3294,7 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: undefined, originalTransaction: {} as Transaction, policy, + archivedReportsIDSet: new Set(), }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.DELETE)).toBe(true); }); @@ -3347,6 +3353,7 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: undefined, originalTransaction, policy, + archivedReportsIDSet: new Set(), }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); @@ -3445,6 +3452,7 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, + archivedReportsIDSet: new Set(), }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(true); }); @@ -3489,6 +3497,7 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, + archivedReportsIDSet: new Set(), }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3534,6 +3543,7 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, + archivedReportsIDSet: new Set(), }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3583,6 +3593,7 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, + archivedReportsIDSet: new Set(), }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); }); @@ -3611,6 +3622,7 @@ describe('getSecondaryTransactionThreadActions', () => { originalTransaction: {} as Transaction, policy, isChatReportArchived: false, + archivedReportsIDSet: new Set(), }); expect(result).toContain(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); }); @@ -3639,6 +3651,7 @@ describe('getSecondaryTransactionThreadActions', () => { originalTransaction: {} as Transaction, policy, isChatReportArchived: false, + archivedReportsIDSet: new Set(), }); expect(result).not.toContain(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); }); diff --git a/tests/unit/hooks/useArchivedReportsIdSet.test.ts b/tests/unit/hooks/useArchivedReportsIDSet.test.ts similarity index 100% rename from tests/unit/hooks/useArchivedReportsIdSet.test.ts rename to tests/unit/hooks/useArchivedReportsIDSet.test.ts diff --git a/tests/unit/navigateAfterOnboardingTest.ts b/tests/unit/navigateAfterOnboardingTest.ts index 004c7a8644a3..044ce2b49f22 100644 --- a/tests/unit/navigateAfterOnboardingTest.ts +++ b/tests/unit/navigateAfterOnboardingTest.ts @@ -121,14 +121,14 @@ describe('navigateAfterOnboarding', () => { expect(navigate).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(REPORT_ID)); }); - it('should pass archivedReportsIdSet when looking up last accessed report', () => { - const archivedReportsIdSet = new Set(['report_1']); + it('should pass archivedReportsIDSet when looking up last accessed report', () => { + const archivedReportsIDSet = new Set(['report_1']); mockFindLastAccessedReport.mockReturnValue(undefined); mockShouldOpenOnAdminRoom.mockReturnValue(false); - navigateAfterOnboarding(true, true, '', archivedReportsIdSet, ONBOARDING_POLICY_ID, ONBOARDING_ADMINS_CHAT_REPORT_ID); + navigateAfterOnboarding(true, true, '', archivedReportsIDSet, ONBOARDING_POLICY_ID, ONBOARDING_ADMINS_CHAT_REPORT_ID); - expect(mockFindLastAccessedReport).toHaveBeenCalledWith(false, false, undefined, archivedReportsIdSet); + expect(mockFindLastAccessedReport).toHaveBeenCalledWith(false, false, undefined, archivedReportsIDSet); }); it('should navigate to Concierge room if user uses a test email', () => { From 35524bf13f1edeee355bc93b985b53f8966f3c18 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Thu, 28 May 2026 18:58:10 +0530 Subject: [PATCH 70/78] Avoid unnecessary archived report set rebuilds --- src/libs/ReportUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9a5f82ad97c8..5b5ee72d938d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4986,7 +4986,6 @@ function canEditFieldOfMoneyRequest({ // Unreported transaction from OldDot can have the reportID as an empty string const isUnreportedExpense = !transaction?.reportID || transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; - const archivedReportIDs = archivedReportsIDSet ?? buildArchivedReportsIDSet(allReportNameValuePair); if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE) { // The distance rate can be modified only on the distance expense reports @@ -5006,6 +5005,8 @@ function canEditFieldOfMoneyRequest({ return true; } + const archivedReportIDs = archivedReportsIDSet ?? buildArchivedReportsIDSet(allReportNameValuePair); + if (!isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID, archivedReportIDs)) { return false; } From 9df935857393ff88549c124fc998b8a91a629f33 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:12:15 +0530 Subject: [PATCH 71/78] Fix archived reports loading fallback --- src/components/Search/index.tsx | 4 ++-- src/hooks/useArchivedReportsIDSet.ts | 11 ++++++++--- src/libs/ReportSecondaryActionUtils.ts | 2 +- src/libs/ReportUtils.ts | 5 +++-- src/libs/actions/Report/MarkAllMessageAsRead.tsx | 4 ++-- src/libs/navigateAfterOnboarding.ts | 6 +++--- src/pages/tasks/TaskShareDestinationSelectorModal.tsx | 4 ++-- 7 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index ed7aa4ccd5ca..165a5bc8c7f0 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -125,7 +125,7 @@ function mapTransactionItemToSelectedEntry( originalItemTransaction: OnyxEntry, currentUserLogin: string, currentUserAccountID: number, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet | undefined, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, allowNegativeAmount = true, parentReport?: OnyxEntry, @@ -203,7 +203,7 @@ function prepareTransactionsList( selectedTransactions: SelectedTransactions, currentUserLogin: string, currentUserAccountID: number, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet | undefined, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, parentReport?: OnyxEntry, selfDMReport?: OnyxEntry, diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts index 5b3ba137bf98..09764211a7f5 100644 --- a/src/hooks/useArchivedReportsIDSet.ts +++ b/src/hooks/useArchivedReportsIDSet.ts @@ -1,13 +1,18 @@ import {buildArchivedReportsIDSet} from '@libs/ReportUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import useOnyx from './useOnyx'; /** - * Hook that returns a Set of archived report IDs + * Hook that returns a Set of archived report IDs once report name-value pairs are loaded */ -function useArchivedReportsIDSet(): ArchivedReportsIDSet { - const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); +function useArchivedReportsIDSet(): ArchivedReportsIDSet | undefined { + const [reportNameValuePairs, reportNameValuePairsResult] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); + + if (isLoadingOnyxValue(reportNameValuePairsResult)) { + return undefined; + } return buildArchivedReportsIDSet(reportNameValuePairs); } diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index e5e3aeab5363..01cf705ba0b6 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -1137,7 +1137,7 @@ function getSecondaryTransactionThreadActions({ outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; isChatReportArchived?: boolean; grandParentReport?: OnyxEntry; - archivedReportsIDSet: ArchivedReportsIDSet; + archivedReportsIDSet?: ArchivedReportsIDSet; }): Array> { const options: Array> = []; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5b5ee72d938d..1e7b799b1ba1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2357,8 +2357,8 @@ function isArchivedReport(reportNameValuePairs?: OnyxInputOrEntry = deprecatedAllReports, allowSubmitted = true, diff --git a/src/libs/actions/Report/MarkAllMessageAsRead.tsx b/src/libs/actions/Report/MarkAllMessageAsRead.tsx index b7d0a014aa9a..0ca947943db2 100644 --- a/src/libs/actions/Report/MarkAllMessageAsRead.tsx +++ b/src/libs/actions/Report/MarkAllMessageAsRead.tsx @@ -27,7 +27,7 @@ Onyx.connectWithoutView({ callback: (value) => (allReports = value), }); -function markAllMessagesAsRead(archivedReportsIDSet: ArchivedReportsIDSet) { +function markAllMessagesAsRead(archivedReportsIDSet: ArchivedReportsIDSet | undefined) { if (isAnonymousUser()) { return; } @@ -48,7 +48,7 @@ function markAllMessagesAsRead(archivedReportsIDSet: ArchivedReportsIDSet) { const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`]; const oneTransactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]); const oneTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneTransactionThreadReportID}`]; - const isArchivedReport = archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`); + const isArchivedReport = archivedReportsIDSet?.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`) ?? false; if (!isUnread(report, oneTransactionThreadReport, isArchivedReport)) { continue; } diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts index 4c76ae21790c..0c89d8d03589 100644 --- a/src/libs/navigateAfterOnboarding.ts +++ b/src/libs/navigateAfterOnboarding.ts @@ -31,7 +31,7 @@ function getReportIDAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet | undefined, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -62,7 +62,7 @@ function navigateAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet | undefined, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -106,7 +106,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet, + archivedReportsIDSet: ArchivedReportsIDSet | undefined, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index 32b0c7064d80..68d84116cf72 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -37,10 +37,10 @@ const selectReportHandler = (option: unknown) => { Navigation.goBack(ROUTES.NEW_TASK.getRoute()); }; -const reportFilter = (reportOptions: Array>, archivedReportsIDList: ArchivedReportsIDSet) => +const reportFilter = (reportOptions: Array>, archivedReportsIDList: ArchivedReportsIDSet | undefined) => (reportOptions ?? []).reduce((filtered: Array>, option) => { const report = option.item; - const isReportArchived = archivedReportsIDList.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`); + const isReportArchived = archivedReportsIDList?.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`) ?? false; if (canUserPerformWriteAction(report, isReportArchived) && canCreateTaskInReport(report) && !isCanceledTaskReport(report)) { filtered.push(option); } From a0f2fa928313475a9899b7155378ba99285b1e76 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:21:03 +0530 Subject: [PATCH 72/78] Revert archived reports loading fallback --- src/components/Search/index.tsx | 4 ++-- src/hooks/useArchivedReportsIDSet.ts | 11 +++-------- src/libs/ReportSecondaryActionUtils.ts | 2 +- src/libs/ReportUtils.ts | 4 ++-- src/libs/actions/Report/MarkAllMessageAsRead.tsx | 4 ++-- src/libs/navigateAfterOnboarding.ts | 6 +++--- src/pages/tasks/TaskShareDestinationSelectorModal.tsx | 4 ++-- 7 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 165a5bc8c7f0..ed7aa4ccd5ca 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -125,7 +125,7 @@ function mapTransactionItemToSelectedEntry( originalItemTransaction: OnyxEntry, currentUserLogin: string, currentUserAccountID: number, - archivedReportsIDSet: ArchivedReportsIDSet | undefined, + archivedReportsIDSet: ArchivedReportsIDSet, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, allowNegativeAmount = true, parentReport?: OnyxEntry, @@ -203,7 +203,7 @@ function prepareTransactionsList( selectedTransactions: SelectedTransactions, currentUserLogin: string, currentUserAccountID: number, - archivedReportsIDSet: ArchivedReportsIDSet | undefined, + archivedReportsIDSet: ArchivedReportsIDSet, outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue, parentReport?: OnyxEntry, selfDMReport?: OnyxEntry, diff --git a/src/hooks/useArchivedReportsIDSet.ts b/src/hooks/useArchivedReportsIDSet.ts index 09764211a7f5..5b3ba137bf98 100644 --- a/src/hooks/useArchivedReportsIDSet.ts +++ b/src/hooks/useArchivedReportsIDSet.ts @@ -1,18 +1,13 @@ import {buildArchivedReportsIDSet} from '@libs/ReportUtils'; import type {ArchivedReportsIDSet} from '@libs/SearchUIUtils'; import ONYXKEYS from '@src/ONYXKEYS'; -import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import useOnyx from './useOnyx'; /** - * Hook that returns a Set of archived report IDs once report name-value pairs are loaded + * Hook that returns a Set of archived report IDs */ -function useArchivedReportsIDSet(): ArchivedReportsIDSet | undefined { - const [reportNameValuePairs, reportNameValuePairsResult] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); - - if (isLoadingOnyxValue(reportNameValuePairsResult)) { - return undefined; - } +function useArchivedReportsIDSet(): ArchivedReportsIDSet { + const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); return buildArchivedReportsIDSet(reportNameValuePairs); } diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index 01cf705ba0b6..e5e3aeab5363 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -1137,7 +1137,7 @@ function getSecondaryTransactionThreadActions({ outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; isChatReportArchived?: boolean; grandParentReport?: OnyxEntry; - archivedReportsIDSet?: ArchivedReportsIDSet; + archivedReportsIDSet: ArchivedReportsIDSet; }): Array> { const options: Array> = []; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1e7b799b1ba1..762f498add31 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2357,8 +2357,8 @@ function isArchivedReport(reportNameValuePairs?: OnyxInputOrEntry (allReports = value), }); -function markAllMessagesAsRead(archivedReportsIDSet: ArchivedReportsIDSet | undefined) { +function markAllMessagesAsRead(archivedReportsIDSet: ArchivedReportsIDSet) { if (isAnonymousUser()) { return; } @@ -48,7 +48,7 @@ function markAllMessagesAsRead(archivedReportsIDSet: ArchivedReportsIDSet | unde const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`]; const oneTransactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]); const oneTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneTransactionThreadReportID}`]; - const isArchivedReport = archivedReportsIDSet?.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`) ?? false; + const isArchivedReport = archivedReportsIDSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`); if (!isUnread(report, oneTransactionThreadReport, isArchivedReport)) { continue; } diff --git a/src/libs/navigateAfterOnboarding.ts b/src/libs/navigateAfterOnboarding.ts index 0c89d8d03589..4c76ae21790c 100644 --- a/src/libs/navigateAfterOnboarding.ts +++ b/src/libs/navigateAfterOnboarding.ts @@ -31,7 +31,7 @@ function getReportIDAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet | undefined, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -62,7 +62,7 @@ function navigateAfterOnboarding( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet | undefined, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, @@ -106,7 +106,7 @@ function navigateAfterOnboardingWithMicrotaskQueue( isSmallScreenWidth: boolean, canUseDefaultRooms: boolean | undefined, conciergeReportID: string, - archivedReportsIDSet: ArchivedReportsIDSet | undefined, + archivedReportsIDSet: ArchivedReportsIDSet, onboardingPolicyID?: string, onboardingAdminsChatReportID?: string, shouldPreventOpenAdminRoom = false, diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx index 68d84116cf72..32b0c7064d80 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.tsx +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.tsx @@ -37,10 +37,10 @@ const selectReportHandler = (option: unknown) => { Navigation.goBack(ROUTES.NEW_TASK.getRoute()); }; -const reportFilter = (reportOptions: Array>, archivedReportsIDList: ArchivedReportsIDSet | undefined) => +const reportFilter = (reportOptions: Array>, archivedReportsIDList: ArchivedReportsIDSet) => (reportOptions ?? []).reduce((filtered: Array>, option) => { const report = option.item; - const isReportArchived = archivedReportsIDList?.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`) ?? false; + const isReportArchived = archivedReportsIDList.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`); if (canUserPerformWriteAction(report, isReportArchived) && canCreateTaskInReport(report) && !isCanceledTaskReport(report)) { filtered.push(option); } From f123c844864e3494767a3ed272fc46bd409236fe Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Wed, 3 Jun 2026 20:39:41 +0530 Subject: [PATCH 73/78] Fix archived reports merge follow-ups --- src/components/Search/index.tsx | 114 +++++------------- .../workspace/rooms/WorkspaceRoomsPage.tsx | 8 +- 2 files changed, 34 insertions(+), 88 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index fc07728bb2ee..5d0d76500d0e 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -120,19 +120,24 @@ type HoldMenuCallback = (item: TransactionReportGroupListItemType, requestType: const hashToString = (queryHash?: number) => (queryHash || queryHash === 0 ? String(queryHash) : undefined); +type TransactionSelectionContext = { + currentUserLogin: string; + currentUserAccountID: number; + archivedReportsIDSet: ArchivedReportsIDSet; + outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined; + selfDMReport: OnyxEntry; + isProduction: boolean; +}; + function mapTransactionItemToSelectedEntry( item: TransactionListItemType, itemTransaction: OnyxEntry, originalItemTransaction: OnyxEntry, - currentUserLogin: string, - currentUserAccountID: number, - archivedReportsIDSet: ArchivedReportsIDSet, - outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined, + transactionSelectionContext: TransactionSelectionContext, allowNegativeAmount: boolean, parentReport: OnyxEntry | undefined, - selfDMReport: OnyxEntry | undefined, - isProduction: boolean, ): [string, SelectedTransactionInfo] { + const {currentUserLogin, currentUserAccountID, archivedReportsIDSet, outstandingReportsByPolicyID, selfDMReport, isProduction} = transactionSelectionContext; const {canHoldRequest, canUnholdRequest} = canHoldUnholdReportAction(item.report, item.reportAction, item.holdReportAction, item, item.policy, currentUserAccountID); const canRejectRequest = item.report ? canRejectReportAction(currentUserLogin, item.report) : false; const amount = hasValidModifiedAmount(item) ? Number(item.modifiedAmount) : item.amount; @@ -230,13 +235,8 @@ function prepareTransactionsList( itemTransaction: OnyxEntry, originalItemTransaction: OnyxEntry, selectedTransactions: SelectedTransactions, - currentUserLogin: string, - currentUserAccountID: number, - archivedReportsIDSet: ArchivedReportsIDSet, - outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined, + transactionSelectionContext: TransactionSelectionContext, parentReport: OnyxEntry | undefined, - selfDMReport: OnyxEntry | undefined, - isProduction: boolean, ) { if (selectedTransactions[item.keyForList]?.isSelected) { const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions; @@ -244,19 +244,7 @@ function prepareTransactionsList( return transactions; } - const [key, selectedInfo] = mapTransactionItemToSelectedEntry( - item, - itemTransaction, - originalItemTransaction, - currentUserLogin, - currentUserAccountID, - archivedReportsIDSet, - outstandingReportsByPolicyID, - false, - parentReport, - selfDMReport, - isProduction, - ); + const [key, selectedInfo] = mapTransactionItemToSelectedEntry(item, itemTransaction, originalItemTransaction, transactionSelectionContext, false, parentReport); return { ...selectedTransactions, @@ -331,6 +319,17 @@ function Search({ const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; const archivedReportsIDSet = useArchivedReportsIDSet(); + const transactionSelectionContext = useMemo( + () => ({ + currentUserLogin: email ?? '', + currentUserAccountID: accountID, + archivedReportsIDSet, + outstandingReportsByPolicyID, + selfDMReport, + isProduction, + }), + [accountID, archivedReportsIDSet, email, isProduction, outstandingReportsByPolicyID, selfDMReport], + ); const [exportReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, { selector: selectFilteredReportActions, @@ -1064,19 +1063,7 @@ function Search({ const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${item.transactionID}`] as OnyxEntry; const originalItemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; const itemParentReport = searchResults?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${item.report?.parentReportID}`] as OnyxEntry; - const updatedTransactions = prepareTransactionsList( - item, - itemTransaction, - originalItemTransaction, - selectedTransactions, - email ?? '', - accountID, - archivedReportsIDSet, - outstandingReportsByPolicyID, - itemParentReport, - selfDMReport, - isProduction, - ); + const updatedTransactions = prepareTransactionsList(item, itemTransaction, originalItemTransaction, selectedTransactions, transactionSelectionContext, itemParentReport); // Tag individual transactions with their parent group key so export filtering can derive the group when needed. if (areItemsGrouped) { @@ -1152,14 +1139,9 @@ function Search({ transactionItem, itemTransaction, originalItemTransaction, - email ?? '', - accountID, - archivedReportsIDSet, - outstandingReportsByPolicyID, + transactionSelectionContext, true, itemParentReport, - selfDMReport, - isProduction, ); return [key, {...entry, groupKey: item.keyForList}]; }), @@ -1168,21 +1150,7 @@ function Search({ setSelectedTransactions(updatedTransactions); updateSelectAllMatchingItemsState(updatedTransactions); }, - [ - selectedTransactions, - setSelectedTransactions, - updateSelectAllMatchingItemsState, - transactions, - searchResults?.data, - email, - accountID, - archivedReportsIDSet, - outstandingReportsByPolicyID, - selfDMReport, - isProduction, - areItemsGrouped, - filteredData, - ], + [selectedTransactions, setSelectedTransactions, updateSelectAllMatchingItemsState, transactions, searchResults?.data, transactionSelectionContext, areItemsGrouped, filteredData], ); const onSelectRowInMobileSelectionMode = (item: SearchListItem) => { @@ -1490,14 +1458,9 @@ function Search({ transactionItem, itemTransaction, originalItemTransaction, - email ?? '', - accountID, - archivedReportsIDSet, - outstandingReportsByPolicyID, + transactionSelectionContext, true, itemParentReport, - selfDMReport, - isProduction, ); return [key, {...entry, groupKey: item.keyForList}] as [string, SelectedTransactionInfo]; }); @@ -1512,19 +1475,7 @@ function Search({ const itemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; const itemParentReport = searchResults?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionItem.report?.parentReportID}`] as OnyxEntry; - return mapTransactionItemToSelectedEntry( - transactionItem, - itemTransaction, - originalItemTransaction, - email ?? '', - accountID, - archivedReportsIDSet, - outstandingReportsByPolicyID, - true, - itemParentReport, - selfDMReport, - isProduction, - ); + return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, transactionSelectionContext, true, itemParentReport); }), ); } @@ -1538,13 +1489,8 @@ function Search({ updateSelectAllMatchingItemsState, clearSelectedTransactions, transactions, - email, - accountID, - archivedReportsIDSet, - outstandingReportsByPolicyID, searchResults?.data, - selfDMReport, - isProduction, + transactionSelectionContext, ]); const onLayoutBase = useCallback(() => { diff --git a/src/pages/workspace/rooms/WorkspaceRoomsPage.tsx b/src/pages/workspace/rooms/WorkspaceRoomsPage.tsx index 45e451170ce6..08fd9000894f 100644 --- a/src/pages/workspace/rooms/WorkspaceRoomsPage.tsx +++ b/src/pages/workspace/rooms/WorkspaceRoomsPage.tsx @@ -8,7 +8,7 @@ import {usePersonalDetails} from '@components/OnyxListItemProvider'; import ScreenWrapper from '@components/ScreenWrapper'; import WorkspaceRoomsTable from '@components/Tables/WorkspaceRoomsTable'; import type {WorkspaceRoomRowData} from '@components/Tables/WorkspaceRoomsTable'; -import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -45,15 +45,15 @@ function WorkspaceRoomsPage({route}: WorkspaceRoomsPageProps) { useWorkspaceDocumentTitle(policy?.name, 'workspace.common.rooms'); const reportAttributes = useReportAttributes(); - const archivedReportsIdSet = useArchivedReportsIdSet(); + const archivedReportsIDSet = useArchivedReportsIDSet(); const personalDetails = usePersonalDetails(); const [policyReports] = useOnyx( ONYXKEYS.COLLECTION.REPORT, { - selector: policyChatRoomsSelector(policyID, archivedReportsIdSet), + selector: policyChatRoomsSelector(policyID, archivedReportsIDSet), }, - [policyID, archivedReportsIdSet], + [policyID, archivedReportsIDSet], ); const rooms: WorkspaceRoomRowData[] = (policyReports ?? []).map((report) => { From ae754ecfece59f32966d53108367dcc10fdb8e68 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:19:25 +0530 Subject: [PATCH 74/78] Address archived reports review follow-ups --- .../sections/ReportField.tsx | 10 +-- .../sections/selectors.ts | 12 --- src/components/Search/index.tsx | 90 +++++++++++++------ src/hooks/useOutstandingReports.ts | 42 ++------- 4 files changed, 70 insertions(+), 84 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx index 9f5a57c93a9b..64e01700bf27 100644 --- a/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx +++ b/src/components/MoneyRequestConfirmationList/sections/ReportField.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useOutstandingReports from '@hooks/useOutstandingReports'; @@ -8,14 +9,14 @@ import useReportAttributes from '@hooks/useReportAttributes'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import {getReportName} from '@libs/ReportNameUtils'; -import {buildArchivedReportsIDSet, generateReportID, getOutstandingReportsForUser, isMoneyRequestReport, isReportOutstanding} from '@libs/ReportUtils'; +import {generateReportID, getOutstandingReportsForUser, isMoneyRequestReport, isReportOutstanding} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import type {IOUAction, IOUType} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; -import {createOutstandingReportsForPolicySelector, createOutstandingReportsNVPsSelector} from './selectors'; +import {createOutstandingReportsForPolicySelector} from './selectors'; type ReportFieldProps = { /** The selected participants */ @@ -53,10 +54,7 @@ function ReportField({selectedParticipants, iouType, reportID, reportActionID, a const reportAttributes = useReportAttributes(); const policyID = selectedParticipants?.at(0)?.policyID; const [outstandingReportsForPolicy] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID, {selector: createOutstandingReportsForPolicySelector(policyID)}, [policyID]); - const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {selector: createOutstandingReportsNVPsSelector(outstandingReportsForPolicy)}, [ - outstandingReportsForPolicy, - ]); - const archivedReportsIDSet = buildArchivedReportsIDSet(reportNameValuePairs); + const archivedReportsIDSet = useArchivedReportsIDSet(); // Per-key report subscriptions instead of full COLLECTION.REPORT const [transactionReportEntry] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`); diff --git a/src/components/MoneyRequestConfirmationList/sections/selectors.ts b/src/components/MoneyRequestConfirmationList/sections/selectors.ts index 8efd2ccb24a2..adbcb487048a 100644 --- a/src/components/MoneyRequestConfirmationList/sections/selectors.ts +++ b/src/components/MoneyRequestConfirmationList/sections/selectors.ts @@ -1,7 +1,6 @@ /** Onyx selectors used by the confirmation field leaves. */ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {canSendInvoice} from '@libs/PolicyUtils'; -import getReportNameValuePairsForReports from '@libs/ReportNameValuePairsUtils'; import { getCategory, getCreated, @@ -195,7 +194,6 @@ const taxSliceSelector = (t: OnyxEntry): TaxSlice | undefined => { // --- ReportField --- type ReportFieldTransactionState = {reportID: Transaction['reportID']; isFromGlobalCreate: boolean}; -type OutstandingReportsForPolicy = OnyxTypes.OutstandingReportsByPolicyIDDerivedValue[string]; const reportFieldTransactionStateSelector = (t: OnyxEntry): ReportFieldTransactionState | undefined => { if (!t) { @@ -210,15 +208,6 @@ const reportFieldTransactionStateSelector = (t: OnyxEntry): ReportF const createOutstandingReportsForPolicySelector = (policyID: string | undefined) => (derived: OnyxEntry) => derived?.[policyID ?? CONST.DEFAULT_NUMBER_ID]; -const createOutstandingReportsNVPsSelector = - (outstandingReports: OutstandingReportsForPolicy | undefined) => - (allNVPs: OnyxCollection): OnyxCollection | undefined => { - if (!outstandingReports || !allNVPs) { - return undefined; - } - return getReportNameValuePairsForReports(outstandingReports, allNVPs); - }; - // --- InvoiceSenderField --- type InvoiceSenderWorkspace = {id: string | undefined; name: string | undefined; avatarURL: string | undefined} | undefined; @@ -242,7 +231,6 @@ export { attendeeSliceSelector, categoryStateSelector, createCanUpdateSenderWorkspaceSelector, - createOutstandingReportsNVPsSelector, createOutstandingReportsForPolicySelector, createTagDisplaySelector, dateStateSelector, diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 5d0d76500d0e..7a91b2df2eec 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -129,14 +129,23 @@ type TransactionSelectionContext = { isProduction: boolean; }; -function mapTransactionItemToSelectedEntry( - item: TransactionListItemType, - itemTransaction: OnyxEntry, - originalItemTransaction: OnyxEntry, - transactionSelectionContext: TransactionSelectionContext, - allowNegativeAmount: boolean, - parentReport: OnyxEntry | undefined, -): [string, SelectedTransactionInfo] { +type MapTransactionItemToSelectedEntryParams = { + item: TransactionListItemType; + itemTransaction: OnyxEntry; + originalItemTransaction: OnyxEntry; + transactionSelectionContext: TransactionSelectionContext; + allowNegativeAmount: boolean; + parentReport: OnyxEntry | undefined; +}; + +function mapTransactionItemToSelectedEntry({ + item, + itemTransaction, + originalItemTransaction, + transactionSelectionContext, + allowNegativeAmount, + parentReport, +}: MapTransactionItemToSelectedEntryParams): [string, SelectedTransactionInfo] { const {currentUserLogin, currentUserAccountID, archivedReportsIDSet, outstandingReportsByPolicyID, selfDMReport, isProduction} = transactionSelectionContext; const {canHoldRequest, canUnholdRequest} = canHoldUnholdReportAction(item.report, item.reportAction, item.holdReportAction, item, item.policy, currentUserAccountID); const canRejectRequest = item.report ? canRejectReportAction(currentUserLogin, item.report) : false; @@ -230,21 +239,30 @@ function mapEmptyReportToSelectedEntry(item: TransactionReportGroupListItemType ]; } -function prepareTransactionsList( - item: TransactionListItemType, - itemTransaction: OnyxEntry, - originalItemTransaction: OnyxEntry, - selectedTransactions: SelectedTransactions, - transactionSelectionContext: TransactionSelectionContext, - parentReport: OnyxEntry | undefined, -) { +type PrepareTransactionsListParams = { + item: TransactionListItemType; + itemTransaction: OnyxEntry; + originalItemTransaction: OnyxEntry; + selectedTransactions: SelectedTransactions; + transactionSelectionContext: TransactionSelectionContext; + parentReport: OnyxEntry | undefined; +}; + +function prepareTransactionsList({item, itemTransaction, originalItemTransaction, selectedTransactions, transactionSelectionContext, parentReport}: PrepareTransactionsListParams) { if (selectedTransactions[item.keyForList]?.isSelected) { const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions; return transactions; } - const [key, selectedInfo] = mapTransactionItemToSelectedEntry(item, itemTransaction, originalItemTransaction, transactionSelectionContext, false, parentReport); + const [key, selectedInfo] = mapTransactionItemToSelectedEntry({ + item, + itemTransaction, + originalItemTransaction, + transactionSelectionContext, + allowNegativeAmount: false, + parentReport, + }); return { ...selectedTransactions, @@ -1063,7 +1081,14 @@ function Search({ const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${item.transactionID}`] as OnyxEntry; const originalItemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; const itemParentReport = searchResults?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${item.report?.parentReportID}`] as OnyxEntry; - const updatedTransactions = prepareTransactionsList(item, itemTransaction, originalItemTransaction, selectedTransactions, transactionSelectionContext, itemParentReport); + const updatedTransactions = prepareTransactionsList({ + item, + itemTransaction, + originalItemTransaction, + selectedTransactions, + transactionSelectionContext, + parentReport: itemParentReport, + }); // Tag individual transactions with their parent group key so export filtering can derive the group when needed. if (areItemsGrouped) { @@ -1135,14 +1160,14 @@ function Search({ searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`] ?? transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; const itemParentReport = searchResults?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionItem.report?.parentReportID}`] as OnyxEntry; - const [key, entry] = mapTransactionItemToSelectedEntry( - transactionItem, + const [key, entry] = mapTransactionItemToSelectedEntry({ + item: transactionItem, itemTransaction, originalItemTransaction, transactionSelectionContext, - true, - itemParentReport, - ); + allowNegativeAmount: true, + parentReport: itemParentReport, + }); return [key, {...entry, groupKey: item.keyForList}]; }), ), @@ -1454,14 +1479,14 @@ function Search({ const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; const itemParentReport = searchResults?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionItem.report?.parentReportID}`] as OnyxEntry; - const [key, entry] = mapTransactionItemToSelectedEntry( - transactionItem, + const [key, entry] = mapTransactionItemToSelectedEntry({ + item: transactionItem, itemTransaction, originalItemTransaction, transactionSelectionContext, - true, - itemParentReport, - ); + allowNegativeAmount: true, + parentReport: itemParentReport, + }); return [key, {...entry, groupKey: item.keyForList}] as [string, SelectedTransactionInfo]; }); }); @@ -1475,7 +1500,14 @@ function Search({ const itemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`] as OnyxEntry; const originalItemTransaction = searchResults?.data?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${itemTransaction?.comment?.originalTransactionID}`]; const itemParentReport = searchResults?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionItem.report?.parentReportID}`] as OnyxEntry; - return mapTransactionItemToSelectedEntry(transactionItem, itemTransaction, originalItemTransaction, transactionSelectionContext, true, itemParentReport); + return mapTransactionItemToSelectedEntry({ + item: transactionItem, + itemTransaction, + originalItemTransaction, + transactionSelectionContext, + allowNegativeAmount: true, + parentReport: itemParentReport, + }); }), ); } diff --git a/src/hooks/useOutstandingReports.ts b/src/hooks/useOutstandingReports.ts index 3d1d236ec497..256c070e0c94 100644 --- a/src/hooks/useOutstandingReports.ts +++ b/src/hooks/useOutstandingReports.ts @@ -1,54 +1,22 @@ -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import getReportNameValuePairsForReports from '@libs/ReportNameValuePairsUtils'; -import {buildArchivedReportsIDSet, getOutstandingReportsForUser, isSelfDM} from '@libs/ReportUtils'; +import type {OnyxEntry} from 'react-native-onyx'; +import {getOutstandingReportsForUser, isSelfDM} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {OutstandingReportsByPolicyIDDerivedValue, Policy, ReportNameValuePairs} from '@src/types/onyx'; +import type {Policy} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import useArchivedReportsIDSet from './useArchivedReportsIDSet'; import useMappedPolicies from './useMappedPolicies'; import useOnyx from './useOnyx'; const policyIdMapper = (policy: OnyxEntry) => policy?.id; -const createOutstandingReportsNVPsSelector = - ( - outstandingReportsByPolicyID: OnyxEntry, - selectedPolicyID: string | undefined, - personalPolicyID: string | undefined, - allPoliciesID: OnyxCollection, - shouldUseAllPolicies: boolean, - ) => - (allNVPs: OnyxCollection): OnyxCollection | undefined => { - if (!outstandingReportsByPolicyID || !allNVPs) { - return undefined; - } - - const result: OnyxCollection = {}; - if (shouldUseAllPolicies) { - for (const policyID of Object.values(allPoliciesID ?? {})) { - if (!policyID || policyID === personalPolicyID) { - continue; - } - Object.assign(result, getReportNameValuePairsForReports(outstandingReportsByPolicyID[policyID], allNVPs)); - } - return result; - } - - return getReportNameValuePairsForReports(outstandingReportsByPolicyID[selectedPolicyID ?? CONST.DEFAULT_NUMBER_ID], allNVPs); - }; - export default function useOutstandingReports(selectedReportID: string | undefined, selectedPolicyID: string | undefined, ownerAccountID: number | undefined, isEditing: boolean) { const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); const [allPoliciesID] = useMappedPolicies(policyIdMapper); const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); const shouldUseAllPolicies = !selectedPolicyID || selectedPolicyID === personalPolicyID || isSelfDM(selectedReport); - const [reportNameValuePairs] = useOnyx( - ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, - {selector: createOutstandingReportsNVPsSelector(outstandingReportsByPolicyID, selectedPolicyID, personalPolicyID, allPoliciesID, shouldUseAllPolicies)}, - [outstandingReportsByPolicyID, selectedPolicyID, personalPolicyID, allPoliciesID, shouldUseAllPolicies], - ); - const archivedReportsIDSet = buildArchivedReportsIDSet(reportNameValuePairs); + const archivedReportsIDSet = useArchivedReportsIDSet(); // Early return if no reports are available to prevent useless loop if (!outstandingReportsByPolicyID || isEmptyObject(outstandingReportsByPolicyID)) { From c9ddd7c13283d68aa02a486b9b2baa9d66f5a61f Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:33:13 +0530 Subject: [PATCH 75/78] Remove unused report name value pairs utility --- src/libs/ReportNameValuePairsUtils.ts | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 src/libs/ReportNameValuePairsUtils.ts diff --git a/src/libs/ReportNameValuePairsUtils.ts b/src/libs/ReportNameValuePairsUtils.ts deleted file mode 100644 index cd06cbe79767..000000000000 --- a/src/libs/ReportNameValuePairsUtils.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type {OnyxCollection} from 'react-native-onyx'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report, ReportNameValuePairs} from '@src/types/onyx'; - -function getReportNameValuePairsForReports(reports: OnyxCollection | undefined, allNVPs: NonNullable>): OnyxCollection { - const result: OnyxCollection = {}; - for (const report of Object.values(reports ?? {})) { - if (!report?.reportID) { - continue; - } - const key = `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}` as const; - if (allNVPs[key] !== undefined) { - result[key] = allNVPs[key]; - } - } - return result; -} - -export default getReportNameValuePairsForReports; From 43c7c8c9d0eb8cc3786b8b5ef18332b19201e1ee Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Tue, 9 Jun 2026 03:28:44 +0530 Subject: [PATCH 76/78] Address comment --- src/components/MoneyRequestHeaderSecondaryActions.tsx | 3 --- src/libs/ReportSecondaryActionUtils.ts | 8 -------- 2 files changed, 11 deletions(-) diff --git a/src/components/MoneyRequestHeaderSecondaryActions.tsx b/src/components/MoneyRequestHeaderSecondaryActions.tsx index a0114d0bc635..b70e12a00948 100644 --- a/src/components/MoneyRequestHeaderSecondaryActions.tsx +++ b/src/components/MoneyRequestHeaderSecondaryActions.tsx @@ -6,7 +6,6 @@ import React, {useRef, useState} from 'react'; // eslint-disable-next-line no-restricted-imports import {InteractionManager} from 'react-native'; import type {ValueOf} from 'type-fest'; -import useArchivedReportsIDSet from '@hooks/useArchivedReportsIDSet'; import useConfirmModal from '@hooks/useConfirmModal'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -118,7 +117,6 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money const {wideRHPRouteKeys} = useWideRHPState(); const isNarrowButton = !useShouldDisplayButtonsInSeparateLine() || (wideRHPRouteKeys.length > 0 && !isSmallScreenWidth); const {isOffline} = useNetwork(); - const archivedReportsIDSet = useArchivedReportsIDSet(); // Per-key Onyx subscriptions const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); @@ -293,7 +291,6 @@ function MoneyRequestHeaderSecondaryActions({reportID, onBackButtonPress}: Money outstandingReportsByPolicyID, isChatReportArchived: isChatIOUReportArchived, grandParentReport, - archivedReportsIDSet, isProduction, }); })(); diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index 90ede73a5e3d..9eb83effb371 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -79,7 +79,6 @@ import { isSettled, isWorkspaceEligibleForReportChange, } from './ReportUtils'; -import type {ArchivedReportsIDSet} from './SearchUIUtils'; import { allHavePendingRTERViolation, getOriginalTransactionWithSplitInfo, @@ -905,7 +904,6 @@ function getSecondaryReportActions({ outstandingReportsByPolicyID, isChatReportArchived = false, parentReport, - archivedReportsIDSet, isProduction, }: { currentUserLogin: string; @@ -926,8 +924,6 @@ function getSecondaryReportActions({ canUseNewDotSplits?: boolean; isChatReportArchived?: boolean; parentReport?: OnyxEntry; - // Temporarily optional while archived report checks are migrated in smaller PRs. Remove this fallback as part of https://github.com/Expensify/App/issues/66422. - archivedReportsIDSet?: ArchivedReportsIDSet; isProduction: boolean; }): Array> { const options: Array> = []; @@ -1066,7 +1062,6 @@ function getSecondaryReportActions({ isChatReportArchived, outstandingReportsByPolicyID, transaction, - archivedReportsIDSet, }); const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(report, isChatReportArchived); @@ -1129,7 +1124,6 @@ function getSecondaryTransactionThreadActions({ outstandingReportsByPolicyID, isChatReportArchived, grandParentReport, - archivedReportsIDSet, isProduction, }: { currentUserLogin: string; @@ -1143,7 +1137,6 @@ function getSecondaryTransactionThreadActions({ outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue; isChatReportArchived?: boolean; grandParentReport?: OnyxEntry; - archivedReportsIDSet: ArchivedReportsIDSet; isProduction: boolean; }): Array> { const options: Array> = []; @@ -1184,7 +1177,6 @@ function getSecondaryTransactionThreadActions({ isChatReportArchived, outstandingReportsByPolicyID, transaction: reportTransaction, - archivedReportsIDSet, }) && canUserPerformWriteActionReportUtils(parentReport, isChatReportArchived) ) { From afcf42566fc53f17ae5d4746afbaeb4b25a0cd24 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Tue, 9 Jun 2026 03:34:37 +0530 Subject: [PATCH 77/78] Update tests --- tests/unit/ReportSecondaryActionUtilsTest.ts | 34 -------------------- 1 file changed, 34 deletions(-) diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index 6f1da142fdf1..685e2cb2ab54 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -2243,8 +2243,6 @@ describe('getSecondaryAction', () => { reportAction: undefined, originalTransaction: {} as Transaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); @@ -3356,8 +3354,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: undefined, originalTransaction: {} as Transaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }), ).toEqual(result); @@ -3388,8 +3384,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.HOLD)).toBe(true); @@ -3417,8 +3411,6 @@ describe('getSecondaryTransactionThreadActions', () => { originalTransaction: {} as Transaction, policy, transactionThreadReport, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result).toContain(CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD); @@ -3434,8 +3426,6 @@ describe('getSecondaryTransactionThreadActions', () => { originalTransaction: {} as Transaction, policy, transactionThreadReport, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result2).not.toContain(CONST.REPORT.SECONDARY_ACTIONS.REMOVE_HOLD); @@ -3462,8 +3452,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: undefined, originalTransaction: {} as Transaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.DELETE)).toBe(true); @@ -3524,8 +3512,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: undefined, originalTransaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); @@ -3628,8 +3614,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(true); @@ -3692,8 +3676,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); @@ -3741,8 +3723,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); @@ -3789,8 +3769,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); @@ -3841,8 +3819,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); @@ -3872,8 +3848,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(true); @@ -3910,8 +3884,6 @@ describe('getSecondaryTransactionThreadActions', () => { originalTransaction: {} as Transaction, policy, grandParentReport: selfDMReport, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(true); @@ -3941,8 +3913,6 @@ describe('getSecondaryTransactionThreadActions', () => { reportAction: actionR14932, originalTransaction: {} as Transaction, policy, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.SPLIT)).toBe(false); @@ -3972,8 +3942,6 @@ describe('getSecondaryTransactionThreadActions', () => { originalTransaction: {} as Transaction, policy, isChatReportArchived: false, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result).toContain(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); @@ -4003,8 +3971,6 @@ describe('getSecondaryTransactionThreadActions', () => { originalTransaction: {} as Transaction, policy, isChatReportArchived: false, - - archivedReportsIDSet: new Set(), isProduction: false, }); expect(result).not.toContain(CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MOVE_EXPENSE); From c5d7849af41aa5eb5b7a34dcf2455625138e3a1e Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Tue, 9 Jun 2026 18:31:13 +0530 Subject: [PATCH 78/78] Flatten search transaction selection params --- src/components/Search/index.tsx | 110 +++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 30 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index a86c9f150ce8..b486d39ef2b3 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -119,20 +119,16 @@ type HoldMenuCallback = (item: TransactionReportGroupListItemType, requestType: const hashToString = (queryHash?: number) => (queryHash || queryHash === 0 ? String(queryHash) : undefined); -type TransactionSelectionContext = { +type MapTransactionItemToSelectedEntryParams = { + item: TransactionListItemType; + itemTransaction: OnyxEntry; + originalItemTransaction: OnyxEntry; currentUserLogin: string; currentUserAccountID: number; archivedReportsIDSet: ArchivedReportsIDSet; outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined; selfDMReport: OnyxEntry; isProduction: boolean; -}; - -type MapTransactionItemToSelectedEntryParams = { - item: TransactionListItemType; - itemTransaction: OnyxEntry; - originalItemTransaction: OnyxEntry; - transactionSelectionContext: TransactionSelectionContext; allowNegativeAmount: boolean; parentReport: OnyxEntry | undefined; }; @@ -141,11 +137,15 @@ function mapTransactionItemToSelectedEntry({ item, itemTransaction, originalItemTransaction, - transactionSelectionContext, + currentUserLogin, + currentUserAccountID, + archivedReportsIDSet, + outstandingReportsByPolicyID, + selfDMReport, + isProduction, allowNegativeAmount, parentReport, }: MapTransactionItemToSelectedEntryParams): [string, SelectedTransactionInfo] { - const {currentUserLogin, currentUserAccountID, archivedReportsIDSet, outstandingReportsByPolicyID, selfDMReport, isProduction} = transactionSelectionContext; const {canHoldRequest, canUnholdRequest} = canHoldUnholdReportAction(item.report, item.reportAction, item.holdReportAction, item, item.policy, currentUserAccountID); const canRejectRequest = item.report ? canRejectReportAction(currentUserLogin, item.report) : false; const amount = hasValidModifiedAmount(item) ? Number(item.modifiedAmount) : item.amount; @@ -243,11 +243,28 @@ type PrepareTransactionsListParams = { itemTransaction: OnyxEntry; originalItemTransaction: OnyxEntry; selectedTransactions: SelectedTransactions; - transactionSelectionContext: TransactionSelectionContext; + currentUserLogin: string; + currentUserAccountID: number; + archivedReportsIDSet: ArchivedReportsIDSet; + outstandingReportsByPolicyID: OutstandingReportsByPolicyIDDerivedValue | undefined; + selfDMReport: OnyxEntry; + isProduction: boolean; parentReport: OnyxEntry | undefined; }; -function prepareTransactionsList({item, itemTransaction, originalItemTransaction, selectedTransactions, transactionSelectionContext, parentReport}: PrepareTransactionsListParams) { +function prepareTransactionsList({ + item, + itemTransaction, + originalItemTransaction, + selectedTransactions, + currentUserLogin, + currentUserAccountID, + archivedReportsIDSet, + outstandingReportsByPolicyID, + selfDMReport, + isProduction, + parentReport, +}: PrepareTransactionsListParams) { if (selectedTransactions[item.keyForList]?.isSelected) { const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions; @@ -258,7 +275,12 @@ function prepareTransactionsList({item, itemTransaction, originalItemTransaction item, itemTransaction, originalItemTransaction, - transactionSelectionContext, + currentUserLogin, + currentUserAccountID, + archivedReportsIDSet, + outstandingReportsByPolicyID, + selfDMReport, + isProduction, allowNegativeAmount: false, parentReport, }); @@ -335,17 +357,6 @@ function Search({ const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; const archivedReportsIDSet = useArchivedReportsIDSet(); - const transactionSelectionContext = useMemo( - () => ({ - currentUserLogin: email ?? '', - currentUserAccountID: accountID, - archivedReportsIDSet, - outstandingReportsByPolicyID, - selfDMReport, - isProduction, - }), - [accountID, archivedReportsIDSet, email, isProduction, outstandingReportsByPolicyID, selfDMReport], - ); const {policyForMovingExpenses} = usePolicyForMovingExpenses(); // Only the boolean derived from policyForMovingExpenses is consumed by row components downstream. @@ -1071,7 +1082,12 @@ function Search({ itemTransaction, originalItemTransaction, selectedTransactions, - transactionSelectionContext, + currentUserLogin: email ?? '', + currentUserAccountID: accountID, + archivedReportsIDSet, + outstandingReportsByPolicyID, + selfDMReport, + isProduction, parentReport: itemParentReport, }); @@ -1149,7 +1165,12 @@ function Search({ item: transactionItem, itemTransaction, originalItemTransaction, - transactionSelectionContext, + currentUserLogin: email ?? '', + currentUserAccountID: accountID, + archivedReportsIDSet, + outstandingReportsByPolicyID, + selfDMReport, + isProduction, allowNegativeAmount: true, parentReport: itemParentReport, }); @@ -1160,7 +1181,21 @@ function Search({ setSelectedTransactions(updatedTransactions); updateSelectAllMatchingItemsState(updatedTransactions); }, - [selectedTransactions, setSelectedTransactions, updateSelectAllMatchingItemsState, transactions, searchResults?.data, transactionSelectionContext, areItemsGrouped, filteredData], + [ + selectedTransactions, + setSelectedTransactions, + updateSelectAllMatchingItemsState, + transactions, + searchResults?.data, + email, + accountID, + archivedReportsIDSet, + outstandingReportsByPolicyID, + selfDMReport, + isProduction, + areItemsGrouped, + filteredData, + ], ); const onSelectRowInMobileSelectionMode = (item: SearchListItem) => { @@ -1468,7 +1503,12 @@ function Search({ item: transactionItem, itemTransaction, originalItemTransaction, - transactionSelectionContext, + currentUserLogin: email ?? '', + currentUserAccountID: accountID, + archivedReportsIDSet, + outstandingReportsByPolicyID, + selfDMReport, + isProduction, allowNegativeAmount: true, parentReport: itemParentReport, }); @@ -1489,7 +1529,12 @@ function Search({ item: transactionItem, itemTransaction, originalItemTransaction, - transactionSelectionContext, + currentUserLogin: email ?? '', + currentUserAccountID: accountID, + archivedReportsIDSet, + outstandingReportsByPolicyID, + selfDMReport, + isProduction, allowNegativeAmount: true, parentReport: itemParentReport, }); @@ -1507,7 +1552,12 @@ function Search({ clearSelectedTransactions, transactions, searchResults?.data, - transactionSelectionContext, + email, + accountID, + archivedReportsIDSet, + outstandingReportsByPolicyID, + selfDMReport, + isProduction, ]); const onLayoutBase = useCallback(() => {