diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 38d859def1f7..73ed11b5ea22 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -30,10 +30,10 @@ import { isCurrentUserSubmitter, isInvoiceReport, navigateBackOnDeleteTransaction, + reportTransactionsSelector, } from '@libs/ReportUtils'; import { allHavePendingRTERViolation, - getAllReportTransactions, isDuplicate as isDuplicateTransactionUtils, isExpensifyCardTransaction, isOnHold as isOnHoldTransactionUtils, @@ -115,12 +115,13 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea } return reportActions.find((action): action is OnyxTypes.ReportAction => action.reportActionID === transactionThreadReport.parentReportActionID); }, [reportActions, transactionThreadReport?.parentReportActionID]); - const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION); + const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { + selector: (_transactions) => reportTransactionsSelector(_transactions, moneyRequestReport?.reportID), + initialValue: [], + }); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${isMoneyRequestAction(requestParentReportAction) && getOriginalMessage(requestParentReportAction)?.IOUTransactionID}`); const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {initialValue: true}); const isLoadingHoldUseExplained = isLoadingOnyxValue(dismissedHoldUseExplanationResult); - const transaction = - transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${isMoneyRequestAction(requestParentReportAction) && getOriginalMessage(requestParentReportAction)?.IOUTransactionID}`] ?? - undefined; const styles = useThemeStyles(); const theme = useTheme(); @@ -139,15 +140,16 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); const [paymentType, setPaymentType] = useState(); const [requestType, setRequestType] = useState(); - const allTransactions = useMemo(() => getAllReportTransactions(moneyRequestReport?.reportID, transactions), [moneyRequestReport?.reportID, transactions]); const canAllowSettlement = hasUpdatedTotal(moneyRequestReport, policy); const policyType = policy?.type; const connectedIntegration = getConnectedIntegration(policy); const navigateBackToAfterDelete = useRef(); const hasHeldExpenses = hasHeldExpensesReportUtils(moneyRequestReport?.reportID); const hasScanningReceipt = getTransactionsWithReceipts(moneyRequestReport?.reportID).some((t) => isReceiptBeingScanned(t)); - const hasOnlyPendingTransactions = allTransactions.length > 0 && allTransactions.every((t) => isExpensifyCardTransaction(t) && isPending(t)); - const transactionIDs = allTransactions.map((t) => t.transactionID) ?? []; + const hasOnlyPendingTransactions = useMemo(() => { + return !!transactions && transactions.length > 0 && transactions.every((t) => isExpensifyCardTransaction(t) && isPending(t)); + }, [transactions]); + const transactionIDs = transactions?.map((t) => t.transactionID) ?? []; const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs); const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationTransactionUtils(transactionIDs, moneyRequestReport, policy); const hasOnlyHeldExpenses = hasOnlyHeldExpensesReportUtils(moneyRequestReport?.reportID); @@ -502,7 +504,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea paymentType={paymentType} chatReport={chatReport} moneyRequestReport={moneyRequestReport} - transactionCount={transactionIDs.length} + transactionCount={transactionIDs?.length ?? 0} /> )} reportTransactionsSelector(_transactions, iouReportID), + initialValue: [], + }); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET); const [invoiceReceiverPolicy] = useOnyx( @@ -157,7 +160,6 @@ function ReportPreview({ const styles = useThemeStyles(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const allTransactions = useMemo(() => getAllReportTransactions(iouReportID, transactions), [iouReportID, transactions]); const {hasMissingSmartscanFields, areAllRequestsBeingSmartScanned, hasOnlyTransactionsWithPendingRoutes, hasNonReimbursableTransactions} = useMemo( () => ({ @@ -179,8 +181,8 @@ function ReportPreview({ const getCanIOUBePaid = useCallback( (onlyShowPayElsewhere = false, shouldCheckApprovedState = true) => - canIOUBePaidIOUActions(iouReport, chatReport, policy, allTransactions, onlyShowPayElsewhere, undefined, undefined, shouldCheckApprovedState), - [iouReport, chatReport, policy, allTransactions], + canIOUBePaidIOUActions(iouReport, chatReport, policy, transactions, onlyShowPayElsewhere, undefined, undefined, shouldCheckApprovedState), + [iouReport, chatReport, policy, transactions], ); const canIOUBePaid = useMemo(() => getCanIOUBePaid(), [getCanIOUBePaid]); @@ -217,7 +219,7 @@ function ReportPreview({ const isInvoiceRoom = isInvoiceRoomReportUtils(chatReport); const canAllowSettlement = hasUpdatedTotal(iouReport, policy); - const numberOfRequests = allTransactions.length; + const numberOfRequests = transactions?.length ?? 0; const transactionsWithReceipts = getTransactionsWithReceipts(iouReportID); const numberOfScanningReceipts = transactionsWithReceipts.filter((transaction) => isReceiptBeingScanned(transaction)).length; const numberOfPendingRequests = transactionsWithReceipts.filter((transaction) => isPending(transaction) && isCardTransaction(transaction)).length; @@ -232,13 +234,14 @@ function ReportPreview({ hasWarningTypeViolations(iouReportID, transactionViolations, true) || (isReportOwner(iouReport) && hasReportViolations(iouReportID)) || hasActionsWithErrors(iouReportID); - const lastThreeTransactions = allTransactions.slice(-3); + const lastThreeTransactions = transactions?.slice(-3) ?? []; + const lastTransaction = transactions?.at(0); const lastThreeReceipts = lastThreeTransactions.map((transaction) => ({...getThumbnailAndImageURIs(transaction), transaction})); - const showRTERViolationMessage = numberOfRequests === 1 && hasPendingUI(allTransactions.at(0), getTransactionViolations(allTransactions.at(0)?.transactionID, transactionViolations)); - const transactionIDList = [allTransactions.at(0)?.transactionID].filter((transactionID): transactionID is string => transactionID !== undefined); + const showRTERViolationMessage = numberOfRequests === 1 && hasPendingUI(lastTransaction, getTransactionViolations(lastTransaction?.transactionID, transactionViolations)); + const transactionIDList = [lastTransaction?.transactionID].filter((transactionID): transactionID is string => transactionID !== undefined); const shouldShowBrokenConnectionViolation = numberOfRequests === 1 && shouldShowBrokenConnectionViolationTransactionUtils(transactionIDList, iouReport, policy); - let formattedMerchant = numberOfRequests === 1 ? getMerchant(allTransactions.at(0)) : null; - const formattedDescription = numberOfRequests === 1 ? getDescription(allTransactions.at(0)) : null; + let formattedMerchant = numberOfRequests === 1 ? getMerchant(lastTransaction) : null; + const formattedDescription = numberOfRequests === 1 ? getDescription(lastTransaction) : null; if (isPartialMerchant(formattedMerchant ?? '')) { formattedMerchant = null; @@ -423,7 +426,7 @@ function ReportPreview({ const shouldShowScanningSubtitle = (numberOfScanningReceipts === 1 && numberOfRequests === 1) || (numberOfScanningReceipts >= 1 && Number(nonHeldAmount) === 0); const shouldShowPendingSubtitle = numberOfPendingRequests === 1 && numberOfRequests === 1; - const isPayAtEndExpense = isPayAtEndExpenseReport(iouReportID, allTransactions); + const isPayAtEndExpense = isPayAtEndExpenseReport(iouReportID, transactions); const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReportID}`, {selector: getArchiveReason}); const getPendingMessageProps: () => PendingMessageProps = () => { @@ -545,7 +548,7 @@ function ReportPreview({ {lastThreeReceipts.length > 0 && ( )} diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index 61f636703b95..a2d83259c52b 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -3,13 +3,14 @@ import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {OnyxInputOrEntry, PersonalDetails, Report, Transaction} from '@src/types/onyx'; +import type {OnyxInputOrEntry, PersonalDetails, Report} from '@src/types/onyx'; import type {Attendee} from '@src/types/onyx/IOU'; import type {IOURequestType} from './actions/IOU'; -import * as CurrencyUtils from './CurrencyUtils'; +import {getCurrencyUnit} from './CurrencyUtils'; import DateUtils from './DateUtils'; import Navigation from './Navigation/Navigation'; -import * as TransactionUtils from './TransactionUtils'; +import {getReportTransactions} from './ReportUtils'; +import {getCurrency, getTagArrayFromName} from './TransactionUtils'; let lastLocationPermissionPrompt: string; Onyx.connect({ @@ -47,7 +48,7 @@ function navigateToStartMoneyRequestStep(requestType: IOURequestType, iouType: I function calculateAmount(numberOfParticipants: number, total: number, currency: string, isDefaultUser = false): number { // Since the backend can maximum store 2 decimal places, any currency with more than 2 decimals // has to be capped to 2 decimal places - const currencyUnit = Math.min(100, CurrencyUtils.getCurrencyUnit(currency)); + const currencyUnit = Math.min(100, getCurrencyUnit(currency)); const totalInCurrencySubunit = (total / 100) * currencyUnit; const totalParticipants = numberOfParticipants + 1; const amountPerPerson = Math.round(totalInCurrencySubunit / totalParticipants); @@ -118,8 +119,8 @@ function updateIOUOwnerAndTotal>( * that are either created or cancelled offline, and thus haven't been converted to the report's currency yet */ function isIOUReportPendingCurrencyConversion(iouReport: Report): boolean { - const reportTransactions: Transaction[] = TransactionUtils.getAllReportTransactions(iouReport.reportID); - const pendingRequestsInDifferentCurrency = reportTransactions.filter((transaction) => transaction.pendingAction && TransactionUtils.getCurrency(transaction) !== iouReport.currency); + const reportTransactions = getReportTransactions(iouReport.reportID); + const pendingRequestsInDifferentCurrency = reportTransactions.filter((transaction) => transaction.pendingAction && getCurrency(transaction) !== iouReport.currency); return pendingRequestsInDifferentCurrency.length > 0; } @@ -150,7 +151,7 @@ function isValidMoneyRequestType(iouType: string): boolean { * @returns */ function insertTagIntoTransactionTagsString(transactionTags: string, tag: string, tagIndex: number): string { - const tagArray = TransactionUtils.getTagArrayFromName(transactionTags); + const tagArray = getTagArrayFromName(transactionTags); tagArray[tagIndex] = tag; while (tagArray.length > 0 && !tagArray.at(-1)) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 4353aeb1797d..fdbcb718aac4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -157,7 +157,6 @@ import { wasActionTakenByCurrentUser, } from './ReportActionsUtils'; import { - getAllReportTransactions, getAttendees, getBillable, getCardID, @@ -900,6 +899,14 @@ function getReportOrDraftReport(reportID: string | undefined): OnyxEntry return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? allReportsDraft?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`]; } +function reportTransactionsSelector(transactions: OnyxCollection, reportID: string | undefined): Transaction[] { + if (!transactions || !reportID) { + return []; + } + + return Object.values(transactions).filter((transaction): transaction is Transaction => !!transaction && transaction.reportID === reportID); +} + function getReportTransactions(reportID: string | undefined): Transaction[] { if (!reportID) { return []; @@ -1895,7 +1902,7 @@ function isPayAtEndExpenseReport(reportID: string | undefined, transactions: Tra return false; } - return isPayAtEndExpense(transactions?.[0] ?? getAllReportTransactions(reportID).at(0)); + return isPayAtEndExpense(transactions?.[0] ?? getReportTransactions(reportID).at(0)); } /** @@ -2992,7 +2999,7 @@ function getReasonAndReportActionThatRequiresAttention( const iouReportActionToApproveOrPay = getIOUReportActionToApproveOrPay(optionOrReport, optionOrReport.reportID); const iouReportID = getIOUReportIDFromReportActionPreview(iouReportActionToApproveOrPay); - const transactions = getAllReportTransactions(iouReportID); + const transactions = getReportTransactions(iouReportID); const hasOnlyPendingTransactions = transactions.length > 0 && transactions.every((t) => isExpensifyCardTransaction(t) && isPending(t)); // Has a child report that is awaiting action (e.g. approve, pay, add bank account) from current user @@ -3736,7 +3743,7 @@ function getReportPreviewMessage( return reportActionMessage; } - const allReportTransactions = getAllReportTransactions(report.reportID); + const allReportTransactions = getReportTransactions(report.reportID); const transactionsWithReceipts = allReportTransactions.filter(hasReceiptTransactionUtils); const numberOfScanningReceipts = transactionsWithReceipts.filter(isReceiptBeingScanned).length; @@ -8975,6 +8982,8 @@ export { getReportFieldKey, getReportIDFromLink, getReportName, + getReportTransactions, + reportTransactionsSelector, getReportNotificationPreference, getReportOfflinePendingActionAndErrors, getReportParticipantsTitle, diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index b52a024879a9..b60419090c41 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -26,7 +26,7 @@ import { isPolicyAdmin, } from '@libs/PolicyUtils'; import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils'; -import {isOpenExpenseReport, isProcessingReport, isReportApproved, isSettled, isThread} from '@libs/ReportUtils'; +import {getReportTransactions, isOpenExpenseReport, isProcessingReport, isReportApproved, isSettled, isThread} from '@libs/ReportUtils'; import type {IOURequestType} from '@userActions/IOU'; import CONST from '@src/CONST'; import type {IOUType} from '@src/CONST'; @@ -69,6 +69,7 @@ type BuildOptimisticTransactionParams = { }; let allTransactions: OnyxCollection = {}; + Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, @@ -836,13 +837,6 @@ function hasRoute(transaction: OnyxEntry, isDistanceRequestType?: b return !!transaction?.routes?.route0?.geometry?.coordinates || (!!isDistanceRequestType && !!transaction?.comment?.customUnit?.quantity); } -function getAllReportTransactions(reportID?: string, transactions?: OnyxCollection): Transaction[] { - const reportTransactions: Transaction[] = Object.values(transactions ?? allTransactions ?? {}).filter( - (transaction): transaction is Transaction => !!transaction && transaction.reportID === reportID, - ); - return reportTransactions; -} - function waypointHasValidAddress(waypoint: RecentWaypoint | Waypoint): boolean { return !!waypoint?.address?.trim(); } @@ -1359,7 +1353,7 @@ function getCategoryTaxCodeAndAmount(category: string, transaction: OnyxEntry> { - return getAllReportTransactions(iouReportID).sort((transA, transB) => { + return getReportTransactions(iouReportID).sort((transA, transB) => { if (transA.created < transB.created) { return -1; } @@ -1407,7 +1401,6 @@ export { getTagArrayFromName, getTagForDisplay, getTransactionViolations, - getAllReportTransactions, hasReceipt, hasEReceipt, hasRoute, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index b2ec4478284c..363e75271422 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -107,6 +107,7 @@ import { getPersonalDetailsForAccountID, getReportNameValuePairs, getReportOrDraftReport, + getReportTransactions, getTransactionDetails, hasHeldExpenses as hasHeldExpensesReportUtils, hasNonReimbursableTransactions as hasNonReimbursableTransactionsReportUtils, @@ -135,7 +136,6 @@ import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import { allHavePendingRTERViolation, buildOptimisticTransaction, - getAllReportTransactions, getAmount, getCategoryTaxCodeAndAmount, getCurrency, @@ -7634,7 +7634,7 @@ function getPayMoneyRequestParams( // Optimistically unhold all transactions if we pay all requests if (full) { - const reportTransactions = getAllReportTransactions(iouReport?.reportID); + const reportTransactions = getReportTransactions(iouReport?.reportID); for (const transaction of reportTransactions) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -7758,7 +7758,7 @@ function canApproveIOU( const reportNameValuePairs = chatReportRNVP ?? getReportNameValuePairs(iouReport?.reportID); const isArchivedExpenseReport = isArchivedReport(iouReport, reportNameValuePairs); let isTransactionBeingScanned = false; - const reportTransactions = getAllReportTransactions(iouReport?.reportID); + const reportTransactions = getReportTransactions(iouReport?.reportID); for (const transaction of reportTransactions) { const hasReceipt = hasReceiptTransactionUtils(transaction); const isReceiptBeingScanned = isReceiptBeingScannedTransactionUtils(transaction); diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index c926b5552d11..856743ecdde2 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -75,7 +75,6 @@ import * as PhoneNumber from '@libs/PhoneNumber'; import * as PolicyUtils from '@libs/PolicyUtils'; import {navigateWhenEnableFeature} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import {getAllReportTransactions} from '@libs/TransactionUtils'; import type {PolicySelector} from '@pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover'; import * as PaymentMethods from '@userActions/PaymentMethods'; import * as PersistedRequests from '@userActions/PersistedRequests'; @@ -2588,7 +2587,7 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF }); // The expense report transactions need to have the amount reversed to negative values - const reportTransactions = getAllReportTransactions(iouReportID); + const reportTransactions = ReportUtils.getReportTransactions(iouReportID); // For performance reasons, we are going to compose a merge collection data for transactions const transactionsOptimisticData: Record = {}; diff --git a/src/libs/migrations/TransactionBackupsToCollection.ts b/src/libs/migrations/TransactionBackupsToCollection.ts index addbcb02cc02..43f49392ba18 100644 --- a/src/libs/migrations/TransactionBackupsToCollection.ts +++ b/src/libs/migrations/TransactionBackupsToCollection.ts @@ -7,7 +7,7 @@ import type {Transaction} from '@src/types/onyx'; /** * This migration moves all the transaction backups stored in the transaction collection, ONYXKEYS.COLLECTION.TRANSACTION, to a reserved collection that only * stores draft transactions, ONYXKEYS.COLLECTION.TRANSACTION_DRAFT. The purpose of the migration is that there is a possibility that transaction backups are - * not filtered by most functions, e.g, getAllReportTransactions (src/libs/TransactionUtils/index.ts). One problem that arose from storing transaction backups with + * not filtered by most functions, e.g, getReportTransactions (src/libs/ReportUtils/index.ts). One problem that arose from storing transaction backups with * the other transactions is that for every distance expense which have their waypoints updated offline, we expect the ReportPreview component to display the * default image of a pending map. However, due to the presence of the transaction backup, the previous map image will be displayed alongside the pending map. * The problem was further discussed in this PR. https://github.com/Expensify/App/pull/30232#issuecomment-178110172 diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index b48a90e88d95..ac6bd0fe904d 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -6,6 +6,7 @@ import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import ConfirmModal from '@components/ConfirmModal'; import DecisionModal from '@components/DecisionModal'; import DelegateNoAccessModal from '@components/DelegateNoAccessModal'; import DisplayNames from '@components/DisplayNames'; @@ -31,21 +32,100 @@ import useNetwork from '@hooks/useNetwork'; import usePaginatedReportActions from '@hooks/usePaginatedReportActions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ReportActions from '@libs/actions/Report'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; +import {getConnectedIntegration, isPolicyAdmin as isPolicyAdminUtil, isPolicyEmployee as isPolicyEmployeeUtil, isSubmitAndClose, shouldShowPolicy} from '@libs/PolicyUtils'; +import { + getOneTransactionThreadReportID, + getOriginalMessage, + getReportAction, + getTrackExpenseActionableWhisper, + isDeletedAction, + isMoneyRequestAction, + isTrackExpenseAction, +} from '@libs/ReportActionsUtils'; +import { + canDeleteTransaction, + canEditReportDescription as canEditReportDescriptionUtil, + canHoldUnholdReportAction as canHoldUnholdReportActionUtil, + canJoinChat, + canLeaveChat, + canWriteInReport, + createDraftTransactionAndNavigateToParticipantSelector, + getAvailableReportFields, + getChatRoomSubtitle, + getDisplayNamesWithTooltips, + getIcons, + getOriginalReportID, + getParentNavigationSubtitle, + getParticipantsAccountIDsForDisplay, + getParticipantsList, + getReportDescription, + getReportFieldKey, + getReportName, + isAdminOwnerApproverOrReportOwner, + isArchivedNonExpenseReport, + isCanceledTaskReport as isCanceledTaskReportUtil, + isChatRoom as isChatRoomUtil, + isChatThread as isChatThreadUtil, + isClosedReport, + isCompletedTaskReport, + isConciergeChatReport, + isDefaultRoom as isDefaultRoomUtil, + isExpenseReport as isExpenseReportUtil, + isExported, + isGroupChat as isGroupChatUtil, + isHiddenForCurrentUser, + isInvoiceReport as isInvoiceReportUtil, + isInvoiceRoom as isInvoiceRoomUtil, + isMoneyRequestReport as isMoneyRequestReportUtil, + isMoneyRequest as isMoneyRequestUtil, + isPayer as isPayerUtil, + isPolicyExpenseChat as isPolicyExpenseChatUtil, + isPublicRoom as isPublicRoomUtil, + isReportApproved as isReportApprovedUtil, + isReportFieldDisabled, + isReportFieldOfTypeTitle, + isReportManager as isReportManagerUtil, + isRootGroupChat as isRootGroupChatUtil, + isSelfDM as isSelfDMUtil, + isSettled as isSettledUtil, + isSystemChat as isSystemChatUtil, + isTaskReport as isTaskReportUtil, + isThread as isThreadUtil, + isTrackExpenseReport as isTrackExpenseReportUtil, + isUserCreatedPolicyRoom as isUserCreatedPolicyRoomUtil, + navigateBackOnDeleteTransaction, + navigateToPrivateNotes, + reportTransactionsSelector, + shouldDisableRename as shouldDisableRenameUtil, + shouldUseFullTitleToDisplay, +} from '@libs/ReportUtils'; import StringUtils from '@libs/StringUtils'; -import {getAllReportTransactions} from '@libs/TransactionUtils'; -import * as IOU from '@userActions/IOU'; -import * as Report from '@userActions/Report'; -import * as Session from '@userActions/Session'; -import * as Task from '@userActions/Task'; -import ConfirmModal from '@src/components/ConfirmModal'; +import { + cancelPayment as cancelPaymentAction, + deleteMoneyRequest, + deleteTrackExpense, + getNavigationUrlAfterTrackExpenseDelete, + getNavigationUrlOnMoneyRequestDelete, + unapproveExpenseReport, +} from '@userActions/IOU'; +import { + clearAvatarErrors, + clearPolicyRoomNameErrors, + clearReportFieldKeyErrors, + exportReportToCSV, + getReportPrivateNote, + hasErrorInPrivateNotes, + leaveGroupChat, + leaveRoom, + setDeleteTransactionNavigateBackUrl, + updateGroupChatAvatar, +} from '@userActions/Report'; +import {checkIfActionIsAllowed} from '@userActions/Session'; +import {canActionTask as canActionTaskAction, canModifyTask as canModifyTaskAction, deleteTask, reopenTask} from '@userActions/Task'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -99,16 +179,16 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth} = useResponsiveLayout(); - const transactionThreadReportID = useMemo( - () => ReportActionsUtils.getOneTransactionThreadReportID(report.reportID, reportActions ?? [], isOffline), - [report.reportID, reportActions, isOffline], - ); + const transactionThreadReportID = useMemo(() => getOneTransactionThreadReportID(report.reportID, reportActions ?? [], isOffline), [report.reportID, reportActions, isOffline]); const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`); const [isDebugModeEnabled] = useOnyx(ONYXKEYS.USER, {selector: (user) => !!user?.isDebugModeEnabled}); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const [session] = useOnyx(ONYXKEYS.SESSION); - const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION); + const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { + selector: (_transactions) => reportTransactionsSelector(_transactions, report.reportID), + initialValue: [], + }); const [isLastMemberLeavingGroupModalVisible, setIsLastMemberLeavingGroupModalVisible] = useState(false); const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); @@ -117,34 +197,34 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const [offlineModalVisible, setOfflineModalVisible] = useState(false); const [downloadErrorModalVisible, setDownloadErrorModalVisible] = useState(false); const policy = useMemo(() => policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`], [policies, report?.policyID]); - const isPolicyAdmin = useMemo(() => PolicyUtils.isPolicyAdmin(policy), [policy]); - const isPolicyEmployee = useMemo(() => PolicyUtils.isPolicyEmployee(report?.policyID, policies), [report?.policyID, policies]); - const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(report), [report]); - const shouldUseFullTitle = useMemo(() => ReportUtils.shouldUseFullTitleToDisplay(report), [report]); - const isChatRoom = useMemo(() => ReportUtils.isChatRoom(report), [report]); - const isUserCreatedPolicyRoom = useMemo(() => ReportUtils.isUserCreatedPolicyRoom(report), [report]); - const isDefaultRoom = useMemo(() => ReportUtils.isDefaultRoom(report), [report]); - const isChatThread = useMemo(() => ReportUtils.isChatThread(report), [report]); - const isArchivedRoom = useMemo(() => ReportUtils.isArchivedNonExpenseReport(report, reportNameValuePairs), [report, reportNameValuePairs]); - const isMoneyRequestReport = useMemo(() => ReportUtils.isMoneyRequestReport(report), [report]); - const isMoneyRequest = useMemo(() => ReportUtils.isMoneyRequest(report), [report]); - const isInvoiceReport = useMemo(() => ReportUtils.isInvoiceReport(report), [report]); - const isInvoiceRoom = useMemo(() => ReportUtils.isInvoiceRoom(report), [report]); - const isTaskReport = useMemo(() => ReportUtils.isTaskReport(report), [report]); - const isSelfDM = useMemo(() => ReportUtils.isSelfDM(report), [report]); - const isTrackExpenseReport = ReportUtils.isTrackExpenseReport(report); - const parentReportAction = ReportActionsUtils.getReportAction(report?.parentReportID, report?.parentReportActionID); - const isCanceledTaskReport = ReportUtils.isCanceledTaskReport(report, parentReportAction); - const canEditReportDescription = useMemo(() => ReportUtils.canEditReportDescription(report, policy), [report, policy]); + const isPolicyAdmin = useMemo(() => isPolicyAdminUtil(policy), [policy]); + const isPolicyEmployee = useMemo(() => isPolicyEmployeeUtil(report?.policyID, policies), [report?.policyID, policies]); + const isPolicyExpenseChat = useMemo(() => isPolicyExpenseChatUtil(report), [report]); + const shouldUseFullTitle = useMemo(() => shouldUseFullTitleToDisplay(report), [report]); + const isChatRoom = useMemo(() => isChatRoomUtil(report), [report]); + const isUserCreatedPolicyRoom = useMemo(() => isUserCreatedPolicyRoomUtil(report), [report]); + const isDefaultRoom = useMemo(() => isDefaultRoomUtil(report), [report]); + const isChatThread = useMemo(() => isChatThreadUtil(report), [report]); + const isArchivedRoom = useMemo(() => isArchivedNonExpenseReport(report, reportNameValuePairs), [report, reportNameValuePairs]); + const isMoneyRequestReport = useMemo(() => isMoneyRequestReportUtil(report), [report]); + const isMoneyRequest = useMemo(() => isMoneyRequestUtil(report), [report]); + const isInvoiceReport = useMemo(() => isInvoiceReportUtil(report), [report]); + const isInvoiceRoom = useMemo(() => isInvoiceRoomUtil(report), [report]); + const isTaskReport = useMemo(() => isTaskReportUtil(report), [report]); + const isSelfDM = useMemo(() => isSelfDMUtil(report), [report]); + const isTrackExpenseReport = useMemo(() => isTrackExpenseReportUtil(report), [report]); + const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); + const isCanceledTaskReport = isCanceledTaskReportUtil(report, parentReportAction); + const canEditReportDescription = useMemo(() => canEditReportDescriptionUtil(report, policy), [report, policy]); const shouldShowReportDescription = isChatRoom && (canEditReportDescription || report.description !== ''); const isExpenseReport = isMoneyRequestReport || isInvoiceReport || isMoneyRequest; const isSingleTransactionView = isMoneyRequest || isTrackExpenseReport; - const isSelfDMTrackExpenseReport = isTrackExpenseReport && ReportUtils.isSelfDM(parentReport); - const shouldDisableRename = useMemo(() => ReportUtils.shouldDisableRename(report), [report]); - const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(report); + const isSelfDMTrackExpenseReport = isTrackExpenseReport && isSelfDMUtil(parentReport); + const shouldDisableRename = useMemo(() => shouldDisableRenameUtil(report), [report]); + const parentNavigationSubtitleData = getParentNavigationSubtitle(report); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx const chatRoomSubtitle = useMemo(() => { - const subtitle = ReportUtils.getChatRoomSubtitle(report); + const subtitle = getChatRoomSubtitle(report); if (subtitle) { return subtitle; @@ -152,22 +232,22 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta return ''; }, [report]); - const isSystemChat = useMemo(() => ReportUtils.isSystemChat(report), [report]); - const isGroupChat = useMemo(() => ReportUtils.isGroupChat(report), [report]); - const isRootGroupChat = useMemo(() => ReportUtils.isRootGroupChat(report), [report]); - const isThread = useMemo(() => ReportUtils.isThread(report), [report]); + const isSystemChat = useMemo(() => isSystemChatUtil(report), [report]); + const isGroupChat = useMemo(() => isGroupChatUtil(report), [report]); + const isRootGroupChat = useMemo(() => isRootGroupChatUtil(report), [report]); + const isThread = useMemo(() => isThreadUtil(report), [report]); const shouldOpenRoomMembersPage = isUserCreatedPolicyRoom || isChatThread || (isPolicyExpenseChat && isPolicyAdmin); const participants = useMemo(() => { - return ReportUtils.getParticipantsList(report, personalDetails, shouldOpenRoomMembersPage); + return getParticipantsList(report, personalDetails, shouldOpenRoomMembersPage); }, [report, personalDetails, shouldOpenRoomMembersPage]); - const connectedIntegration = PolicyUtils.getConnectedIntegration(policy); + const connectedIntegration = getConnectedIntegration(policy); const transactionIDList = useMemo(() => { - if (!isMoneyRequestReport) { + if (!isMoneyRequestReport || !transactions) { return []; } - return getAllReportTransactions(report.reportID, transactions).map((transaction) => transaction.transactionID); - }, [isMoneyRequestReport, report.reportID, transactions]); + return transactions.map((transaction) => transaction.transactionID); + }, [isMoneyRequestReport, transactions]); // Get the active chat members by filtering out the pending members with delete action const activeChatMembers = participants.flatMap((accountID) => { @@ -206,7 +286,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const isActionOwner = typeof requestParentReportAction?.actorAccountID === 'number' && typeof session?.accountID === 'number' && requestParentReportAction.actorAccountID === session?.accountID; - const isDeletedParentAction = ReportActionsUtils.isDeletedAction(requestParentReportAction); + const isDeletedParentAction = isDeletedAction(requestParentReportAction); const moneyRequestReport: OnyxEntry = useMemo(() => { if (caseID === CASES.MONEY_REQUEST) { @@ -217,21 +297,14 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const moneyRequestAction = transactionThreadReportID ? requestParentReportAction : parentReportAction; - const canModifyTask = Task.canModifyTask(report, session?.accountID ?? CONST.DEFAULT_NUMBER_ID); - const canActionTask = Task.canActionTask(report, session?.accountID ?? CONST.DEFAULT_NUMBER_ID); + const canModifyTask = canModifyTaskAction(report, session?.accountID ?? CONST.DEFAULT_NUMBER_ID); + const canActionTask = canActionTaskAction(report, session?.accountID ?? CONST.DEFAULT_NUMBER_ID); const shouldShowTaskDeleteButton = - isTaskReport && - !isCanceledTaskReport && - ReportUtils.canWriteInReport(report) && - report.stateNum !== CONST.REPORT.STATE_NUM.APPROVED && - !ReportUtils.isClosedReport(report) && - canModifyTask && - canActionTask; - const canDeleteRequest = isActionOwner && (ReportUtils.canDeleteTransaction(moneyRequestReport) || isSelfDMTrackExpenseReport) && !isDeletedParentAction; + isTaskReport && !isCanceledTaskReport && canWriteInReport(report) && report.stateNum !== CONST.REPORT.STATE_NUM.APPROVED && !isClosedReport(report) && canModifyTask && canActionTask; + const canDeleteRequest = isActionOwner && (canDeleteTransaction(moneyRequestReport) || isSelfDMTrackExpenseReport) && !isDeletedParentAction; const shouldShowDeleteButton = shouldShowTaskDeleteButton || canDeleteRequest; - const canUnapproveRequest = - ReportUtils.isExpenseReport(report) && (ReportUtils.isReportManager(report) || isPolicyAdmin) && ReportUtils.isReportApproved(report) && !PolicyUtils.isSubmitAndClose(policy); + const canUnapproveRequest = isExpenseReportUtil(report) && (isReportManagerUtil(report) || isPolicyAdmin) && isReportApprovedUtil(report) && !isSubmitAndClose(policy); useEffect(() => { if (canDeleteRequest) { @@ -247,23 +320,23 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta return; } - Report.getReportPrivateNote(report?.reportID); + getReportPrivateNote(report?.reportID); }, [report?.reportID, isOffline, isPrivateNotesFetchTriggered, isSelfDM]); const leaveChat = useCallback(() => { Navigation.dismissModal(); Navigation.isNavigationReady().then(() => { if (isRootGroupChat) { - Report.leaveGroupChat(report.reportID); + leaveGroupChat(report.reportID); return; } const isWorkspaceMemberLeavingWorkspaceRoom = (report.visibility === CONST.REPORT.VISIBILITY.RESTRICTED || isPolicyExpenseChat) && isPolicyEmployee; - Report.leaveRoom(report.reportID, isWorkspaceMemberLeavingWorkspaceRoom); + leaveRoom(report.reportID, isWorkspaceMemberLeavingWorkspaceRoom); }); }, [isPolicyEmployee, isPolicyExpenseChat, isRootGroupChat, report.reportID, report.visibility]); const [moneyRequestReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`); - const isMoneyRequestExported = ReportUtils.isExported(moneyRequestReportActions); + const isMoneyRequestExported = isExported(moneyRequestReportActions); const {isDelegateAccessRestricted} = useDelegateUserDetails(); const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false); @@ -275,16 +348,16 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta return; } Navigation.dismissModal(); - IOU.unapproveExpenseReport(moneyRequestReport); + unapproveExpenseReport(moneyRequestReport); }, [isMoneyRequestExported, moneyRequestReport, isDelegateAccessRestricted]); - const shouldShowLeaveButton = ReportUtils.canLeaveChat(report, policy); - const shouldShowGoToWorkspace = PolicyUtils.shouldShowPolicy(policy, false, session?.email) && !policy?.isJoinRequestPending; + const shouldShowLeaveButton = canLeaveChat(report, policy); + const shouldShowGoToWorkspace = shouldShowPolicy(policy, false, session?.email) && !policy?.isJoinRequestPending; - const reportName = ReportUtils.getReportName(report); + const reportName = getReportName(report); const additionalRoomDetails = - (isPolicyExpenseChat && !!report?.isOwnPolicyExpenseChat) || ReportUtils.isExpenseReport(report) || isPolicyExpenseChat || isInvoiceRoom + (isPolicyExpenseChat && !!report?.isOwnPolicyExpenseChat) || isExpenseReportUtil(report) || isPolicyExpenseChat || isInvoiceRoom ? chatRoomSubtitle : `${translate('threads.in')} ${chatRoomSubtitle}`; @@ -297,24 +370,24 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta roomDescription = translate('newRoomPage.roomName'); } - const shouldShowNotificationPref = !isMoneyRequestReport && !ReportUtils.isHiddenForCurrentUser(report); + const shouldShowNotificationPref = !isMoneyRequestReport && !isHiddenForCurrentUser(report); const shouldShowWriteCapability = !isMoneyRequestReport; const shouldShowMenuItem = shouldShowNotificationPref || shouldShowWriteCapability || (!!report?.visibility && report.chatType !== CONST.REPORT.CHAT_TYPE.INVOICE); - const isPayer = ReportUtils.isPayer(session, moneyRequestReport); - const isSettled = ReportUtils.isSettled(moneyRequestReport?.reportID); + const isPayer = isPayerUtil(session, moneyRequestReport); + const isSettled = isSettledUtil(moneyRequestReport?.reportID); - const shouldShowCancelPaymentButton = caseID === CASES.MONEY_REPORT && isPayer && isSettled && ReportUtils.isExpenseReport(moneyRequestReport); + const shouldShowCancelPaymentButton = caseID === CASES.MONEY_REPORT && isPayer && isSettled && isExpenseReportUtil(moneyRequestReport); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${moneyRequestReport?.chatReportID}`); - const iouTransactionID = ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID : ''; + const iouTransactionID = isMoneyRequestAction(requestParentReportAction) ? getOriginalMessage(requestParentReportAction)?.IOUTransactionID : ''; const cancelPayment = useCallback(() => { if (!chatReport) { return; } - IOU.cancelPayment(moneyRequestReport, chatReport); + cancelPaymentAction(moneyRequestReport, chatReport); setIsConfirmModalVisible(false); }, [moneyRequestReport, chatReport]); @@ -337,8 +410,8 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta (isGroupChat || (isDefaultRoom && isChatThread && isPolicyEmployee) || (!isUserCreatedPolicyRoom && participants.length) || - (isUserCreatedPolicyRoom && (isPolicyEmployee || (isChatThread && !ReportUtils.isPublicRoom(report))))) && - !ReportUtils.isConciergeChatReport(report) && + (isUserCreatedPolicyRoom && (isPolicyEmployee || (isChatThread && !isPublicRoomUtil(report))))) && + !isConciergeChatReport(report) && !isSystemChat ) { items.push({ @@ -383,8 +456,8 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta } if (isTrackExpenseReport && !isDeletedParentAction) { - const actionReportID = ReportUtils.getOriginalReportID(report.reportID, parentReportAction); - const whisperAction = ReportActionsUtils.getTrackExpenseActionableWhisper(iouTransactionID, moneyRequestReport?.reportID); + const actionReportID = getOriginalReportID(report.reportID, parentReportAction); + const whisperAction = getTrackExpenseActionableWhisper(iouTransactionID, moneyRequestReport?.reportID); const actionableWhisperReportActionID = whisperAction?.reportActionID; items.push({ key: CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS, @@ -393,7 +466,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta isAnonymousAction: false, shouldShowRightIcon: true, action: () => { - ReportUtils.createDraftTransactionAndNavigateToParticipantSelector(iouTransactionID, actionReportID, CONST.IOU.ACTION.SUBMIT, actionableWhisperReportActionID); + createDraftTransactionAndNavigateToParticipantSelector(iouTransactionID, actionReportID, CONST.IOU.ACTION.SUBMIT, actionableWhisperReportActionID); }, }); items.push({ @@ -403,7 +476,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta isAnonymousAction: false, shouldShowRightIcon: true, action: () => { - ReportUtils.createDraftTransactionAndNavigateToParticipantSelector(iouTransactionID, actionReportID, CONST.IOU.ACTION.CATEGORIZE, actionableWhisperReportActionID); + createDraftTransactionAndNavigateToParticipantSelector(iouTransactionID, actionReportID, CONST.IOU.ACTION.CATEGORIZE, actionableWhisperReportActionID); }, }); items.push({ @@ -413,7 +486,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta isAnonymousAction: false, shouldShowRightIcon: true, action: () => { - ReportUtils.createDraftTransactionAndNavigateToParticipantSelector(iouTransactionID, actionReportID, CONST.IOU.ACTION.SHARE, actionableWhisperReportActionID); + createDraftTransactionAndNavigateToParticipantSelector(iouTransactionID, actionReportID, CONST.IOU.ACTION.SHARE, actionableWhisperReportActionID); }, }); } @@ -426,22 +499,22 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta icon: Expensicons.Pencil, isAnonymousAction: false, shouldShowRightIcon: true, - action: () => ReportUtils.navigateToPrivateNotes(report, session, backTo), - brickRoadIndicator: Report.hasErrorInPrivateNotes(report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, + action: () => navigateToPrivateNotes(report, session, backTo), + brickRoadIndicator: hasErrorInPrivateNotes(report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, }); } // Show actions related to Task Reports if (isTaskReport && !isCanceledTaskReport) { - if (ReportUtils.isCompletedTaskReport(report) && canModifyTask && canActionTask) { + if (isCompletedTaskReport(report) && canModifyTask && canActionTask) { items.push({ key: CONST.REPORT_DETAILS_MENU_ITEM.MARK_AS_INCOMPLETE, icon: Expensicons.Checkmark, translationKey: 'task.markAsIncomplete', isAnonymousAction: false, - action: Session.checkIfActionIsAllowed(() => { + action: checkIfActionIsAllowed(() => { Navigation.dismissModal(); - Task.reopenTask(report); + reopenTask(report); }), }); } @@ -469,7 +542,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta return; } - ReportActions.exportReportToCSV({reportID: report.reportID, transactionIDList}, () => { + exportReportToCSV({reportID: report.reportID, transactionIDList}, () => { setDownloadErrorModalVisible(true); }); }, @@ -525,7 +598,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta icon: Expensicons.Exit, isAnonymousAction: true, action: () => { - if (ReportUtils.getParticipantsAccountIDsForDisplay(report, false, true).length === 1 && isRootGroupChat) { + if (getParticipantsAccountIDsForDisplay(report, false, true).length === 1 && isRootGroupChat) { setIsLastMemberLeavingGroupModalVisible(true); return; } @@ -595,10 +668,10 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const displayNamesWithTooltips = useMemo(() => { const hasMultipleParticipants = participants.length > 1; - return ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs(participants, personalDetails), hasMultipleParticipants); + return getDisplayNamesWithTooltips(getPersonalDetailsForAccountIDs(participants, personalDetails), hasMultipleParticipants); }, [participants, personalDetails]); - const icons = useMemo(() => ReportUtils.getIcons(report, personalDetails, null, '', -1, policy), [report, personalDetails, policy]); + const icons = useMemo(() => getIcons(report, personalDetails, null, '', -1, policy), [report, personalDetails, policy]); const chatRoomSubtitleText = chatRoomSubtitle ? ( Navigation.navigate(ROUTES.REPORT_AVATAR.getRoute(report.reportID))} onImageRemoved={() => { // Calling this without a file will remove the avatar - Report.updateGroupChatAvatar(report.reportID); + updateGroupChatAvatar(report.reportID); }} - onImageSelected={(file) => Report.updateGroupChatAvatar(report.reportID, file)} + onImageSelected={(file) => updateGroupChatAvatar(report.reportID, file)} editIcon={Expensicons.Camera} editIconStyle={styles.smallEditIconAccount} pendingAction={report.pendingFields?.avatar ?? undefined} errors={report.errorFields?.avatar ?? null} errorRowStyles={styles.mt6} - onErrorClose={() => Report.clearAvatarErrors(report.reportID)} + onErrorClose={() => clearAvatarErrors(report.reportID)} shouldUseStyleUtilityForAnchorPosition style={[styles.w100, styles.mb3]} /> @@ -664,12 +737,12 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta ); }, [report, icons, isMoneyRequestReport, isInvoiceReport, isGroupChat, isThread, styles]); - const canHoldUnholdReportAction = ReportUtils.canHoldUnholdReportAction(moneyRequestAction); + const canHoldUnholdReportAction = canHoldUnholdReportActionUtil(moneyRequestAction); const shouldShowHoldAction = caseID !== CASES.DEFAULT && (canHoldUnholdReportAction.canHoldRequest || canHoldUnholdReportAction.canUnholdRequest) && - !ReportUtils.isArchivedNonExpenseReport(transactionThreadReportID ? report : parentReport, parentReportNameValuePairs); - const canJoin = ReportUtils.canJoinChat(report, parentReportAction, policy); + !isArchivedNonExpenseReport(transactionThreadReportID ? report : parentReport, parentReportNameValuePairs); + const canJoin = canJoinChat(report, parentReportAction, policy); const promotedActions = useMemo(() => { const result: PromotedAction[] = []; @@ -765,7 +838,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta pendingAction={report?.pendingFields?.reportName} errors={report?.errorFields?.reportName} errorRowStyles={[styles.ph5]} - onClose={() => Report.clearPolicyRoomNameErrors(report?.reportID)} + onClose={() => clearPolicyRoomNameErrors(report?.reportID)} > ((): OnyxTypes.PolicyReportField | undefined => { - const fields = ReportUtils.getAvailableReportFields(report, Object.values(policy?.fieldList ?? {})); - return fields.find((reportField) => ReportUtils.isReportFieldOfTypeTitle(reportField)); + const fields = getAvailableReportFields(report, Object.values(policy?.fieldList ?? {})); + return fields.find((reportField) => isReportFieldOfTypeTitle(reportField)); }, [report, policy?.fieldList]); - const fieldKey = ReportUtils.getReportFieldKey(titleField?.fieldID); - const isFieldDisabled = ReportUtils.isReportFieldDisabled(report, titleField, policy); + const fieldKey = getReportFieldKey(titleField?.fieldID); + const isFieldDisabled = isReportFieldDisabled(report, titleField, policy); - const shouldShowTitleField = caseID !== CASES.MONEY_REQUEST && !isFieldDisabled && ReportUtils.isAdminOwnerApproverOrReportOwner(report, policy); + const shouldShowTitleField = caseID !== CASES.MONEY_REQUEST && !isFieldDisabled && isAdminOwnerApproverOrReportOwner(report, policy); const nameSectionFurtherDetailsContent = ( { if (report.errorFields?.reportName) { - Report.clearPolicyRoomNameErrors(report.reportID); + clearPolicyRoomNameErrors(report.reportID); } - Report.clearReportFieldKeyErrors(report.reportID, fieldKey); + clearReportFieldKeyErrors(report.reportID, fieldKey); }} > @@ -841,7 +914,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const deleteTransaction = useCallback(() => { if (caseID === CASES.DEFAULT) { - Task.deleteTask(report); + deleteTask(report); return; } @@ -849,12 +922,12 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta return; } - const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); + const isTrackExpense = isTrackExpenseAction(requestParentReportAction); if (isTrackExpense) { - IOU.deleteTrackExpense(moneyRequestReport?.reportID, iouTransactionID, requestParentReportAction, isSingleTransactionView); + deleteTrackExpense(moneyRequestReport?.reportID, iouTransactionID, requestParentReportAction, isSingleTransactionView); } else { - IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); + deleteMoneyRequest(iouTransactionID, requestParentReportAction, isSingleTransactionView); } }, [caseID, iouTransactionID, isSingleTransactionView, moneyRequestReport?.reportID, report, requestParentReportAction]); @@ -884,19 +957,19 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta // Only proceed with navigation logic if transaction was actually deleted if (!isEmptyObject(requestParentReportAction)) { - const isTrackExpense = ReportActionsUtils.isTrackExpenseAction(requestParentReportAction); + const isTrackExpense = isTrackExpenseAction(requestParentReportAction); if (isTrackExpense) { - urlToNavigateBack = IOU.getNavigationUrlAfterTrackExpenseDelete(moneyRequestReport?.reportID, iouTransactionID, requestParentReportAction, isSingleTransactionView); + urlToNavigateBack = getNavigationUrlAfterTrackExpenseDelete(moneyRequestReport?.reportID, iouTransactionID, requestParentReportAction, isSingleTransactionView); } else { - urlToNavigateBack = IOU.getNavigationUrlOnMoneyRequestDelete(iouTransactionID, requestParentReportAction, isSingleTransactionView); + urlToNavigateBack = getNavigationUrlOnMoneyRequestDelete(iouTransactionID, requestParentReportAction, isSingleTransactionView); } } if (!urlToNavigateBack) { Navigation.dismissModal(); } else { - Report.setDeleteTransactionNavigateBackUrl(urlToNavigateBack); - ReportUtils.navigateBackOnDeleteTransaction(urlToNavigateBack as Route, true); + setDeleteTransactionNavigateBackUrl(urlToNavigateBack); + navigateBackOnDeleteTransaction(urlToNavigateBack as Route, true); } }, [iouTransactionID, requestParentReportAction, isSingleTransactionView, isTransactionDeleted, moneyRequestReport?.reportID]); @@ -925,7 +998,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta { setIsUnapproveModalVisible(false); Navigation.dismissModal(); - IOU.unapproveExpenseReport(moneyRequestReport); + unapproveExpenseReport(moneyRequestReport); }} cancelText={translate('common.cancel')} onCancel={() => setIsUnapproveModalVisible(false)} diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts index 142f1df6f367..b8a64dd883d8 100644 --- a/tests/actions/EnforceActionExportRestrictions.ts +++ b/tests/actions/EnforceActionExportRestrictions.ts @@ -1,3 +1,6 @@ +// this file is for testing which methods should not be exported so it is not possible to use named imports - that's why we need to disable the no-restricted-syntax rule + +/* eslint-disable no-restricted-syntax */ import * as IOU from '@libs/actions/IOU'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -19,11 +22,6 @@ describe('ReportUtils', () => { expect(ReportUtils.getReport).toBeUndefined(); }); - it('does not export getReportTransactions', () => { - // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal - expect(ReportUtils.getReportTransactions).toBeUndefined(); - }); - it('does not export isOneTransactionReport', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal expect(ReportUtils.isOneTransactionReport).toBeUndefined();