From ab30c652883d6eecca97d6834126cb7f0146cff1 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 13 Jun 2024 12:34:28 +0700 Subject: [PATCH 01/16] Remove export ReportUtil.getReport function --- .../AttachmentCarousel/extractAttachments.ts | 11 +++--- .../AttachmentCarousel/index.native.tsx | 2 +- .../Attachments/AttachmentCarousel/index.tsx | 4 +-- .../HTMLRenderers/MentionReportRenderer.tsx | 8 ++--- .../LHNOptionsList/OptionRowLHN.tsx | 5 ++- src/components/SettlementButton.tsx | 4 +-- src/libs/Navigation/Navigation.ts | 13 +++++-- .../subscribePushNotification/index.ts | 14 ++++++-- src/libs/OptionsListUtils.ts | 36 ++++++++++++++++--- src/libs/ReportUtils.ts | 13 ++++--- src/libs/actions/IOU.ts | 35 ++++++++++++++---- src/libs/actions/Report.ts | 16 +++++++-- src/libs/actions/ReportActions.ts | 35 ++++++++++-------- src/libs/actions/Task.ts | 16 +++++++-- src/pages/home/ReportScreen.tsx | 26 +++++++++++++- .../BaseReportActionContextMenu.tsx | 8 +++-- .../report/ContextMenu/ContextMenuActions.tsx | 9 +++-- src/pages/home/report/ReportActionItem.tsx | 7 ++-- .../report/ReportActionItemParentAction.tsx | 8 +++-- src/pages/home/report/ReportAttachments.tsx | 3 +- .../report/SystemChatReportFooterMessage.tsx | 9 ++--- src/pages/iou/HoldReasonPage.tsx | 3 +- .../step/IOURequestStepParticipants.tsx | 2 +- .../EnforceActionExportRestrictions.ts | 31 ++++++++++++++++ 24 files changed, 241 insertions(+), 77 deletions(-) diff --git a/src/components/Attachments/AttachmentCarousel/extractAttachments.ts b/src/components/Attachments/AttachmentCarousel/extractAttachments.ts index f2325eda532d..d3057bd1fed8 100644 --- a/src/components/Attachments/AttachmentCarousel/extractAttachments.ts +++ b/src/components/Attachments/AttachmentCarousel/extractAttachments.ts @@ -4,19 +4,22 @@ import type {ValueOf} from 'type-fest'; import type {Attachment} from '@components/Attachments/types'; import * as FileUtils from '@libs/fileDownload/FileUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import {getReport} from '@libs/ReportUtils'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; import CONST from '@src/CONST'; -import type {ReportAction, ReportActions} from '@src/types/onyx'; +import type {Report, ReportAction, ReportActions} from '@src/types/onyx'; /** * Constructs the initial component state from report actions */ function extractAttachments( type: ValueOf, - {reportID, accountID, parentReportAction, reportActions}: {reportID?: string; accountID?: number; parentReportAction?: OnyxEntry; reportActions?: OnyxEntry}, + { + report, + accountID, + parentReportAction, + reportActions, + }: {report?: OnyxEntry; accountID?: number; parentReportAction?: OnyxEntry; reportActions?: OnyxEntry}, ) { - const report = getReport(reportID); const privateNotes = report?.privateNotes; const targetNote = privateNotes?.[Number(accountID)]?.note ?? ''; const attachments: Attachment[] = []; diff --git a/src/components/Attachments/AttachmentCarousel/index.native.tsx b/src/components/Attachments/AttachmentCarousel/index.native.tsx index aad307073c0f..0d96cfbc900d 100644 --- a/src/components/Attachments/AttachmentCarousel/index.native.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.native.tsx @@ -33,7 +33,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const parentReportAction = report.parentReportActionID && parentReportActions ? parentReportActions[report.parentReportActionID] : undefined; let targetAttachments: Attachment[] = []; if (type === CONST.ATTACHMENT_TYPE.NOTE && accountID) { - targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {reportID: report.reportID, accountID}); + targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {report, accountID}); } else { targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.REPORT, {parentReportAction, reportActions}); } diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index 947569538d32..fcb8ba7826ee 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -59,7 +59,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const parentReportAction = report.parentReportActionID && parentReportActions ? parentReportActions[report.parentReportActionID] : undefined; let targetAttachments: Attachment[] = []; if (type === CONST.ATTACHMENT_TYPE.NOTE && accountID) { - targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {reportID: report.reportID, accountID}); + targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {report, accountID}); } else { targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.REPORT, {parentReportAction, reportActions: reportActions ?? undefined}); } @@ -91,7 +91,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate(targetAttachments[initialPage]); } } - }, [reportActions, parentReportActions, compareImage, report.parentReportActionID, attachments, setDownloadButtonVisibility, onNavigate, accountID, report.reportID, type]); + }, [report, reportActions, parentReportActions, compareImage, report.parentReportActionID, attachments, setDownloadButtonVisibility, onNavigate, accountID, report.reportID, type]); // Scroll position is affected when window width is resized, so we readjust it on width changes useEffect(() => { diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index 345bd338f365..9594acf60fc7 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -3,14 +3,13 @@ import React, {useMemo} from 'react'; import type {TextStyle} from 'react-native'; import {StyleSheet} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html'; import {ShowContextMenuContext} from '@components/ShowContextMenuContext'; import Text from '@components/Text'; import useCurrentReportID from '@hooks/useCurrentReportID'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getReport} from '@libs/ReportUtils'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -34,8 +33,7 @@ const getMentionDetails = (htmlAttributeReportID: string, currentReport: OnyxEnt // Get mention details based on reportID from tag attribute if (!isEmpty(htmlAttributeReportID)) { - const report = getReport(htmlAttributeReportID); - + const report = reports?.[htmlAttributeReportID]; reportID = report?.reportID ?? htmlAttributeReportID; mentionDisplayText = removeLeadingLTRAndHash(report?.reportName ?? report?.displayName ?? htmlAttributeReportID); // Get mention details from name inside tnode @@ -61,7 +59,7 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa const htmlAttributeReportID = tnode.attributes.reportid; const currentReportID = useCurrentReportID(); - const currentReport = getReport(currentReportID?.currentReportID); + const [currentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${currentReportID?.currentReportID}`); // When we invite someone to a room they don't have the policy object, but we still want them to be able to see and click on report mentions, so we only check if the policyID in the report is from a workspace const isGroupPolicyReport = useMemo(() => currentReport && !isEmptyObject(currentReport) && !!currentReport.policyID && currentReport.policyID !== CONST.POLICY.ID_FAKE, [currentReport]); diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index de7ffabe035e..e026f80ec747 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -3,6 +3,7 @@ import {ExpensiMark} from 'expensify-common'; import React, {useCallback, useRef, useState} from 'react'; import type {GestureResponderEvent, ViewStyle} from 'react-native'; import {StyleSheet, View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import DisplayNames from '@components/DisplayNames'; import Hoverable from '@components/Hoverable'; import Icon from '@components/Icon'; @@ -26,6 +27,7 @@ import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManag import * as ReportUtils from '@libs/ReportUtils'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {OptionRowLHNProps} from './types'; @@ -39,6 +41,8 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const isFocusedRef = useRef(true); const {shouldUseNarrowLayout} = useResponsiveLayout(); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${optionItem?.reportID ?? 0}`); + const {translate} = useLocalize(); const [isContextMenuActive, setIsContextMenuActive] = useState(false); @@ -122,7 +126,6 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const statusClearAfterDate = optionItem.status?.clearAfter ?? ''; const formattedDate = DateUtils.getStatusUntilDate(statusClearAfterDate); const statusContent = formattedDate ? `${statusText ? `${statusText} ` : ''}(${formattedDate})` : statusText; - const report = ReportUtils.getReport(optionItem.reportID ?? ''); const isStatusVisible = !!emojiCode && ReportUtils.isOneOnOneChat(!isEmptyObject(report) ? report : undefined); const isGroupChat = ReportUtils.isGroupChat(optionItem) || ReportUtils.isDeprecatedGroupDM(optionItem); diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 85954e68c5a9..39551d8e3cf3 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -1,7 +1,7 @@ import React, {useMemo} from 'react'; import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import * as ReportUtils from '@libs/ReportUtils'; @@ -144,7 +144,7 @@ function SettlementButton({ const {isOffline} = useNetwork(); const session = useSession(); - const chatReport = ReportUtils.getReport(chatReportID); + const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false; const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); const shouldShowPaywithExpensifyOption = !isPaidGroupPolicy || (!shouldHidePaymentOptions && ReportUtils.isPayer(session, iouReport as OnyxEntry)); diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 34e093f2b74b..6389876a1858 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -1,11 +1,13 @@ import {findFocusedRoute} from '@react-navigation/core'; import type {EventArg, NavigationContainerEventMap} from '@react-navigation/native'; import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native'; +import type {OnyxCollection} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; import * as ReportUtils from '@libs/ReportUtils'; -import {getReport} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; +import ONYXKEYS from '@src/ONYXKEYS'; import type {HybridAppRoute, Route} from '@src/ROUTES'; import ROUTES, {HYBRID_APP_ROUTES} from '@src/ROUTES'; import {PROTECTED_SCREENS} from '@src/SCREENS'; @@ -32,6 +34,13 @@ let pendingRoute: Route | null = null; let shouldPopAllStateOnUP = false; +let allReports: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => (allReports = value), +}); + /** * Inform the navigation that next time user presses UP we should pop all the state back to LHN. */ @@ -59,7 +68,7 @@ const dismissModal = (reportID?: string, ref = navigationRef) => { originalDismissModal(ref); return; } - const report = getReport(reportID); + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; originalDismissModalWithReport({reportID, ...report}, ref); }; // Re-exporting the closeRHPFlow here to fill in default value for navigationRef. The closeRHPFlow isn't defined in this file to avoid cyclic dependencies. diff --git a/src/libs/Notification/PushNotification/subscribePushNotification/index.ts b/src/libs/Notification/PushNotification/subscribePushNotification/index.ts index ec18f776d2d2..b8b2bbb71b3d 100644 --- a/src/libs/Notification/PushNotification/subscribePushNotification/index.ts +++ b/src/libs/Notification/PushNotification/subscribePushNotification/index.ts @@ -1,4 +1,5 @@ import Onyx from 'react-native-onyx'; +import type {OnyxCollection} from 'react-native-onyx'; import applyOnyxUpdatesReliably from '@libs/actions/applyOnyxUpdatesReliably'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import Log from '@libs/Log'; @@ -6,13 +7,13 @@ import Navigation from '@libs/Navigation/Navigation'; import type {ReportActionPushNotificationData} from '@libs/Notification/PushNotification/NotificationType'; import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; import {extractPolicyIDFromPath} from '@libs/PolicyUtils'; -import {doesReportBelongToWorkspace, getReport} from '@libs/ReportUtils'; +import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; import Visibility from '@libs/Visibility'; import * as Modal from '@userActions/Modal'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {OnyxUpdatesFromServer} from '@src/types/onyx'; +import type {OnyxUpdatesFromServer, Report} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import PushNotification from '..'; @@ -27,6 +28,13 @@ Onyx.connect({ }, }); +let allReports: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => (allReports = value), +}); + function getLastUpdateIDAppliedToClient(): Promise { return new Promise((resolve) => { Onyx.connect({ @@ -74,7 +82,7 @@ function navigateToReport({reportID, reportActionID}: ReportActionPushNotificati Log.info('[PushNotification] Navigating to report', false, {reportID, reportActionID}); const policyID = lastVisitedPath && extractPolicyIDFromPath(lastVisitedPath); - const report = getReport(reportID.toString()); + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const policyEmployeeAccountIDs = policyID ? getPolicyEmployeeAccountIDs(policyID) : []; const reportBelongsToWorkspace = policyID && !isEmptyObject(report) && doesReportBelongToWorkspace(report, policyEmployeeAccountIDs, policyID); diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 9b30764b065d..8fc4daeda379 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -335,6 +335,34 @@ Onyx.connect({ }, }); +let allReports: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => (allReports = value), +}); + +let allReportsDraft: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_DRAFT, + waitForCollectionCallback: true, + callback: (value) => (allReportsDraft = value), +}); + +/** + * Get the report given a reportID + */ +function getReport(reportID: string | undefined): OnyxEntry { + if (!allReports && !allReportsDraft) { + return undefined; + } + + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const draftReport = allReportsDraft?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`]; + + return report ?? draftReport; +} + /** * @param defaultValues {login: accountID} In workspace invite page, when new user is added we pass available data to opt in * @returns Returns avatar data for a list of user accountIDs @@ -577,7 +605,7 @@ function getLastActorDisplayName(lastActorDetails: Partial | nu * Update alternate text for the option when applicable */ function getAlternateText(option: ReportUtils.OptionData, {showChatPreviewLine = false, forcePolicyNamePreview = false}: PreviewConfig) { - const report = ReportUtils.getReport(option.reportID); + const report = getReport(option.reportID); const isAdminRoom = ReportUtils.isAdminRoom(report); const isAnnounceRoom = ReportUtils.isAnnounceRoom(report); @@ -653,7 +681,7 @@ function getLastMessageTextForReport(report: OnyxEntry, lastActorDetails const properSchemaForMoneyRequestMessage = ReportUtils.getReportPreviewMessage(report, lastReportAction, true, false, null, true); lastMessageTextFromReport = ReportUtils.formatReportLastMessageText(properSchemaForMoneyRequestMessage); } else if (ReportActionUtils.isReportPreviewAction(lastReportAction)) { - const iouReport = ReportUtils.getReport(ReportActionUtils.getIOUReportIDFromReportActionPreview(lastReportAction)); + const iouReport = getReport(ReportActionUtils.getIOUReportIDFromReportActionPreview(lastReportAction)); const lastIOUMoneyReportAction = allSortedReportActions[iouReport?.reportID ?? '']?.find( (reportAction, key) => ReportActionUtils.shouldReportActionBeVisible(reportAction, key) && @@ -838,7 +866,7 @@ function createOption( * Get the option for a given report. */ function getReportOption(participant: Participant): ReportUtils.OptionData { - const report = ReportUtils.getReport(participant.reportID); + const report = getReport(participant.reportID); // For 1:1 chat, we don't want to include currentUser as participants in order to not mark 1:1 chats as having multiple participants const isOneOnOneChat = ReportUtils.isOneOnOneChat(report); @@ -878,7 +906,7 @@ function getReportOption(participant: Participant): ReportUtils.OptionData { * Get the option for a policy expense report. */ function getPolicyExpenseReportOption(participant: Participant | ReportUtils.OptionData): ReportUtils.OptionData { - const expenseReport = ReportUtils.isPolicyExpenseChat(participant) ? ReportUtils.getReport(participant.reportID) : null; + const expenseReport = ReportUtils.isPolicyExpenseChat(participant) ? getReport(participant.reportID) : null; const visibleParticipantAccountIDs = Object.entries(expenseReport?.participants ?? {}) .filter(([, reportParticipant]) => reportParticipant && !reportParticipant.hidden) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 914c653b6f91..e4fec78c8be7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -884,7 +884,8 @@ function isPolicyExpenseChat(report: OnyxInputOrEntry | Participant | Em return getChatType(report) === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT || (report?.isPolicyExpenseChat ?? false); } -function isInvoiceRoom(report: OnyxEntry | EmptyObject): boolean { +function isInvoiceRoom(reportOrID: OnyxEntry | EmptyObject | string): boolean { + const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; return getChatType(report) === CONST.REPORT.CHAT_TYPE.INVOICE; } @@ -2166,7 +2167,8 @@ function getDeletedParentActionMessageForChatReport(reportAction: OnyxEntry, report: OnyxEntry, shouldUseShortDisplayName = true): string { +function getReimbursementQueuedActionMessage(reportAction: OnyxEntry, reportOrID: OnyxEntry | string, shouldUseShortDisplayName = true): string { + const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; const submitterDisplayName = getDisplayNameForParticipant(report?.ownerAccountID, shouldUseShortDisplayName) ?? ''; const originalMessage = reportAction?.originalMessage as IOUMessage | undefined; let messageKey: TranslationPaths; @@ -2184,9 +2186,10 @@ function getReimbursementQueuedActionMessage(reportAction: OnyxEntry, - report: OnyxEntry | EmptyObject, + reportOrID: OnyxEntry | EmptyObject | string, isLHNPreview = false, ): string { + const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; const originalMessage = reportAction?.originalMessage; const amount = originalMessage?.amount; const currency = originalMessage?.currency; @@ -2915,7 +2918,7 @@ function getTransactionReportName(reportAction: OnyxEntry | EmptyObject, + reportOrID: OnyxInputOrEntry | EmptyObject | string, iouReportAction: OnyxInputOrEntry | EmptyObject = {}, shouldConsiderScanningReceiptOrPendingRoute = false, isPreviewMessageForParentChatReport = false, @@ -2923,6 +2926,7 @@ function getReportPreviewMessage( isForListPreview = false, originalReportAction: OnyxInputOrEntry | EmptyObject = iouReportAction, ): string { + const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; const reportActionMessage = iouReportAction?.message?.[0]?.html ?? ''; if (isEmptyObject(report) || !report?.reportID) { @@ -7058,7 +7062,6 @@ export { getPolicyType, getReimbursementDeQueuedActionMessage, getReimbursementQueuedActionMessage, - getReport, getReportActionActorAccountID, getReportDescriptionText, getReportFieldKey, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e1b726be23c0..4da0d1beae50 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -172,6 +172,13 @@ Onyx.connect({ callback: (value) => (allReports = value ?? null), }); +let allReportsDraft: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_DRAFT, + waitForCollectionCallback: true, + callback: (value) => (allReportsDraft = value), +}); + let allTransactions: NonNullable> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, @@ -284,6 +291,20 @@ Onyx.connect({ }, }); +/** + * Get the report given a reportID + */ +function getReport(reportID: string | undefined): OnyxEntry { + if (!allReports && !allReportsDraft) { + return undefined; + } + + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const draftReport = allReportsDraft?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`]; + + return report ?? draftReport; +} + /** * Find the report preview action from given chat report and iou report */ @@ -2307,7 +2328,7 @@ function createDistanceRequest( ) { // If the report is an iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - const currentChatReport = isMoneyRequestReport ? ReportUtils.getReport(report?.chatReportID) : report; + const currentChatReport = isMoneyRequestReport ? getReport(report?.chatReportID) : report; const moneyRequestReportID = isMoneyRequestReport ? report?.reportID : ''; const optimisticReceipt: Receipt = { @@ -3418,7 +3439,7 @@ function requestMoney( ) { // If the report is iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - const currentChatReport = isMoneyRequestReport ? ReportUtils.getReport(report?.chatReportID) : report; + const currentChatReport = isMoneyRequestReport ? getReport(report?.chatReportID) : report; const moneyRequestReportID = isMoneyRequestReport ? report?.reportID : ''; const isMovingTransactionFromTrackExpense = IOUUtils.isMovingTransactionFromTrackExpense(action); @@ -3596,7 +3617,7 @@ function trackExpense( linkedTrackedExpenseReportID?: string, ) { const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - const currentChatReport = isMoneyRequestReport ? ReportUtils.getReport(report.chatReportID) : report; + const currentChatReport = isMoneyRequestReport ? getReport(report.chatReportID) : report; const moneyRequestReportID = isMoneyRequestReport ? report.reportID : ''; const isMovingTransactionFromTrackExpense = IOUUtils.isMovingTransactionFromTrackExpense(action); @@ -6167,7 +6188,7 @@ function hasIOUToApproveOrPay(chatReport: OnyxEntry | EmptyObj const chatReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`] ?? {}; return Object.values(chatReportActions).some((action) => { - const iouReport = ReportUtils.getReport(action.childReportID ?? ''); + const iouReport = getReport(action.childReportID ?? ''); const policy = PolicyUtils.getPolicy(iouReport?.policyID); const shouldShowSettlementButton = canIOUBePaid(iouReport, chatReport, policy) || canApproveIOU(iouReport, chatReport, policy); return action.childReportID?.toString() !== excludedIOUReportID && action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && shouldShowSettlementButton; @@ -6183,7 +6204,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full } const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport.currency ?? '', expenseReport.reportID); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.APPROVED); - const chatReport = ReportUtils.getReport(expenseReport.chatReportID); + const chatReport = getReport(expenseReport.chatReportID); const optimisticReportActionsData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, @@ -6309,7 +6330,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full function submitReport(expenseReport: OnyxTypes.Report) { const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; - const parentReport = ReportUtils.getReport(expenseReport.parentReportID); + const parentReport = getReport(expenseReport.parentReportID); const policy = PolicyUtils.getPolicy(expenseReport.policyID); const isCurrentUserManager = currentUserPersonalDetails.accountID === expenseReport.managerID; const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); @@ -6641,7 +6662,7 @@ function replaceReceipt(transactionID: string, file: File, source: string) { */ function setMoneyRequestParticipantsFromReport(transactionID: string, report: OnyxEntry): Participant[] { // If the report is iou or expense report, we should get the chat report to set participant for request money - const chatReport = ReportUtils.isMoneyRequestReport(report) ? ReportUtils.getReport(report?.chatReportID) : report; + const chatReport = ReportUtils.isMoneyRequestReport(report) ? getReport(report?.chatReportID) : report; const currentUserAccountID = currentUserPersonalDetails.accountID; const shouldAddAsReport = !isEmptyObject(chatReport) && ReportUtils.isSelfDM(chatReport); let participants: Participant[] = []; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 3adf48046936..60b5296ece73 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -268,6 +268,18 @@ Onyx.connect({ callback: (val) => (quickAction = val), }); +/** + * Get the report given a reportID + */ +function getReport(reportID: string | undefined): OnyxEntry { + if (!currentReportData || !reportID) { + return undefined; + } + + const report = currentReportData?.[reportID]; + return report; +} + function clearGroupChat() { Onyx.set(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, null); } @@ -477,7 +489,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { lastReadTime: currentTime, }; - const report = ReportUtils.getReport(reportID); + const report = getReport(reportID); if (!isEmptyObject(report) && ReportUtils.getReportNotificationPreference(report) === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { optimisticReport.notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS; @@ -2588,7 +2600,7 @@ function joinRoom(report: OnyxEntry) { } function leaveGroupChat(reportID: string) { - const report = ReportUtils.getReport(reportID); + const report = getReport(reportID); if (!report) { Log.warn('Attempting to leave Group Chat that does not existing locally'); return; diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 4fc72bae1e7d..37e4b5b1cce0 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -4,12 +4,31 @@ import * as ReportActionUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ReportActions} from '@src/types/onyx/ReportAction'; +import type {Report as OnyxReportType, ReportActions} from '@src/types/onyx'; import type ReportAction from '@src/types/onyx/ReportAction'; import * as Report from './Report'; type IgnoreDirection = 'parent' | 'child'; +let allReportActions: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (actions) => { + if (!actions) { + return; + } + allReportActions = actions; + }, +}); + +let allReports: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => (allReports = value), +}); + function clearReportActionErrors(reportID: string, reportAction: ReportAction, keys?: string[]) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); @@ -58,18 +77,6 @@ function clearReportActionErrors(reportID: string, reportAction: ReportAction, k }); } -let allReportActions: OnyxCollection; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, - waitForCollectionCallback: true, - callback: (actions) => { - if (!actions) { - return; - } - allReportActions = actions; - }, -}); - /** * ignore: `undefined` means we want to check both parent and children report actions @@ -83,7 +90,7 @@ function clearAllRelatedReportActionErrors(reportID: string, reportAction: Repor clearReportActionErrors(reportID, reportAction, keys); - const report = ReportUtils.getReport(reportID); + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (report?.parentReportID && report?.parentReportActionID && ignore !== 'parent') { const parentReportAction = ReportActionUtils.getReportAction(report.parentReportID, report.parentReportActionID); const parentErrorKeys = Object.keys(parentReportAction?.errors ?? {}).filter((err) => errorKeys.includes(err)); diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 7bff7bdea887..7f8584ce88de 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -85,6 +85,18 @@ Onyx.connect({ callback: (value) => (allReports = value), }); +/** + * Get the report given a reportID + */ +function getReport(reportID: string | undefined): OnyxEntry { + if (!allReports) { + return undefined; + } + + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + return report; +} + /** * Clears out the task info from the store */ @@ -1008,7 +1020,7 @@ function canModifyTask(taskReport: OnyxEntry, sessionAccountID return true; } - if (!ReportUtils.canWriteInReport(ReportUtils.getReport(taskReport?.reportID))) { + if (!ReportUtils.canWriteInReport(getReport(taskReport?.reportID))) { return false; } @@ -1016,7 +1028,7 @@ function canModifyTask(taskReport: OnyxEntry, sessionAccountID } function clearTaskErrors(reportID: string) { - const report = ReportUtils.getReport(reportID); + const report = getReport(reportID); // Delete the task preview in the parent report if (report?.pendingFields?.createChat === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 2ebd6fc2240c..3a4d41da81aa 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -149,6 +149,7 @@ function ReportScreen({ const [accountManagerReportID] = useOnyx(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, {initialValue: ''}); const [userLeavingStatus] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${getReportID(route)}`, {initialValue: false}); const [reportOnyx, reportResult] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getReportID(route)}`, {allowStaleData: true}); + const [parentReportOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportOnyx?.parentReportID ? reportOnyx?.parentReportID : 0}`); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportOnyx?.parentReportID || 0}`, { canEvict: false, @@ -251,6 +252,29 @@ function ReportScreen({ ], ); + const parentReport = useMemo( + (): OnyxTypes.Report => ({ + reportID: parentReportOnyx?.reportID ?? '', + type: parentReportOnyx?.type, + errorFields: parentReportOnyx?.errorFields, + parentReportID: parentReportOnyx?.parentReportID, + parentReportActionID: parentReportOnyx?.parentReportActionID, + pendingFields: parentReportOnyx?.pendingFields, + isDeletedParentAction: parentReportOnyx?.isDeletedParentAction, + pendingAction: parentReportOnyx?.pendingAction, + }), + [ + parentReportOnyx?.reportID, + parentReportOnyx?.type, + parentReportOnyx?.errorFields, + parentReportOnyx?.parentReportID, + parentReportOnyx?.parentReportActionID, + parentReportOnyx?.pendingFields, + parentReportOnyx?.isDeletedParentAction, + parentReportOnyx?.pendingAction, + ], + ); + const prevReport = usePrevious(report); const prevUserLeavingStatus = usePrevious(userLeavingStatus); const [isLinkingToMessage, setIsLinkingToMessage] = useState(!!reportActionIDFromRoute); @@ -530,7 +554,6 @@ function ReportScreen({ } if (prevReport.parentReportID) { // Prevent navigation to the IOU/Expense Report if it is pending deletion. - const parentReport = ReportUtils.getReport(prevReport.parentReportID); if (ReportUtils.isMoneyRequestReportPendingDeletion(parentReport)) { return; } @@ -566,6 +589,7 @@ function ReportScreen({ prevReport, reportIDFromRoute, isFocused, + parentReport, ]); useEffect(() => { diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index f363edf420ce..96597ade9a94 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -5,7 +5,7 @@ import {InteractionManager, View} from 'react-native'; // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, Text as RNText, View as ViewType} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {ContextMenuItemHandle} from '@components/ContextMenuItem'; import ContextMenuItem from '@components/ContextMenuItem'; import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal'; @@ -135,6 +135,10 @@ function BaseReportActionContextMenu({ return reportActions[reportActionID]; }, [reportActions, reportActionID]); + const originalReportID = useMemo(() => ReportUtils.getOriginalReportID(reportID, reportAction), [reportID, reportAction]); + + const [originalReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`); + const shouldEnableArrowNavigation = !isMini && (isVisible || shouldKeepOpen); let filteredContextMenuActions = ContextMenuActions.filter( (contextAction) => @@ -202,8 +206,6 @@ function BaseReportActionContextMenu({ ); const openOverflowMenu = (event: GestureResponderEvent | MouseEvent, anchorRef: MutableRefObject) => { - const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); - const originalReport = ReportUtils.getReport(originalReportID); showContextMenu( CONST.CONTEXT_MENU_TYPES.REPORT_ACTION, event, diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index fc25e29b5778..bba0881ba71a 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -365,8 +365,8 @@ const ContextMenuActions: ContextMenuAction[] = [ if (!isAttachment) { const content = selection || messageHtml; if (isReportPreviewAction) { - const iouReport = ReportUtils.getReport(ReportActionsUtils.getIOUReportIDFromReportActionPreview(reportAction)); - const displayMessage = ReportUtils.getReportPreviewMessage(iouReport, reportAction); + const iouReportID = ReportActionsUtils.getIOUReportIDFromReportActionPreview(reportAction); + const displayMessage = ReportUtils.getReportPreviewMessage(iouReportID, reportAction); Clipboard.setString(displayMessage); } else if (ReportActionsUtils.isTaskAction(reportAction)) { const displayMessage = TaskUtils.getTaskReportActionMessage(reportAction).text; @@ -376,8 +376,7 @@ const ContextMenuActions: ContextMenuAction[] = [ Clipboard.setString(modifyExpenseMessage); } else if (ReportActionsUtils.isReimbursementDeQueuedAction(reportAction)) { const {expenseReportID} = reportAction.originalMessage; - const expenseReport = ReportUtils.getReport(expenseReportID); - const displayMessage = ReportUtils.getReimbursementDeQueuedActionMessage(reportAction, expenseReport); + const displayMessage = ReportUtils.getReimbursementDeQueuedActionMessage(reportAction, expenseReportID); Clipboard.setString(displayMessage); } else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { const displayMessage = ReportUtils.getIOUReportActionDisplayMessage(reportAction, transaction); @@ -389,7 +388,7 @@ const ContextMenuActions: ContextMenuAction[] = [ const logMessage = ReportActionsUtils.getMemberChangeMessageFragment(reportAction).html ?? ''; setClipboardMessage(logMessage); } else if (ReportActionsUtils.isReimbursementQueuedAction(reportAction)) { - Clipboard.setString(ReportUtils.getReimbursementQueuedActionMessage(reportAction, ReportUtils.getReport(reportID), false)); + Clipboard.setString(ReportUtils.getReimbursementQueuedActionMessage(reportAction, reportID, false)); } else if (ReportActionsUtils.isActionableMentionWhisper(reportAction)) { const mentionWhisperMessage = ReportActionsUtils.getActionableMentionWhisperMessage(reportAction); setClipboardMessage(mentionWhisperMessage); diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 01ba83a26fc0..84083328c764 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -3,7 +3,7 @@ import React, {memo, useCallback, useContext, useEffect, useMemo, useRef, useSta import type {GestureResponderEvent, TextInput} from 'react-native'; import {InteractionManager, View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {Emoji} from '@assets/emojis/types'; import {AttachmentContext} from '@components/AttachmentContext'; import Button from '@components/Button'; @@ -213,7 +213,8 @@ function ReportActionItem({ const downloadedPreviews = useRef([]); const prevDraftMessage = usePrevious(draftMessage); const originalReportID = ReportUtils.getOriginalReportID(report.reportID, action); - const originalReport = report.reportID === originalReportID ? report : ReportUtils.getReport(originalReportID); + const [originalReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`); + const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`); const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID; const transactionCurrency = TransactionUtils.getCurrency(transaction); const reportScrollManager = useReportScrollManager(); @@ -574,7 +575,7 @@ function ReportActionItem({ ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENT_QUEUED) { - const linkedReport = ReportUtils.isChatThread(report) ? ReportUtils.getReport(report.parentReportID) : report; + const linkedReport = ReportUtils.isChatThread(report) ? parentReport : report; const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails[linkedReport?.ownerAccountID ?? -1]); const paymentType = action.originalMessage.paymentType ?? ''; diff --git a/src/pages/home/report/ReportActionItemParentAction.tsx b/src/pages/home/report/ReportActionItemParentAction.tsx index dd4e35510ed9..a4a1909e8cf2 100644 --- a/src/pages/home/report/ReportActionItemParentAction.tsx +++ b/src/pages/home/report/ReportActionItemParentAction.tsx @@ -63,6 +63,7 @@ function ReportActionItemParentAction({ }: ReportActionItemParentActionProps) { const styles = useThemeStyles(); const ancestorIDs = useRef(ReportUtils.getAllAncestorReportActionIDs(report)); + const ancestorReports = useRef>>({}); const [allAncestors, setAllAncestors] = useState([]); const {isOffline} = useNetwork(); @@ -73,7 +74,8 @@ function ReportActionItemParentAction({ unsubscribeReports.push( onyxSubscribe({ key: `${ONYXKEYS.COLLECTION.REPORT}${ancestorReportID}`, - callback: () => { + callback: (val) => { + ancestorReports.current[ancestorReportID] = val; setAllAncestors(ReportUtils.getAllAncestorReportActions(report)); }, }), @@ -109,11 +111,11 @@ function ReportActionItemParentAction({ > { const isVisibleAction = ReportActionsUtils.shouldReportActionBeVisible(ancestor.reportAction, ancestor.reportAction.reportActionID ?? ''); // Pop the thread report screen before navigating to the chat report. diff --git a/src/pages/home/report/ReportAttachments.tsx b/src/pages/home/report/ReportAttachments.tsx index c537fedfe994..555a9b5b8930 100644 --- a/src/pages/home/report/ReportAttachments.tsx +++ b/src/pages/home/report/ReportAttachments.tsx @@ -6,7 +6,6 @@ import type {Attachment} from '@components/Attachments/types'; import ComposerFocusManager from '@libs/ComposerFocusManager'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; -import * as ReportUtils from '@libs/ReportUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; @@ -17,7 +16,7 @@ function ReportAttachments({route}: ReportAttachmentsProps) { const reportID = route.params.reportID; const type = route.params.type; const accountID = route.params.accountID; - const report = ReportUtils.getReport(reportID); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); // In native the imported images sources are of type number. Ref: https://reactnative.dev/docs/image#imagesource diff --git a/src/pages/home/report/SystemChatReportFooterMessage.tsx b/src/pages/home/report/SystemChatReportFooterMessage.tsx index 6d739762c610..da91f3cfaa50 100644 --- a/src/pages/home/report/SystemChatReportFooterMessage.tsx +++ b/src/pages/home/report/SystemChatReportFooterMessage.tsx @@ -1,5 +1,5 @@ import React, {useMemo} from 'react'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx, withOnyx} from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Banner from '@components/Banner'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -8,7 +8,6 @@ import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import Navigation from '@navigation/Navigation'; import * as ReportInstance from '@userActions/Report'; import type {OnboardingPurposeType} from '@src/CONST'; @@ -34,14 +33,16 @@ function SystemChatReportFooterMessage({choice, policies, activePolicyID}: Syste const {translate} = useLocalize(); const styles = useThemeStyles(); - const adminChatReport = useMemo(() => { + const adminChatReportID = useMemo(() => { const adminPolicy = activePolicyID ? PolicyUtils.getPolicy(activePolicyID) : Object.values(policies ?? {}).find((policy) => PolicyUtils.shouldShowPolicy(policy, false) && policy?.role === CONST.POLICY.ROLE.ADMIN && policy?.chatReportIDAdmins); - return ReportUtils.getReport(String(adminPolicy?.chatReportIDAdmins)); + return String(adminPolicy?.chatReportIDAdmins); }, [activePolicyID, policies]); + const [adminChatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${adminChatReportID}`); + const content = useMemo(() => { switch (choice) { case CONST.ONBOARDING_CHOICES.MANAGE_TEAM: diff --git a/src/pages/iou/HoldReasonPage.tsx b/src/pages/iou/HoldReasonPage.tsx index 18b290a81ea4..dbbc09912c47 100644 --- a/src/pages/iou/HoldReasonPage.tsx +++ b/src/pages/iou/HoldReasonPage.tsx @@ -1,6 +1,7 @@ import type {RouteProp} from '@react-navigation/native'; import React, {useCallback, useEffect} from 'react'; import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; @@ -45,7 +46,7 @@ function HoldReasonPage({route}: HoldReasonPageProps) { const {transactionID, reportID, backTo} = route.params; - const report = ReportUtils.getReport(reportID); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${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/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index d9d7a8d192a0..7f1f8961076a 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -92,7 +92,7 @@ function IOURequestStepParticipants({ const firstParticipantReportID = val[0]?.reportID ?? ''; const rateID = DistanceRequestUtils.getCustomUnitRateID(firstParticipantReportID); - const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && ReportUtils.isInvoiceRoom(ReportUtils.getReport(firstParticipantReportID)); + const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && ReportUtils.isInvoiceRoom(firstParticipantReportID); numberOfParticipants.current = val.length; IOU.setMoneyRequestParticipants(transactionID, val); diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts index 710c37789a2f..fe11790c9724 100644 --- a/tests/actions/EnforceActionExportRestrictions.ts +++ b/tests/actions/EnforceActionExportRestrictions.ts @@ -1,6 +1,8 @@ import * as IOU from '@libs/actions/IOU'; +import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as Policy from '@userActions/Policy/Policy'; +import * as Report from '@userActions/Report'; import * as Task from '@userActions/Task'; // There are some methods that are OK to use inside an action file, but should not be exported. These are typically methods that look up and return Onyx data. @@ -27,6 +29,11 @@ describe('ReportUtils', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal expect(ReportUtils.getAllReportActions).toBeUndefined(); }); + + it('does not export getReport', () => { + // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal + expect(ReportUtils.getReport).toBeUndefined(); + }); }); describe('Policy', () => { @@ -41,6 +48,11 @@ describe('IOU', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal expect(IOU.getPolicy).toBeUndefined(); }); + + it('does not export getReport', () => { + // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal + expect(IOU.getReport).toBeUndefined(); + }); }); describe('Task', () => { @@ -48,4 +60,23 @@ describe('Task', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal expect(Task.getParentReport).toBeUndefined(); }); + + it('does not export getReport', () => { + // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal + expect(Task.getReport).toBeUndefined(); + }); +}); + +describe('Report', () => { + it('does not export getReport', () => { + // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal + expect(Report.getReport).toBeUndefined(); + }); +}); + +describe('OptionsListUtils', () => { + it('does not export getReport', () => { + // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal + expect(OptionsListUtils.getReport).toBeUndefined(); + }); }); From 395f4a092604ccbc5e6eea8c63eabb6132d87b06 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 17 Jun 2024 11:20:35 +0700 Subject: [PATCH 02/16] rename function --- .../AttachmentCarousel/extractAttachments.ts | 8 ++--- .../AttachmentCarousel/index.native.tsx | 2 +- .../Attachments/AttachmentCarousel/index.tsx | 2 +- src/libs/ModifiedExpenseMessage.ts | 12 +++++-- src/libs/OptionsListUtils.ts | 12 +++---- src/libs/ReportUtils.ts | 18 ++++------ src/libs/actions/IOU.ts | 18 +++++----- src/libs/actions/Report.ts | 16 ++------- src/libs/actions/Task.ts | 16 ++------- src/libs/getReportPolicyID.ts | 33 ------------------- src/pages/home/ReportScreen.tsx | 27 +-------------- .../BaseReportActionContextMenu.tsx | 8 ++--- 12 files changed, 44 insertions(+), 128 deletions(-) delete mode 100644 src/libs/getReportPolicyID.ts diff --git a/src/components/Attachments/AttachmentCarousel/extractAttachments.ts b/src/components/Attachments/AttachmentCarousel/extractAttachments.ts index d3057bd1fed8..38abe075ef81 100644 --- a/src/components/Attachments/AttachmentCarousel/extractAttachments.ts +++ b/src/components/Attachments/AttachmentCarousel/extractAttachments.ts @@ -6,7 +6,8 @@ import * as FileUtils from '@libs/fileDownload/FileUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; import CONST from '@src/CONST'; -import type {Report, ReportAction, ReportActions} from '@src/types/onyx'; +import type {ReportAction, ReportActions} from '@src/types/onyx'; +import type {Note} from '@src/types/onyx/Report'; /** * Constructs the initial component state from report actions @@ -14,13 +15,12 @@ import type {Report, ReportAction, ReportActions} from '@src/types/onyx'; function extractAttachments( type: ValueOf, { - report, + privateNotes, accountID, parentReportAction, reportActions, - }: {report?: OnyxEntry; accountID?: number; parentReportAction?: OnyxEntry; reportActions?: OnyxEntry}, + }: {privateNotes?: Record; accountID?: number; parentReportAction?: OnyxEntry; reportActions?: OnyxEntry}, ) { - const privateNotes = report?.privateNotes; const targetNote = privateNotes?.[Number(accountID)]?.note ?? ''; const attachments: Attachment[] = []; diff --git a/src/components/Attachments/AttachmentCarousel/index.native.tsx b/src/components/Attachments/AttachmentCarousel/index.native.tsx index 0d96cfbc900d..d17ee20497f4 100644 --- a/src/components/Attachments/AttachmentCarousel/index.native.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.native.tsx @@ -33,7 +33,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const parentReportAction = report.parentReportActionID && parentReportActions ? parentReportActions[report.parentReportActionID] : undefined; let targetAttachments: Attachment[] = []; if (type === CONST.ATTACHMENT_TYPE.NOTE && accountID) { - targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {report, accountID}); + targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {privateNotes: report?.privateNotes, accountID}); } else { targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.REPORT, {parentReportAction, reportActions}); } diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index fcb8ba7826ee..be8d4d883d18 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -59,7 +59,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const parentReportAction = report.parentReportActionID && parentReportActions ? parentReportActions[report.parentReportActionID] : undefined; let targetAttachments: Attachment[] = []; if (type === CONST.ATTACHMENT_TYPE.NOTE && accountID) { - targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {report, accountID}); + targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {privateNotes: report?.privateNotes, accountID}); } else { targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.REPORT, {parentReportAction, reportActions: reportActions ?? undefined}); } diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index fc2288c5dbf6..da781f6c0bc9 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -2,11 +2,10 @@ import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PolicyTagList, ReportAction} from '@src/types/onyx'; +import type {PolicyTagList, Report, ReportAction} from '@src/types/onyx'; import type {ModifiedExpense} from '@src/types/onyx/OriginalMessage'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; -import getReportPolicyID from './getReportPolicyID'; import * as Localize from './Localize'; import * as PolicyUtils from './PolicyUtils'; import * as TransactionUtils from './TransactionUtils'; @@ -24,6 +23,13 @@ Onyx.connect({ }, }); +let allReports: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => (allReports = value), +}); + /** * Builds the partial message fragment for a modified field on the expense. */ @@ -110,7 +116,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr return ''; } const reportActionOriginalMessage = reportAction?.originalMessage as ModifiedExpense | undefined; - const policyID = getReportPolicyID(reportID) ?? '-1'; + const policyID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.policyID ?? '-1'; const removalFragments: string[] = []; const setFragments: string[] = []; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 9eb82ed43e6f..84b49f49bcd2 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -350,9 +350,9 @@ Onyx.connect({ }); /** - * Get the report given a reportID + * Get the report or draft report given a reportID */ -function getReport(reportID: string | undefined): OnyxEntry { +function getReportOrDraftReport(reportID: string | undefined): OnyxEntry { if (!allReports && !allReportsDraft) { return undefined; } @@ -605,7 +605,7 @@ function getLastActorDisplayName(lastActorDetails: Partial | nu * Update alternate text for the option when applicable */ function getAlternateText(option: ReportUtils.OptionData, {showChatPreviewLine = false, forcePolicyNamePreview = false}: PreviewConfig) { - const report = getReport(option.reportID); + const report = getReportOrDraftReport(option.reportID); const isAdminRoom = ReportUtils.isAdminRoom(report); const isAnnounceRoom = ReportUtils.isAnnounceRoom(report); @@ -681,7 +681,7 @@ function getLastMessageTextForReport(report: OnyxEntry, lastActorDetails const properSchemaForMoneyRequestMessage = ReportUtils.getReportPreviewMessage(report, lastReportAction, true, false, null, true); lastMessageTextFromReport = ReportUtils.formatReportLastMessageText(properSchemaForMoneyRequestMessage); } else if (ReportActionUtils.isReportPreviewAction(lastReportAction)) { - const iouReport = getReport(ReportActionUtils.getIOUReportIDFromReportActionPreview(lastReportAction)); + const iouReport = getReportOrDraftReport(ReportActionUtils.getIOUReportIDFromReportActionPreview(lastReportAction)); const lastIOUMoneyReportAction = allSortedReportActions[iouReport?.reportID ?? '-1']?.find( (reportAction, key) => ReportActionUtils.shouldReportActionBeVisible(reportAction, key) && @@ -866,7 +866,7 @@ function createOption( * Get the option for a given report. */ function getReportOption(participant: Participant): ReportUtils.OptionData { - const report = getReport(participant.reportID); + const report = getReportOrDraftReport(participant.reportID); // For 1:1 chat, we don't want to include currentUser as participants in order to not mark 1:1 chats as having multiple participants const isOneOnOneChat = ReportUtils.isOneOnOneChat(report); @@ -906,7 +906,7 @@ function getReportOption(participant: Participant): ReportUtils.OptionData { * Get the option for a policy expense report. */ function getPolicyExpenseReportOption(participant: Participant | ReportUtils.OptionData): ReportUtils.OptionData { - const expenseReport = ReportUtils.isPolicyExpenseChat(participant) ? getReport(participant.reportID) : null; + const expenseReport = ReportUtils.isPolicyExpenseChat(participant) ? getReportOrDraftReport(participant.reportID) : null; const visibleParticipantAccountIDs = Object.entries(expenseReport?.participants ?? {}) .filter(([, reportParticipant]) => reportParticipant && !reportParticipant.hidden) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 949f74d8f086..eec706c2b20c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -64,7 +64,6 @@ import * as store from './actions/ReimbursementAccount/store'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import {hasValidDraftComment} from './DraftCommentUtils'; -import originalGetReportPolicyID from './getReportPolicyID'; import isReportMessageAttachment from './isReportMessageAttachment'; import localeCompare from './LocaleCompare'; import * as LocalePhoneNumber from './LocalePhoneNumber'; @@ -1228,7 +1227,8 @@ function isClosedExpenseReportWithNoExpenses(report: OnyxEntry): boolean /** * Whether the provided report is an archived room */ -function isArchivedRoom(report: OnyxInputOrEntry | EmptyObject, reportNameValuePairs?: OnyxInputOrEntry | EmptyObject): boolean { +function isArchivedRoom(reportOrID: OnyxInputOrEntry | EmptyObject | string, reportNameValuePairs?: OnyxInputOrEntry | EmptyObject): boolean { + const report = typeof reportOrID === 'string' ? getReport(reportOrID) : reportOrID; if (reportNameValuePairs) { return reportNameValuePairs.isArchived; } @@ -5537,7 +5537,8 @@ function getAllPolicyReports(policyID: string): Array> { /** * Returns true if Chronos is one of the chat participants (1:1) */ -function chatIncludesChronos(report: OnyxInputOrEntry | EmptyObject): boolean { +function chatIncludesChronos(reportOrID: OnyxInputOrEntry | EmptyObject | string): boolean { + const report = typeof reportOrID === 'string' ? getReport(reportOrID) : reportOrID; const participantAccountIDs = Object.keys(report?.participants ?? {}).map(Number); return participantAccountIDs.includes(CONST.ACCOUNT_ID.CHRONOS); } @@ -5683,13 +5684,6 @@ function getReportIDFromLink(url: string | null): string { return reportID; } -/** - * Get the report policyID given a reportID - */ -function getReportPolicyID(reportID?: string): string | undefined { - return originalGetReportPolicyID(reportID); -} - /** * Check if the chat report is linked to an iou that is waiting for the current user to add a credit bank account. */ @@ -6038,7 +6032,8 @@ function getAddWorkspaceRoomOrChatReportErrors(report: OnyxEntry): Error /** * Return true if the expense report is marked for deletion. */ -function isMoneyRequestReportPendingDeletion(report: OnyxEntry | EmptyObject): boolean { +function isMoneyRequestReportPendingDeletion(reportOrID: OnyxEntry | EmptyObject | string): boolean { + const report = typeof reportOrID === 'string' ? getReport(reportOrID) : reportOrID; if (!isMoneyRequestReport(report)) { return false; } @@ -7078,7 +7073,6 @@ export { getReportNotificationPreference, getReportOfflinePendingActionAndErrors, getReportParticipantsTitle, - getReportPolicyID, getReportPreviewMessage, getReportRecipientAccountIDs, getRoom, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 694043dc8ed5..00890aa3b5a5 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -292,9 +292,9 @@ Onyx.connect({ }); /** - * Get the report given a reportID + * Get the report or draft report given a reportID */ -function getReport(reportID: string | undefined): OnyxEntry { +function getReportOrDraftReport(reportID: string | undefined): OnyxEntry { if (!allReports && !allReportsDraft) { return undefined; } @@ -2328,7 +2328,7 @@ function createDistanceRequest( ) { // If the report is an iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - const currentChatReport = isMoneyRequestReport ? getReport(report?.chatReportID) : report; + const currentChatReport = isMoneyRequestReport ? getReportOrDraftReport(report?.chatReportID) : report; const moneyRequestReportID = isMoneyRequestReport ? report?.reportID : ''; const optimisticReceipt: Receipt = { @@ -3439,7 +3439,7 @@ function requestMoney( ) { // If the report is iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - const currentChatReport = isMoneyRequestReport ? getReport(report?.chatReportID) : report; + const currentChatReport = isMoneyRequestReport ? getReportOrDraftReport(report?.chatReportID) : report; const moneyRequestReportID = isMoneyRequestReport ? report?.reportID : ''; const isMovingTransactionFromTrackExpense = IOUUtils.isMovingTransactionFromTrackExpense(action); @@ -3617,7 +3617,7 @@ function trackExpense( linkedTrackedExpenseReportID?: string, ) { const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - const currentChatReport = isMoneyRequestReport ? getReport(report.chatReportID) : report; + const currentChatReport = isMoneyRequestReport ? getReportOrDraftReport(report.chatReportID) : report; const moneyRequestReportID = isMoneyRequestReport ? report.reportID : ''; const isMovingTransactionFromTrackExpense = IOUUtils.isMovingTransactionFromTrackExpense(action); @@ -6188,7 +6188,7 @@ function hasIOUToApproveOrPay(chatReport: OnyxEntry | EmptyObj const chatReportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`] ?? {}; return Object.values(chatReportActions).some((action) => { - const iouReport = getReport(action.childReportID ?? '-1'); + const iouReport = getReportOrDraftReport(action.childReportID ?? '-1'); const policy = PolicyUtils.getPolicy(iouReport?.policyID); const shouldShowSettlementButton = canIOUBePaid(iouReport, chatReport, policy) || canApproveIOU(iouReport, chatReport, policy); return action.childReportID?.toString() !== excludedIOUReportID && action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && shouldShowSettlementButton; @@ -6204,7 +6204,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full } const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport.currency ?? '', expenseReport.reportID); const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.APPROVED); - const chatReport = getReport(expenseReport.chatReportID); + const chatReport = getReportOrDraftReport(expenseReport.chatReportID); const optimisticReportActionsData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, @@ -6330,7 +6330,7 @@ function approveMoneyRequest(expenseReport: OnyxTypes.Report | EmptyObject, full function submitReport(expenseReport: OnyxTypes.Report) { const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; - const parentReport = getReport(expenseReport.parentReportID); + const parentReport = getReportOrDraftReport(expenseReport.parentReportID); const policy = PolicyUtils.getPolicy(expenseReport.policyID); const isCurrentUserManager = currentUserPersonalDetails.accountID === expenseReport.managerID; const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); @@ -6662,7 +6662,7 @@ function replaceReceipt(transactionID: string, file: File, source: string) { */ function setMoneyRequestParticipantsFromReport(transactionID: string, report: OnyxEntry): Participant[] { // If the report is iou or expense report, we should get the chat report to set participant for request money - const chatReport = ReportUtils.isMoneyRequestReport(report) ? getReport(report?.chatReportID) : report; + const chatReport = ReportUtils.isMoneyRequestReport(report) ? getReportOrDraftReport(report?.chatReportID) : report; const currentUserAccountID = currentUserPersonalDetails.accountID; const shouldAddAsReport = !isEmptyObject(chatReport) && ReportUtils.isSelfDM(chatReport); let participants: Participant[] = []; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index ed251622d8b0..733e24edd9f4 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -269,18 +269,6 @@ Onyx.connect({ callback: (val) => (quickAction = val), }); -/** - * Get the report given a reportID - */ -function getReport(reportID: string | undefined): OnyxEntry { - if (!currentReportData || !reportID) { - return undefined; - } - - const report = currentReportData?.[reportID]; - return report; -} - function clearGroupChat() { Onyx.set(ONYXKEYS.NEW_GROUP_CHAT_DRAFT, null); } @@ -490,7 +478,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { lastReadTime: currentTime, }; - const report = getReport(reportID); + const report = currentReportData?.[reportID]; if (!isEmptyObject(report) && ReportUtils.getReportNotificationPreference(report) === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { optimisticReport.notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS; @@ -2601,7 +2589,7 @@ function joinRoom(report: OnyxEntry) { } function leaveGroupChat(reportID: string) { - const report = getReport(reportID); + const report = currentReportData?.[reportID]; if (!report) { Log.warn('Attempting to leave Group Chat that does not existing locally'); return; diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 8cb0eab68e76..10b3d87b3a9a 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -85,18 +85,6 @@ Onyx.connect({ callback: (value) => (allReports = value), }); -/** - * Get the report given a reportID - */ -function getReport(reportID: string | undefined): OnyxEntry { - if (!allReports) { - return undefined; - } - - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; - return report; -} - /** * Clears out the task info from the store */ @@ -1020,7 +1008,7 @@ function canModifyTask(taskReport: OnyxEntry, sessionAccountID return true; } - if (!ReportUtils.canWriteInReport(getReport(taskReport?.reportID))) { + if (!ReportUtils.canWriteInReport(taskReport)) { return false; } @@ -1028,7 +1016,7 @@ function canModifyTask(taskReport: OnyxEntry, sessionAccountID } function clearTaskErrors(reportID: string) { - const report = getReport(reportID); + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; // Delete the task preview in the parent report if (report?.pendingFields?.createChat === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { diff --git a/src/libs/getReportPolicyID.ts b/src/libs/getReportPolicyID.ts deleted file mode 100644 index 12124f24fbe7..000000000000 --- a/src/libs/getReportPolicyID.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import Onyx from 'react-native-onyx'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report} from '@src/types/onyx'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; - -let allReports: OnyxCollection; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (value) => (allReports = value), -}); - -/** - * Get the report given a reportID - */ -function getReport(reportID: string | undefined): OnyxEntry | EmptyObject { - if (!allReports) { - return {}; - } - - return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? {}; -} - -/** - * Get the report policyID given a reportID. - * We need to define this method in a separate file to avoid cyclic dependency. - */ -function getReportPolicyID(reportID?: string): string | undefined { - return getReport(reportID)?.policyID; -} - -export default getReportPolicyID; diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 3a4d41da81aa..680ee4332509 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -149,7 +149,6 @@ function ReportScreen({ const [accountManagerReportID] = useOnyx(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, {initialValue: ''}); const [userLeavingStatus] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${getReportID(route)}`, {initialValue: false}); const [reportOnyx, reportResult] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getReportID(route)}`, {allowStaleData: true}); - const [parentReportOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportOnyx?.parentReportID ? reportOnyx?.parentReportID : 0}`); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportOnyx?.parentReportID || 0}`, { canEvict: false, @@ -252,29 +251,6 @@ function ReportScreen({ ], ); - const parentReport = useMemo( - (): OnyxTypes.Report => ({ - reportID: parentReportOnyx?.reportID ?? '', - type: parentReportOnyx?.type, - errorFields: parentReportOnyx?.errorFields, - parentReportID: parentReportOnyx?.parentReportID, - parentReportActionID: parentReportOnyx?.parentReportActionID, - pendingFields: parentReportOnyx?.pendingFields, - isDeletedParentAction: parentReportOnyx?.isDeletedParentAction, - pendingAction: parentReportOnyx?.pendingAction, - }), - [ - parentReportOnyx?.reportID, - parentReportOnyx?.type, - parentReportOnyx?.errorFields, - parentReportOnyx?.parentReportID, - parentReportOnyx?.parentReportActionID, - parentReportOnyx?.pendingFields, - parentReportOnyx?.isDeletedParentAction, - parentReportOnyx?.pendingAction, - ], - ); - const prevReport = usePrevious(report); const prevUserLeavingStatus = usePrevious(userLeavingStatus); const [isLinkingToMessage, setIsLinkingToMessage] = useState(!!reportActionIDFromRoute); @@ -554,7 +530,7 @@ function ReportScreen({ } if (prevReport.parentReportID) { // Prevent navigation to the IOU/Expense Report if it is pending deletion. - if (ReportUtils.isMoneyRequestReportPendingDeletion(parentReport)) { + if (ReportUtils.isMoneyRequestReportPendingDeletion(prevReport.parentReportID)) { return; } Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(prevReport.parentReportID)); @@ -589,7 +565,6 @@ function ReportScreen({ prevReport, reportIDFromRoute, isFocused, - parentReport, ]); useEffect(() => { diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 3361ccf185a5..24ea61e3ffc1 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -5,7 +5,7 @@ import {InteractionManager, View} from 'react-native'; // eslint-disable-next-line no-restricted-imports import type {GestureResponderEvent, Text as RNText, View as ViewType} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import type {ContextMenuItemHandle} from '@components/ContextMenuItem'; import ContextMenuItem from '@components/ContextMenuItem'; import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal'; @@ -137,8 +137,6 @@ function BaseReportActionContextMenu({ const originalReportID = useMemo(() => ReportUtils.getOriginalReportID(reportID, reportAction), [reportID, reportAction]); - const [originalReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`); - const shouldEnableArrowNavigation = !isMini && (isVisible || shouldKeepOpen); let filteredContextMenuActions = ContextMenuActions.filter( (contextAction) => @@ -220,8 +218,8 @@ function BaseReportActionContextMenu({ checkIfContextMenuActive?.(); setShouldKeepOpen(false); }, - ReportUtils.isArchivedRoom(originalReport), - ReportUtils.chatIncludesChronos(originalReport), + ReportUtils.isArchivedRoom(originalReportID), + ReportUtils.chatIncludesChronos(originalReportID), undefined, undefined, filteredContextMenuActions, From d202401da9e3a1f6760fcd326f506434bc76c19f Mon Sep 17 00:00:00 2001 From: nkdengineer <161821005+nkdengineer@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:21:02 +0700 Subject: [PATCH 03/16] Update src/pages/home/report/ReportActionItemParentAction.tsx Co-authored-by: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> --- src/pages/home/report/ReportActionItemParentAction.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemParentAction.tsx b/src/pages/home/report/ReportActionItemParentAction.tsx index f723643835bb..765c1deb7f70 100644 --- a/src/pages/home/report/ReportActionItemParentAction.tsx +++ b/src/pages/home/report/ReportActionItemParentAction.tsx @@ -111,7 +111,7 @@ function ReportActionItemParentAction({ > Date: Mon, 17 Jun 2024 11:21:11 +0700 Subject: [PATCH 04/16] Update src/pages/home/report/ReportActionItemParentAction.tsx Co-authored-by: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> --- src/pages/home/report/ReportActionItemParentAction.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemParentAction.tsx b/src/pages/home/report/ReportActionItemParentAction.tsx index 765c1deb7f70..720c2b174164 100644 --- a/src/pages/home/report/ReportActionItemParentAction.tsx +++ b/src/pages/home/report/ReportActionItemParentAction.tsx @@ -115,7 +115,7 @@ function ReportActionItemParentAction({ /> { const isVisibleAction = ReportActionsUtils.shouldReportActionBeVisible(ancestor.reportAction, ancestor.reportAction.reportActionID ?? '-1'); // Pop the thread report screen before navigating to the chat report. From be3b5adde9c32c9b4b0d5f2bd52e3352f4a430d5 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 17 Jun 2024 17:05:31 +0700 Subject: [PATCH 05/16] remove unnecessary test --- tests/actions/EnforceActionExportRestrictions.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts index fe11790c9724..53561789e9dd 100644 --- a/tests/actions/EnforceActionExportRestrictions.ts +++ b/tests/actions/EnforceActionExportRestrictions.ts @@ -60,11 +60,6 @@ describe('Task', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal expect(Task.getParentReport).toBeUndefined(); }); - - it('does not export getReport', () => { - // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal - expect(Task.getReport).toBeUndefined(); - }); }); describe('Report', () => { From 0eac7b751398597ad7ec54a6a080740f4a6b32a6 Mon Sep 17 00:00:00 2001 From: nkdengineer <161821005+nkdengineer@users.noreply.github.com> Date: Tue, 18 Jun 2024 14:32:40 +0700 Subject: [PATCH 06/16] Update src/components/Attachments/AttachmentCarousel/index.native.tsx Co-authored-by: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> --- src/components/Attachments/AttachmentCarousel/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Attachments/AttachmentCarousel/index.native.tsx b/src/components/Attachments/AttachmentCarousel/index.native.tsx index d17ee20497f4..15740725c42e 100644 --- a/src/components/Attachments/AttachmentCarousel/index.native.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.native.tsx @@ -33,7 +33,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const parentReportAction = report.parentReportActionID && parentReportActions ? parentReportActions[report.parentReportActionID] : undefined; let targetAttachments: Attachment[] = []; if (type === CONST.ATTACHMENT_TYPE.NOTE && accountID) { - targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {privateNotes: report?.privateNotes, accountID}); + targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {privateNotes: report.privateNotes, accountID}); } else { targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.REPORT, {parentReportAction, reportActions}); } From d6aa99500799a0a2d2cd47c8c5175722481de454 Mon Sep 17 00:00:00 2001 From: nkdengineer <161821005+nkdengineer@users.noreply.github.com> Date: Tue, 18 Jun 2024 14:32:54 +0700 Subject: [PATCH 07/16] Update src/components/Attachments/AttachmentCarousel/index.tsx Co-authored-by: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> --- src/components/Attachments/AttachmentCarousel/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index be8d4d883d18..d0173f211fb9 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -59,7 +59,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const parentReportAction = report.parentReportActionID && parentReportActions ? parentReportActions[report.parentReportActionID] : undefined; let targetAttachments: Attachment[] = []; if (type === CONST.ATTACHMENT_TYPE.NOTE && accountID) { - targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {privateNotes: report?.privateNotes, accountID}); + targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.NOTE, {privateNotes: report.privateNotes, accountID}); } else { targetAttachments = extractAttachments(CONST.ATTACHMENT_TYPE.REPORT, {parentReportAction, reportActions: reportActions ?? undefined}); } From 91b85707d59a57780d898b609d2730af902ea664 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 18 Jun 2024 14:41:19 +0700 Subject: [PATCH 08/16] rename function --- .../Attachments/AttachmentCarousel/index.tsx | 2 +- src/libs/ReportUtils.ts | 70 +++++++++---------- .../EnforceActionExportRestrictions.ts | 13 +--- 3 files changed, 39 insertions(+), 46 deletions(-) diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index d0173f211fb9..eeac97bc5fa5 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -91,7 +91,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, onNavigate(targetAttachments[initialPage]); } } - }, [report, reportActions, parentReportActions, compareImage, report.parentReportActionID, attachments, setDownloadButtonVisibility, onNavigate, accountID, report.reportID, type]); + }, [report.privateNotes, reportActions, parentReportActions, compareImage, report.parentReportActionID, attachments, setDownloadButtonVisibility, onNavigate, accountID, type]); // Scroll position is affected when window width is resized, so we readjust it on width changes useEffect(() => { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9ad4609b46f7..2ff5c4ee377c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -584,9 +584,9 @@ function getChatType(report: OnyxInputOrEntry | Participant | EmptyObjec } /** - * Get the report given a reportID + * Get the report or draft report given a reportID */ -function getReport(reportID: string | undefined): OnyxEntry { +function getReportOrDraftReport(reportID: string | undefined): OnyxEntry { if (!allReports && !allReportsDraft) { return undefined; } @@ -630,7 +630,7 @@ function getRootParentReport(report: OnyxEntry | undefined | EmptyObject return report; } - const parentReport = getReport(report?.parentReportID); + const parentReport = getReportOrDraftReport(report?.parentReportID); // Runs recursion to iterate a parent report return getRootParentReport(!isEmptyObject(parentReport) ? parentReport : undefined); @@ -1228,7 +1228,7 @@ function isClosedExpenseReportWithNoExpenses(report: OnyxEntry): boolean * Whether the provided report is an archived room */ function isArchivedRoom(reportOrID: OnyxInputOrEntry | EmptyObject | string, reportNameValuePairs?: OnyxInputOrEntry | EmptyObject): boolean { - const report = typeof reportOrID === 'string' ? getReport(reportOrID) : reportOrID; + const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; if (reportNameValuePairs) { return reportNameValuePairs.isArchived; } @@ -1526,7 +1526,7 @@ function canAddOrDeleteTransactions(moneyRequestReport: OnyxEntry): bool * policy admin */ function canDeleteReportAction(reportAction: OnyxInputOrEntry, reportID: string): boolean { - const report = getReport(reportID); + const report = getReportOrDraftReport(reportID); const isActionOwner = reportAction?.actorAccountID === currentUserAccountID; const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`] ?? null; @@ -1539,7 +1539,7 @@ function canDeleteReportAction(reportAction: OnyxInputOrEntry, rep return false; } - const linkedReport = isThreadFirstChat(reportAction, reportID) ? getReport(report?.parentReportID) : report; + const linkedReport = isThreadFirstChat(reportAction, reportID) ? getReportOrDraftReport(report?.parentReportID) : report; if (isActionOwner) { if (!isEmptyObject(linkedReport) && isMoneyRequestReport(linkedReport)) { return canAddOrDeleteTransactions(linkedReport); @@ -1841,7 +1841,7 @@ function getDisplayNameForParticipant(accountID?: number, shouldUseShortForm = f } function getParticipantAccountIDs(reportID: string, includeOnlyActiveMembers = false) { - const report = getReport(reportID); + const report = getReportOrDraftReport(reportID); if (!report || !report.participants) { return []; } @@ -1896,7 +1896,7 @@ function getGroupChatName(participantAccountIDs?: number[], shouldApplyLimit = f } function getVisibleChatMemberAccountIDs(reportID: string): number[] { - const report = getReport(reportID); + const report = getReportOrDraftReport(reportID); if (!report || !report.participants) { return []; } @@ -1910,7 +1910,7 @@ function getVisibleChatMemberAccountIDs(reportID: string): number[] { } function getParticipants(reportID: string) { - const report = getReport(reportID); + const report = getReportOrDraftReport(reportID); if (!report) { return {}; } @@ -2071,7 +2071,7 @@ function getIcons( } if (isInvoiceReport(report)) { - const invoiceRoomReport = getReport(report.chatReportID); + const invoiceRoomReport = getReportOrDraftReport(report.chatReportID); const icons = [getWorkspaceIcon(invoiceRoomReport, policy)]; if (invoiceRoomReport?.invoiceReceiver?.type === CONST.REPORT.INVOICE_RECEIVER_TYPE.INDIVIDUAL) { @@ -2249,7 +2249,7 @@ function buildOptimisticCancelPaymentReportAction(expenseReportID: string, amoun */ function getLastVisibleMessage(reportID: string | undefined, actionsToMerge: ReportActions = {}): LastVisibleMessage { - const report = getReport(reportID); + const report = getReportOrDraftReport(reportID); const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(reportID ?? '-1', actionsToMerge); // For Chat Report with deleted parent actions, let us fetch the correct message @@ -2301,7 +2301,7 @@ function requiresAttentionFromCurrentUser(optionOrReport: OnyxEntry | Op return true; } - if (isArchivedRoom(optionOrReport) || isArchivedRoom(getReport(optionOrReport.parentReportID))) { + if (isArchivedRoom(optionOrReport) || isArchivedRoom(getReportOrDraftReport(optionOrReport.parentReportID))) { return false; } @@ -2569,7 +2569,7 @@ function getTransactionDetails(transaction: OnyxInputOrEntry, creat if (!transaction) { return; } - const report = getReport(transaction?.reportID); + const report = getReportOrDraftReport(transaction?.reportID); return { created: TransactionUtils.getFormattedCreated(transaction, createdDateFormat), amount: TransactionUtils.getAmount(transaction, !isEmptyObject(report) && isExpenseReport(report)), @@ -2633,7 +2633,7 @@ function canEditMoneyRequest(reportAction: OnyxInputOrEntry): bool return reportAction.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.TRACK; } - const moneyRequestReport = getReport(String(moneyRequestReportID)); + const moneyRequestReport = getReportOrDraftReport(String(moneyRequestReportID)); const isRequestor = currentUserAccountID === reportAction?.actorAccountID; if (isIOUReport(moneyRequestReport)) { @@ -2740,7 +2740,7 @@ function canHoldUnholdReportAction(reportAction: OnyxInputOrEntry) } const moneyRequestReportID = reportAction?.originalMessage?.IOUReportID ?? 0; - const moneyRequestReport = getReport(String(moneyRequestReportID)); + const moneyRequestReport = getReportOrDraftReport(String(moneyRequestReportID)); if (!moneyRequestReportID || !moneyRequestReport) { return {canHoldRequest: false, canUnholdRequest: false}; @@ -2751,7 +2751,7 @@ function canHoldUnholdReportAction(reportAction: OnyxInputOrEntry) const transactionID = moneyRequestReport ? reportAction?.originalMessage?.IOUTransactionID : 0; const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? ({} as Transaction); - const parentReport = getReport(String(moneyRequestReport.parentReportID)); + const parentReport = getReportOrDraftReport(String(moneyRequestReport.parentReportID)); const parentReportAction = ReportActionsUtils.getParentReportAction(moneyRequestReport); const isRequestIOU = parentReport?.type === 'iou'; @@ -2781,7 +2781,7 @@ const changeMoneyRequestHoldStatus = (reportAction: OnyxEntry): vo } const moneyRequestReportID = reportAction?.originalMessage?.IOUReportID ?? 0; - const moneyRequestReport = getReport(String(moneyRequestReportID)); + const moneyRequestReport = getReportOrDraftReport(String(moneyRequestReportID)); if (!moneyRequestReportID || !moneyRequestReport) { return; } @@ -3233,7 +3233,7 @@ function parseReportActionHtmlToText(reportAction: OnyxEntry, repo for (const match of matches) { if (match[1] !== childReportID) { // eslint-disable-next-line @typescript-eslint/no-use-before-define - reportIDToName[match[1]] = getReportName(getReport(match[1])) ?? ''; + reportIDToName[match[1]] = getReportName(getReportOrDraftReport(match[1])) ?? ''; } } @@ -3266,7 +3266,7 @@ function getReportActionMessage(reportAction: ReportAction | EmptyObject, report return ReportActionsUtils.getReportActionMessageText(reportAction); } if (ReportActionsUtils.isReimbursementQueuedAction(reportAction)) { - return getReimbursementQueuedActionMessage(reportAction, getReport(reportID), false); + return getReimbursementQueuedActionMessage(reportAction, getReportOrDraftReport(reportID), false); } return parseReportActionHtmlToText(reportAction, reportID ?? '', childReportID); @@ -3589,7 +3589,7 @@ function addDomainToShortMention(mention: string): string | undefined { function getParsedComment(text: string, parsingDetails?: ParsingDetails): string { let isGroupPolicyReport = false; if (parsingDetails?.reportID) { - const currentReport = getReport(parsingDetails?.reportID); + const currentReport = getReportOrDraftReport(parsingDetails?.reportID); isGroupPolicyReport = isReportInGroupPolicy(currentReport); } @@ -4007,7 +4007,7 @@ function getIOUSubmittedMessage(report: OnyxEntry) { * @param isSettlingUp - Whether we are settling up an IOU */ function getIOUReportActionMessage(iouReportID: string, type: string, total: number, comment: string, currency: string, paymentType = '', isSettlingUp = false): Message[] { - const report = getReport(iouReportID); + const report = getReportOrDraftReport(iouReportID); if (type === CONST.REPORT.ACTIONS.TYPE.SUBMITTED) { return getIOUSubmittedMessage(!isEmptyObject(report) ? report : undefined); @@ -5075,7 +5075,7 @@ function buildTransactionThread( existingTransactionThreadReportID?: string, ): OptimisticChatReport { const participantAccountIDs = [...new Set([currentUserAccountID, Number(reportAction?.actorAccountID)])].filter(Boolean) as number[]; - const existingTransactionThreadReport = getReport(existingTransactionThreadReportID); + const existingTransactionThreadReport = getReportOrDraftReport(existingTransactionThreadReportID); if (existingTransactionThreadReportID && existingTransactionThreadReport) { return { @@ -5257,7 +5257,7 @@ function canAccessReport(report: OnyxEntry, policies: OnyxCollection, currentReportId: string): boolean { - const currentReport = getReport(currentReportId); + const currentReport = getReportOrDraftReport(currentReportId); const parentReport = getParentReport(!isEmptyObject(currentReport) ? currentReport : undefined); const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`] ?? {}; const isChildReportHasComment = Object.values(reportActions ?? {})?.some((reportAction) => (reportAction?.childVisibleActionCount ?? 0) > 0); @@ -5538,7 +5538,7 @@ function getAllPolicyReports(policyID: string): Array> { * Returns true if Chronos is one of the chat participants (1:1) */ function chatIncludesChronos(reportOrID: OnyxInputOrEntry | EmptyObject | string): boolean { - const report = typeof reportOrID === 'string' ? getReport(reportOrID) : reportOrID; + const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; const participantAccountIDs = Object.keys(report?.participants ?? {}).map(Number); return participantAccountIDs.includes(CONST.ACCOUNT_ID.CHRONOS); } @@ -5551,12 +5551,12 @@ function chatIncludesChronos(reportOrID: OnyxInputOrEntry | EmptyObject * - It's an ADD_COMMENT that is not an attachment */ function canFlagReportAction(reportAction: OnyxInputOrEntry, reportID: string | undefined): boolean { - let report = getReport(reportID); + let report = getReportOrDraftReport(reportID); // If the childReportID exists in reportAction and is equal to the reportID, // the report action being evaluated is the parent report action in a thread, and we should get the parent report to evaluate instead. if (reportAction?.childReportID?.toString() === reportID?.toString()) { - report = getReport(report?.parentReportID); + report = getReportOrDraftReport(report?.parentReportID); } const isCurrentUserAction = reportAction?.actorAccountID === currentUserAccountID; if (ReportActionsUtils.isWhisperAction(reportAction)) { @@ -5904,7 +5904,7 @@ function canLeaveRoom(report: OnyxEntry, isPolicyEmployee: boolean): boo return false; } - const invoiceReport = getReport(report?.iouReportID ?? '-1'); + const invoiceReport = getReportOrDraftReport(report?.iouReportID ?? '-1'); if (invoiceReport?.ownerAccountID === currentUserAccountID) { return false; @@ -6033,7 +6033,7 @@ function getAddWorkspaceRoomOrChatReportErrors(report: OnyxEntry): Error * Return true if the expense report is marked for deletion. */ function isMoneyRequestReportPendingDeletion(reportOrID: OnyxEntry | EmptyObject | string): boolean { - const report = typeof reportOrID === 'string' ? getReport(reportOrID) : reportOrID; + const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; if (!isMoneyRequestReport(report)) { return false; } @@ -6285,7 +6285,7 @@ function getIOUReportActionDisplayMessage(reportAction: OnyxEntry, } const originalMessage = reportAction.originalMessage; const {IOUReportID} = originalMessage; - const iouReport = getReport(IOUReportID); + const iouReport = getReportOrDraftReport(IOUReportID); let translationKey: TranslationPaths; if (originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.PAY) { // The `REPORT_ACTION_TYPE.PAY` action type is used for both fulfilling existing requests and sending money. To @@ -6565,7 +6565,7 @@ function shouldDisableThread(reportAction: OnyxInputOrEntry, repor const isReportPreviewAction = ReportActionsUtils.isReportPreviewAction(reportAction); const isIOUAction = ReportActionsUtils.isMoneyRequestAction(reportAction); const isWhisperAction = ReportActionsUtils.isWhisperAction(reportAction) || ReportActionsUtils.isActionableTrackExpense(reportAction); - const isArchivedReport = isArchivedRoom(getReport(reportID)); + const isArchivedReport = isArchivedRoom(getReportOrDraftReport(reportID)); const isActionDisabled = CONST.REPORT.ACTIONS.THREAD_DISABLED.some((action: string) => action === reportAction?.actionName); return ( @@ -6590,7 +6590,7 @@ function getAllAncestorReportActions(report: Report | null | undefined): Ancesto let currentReport = report; while (parentReportID) { - const parentReport = getReport(parentReportID); + const parentReport = getReportOrDraftReport(parentReportID); const parentReportAction = ReportActionsUtils.getReportAction(parentReportID, parentReportActionID ?? '-1'); if (!parentReportAction || ReportActionsUtils.isTransactionThread(parentReportAction) || ReportActionsUtils.isReportPreviewAction(parentReportAction)) { @@ -6634,7 +6634,7 @@ function getAllAncestorReportActionIDs(report: Report | null | undefined, includ let parentReportActionID = report.parentReportActionID; while (parentReportID) { - const parentReport = getReport(parentReportID); + const parentReport = getReportOrDraftReport(parentReportID); const parentReportAction = ReportActionsUtils.getReportAction(parentReportID, parentReportActionID ?? '-1'); if ( @@ -6665,7 +6665,7 @@ function getAllAncestorReportActionIDs(report: Report | null | undefined, includ * @param type The type of action in the child report */ function getOptimisticDataForParentReportAction(reportID: string, lastVisibleActionCreated: string, type: string): Array { - const report = getReport(reportID); + const report = getReportOrDraftReport(reportID); if (!report || isEmptyObject(report)) { return []; @@ -6675,7 +6675,7 @@ function getOptimisticDataForParentReportAction(reportID: string, lastVisibleAct const totalAncestor = ancestors.reportIDs.length; return Array.from(Array(totalAncestor), (_, index) => { - const ancestorReport = getReport(ancestors.reportIDs[index]); + const ancestorReport = getReportOrDraftReport(ancestors.reportIDs[index]); if (!ancestorReport || isEmptyObject(ancestorReport)) { return {} as EmptyObject; @@ -6779,7 +6779,7 @@ function getTripTransactions(tripRoomReportID: string | undefined): Transaction[ } function getTripIDFromTransactionParentReport(transactionParentReport: OnyxEntry | undefined | null): string | undefined { - return getReport(transactionParentReport?.parentReportID)?.tripData?.tripID; + return getReportOrDraftReport(transactionParentReport?.parentReportID)?.tripData?.tripID; } /** diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts index 53561789e9dd..a6801b0f28f4 100644 --- a/tests/actions/EnforceActionExportRestrictions.ts +++ b/tests/actions/EnforceActionExportRestrictions.ts @@ -32,7 +32,7 @@ describe('ReportUtils', () => { it('does not export getReport', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal - expect(ReportUtils.getReport).toBeUndefined(); + expect(ReportUtils.getReportOrDraftReport).toBeUndefined(); }); }); @@ -51,7 +51,7 @@ describe('IOU', () => { it('does not export getReport', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal - expect(IOU.getReport).toBeUndefined(); + expect(IOU.getReportOrDraftReport).toBeUndefined(); }); }); @@ -62,16 +62,9 @@ describe('Task', () => { }); }); -describe('Report', () => { - it('does not export getReport', () => { - // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal - expect(Report.getReport).toBeUndefined(); - }); -}); - describe('OptionsListUtils', () => { it('does not export getReport', () => { // @ts-expect-error the test is asserting that it's undefined, so the TS error is normal - expect(OptionsListUtils.getReport).toBeUndefined(); + expect(OptionsListUtils.getReportOrDraftReport).toBeUndefined(); }); }); From 55e868b79ab0b2ce999cf711b08b4aaa51bee9e1 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 18 Jun 2024 14:58:19 +0700 Subject: [PATCH 09/16] fix app crash bug --- src/components/SettlementButton.tsx | 3 ++- src/pages/home/report/ReportActionItem.tsx | 13 +++++++------ tests/actions/EnforceActionExportRestrictions.ts | 1 - 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 86052183475c..44c17fc31845 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -144,7 +144,8 @@ function SettlementButton({ const {isOffline} = useNetwork(); const session = useSession(); - const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); + // The chatReportID can be an empty string that will make the app crash if we use a nullish ?? operator + const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`); const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false; const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); const shouldShowPaywithExpensifyOption = !isPaidGroupPolicy || (!shouldHidePaymentOptions && ReportUtils.isPayer(session, iouReport as OnyxEntry)); diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 655d16c3c506..b746fff72a9f 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -213,8 +213,9 @@ function ReportActionItem({ const downloadedPreviews = useRef([]); const prevDraftMessage = usePrevious(draftMessage); const originalReportID = ReportUtils.getOriginalReportID(report.reportID, action); - const [originalReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`); - const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`); + // The parentReportID can be an empty string that will make the app crash if we use a nullish ?? operator + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || -1}`); const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID; const transactionCurrency = TransactionUtils.getCurrency(transaction); const reportScrollManager = useReportScrollManager(); @@ -355,8 +356,8 @@ function ReportActionItem({ draftMessage ?? '', () => setIsContextMenuActive(true), toggleContextMenuFromActiveReportAction, - ReportUtils.isArchivedRoom(originalReport), - ReportUtils.chatIncludesChronos(originalReport), + ReportUtils.isArchivedRoom(originalReportID), + ReportUtils.chatIncludesChronos(originalReportID), false, false, [], @@ -364,7 +365,7 @@ function ReportActionItem({ setIsEmojiPickerActive as () => void, ); }, - [draftMessage, action, report.reportID, toggleContextMenuFromActiveReportAction, originalReport, originalReportID], + [draftMessage, action, report.reportID, toggleContextMenuFromActiveReportAction, originalReportID], ); // Handles manual scrolling to the bottom of the chat when the last message is an actionable whisper and it's resolved. @@ -984,7 +985,7 @@ function ReportActionItem({ disabledActions={!ReportUtils.canWriteInReport(report) ? RestrictedReadOnlyContextMenuActions : []} isVisible={hovered && draftMessage === undefined && !hasErrors} draftMessage={draftMessage} - isChronosReport={ReportUtils.chatIncludesChronos(originalReport)} + isChronosReport={ReportUtils.chatIncludesChronos(originalReportID)} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} setIsEmojiPickerActive={setIsEmojiPickerActive} /> diff --git a/tests/actions/EnforceActionExportRestrictions.ts b/tests/actions/EnforceActionExportRestrictions.ts index a6801b0f28f4..bd2a13304078 100644 --- a/tests/actions/EnforceActionExportRestrictions.ts +++ b/tests/actions/EnforceActionExportRestrictions.ts @@ -2,7 +2,6 @@ import * as IOU from '@libs/actions/IOU'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as Policy from '@userActions/Policy/Policy'; -import * as Report from '@userActions/Report'; import * as Task from '@userActions/Task'; // There are some methods that are OK to use inside an action file, but should not be exported. These are typically methods that look up and return Onyx data. From 72ba07919f8b65a3e7ca273f2d73028f55edc657 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 18 Jun 2024 15:01:33 +0700 Subject: [PATCH 10/16] edit comment --- src/components/SettlementButton.tsx | 2 +- src/pages/home/report/ReportActionItem.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 44c17fc31845..e7b6f441bd1a 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -144,7 +144,7 @@ function SettlementButton({ const {isOffline} = useNetwork(); const session = useSession(); - // The chatReportID can be an empty string that will make the app crash if we use a nullish ?? operator + // The chatReportID can be an empty string that can make the app crash if we use a nullish ?? operator const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`); const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false; const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index b746fff72a9f..bd4971580764 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -213,7 +213,7 @@ function ReportActionItem({ const downloadedPreviews = useRef([]); const prevDraftMessage = usePrevious(draftMessage); const originalReportID = ReportUtils.getOriginalReportID(report.reportID, action); - // The parentReportID can be an empty string that will make the app crash if we use a nullish ?? operator + // The parentReportID can be an empty string that can make the app crash if we use a nullish ?? operator // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || -1}`); const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID; From 834d19a23baa723315fa3f32bc6c369f45b6e1de Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 18 Jun 2024 15:10:33 +0700 Subject: [PATCH 11/16] fix lint --- src/pages/home/report/ReportActionItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index bd4971580764..61fb55372591 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -213,7 +213,7 @@ function ReportActionItem({ const downloadedPreviews = useRef([]); const prevDraftMessage = usePrevious(draftMessage); const originalReportID = ReportUtils.getOriginalReportID(report.reportID, action); - // The parentReportID can be an empty string that can make the app crash if we use a nullish ?? operator + // The parentReportID can be an empty string that can make the app crash if we use a nullish ?? operator // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || -1}`); const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID; From 65bba3d66303c6b46b6f24cd615eb5ae9e431a78 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 18 Jun 2024 15:12:05 +0700 Subject: [PATCH 12/16] edit comment --- src/components/SettlementButton.tsx | 2 +- src/pages/home/report/ReportActionItem.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index e7b6f441bd1a..fe689e989064 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -144,7 +144,7 @@ function SettlementButton({ const {isOffline} = useNetwork(); const session = useSession(); - // The chatReportID can be an empty string that can make the app crash if we use a nullish ?? operator + // The chatReportID can be an empty string that can make the app crashes if we use a nullish ?? operator const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`); const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false; const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 61fb55372591..6b3ead078415 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -213,7 +213,7 @@ function ReportActionItem({ const downloadedPreviews = useRef([]); const prevDraftMessage = usePrevious(draftMessage); const originalReportID = ReportUtils.getOriginalReportID(report.reportID, action); - // The parentReportID can be an empty string that can make the app crash if we use a nullish ?? operator + // The parentReportID can be an empty string that can make the app crashes if we use a nullish ?? operator // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || -1}`); const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID; From ae9f408005bb326f95433c9f58aa90b7cc03cd95 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 19 Jun 2024 10:25:04 +0700 Subject: [PATCH 13/16] resolve conflict --- .../HTMLRenderers/MentionReportRenderer.tsx | 2 +- src/libs/ReportUtils.ts | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index 9594acf60fc7..1ecefb8c24dc 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -59,7 +59,7 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa const htmlAttributeReportID = tnode.attributes.reportid; const currentReportID = useCurrentReportID(); - const [currentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${currentReportID?.currentReportID}`); + const [currentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${currentReportID?.currentReportID || -1}`); // When we invite someone to a room they don't have the policy object, but we still want them to be able to see and click on report mentions, so we only check if the policyID in the report is from a workspace const isGroupPolicyReport = useMemo(() => currentReport && !isEmptyObject(currentReport) && !!currentReport.policyID && currentReport.policyID !== CONST.POLICY.ID_FAKE, [currentReport]); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9ec3d516b29b..aedbfcb9fe48 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1898,23 +1898,6 @@ function getGroupChatName(participantAccountIDs?: number[], shouldApplyLimit = f return Localize.translateLocal('groupChat.defaultReportName', {displayName: getDisplayNameForParticipant(participants[0], false)}); } -<<<<<<< HEAD -function getVisibleChatMemberAccountIDs(reportID: string): number[] { - const report = getReportOrDraftReport(reportID); - if (!report || !report.participants) { - return []; - } - const visibleParticipantAccountIDs = Object.entries(report.participants).reduce((accountIDs, [accountID, participant]) => { - if (participant && !participant.hidden) { - accountIDs.push(Number(accountID)); - } - return accountIDs; - }, []); - return visibleParticipantAccountIDs; -} - -======= ->>>>>>> main function getParticipants(reportID: string) { const report = getReportOrDraftReport(reportID); if (!report) { From 82204dda61772aebad38a3ab4be39cb84af6d0cb Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 19 Jun 2024 10:36:08 +0700 Subject: [PATCH 14/16] fallback reportID to -1 --- .../HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx | 1 + src/components/LHNOptionsList/OptionRowLHN.tsx | 3 ++- src/pages/home/report/ReportAttachments.tsx | 2 +- src/pages/home/report/SystemChatReportFooterMessage.tsx | 2 +- src/pages/iou/HoldReasonPage.tsx | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx index 1ecefb8c24dc..27c7f229a247 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer.tsx @@ -59,6 +59,7 @@ function MentionReportRenderer({style, tnode, TDefaultRenderer, reports, ...defa const htmlAttributeReportID = tnode.attributes.reportid; const currentReportID = useCurrentReportID(); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [currentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${currentReportID?.currentReportID || -1}`); // When we invite someone to a room they don't have the policy object, but we still want them to be able to see and click on report mentions, so we only check if the policyID in the report is from a workspace diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 4d59aa6e99af..c7797a37fd12 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -39,7 +39,8 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti const isFocusedRef = useRef(true); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${optionItem?.reportID ?? -1}`); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${optionItem?.reportID || -1}`); const {translate} = useLocalize(); const [isContextMenuActive, setIsContextMenuActive] = useState(false); diff --git a/src/pages/home/report/ReportAttachments.tsx b/src/pages/home/report/ReportAttachments.tsx index 555a9b5b8930..7c6b6f7fa6a2 100644 --- a/src/pages/home/report/ReportAttachments.tsx +++ b/src/pages/home/report/ReportAttachments.tsx @@ -16,7 +16,7 @@ function ReportAttachments({route}: ReportAttachmentsProps) { const reportID = route.params.reportID; const type = route.params.type; const accountID = route.params.accountID; - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`); const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); // In native the imported images sources are of type number. Ref: https://reactnative.dev/docs/image#imagesource diff --git a/src/pages/home/report/SystemChatReportFooterMessage.tsx b/src/pages/home/report/SystemChatReportFooterMessage.tsx index 21dbbf19705a..1c1ff2120674 100644 --- a/src/pages/home/report/SystemChatReportFooterMessage.tsx +++ b/src/pages/home/report/SystemChatReportFooterMessage.tsx @@ -38,7 +38,7 @@ function SystemChatReportFooterMessage({choice, policies, activePolicyID}: Syste ? PolicyUtils.getPolicy(activePolicyID) : Object.values(policies ?? {}).find((policy) => PolicyUtils.shouldShowPolicy(policy, false) && policy?.role === CONST.POLICY.ROLE.ADMIN && policy?.chatReportIDAdmins); - return String(adminPolicy?.chatReportIDAdmins); + return String(adminPolicy?.chatReportIDAdmins ?? -1); }, [activePolicyID, policies]); const [adminChatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${adminChatReportID}`); diff --git a/src/pages/iou/HoldReasonPage.tsx b/src/pages/iou/HoldReasonPage.tsx index 937da6a1eba8..0070f5e5be0a 100644 --- a/src/pages/iou/HoldReasonPage.tsx +++ b/src/pages/iou/HoldReasonPage.tsx @@ -46,7 +46,7 @@ function HoldReasonPage({route}: HoldReasonPageProps) { const {transactionID, reportID, backTo} = route.params; - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`); // 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 From 0c14761ef6c44a00b6044bae25952a60b449978c Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 20 Jun 2024 10:32:19 +0700 Subject: [PATCH 15/16] edit comment --- src/components/SettlementButton.tsx | 2 +- src/pages/home/report/ReportActionItem.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index fe689e989064..2b1cd0729c0b 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -144,7 +144,7 @@ function SettlementButton({ const {isOffline} = useNetwork(); const session = useSession(); - // The chatReportID can be an empty string that can make the app crashes if we use a nullish ?? operator + // The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`); const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false; const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport); diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 580df3f46b9e..7399db1df15f 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -200,7 +200,7 @@ function ReportActionItem({ const downloadedPreviews = useRef([]); const prevDraftMessage = usePrevious(draftMessage); const originalReportID = ReportUtils.getOriginalReportID(report.reportID, action); - // The parentReportID can be an empty string that can make the app crashes if we use a nullish ?? operator + // The app would crash due to subscribing to the entire report collection if parentReportID is an empty string. So we should have a fallback ID here. // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || -1}`); const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID; From 45a3c12b3ab8bc50ab666162ea1d81ede2a84663 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 20 Jun 2024 11:17:26 +0700 Subject: [PATCH 16/16] create a separate method --- src/libs/ReportUtils.ts | 33 +++++++++++++++---- src/libs/actions/ReportActions.ts | 7 +--- .../BaseReportActionContextMenu.tsx | 4 +-- src/pages/home/report/ReportActionItem.tsx | 6 ++-- .../step/IOURequestStepParticipants.tsx | 2 +- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 28f36afb03c6..c0c3f5a6dfa0 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -886,11 +886,16 @@ function isPolicyExpenseChat(report: OnyxInputOrEntry | Participant | Em return getChatType(report) === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT || (report?.isPolicyExpenseChat ?? false); } -function isInvoiceRoom(reportOrID: OnyxEntry | EmptyObject | string): boolean { - const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; +function isInvoiceRoom(report: OnyxEntry | EmptyObject): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.INVOICE; } +function isInvoiceRoomWithID(reportID?: string): boolean { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`]; + return isInvoiceRoom(report); +} + /** * Checks if a report is a completed task report. */ @@ -1232,8 +1237,7 @@ function isClosedExpenseReportWithNoExpenses(report: OnyxEntry): boolean /** * Whether the provided report is an archived room */ -function isArchivedRoom(reportOrID: OnyxInputOrEntry | EmptyObject | string, reportNameValuePairs?: OnyxInputOrEntry | EmptyObject): boolean { - const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; +function isArchivedRoom(report: OnyxInputOrEntry | EmptyObject, reportNameValuePairs?: OnyxInputOrEntry | EmptyObject): boolean { if (reportNameValuePairs) { return reportNameValuePairs.isArchived; } @@ -1241,6 +1245,15 @@ function isArchivedRoom(reportOrID: OnyxInputOrEntry | EmptyObject | str return report?.statusNum === CONST.REPORT.STATUS_NUM.CLOSED && report?.stateNum === CONST.REPORT.STATE_NUM.APPROVED; } +/** + * Whether the report with the provided reportID is an archived room + */ +function isArchivedRoomWithID(reportID?: string) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`]; + return isArchivedRoom(report); +} + /** * Whether the provided report is a closed report */ @@ -5526,12 +5539,17 @@ function getAllPolicyReports(policyID: string): Array> { /** * Returns true if Chronos is one of the chat participants (1:1) */ -function chatIncludesChronos(reportOrID: OnyxInputOrEntry | EmptyObject | string): boolean { - const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; +function chatIncludesChronos(report: OnyxInputOrEntry | EmptyObject): boolean { const participantAccountIDs = Object.keys(report?.participants ?? {}).map(Number); return participantAccountIDs.includes(CONST.ACCOUNT_ID.CHRONOS); } +function chatIncludesChronosWithID(reportID?: string): boolean { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`]; + return chatIncludesChronos(report); +} + /** * Can only flag if: * @@ -7025,6 +7043,7 @@ export { canShowReportRecipientLocalTime, canUserPerformWriteAction, chatIncludesChronos, + chatIncludesChronosWithID, chatIncludesConcierge, createDraftTransactionAndNavigateToParticipantSelector, doesReportBelongToWorkspace, @@ -7130,6 +7149,7 @@ export { isAllowedToSubmitDraftExpenseReport, isAnnounceRoom, isArchivedRoom, + isArchivedRoomWithID, isClosedReport, isCanceledTaskReport, isChatReport, @@ -7201,6 +7221,7 @@ export { isValidReportIDFromPath, isWaitingForAssigneeToCompleteTask, isInvoiceRoom, + isInvoiceRoomWithID, isInvoiceReport, isOpenInvoiceReport, canWriteInReport, diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 124a1dc5f9fd..4435978075e0 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -14,12 +14,7 @@ let allReportActions: OnyxCollection; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, waitForCollectionCallback: true, - callback: (actions) => { - if (!actions) { - return; - } - allReportActions = actions; - }, + callback: (value) => (allReportActions = value), }); let allReports: OnyxCollection; diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index 24ea61e3ffc1..f1e541d2fa91 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -218,8 +218,8 @@ function BaseReportActionContextMenu({ checkIfContextMenuActive?.(); setShouldKeepOpen(false); }, - ReportUtils.isArchivedRoom(originalReportID), - ReportUtils.chatIncludesChronos(originalReportID), + ReportUtils.isArchivedRoomWithID(originalReportID), + ReportUtils.chatIncludesChronosWithID(originalReportID), undefined, undefined, filteredContextMenuActions, diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index 7399db1df15f..b9dda0b5abd9 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -342,8 +342,8 @@ function ReportActionItem({ draftMessage ?? '', () => setIsContextMenuActive(true), toggleContextMenuFromActiveReportAction, - ReportUtils.isArchivedRoom(originalReportID), - ReportUtils.chatIncludesChronos(originalReportID), + ReportUtils.isArchivedRoomWithID(originalReportID), + ReportUtils.chatIncludesChronosWithID(originalReportID), false, false, [], @@ -868,7 +868,7 @@ function ReportActionItem({ disabledActions={!ReportUtils.canWriteInReport(report) ? RestrictedReadOnlyContextMenuActions : []} isVisible={hovered && draftMessage === undefined && !hasErrors} draftMessage={draftMessage} - isChronosReport={ReportUtils.chatIncludesChronos(originalReportID)} + isChronosReport={ReportUtils.chatIncludesChronosWithID(originalReportID)} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} setIsEmojiPickerActive={setIsEmojiPickerActive} /> diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index cd2d9982b49d..7fbc8d260f8a 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -94,7 +94,7 @@ function IOURequestStepParticipants({ const firstParticipantReportID = val[0]?.reportID ?? ''; const rateID = DistanceRequestUtils.getCustomUnitRateID(firstParticipantReportID); - const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && ReportUtils.isInvoiceRoom(firstParticipantReportID); + const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && ReportUtils.isInvoiceRoomWithID(firstParticipantReportID); numberOfParticipants.current = val.length; IOU.setMoneyRequestParticipants(transactionID, val);