From cce6158d9d46383029f1525a1a08b28c64a31c4b Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 20 Jun 2024 14:02:26 +0500 Subject: [PATCH 01/29] perf: use single report connection --- src/libs/DistanceRequestUtils.ts | 13 +-- src/libs/ReportActionsUtils.ts | 28 +++-- src/libs/ReportConnection.ts | 36 +++++++ src/libs/ReportUtils.ts | 131 +++++++++++------------ src/libs/TaskUtils.ts | 16 +-- src/libs/TransactionUtils.ts | 11 +- src/libs/UnreadIndicatorUpdater/index.ts | 20 +--- src/libs/WorkspacesSettingsUtils.ts | 10 +- src/libs/actions/IOU.ts | 32 +++--- src/libs/actions/PriorityMode.ts | 19 +--- src/libs/actions/Report.ts | 34 ++---- src/libs/actions/Task.ts | 10 +- src/libs/getReportPolicyID.ts | 12 +-- src/libs/markAllPolicyReportsAsRead.ts | 35 +++--- src/libs/migrations/Participants.ts | 10 +- 15 files changed, 179 insertions(+), 238 deletions(-) create mode 100644 src/libs/ReportConnection.ts diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 17a933766a69..041214623e14 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -1,16 +1,17 @@ -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {LocaleContextProps} from '@components/LocaleContextProvider'; import type {RateAndUnit} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {LastSelectedDistanceRates, OnyxInputOrEntry, Report} from '@src/types/onyx'; +import type {LastSelectedDistanceRates, OnyxInputOrEntry} from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; import type Policy from '@src/types/onyx/Policy'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; +import getAllReports from './ReportConnection'; import * as ReportUtils from './ReportUtils'; type MileageRate = { @@ -29,13 +30,6 @@ Onyx.connect({ }, }); -let allReports: OnyxCollection; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (value) => (allReports = value), -}); - const METERS_TO_KM = 0.001; // 1 kilometer is 1000 meters const METERS_TO_MILES = 0.000621371; // There are approximately 0.000621371 miles in a meter @@ -252,6 +246,7 @@ function convertToDistanceInMeters(distance: number, unit: Unit): number { * Returns custom unit rate ID for the distance transaction */ function getCustomUnitRateID(reportID: string) { + const allReports = getAllReports(); const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; const policy = PolicyUtils.getPolicy(report?.policyID ?? parentReport?.policyID ?? '-1'); diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index a12e9ce61a63..a47aa8f8ecc5 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -33,6 +33,7 @@ import * as Localize from './Localize'; import Log from './Log'; import type {MessageElementBase, MessageTextElement} from './MessageElement'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; +import getAllReports from './ReportConnection'; import type {OptimisticIOUReportAction} from './ReportUtils'; import StringUtils from './StringUtils'; import * as TransactionUtils from './TransactionUtils'; @@ -58,15 +59,6 @@ type MemberChangeMessageElement = MessageTextElement | MemberChangeMessageUserMe const policyChangeActionsSet = new Set(Object.values(CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG)); -let allReports: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (reports) => { - allReports = reports; - }, -}); - let allReportActions: OnyxCollection; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, @@ -320,7 +312,7 @@ function getCombinedReportActions(reportActions: ReportAction[], transactionThre // Filter out the created action from the transaction thread report actions, since we already have the parent report's created action in `reportActions` const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED); - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const isSelfDM = report?.chatType === CONST.REPORT.CHAT_TYPE.SELF_DM; // Filter out request and send money request actions because we don't want to show any preview actions for one transaction reports const filteredReportActions = [...reportActions, ...filteredTransactionThreadReportActions].filter((action) => { @@ -646,8 +638,13 @@ function replaceBaseURLInPolicyChangeLogAction(reportAction: ReportAction): Repo } function getLastVisibleAction(reportID: string, actionsToMerge: OnyxCollection | OnyxCollectionInputValue = {}): OnyxEntry { - const reportActions = Object.values(fastMerge(allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {}, actionsToMerge ?? {}, true)); - const visibleReportActions = Object.values(reportActions ?? {}).filter((action): action is ReportAction => shouldReportActionBeVisibleAsLastAction(action)); + let reportActions: Array = []; + if (_.isEmpty(actionsToMerge) === false) { + reportActions = Object.values(fastMerge(allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {}, actionsToMerge ?? {}, true)); + } else { + reportActions = Object.values(allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {}); + } + const visibleReportActions = reportActions.filter((action): action is ReportAction => shouldReportActionBeVisibleAsLastAction(action)); const sortedReportActions = getSortedReportActions(visibleReportActions, true); if (sortedReportActions.length === 0) { return undefined; @@ -809,7 +806,7 @@ function getMostRecentReportActionLastModified(): string { // We might not have actions so we also look at the report objects to see if any have a lastVisibleActionLastModified that is more recent. We don't need to get // any reports that have been updated before either a recently updated report or reportAction as we should be up to date on these - Object.values(allReports ?? {}).forEach((report) => { + Object.values(getAllReports() ?? {}).forEach((report) => { const reportLastVisibleActionLastModified = report?.lastVisibleActionLastModified ?? report?.lastVisibleActionCreated; if (!reportLastVisibleActionLastModified || reportLastVisibleActionLastModified < mostRecentReportActionLastModified) { return; @@ -883,7 +880,7 @@ function isTaskAction(reportAction: OnyxEntry): boolean { */ function getOneTransactionThreadReportID(reportID: string, reportActions: OnyxEntry | ReportAction[], isOffline: boolean | undefined = undefined): string | undefined { // If the report is not an IOU, Expense report, or Invoice, it shouldn't be treated as one-transaction report. - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (report?.type !== CONST.REPORT.TYPE.IOU && report?.type !== CONST.REPORT.TYPE.EXPENSE && report?.type !== CONST.REPORT.TYPE.INVOICE) { return; } @@ -1206,8 +1203,7 @@ function isActionableJoinRequest(reportAction: OnyxEntry): reportA * @param reportID */ function isActionableJoinRequestPending(reportID: string): boolean { - const sortedReportActions = getSortedReportActions(Object.values(getAllReportActions(reportID))); - const findPendingRequest = sortedReportActions.find( + const findPendingRequest = Object.values(getAllReportActions(reportID)).find( (reportActionItem) => isActionableJoinRequest(reportActionItem) && reportActionItem.originalMessage.choice === ('' as JoinWorkspaceResolution), ); return !!findPendingRequest; diff --git a/src/libs/ReportConnection.ts b/src/libs/ReportConnection.ts new file mode 100644 index 000000000000..8c28f354afc8 --- /dev/null +++ b/src/libs/ReportConnection.ts @@ -0,0 +1,36 @@ +import type {OnyxCollection} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report} from '@src/types/onyx'; +import * as PriorityModeActions from './actions/PriorityMode'; +import * as ReportHelperActions from './actions/Report'; + +// Dynamic Import to avoid circular dependency +const UnreadIndicatorUpdaterHelper = () => import('./UnreadIndicatorUpdater'); + +let allReports: OnyxCollection; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => { + allReports = value; + UnreadIndicatorUpdaterHelper().then((module) => { + module.triggerUnreadUpdate(); + }); + // Each time a new report is added we will check to see if the user should be switched + PriorityModeActions.autoSwitchToFocusMode(); + + Object.values(value).forEach((report) => { + if (!report) { + return; + } + ReportHelperActions.handleReportChanged(report); + }); + }, +}); + +function getAllReports() { + return allReports; +} + +export default getAllReports; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8a30936ba33a..e56ad24f5de9 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -82,6 +82,7 @@ import * as PhoneNumber from './PhoneNumber'; import * as PolicyUtils from './PolicyUtils'; import type {LastVisibleMessage} from './ReportActionsUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; +import getAllReports from './ReportConnection'; import StringUtils from './StringUtils'; import * as SubscriptionUtils from './SubscriptionUtils'; import * as TransactionUtils from './TransactionUtils'; @@ -478,7 +479,6 @@ let isAnonymousUser = false; const parsedReportActionMessageCache: Record = {}; const defaultAvatarBuildingIconTestID = 'SvgDefaultAvatarBuilding Icon'; - Onyx.connect({ key: ONYXKEYS.SESSION, callback: (value) => { @@ -506,13 +506,6 @@ 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, @@ -557,23 +550,6 @@ Onyx.connect({ }, }); -let lastUpdatedReport: OnyxEntry; - -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - callback: (value) => { - if (!value) { - return; - } - - lastUpdatedReport = value; - }, -}); - -function getLastUpdatedReport(): OnyxEntry { - return lastUpdatedReport; -} - function getCurrentUserAvatar(): AvatarSource | undefined { return currentUserPersonalDetails?.avatar; } @@ -590,6 +566,7 @@ function getChatType(report: OnyxInputOrEntry | Participant | EmptyObjec * Get the report given a reportID */ function getReport(reportID: string | undefined): OnyxEntry { + const allReports = getAllReports(); if (!allReports && !allReportsDraft) { return undefined; } @@ -616,7 +593,7 @@ function getParentReport(report: OnyxEntry | EmptyObject): OnyxEntry, policies: OnyxCollectio return policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]?.type ?? ''; } +const unavailableTranslation = Localize.translateLocal('workspace.common.unavailable'); /** * Get the policy name from a given report */ function getPolicyName(report: OnyxInputOrEntry | undefined | EmptyObject, returnEmptyIfNotFound = false, policy?: OnyxInputOrEntry): string { - const noPolicyFound = returnEmptyIfNotFound ? '' : Localize.translateLocal('workspace.common.unavailable'); + const noPolicyFound = returnEmptyIfNotFound ? '' : unavailableTranslation; if (isEmptyObject(report)) { return noPolicyFound; } if ((!allPolicies || Object.keys(allPolicies).length === 0) && !report?.policyName) { - return Localize.translateLocal('workspace.common.unavailable'); + return unavailableTranslation; } const finalPolicy = policy ?? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; @@ -711,7 +689,7 @@ function isExpenseReport(report: OnyxInputOrEntry | EmptyObject): boolea * Checks if a report is an IOU report using report or reportID */ function isIOUReport(reportOrID: OnyxInputOrEntry | string | EmptyObject): boolean { - const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return report?.type === CONST.REPORT.TYPE.IOU; } @@ -776,7 +754,7 @@ function isReportManager(report: OnyxEntry): boolean { * Checks if the supplied report has been approved */ function isReportApproved(reportOrID: OnyxInputOrEntry | string | EmptyObject): boolean { - const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return report?.stateNum === CONST.REPORT.STATE_NUM.APPROVED && report?.statusNum === CONST.REPORT.STATUS_NUM.APPROVED; } @@ -810,6 +788,7 @@ function hasParticipantInArray(report: OnyxEntry, memberAccountIDs: numb * Whether the Money Request report is settled */ function isSettled(reportID: string | undefined): boolean { + const allReports = getAllReports(); if (!allReports || !reportID) { return false; } @@ -831,6 +810,7 @@ function isSettled(reportID: string | undefined): boolean { * Whether the current user is the submitter of the report */ function isCurrentUserSubmitter(reportID: string): boolean { + const allReports = getAllReports(); if (!allReports) { return false; } @@ -1007,7 +987,7 @@ function isWorkspaceTaskReport(report: OnyxEntry): boolean { if (!isTaskReport(report)) { return false; } - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; + const parentReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; return isPolicyExpenseChat(parentReport); } @@ -1048,13 +1028,12 @@ function isSystemChat(report: OnyxEntry): boolean { * Only returns true if this is our main 1:1 DM report with Concierge. */ function isConciergeChatReport(report: OnyxInputOrEntry): boolean { - const participantAccountIDs = Object.keys(report?.participants ?? {}) - .map(Number) - .filter((accountID) => accountID !== currentUserAccountID); - return participantAccountIDs.length === 1 && participantAccountIDs[0] === CONST.ACCOUNT_ID.CONCIERGE && !isChatThread(report); + const participantAccountIDs = Object.keys(report?.participants ?? {}); + return participantAccountIDs.length === 1 && Number(participantAccountIDs[0]) === CONST.ACCOUNT_ID.CONCIERGE && !isChatThread(report); } function findSelfDMReportID(): string | undefined { + const allReports = getAllReports(); if (!allReports) { return; } @@ -1368,7 +1347,7 @@ function isChildReport(report: OnyxEntry): boolean { function isExpenseRequest(report: OnyxInputOrEntry): boolean { if (isThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; + const parentReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; return isExpenseReport(parentReport) && !isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction); } return false; @@ -1381,7 +1360,7 @@ function isExpenseRequest(report: OnyxInputOrEntry): boolean { function isIOURequest(report: OnyxInputOrEntry): boolean { if (isThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; + const parentReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; return isIOUReport(parentReport) && !isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction); } return false; @@ -1403,7 +1382,7 @@ function isTrackExpenseReport(report: OnyxInputOrEntry): boolean { * Checks if a report is an IOU or expense request. */ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { - const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return isIOURequest(report) || isExpenseRequest(report); } @@ -1411,7 +1390,7 @@ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { * Checks if a report is an IOU or expense report. */ function isMoneyRequestReport(reportOrID: OnyxInputOrEntry | EmptyObject | string): boolean { - const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return isIOUReport(report) || isExpenseReport(report); } @@ -1617,7 +1596,7 @@ function getReportRecipientAccountIDs(report: OnyxEntry, currentLoginAcc // In 1:1 chat threads, the participants will be the same as parent report. If a report is specifically a 1:1 chat thread then we will // get parent report and use its participants array. if (isThread(report) && !(isTaskReport(report) || isMoneyRequestReport(report))) { - const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; + const parentReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; if (isOneOnOneChat(parentReport)) { finalReport = parentReport; } @@ -1806,6 +1785,7 @@ function getPersonalDetailsForAccountID(accountID: number): Partial, allReportsDict?: OnyxCollection): SpendBreakdown { - const allAvailableReports = allReportsDict ?? allReports; + const allAvailableReports = allReportsDict ?? getAllReports(); let moneyRequestReport; if (isMoneyRequestReport(report) || isInvoiceReport(report)) { moneyRequestReport = report; @@ -2677,7 +2657,7 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxInputOrEntry } const iouMessage = reportAction?.originalMessage as IOUMessage; - const moneyRequestReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouMessage?.IOUReportID}`] ?? ({} as Report); + const moneyRequestReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${iouMessage?.IOUReportID}`] ?? ({} as Report); const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${iouMessage?.IOUTransactionID}`] ?? ({} as Transaction); if (isSettled(String(moneyRequestReport.reportID)) || isReportApproved(String(moneyRequestReport.reportID))) { @@ -3290,6 +3270,13 @@ function getInvoicesChatName(report: OnyxEntry): string { return getPolicyName(report, false, allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${invoiceReceiverPolicyID}`]); } +const archivedTranslation = Localize.translateLocal('common.archived'); +const deletedMessageTranslation = Localize.translateLocal('parentReportAction.deletedMessage'); +const attachmentTranslation = `[${Localize.translateLocal('common.attachment')}]`; +const hiddenMessageTranslation = Localize.translateLocal('parentReportAction.hiddenMessage'); +const deletedTaskTranslation = Localize.translateLocal('parentReportAction.deletedTask'); +const deletedReportTranslation = Localize.translateLocal('parentReportAction.deletedReport'); + /** * Get the title for a report. */ @@ -3300,32 +3287,32 @@ function getReportName(report: OnyxEntry, policy?: OnyxEntry): s if (!isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction)) { formattedName = getTransactionReportName(parentReportAction); if (isArchivedRoom(report)) { - formattedName += ` (${Localize.translateLocal('common.archived')})`; + formattedName += ` (${archivedTranslation})`; } return formatReportLastMessageText(formattedName); } if (parentReportAction?.message?.[0]?.isDeletedParentAction) { - return Localize.translateLocal('parentReportAction.deletedMessage'); + return deletedMessageTranslation; } const isAttachment = ReportActionsUtils.isReportActionAttachment(!isEmptyObject(parentReportAction) ? parentReportAction : undefined); const parentReportActionMessage = getReportActionMessage(parentReportAction, report?.parentReportID, report?.reportID ?? '').replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { - return `[${Localize.translateLocal('common.attachment')}]`; + return attachmentTranslation; } if ( parentReportAction?.message?.[0]?.moderationDecision?.decision === CONST.MODERATION.MODERATOR_DECISION_PENDING_HIDE || parentReportAction?.message?.[0]?.moderationDecision?.decision === CONST.MODERATION.MODERATOR_DECISION_HIDDEN || parentReportAction?.message?.[0]?.moderationDecision?.decision === CONST.MODERATION.MODERATOR_DECISION_PENDING_REMOVE ) { - return Localize.translateLocal('parentReportAction.hiddenMessage'); + return hiddenMessageTranslation; } if (isAdminRoom(report) || isUserCreatedPolicyRoom(report)) { return getAdminRoomInvitedParticipants(parentReportAction, parentReportActionMessage); } if (parentReportActionMessage && isArchivedRoom(report)) { - return `${parentReportActionMessage} (${Localize.translateLocal('common.archived')})`; + return `${parentReportActionMessage} (${archivedTranslation})`; } if (ReportActionsUtils.isModifiedExpenseAction(parentReportAction)) { return ModifiedExpenseMessage.getForReportAction(report?.reportID, parentReportAction); @@ -3339,11 +3326,11 @@ function getReportName(report: OnyxEntry, policy?: OnyxEntry): s } if (isClosedExpenseReportWithNoExpenses(report)) { - return Localize.translateLocal('parentReportAction.deletedReport'); + return deletedReportTranslation; } if (isTaskReport(report) && isCanceledTaskReport(report, parentReportAction)) { - return Localize.translateLocal('parentReportAction.deletedTask'); + return deletedTaskTranslation; } if (isGroupChat(report)) { @@ -3367,7 +3354,7 @@ function getReportName(report: OnyxEntry, policy?: OnyxEntry): s } if (isArchivedRoom(report)) { - formattedName += ` (${Localize.translateLocal('common.archived')})`; + formattedName += ` (${archivedTranslation})`; } if (isSelfDM(report)) { @@ -3383,10 +3370,13 @@ function getReportName(report: OnyxEntry, policy?: OnyxEntry): s } // Not a room or PolicyExpenseChat, generate title from first 5 other participants - const participantsWithoutCurrentUser = Object.keys(report?.participants ?? {}) - .map(Number) - .filter((accountID) => accountID !== currentUserAccountID) - .slice(0, 5); + const participantsWithoutCurrentUser: number[] = []; + Object.keys(report?.participants ?? {}).forEach((accountID) => { + const accID = Number(accountID); + if (accID !== currentUserAccountID && participantsWithoutCurrentUser.length < 5) { + participantsWithoutCurrentUser.push(accID); + } + }); const isMultipleParticipantReport = participantsWithoutCurrentUser.length > 1; return participantsWithoutCurrentUser.map((accountID) => getDisplayNameForParticipant(accountID, isMultipleParticipantReport)).join(', '); } @@ -3868,7 +3858,7 @@ function buildOptimisticInvoiceReport(chatReportID: string, policyID: string, re function buildOptimisticExpenseReport(chatReportID: string, policyID: string, payeeAccountID: number, total: number, currency: string, reimbursable = true): OptimisticExpenseReport { // The amount for Expense reports are stored as negative value in the database const storedTotal = total * -1; - const policyName = getPolicyName(allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`]); + const policyName = getPolicyName(getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`]); const formattedTotal = CurrencyUtils.convertToDisplayString(storedTotal, currency); const policy = getPolicy(policyID); @@ -5180,7 +5170,7 @@ function isUnread(report: OnyxEntry): boolean { } function isIOUOwnedByCurrentUser(report: OnyxEntry, allReportsDict?: OnyxCollection): boolean { - const allAvailableReports = allReportsDict ?? allReports; + const allAvailableReports = allReportsDict ?? getAllReports(); if (!report || !allAvailableReports) { return false; } @@ -5336,8 +5326,6 @@ function shouldReportBeInOptionList({ // This can also happen for anyone accessing a public room or archived room for which they don't have access to the underlying policy. // Optionally exclude reports that do not belong to currently active workspace - const participantAccountIDs = Object.keys(report?.participants ?? {}).map(Number); - if ( !report?.reportID || !report?.type || @@ -5345,8 +5333,8 @@ function shouldReportBeInOptionList({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing report?.isHidden || // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - participantAccountIDs.includes(CONST.ACCOUNT_ID.NOTIFICATIONS) || - (participantAccountIDs.length === 0 && + report?.participants?.[CONST.ACCOUNT_ID.NOTIFICATIONS] || + (!report?.participants && !isChatThread(report) && !isPublicRoom(report) && !isUserCreatedPolicyRoom(report) && @@ -5448,6 +5436,7 @@ function shouldReportBeInOptionList({ * Returns the system report from the list of reports. */ function getSystemChat(): OnyxEntry { + const allReports = getAllReports(); if (!allReports) { return undefined; } @@ -5458,7 +5447,7 @@ function getSystemChat(): OnyxEntry { /** * Attempts to find a report in onyx with the provided list of participants. Does not include threads, task, expense, room, and policy expense chat. */ -function getChatByParticipants(newParticipantList: number[], reports: OnyxCollection = allReports): OnyxEntry { +function getChatByParticipants(newParticipantList: number[], reports: OnyxCollection = getAllReports()): OnyxEntry { const sortedNewParticipantList = newParticipantList.sort(); return Object.values(reports ?? {}).find((report) => { const participantAccountIDs = Object.keys(report?.participants ?? {}); @@ -5486,7 +5475,7 @@ function getChatByParticipants(newParticipantList: number[], reports: OnyxCollec /** * Attempts to find an invoice chat report in onyx with the provided policyID and receiverID. */ -function getInvoiceChatByParticipants(policyID: string, receiverID: string | number, reports: OnyxCollection = allReports): OnyxEntry { +function getInvoiceChatByParticipants(policyID: string, receiverID: string | number, reports: OnyxCollection = getAllReports()): OnyxEntry { return Object.values(reports ?? {}).find((report) => { if (!report || !isInvoiceRoom(report)) { return false; @@ -5505,7 +5494,7 @@ function getInvoiceChatByParticipants(policyID: string, receiverID: string | num * Attempts to find a policy expense report in onyx that is owned by ownerAccountID in a given policy */ function getPolicyExpenseChat(ownerAccountID: number, policyID: string): OnyxEntry { - return Object.values(allReports ?? {}).find((report: OnyxEntry) => { + return Object.values(getAllReports() ?? {}).find((report: OnyxEntry) => { // If the report has been deleted, then skip it if (!report) { return false; @@ -5516,7 +5505,7 @@ function getPolicyExpenseChat(ownerAccountID: number, policyID: string): OnyxEnt } function getAllPolicyReports(policyID: string): Array> { - return Object.values(allReports ?? {}).filter((report) => report?.policyID === policyID); + return Object.values(getAllReports() ?? {}).filter((report) => report?.policyID === policyID); } /** @@ -5680,7 +5669,7 @@ function getReportPolicyID(reportID?: string): string | undefined { */ function hasIOUWaitingOnCurrentUserBankAccount(chatReport: OnyxInputOrEntry): boolean { if (chatReport?.iouReportID) { - const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReport?.iouReportID}`]; + const iouReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReport?.iouReportID}`]; if (iouReport?.isWaitingOnBankAccount && iouReport?.ownerAccountID === currentUserAccountID) { return true; } @@ -6001,6 +5990,7 @@ function shouldReportShowSubscript(report: OnyxEntry): boolean { * Return true if reports data exists */ function isReportDataReady(): boolean { + const allReports = getAllReports(); return !isEmptyObject(allReports) && Object.keys(allReports ?? {}).some((key) => allReports?.[key]?.reportID); } @@ -6051,7 +6041,7 @@ function getOriginalReportID(reportID: string, reportAction: OnyxInputOrEntry, policy: OnyxEntry, } function getWorkspaceChats(policyID: string, accountIDs: number[]): Array> { + const allReports = getAllReports(); return Object.values(allReports ?? {}).filter((report) => isPolicyExpenseChat(report) && (report?.policyID ?? '-1') === policyID && accountIDs.includes(report?.ownerAccountID ?? -1)); } @@ -6094,6 +6085,7 @@ function getWorkspaceChats(policyID: string, accountIDs: number[]): Array> { + const allReports = getAllReports(); return Object.values(allReports ?? {}).filter((report) => (report?.policyID ?? '-1') === policyID); } @@ -6399,6 +6391,7 @@ function shouldUseFullTitleToDisplay(report: OnyxEntry): boolean { } function getRoom(type: ValueOf, policyID: string): OnyxEntry | undefined { + const allReports = getAllReports(); const room = Object.values(allReports ?? {}).find((report) => report?.policyID === policyID && report?.chatType === type && !isThread(report)); return room; } @@ -6762,7 +6755,7 @@ function shouldCreateNewMoneyRequestReport(existingIOUReport: OnyxInputOrEntry report && report?.[reportFieldToCompare] === tripRoomReportID) .map((report) => report?.reportID); return tripTransactionReportIDs.flatMap((reportID) => TransactionUtils.getAllReportTransactions(reportID)); @@ -6950,6 +6943,7 @@ function canReportBeMentionedWithinPolicy(report: OnyxEntry, policyID: s } function shouldShowMerchantColumn(transactions: Transaction[]) { + const allReports = getAllReports(); return transactions.some((transaction) => isExpenseReport(allReports?.[transaction.reportID] ?? {})); } @@ -7058,7 +7052,6 @@ export { getIcons, getIconsForParticipants, getIndicatedMissingPaymentMethod, - getLastUpdatedReport, getLastVisibleMessage, getMoneyRequestOptions, getMoneyRequestSpendBreakdown, diff --git a/src/libs/TaskUtils.ts b/src/libs/TaskUtils.ts index bd22ae134ee5..8da9346473ec 100644 --- a/src/libs/TaskUtils.ts +++ b/src/libs/TaskUtils.ts @@ -1,20 +1,10 @@ -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import Onyx from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report} from '@src/types/onyx'; import type {Message} from '@src/types/onyx/ReportAction'; import type ReportAction from '@src/types/onyx/ReportAction'; import * as Localize from './Localize'; - -let allReports: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (reports) => { - allReports = reports; - }, -}); +import getAllReports from './ReportConnection'; /** * Given the Task reportAction name, return the appropriate message to be displayed and copied to clipboard. @@ -38,7 +28,7 @@ function getTaskReportActionMessage(action: OnyxEntry): Pick = {}; Onyx.connect({ @@ -34,13 +35,6 @@ Onyx.connect({ callback: (value) => (allTransactionViolations = value), }); -let allReports: OnyxCollection; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (value) => (allReports = value), -}); - let currentUserEmail = ''; let currentUserAccountID = -1; Onyx.connect({ @@ -194,6 +188,7 @@ function isCreatedMissing(transaction: OnyxEntry) { } function areRequiredFieldsEmpty(transaction: OnyxEntry): boolean { + const allReports = getAllReports(); const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`] ?? null; const isFromExpenseReport = parentReport?.type === CONST.REPORT.TYPE.EXPENSE; const isSplitPolicyExpenseChat = !!transaction?.comment?.splits?.some((participant) => allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`]?.isOwnPolicyExpenseChat); diff --git a/src/libs/UnreadIndicatorUpdater/index.ts b/src/libs/UnreadIndicatorUpdater/index.ts index 7698433c33c1..62c5ddc84ac6 100644 --- a/src/libs/UnreadIndicatorUpdater/index.ts +++ b/src/libs/UnreadIndicatorUpdater/index.ts @@ -1,17 +1,14 @@ import debounce from 'lodash/debounce'; import memoize from 'lodash/memoize'; import type {OnyxCollection} from 'react-native-onyx'; -import Onyx from 'react-native-onyx'; +import getAllReports from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import Navigation, {navigationRef} from '@navigation/Navigation'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import type {Report} from '@src/types/onyx'; import updateUnread from './updateUnread'; -let allReports: OnyxCollection = {}; - -export default function getUnreadReportsForUnreadIndicator(reports: OnyxCollection, currentReportID: string) { +function getUnreadReportsForUnreadIndicator(reports: OnyxCollection, currentReportID: string) { return Object.values(reports ?? {}).filter( (report) => ReportUtils.isUnread(report) && @@ -43,20 +40,13 @@ const triggerUnreadUpdate = debounce(() => { const currentReportID = navigationRef.isReady() ? Navigation.getTopmostReportId() ?? '-1' : '-1'; // We want to keep notification count consistent with what can be accessed from the LHN list - const unreadReports = memoizedGetUnreadReportsForUnreadIndicator(allReports, currentReportID); + const unreadReports = memoizedGetUnreadReportsForUnreadIndicator(getAllReports(), currentReportID); updateUnread(unreadReports.length); }, CONST.TIMING.UNREAD_UPDATE_DEBOUNCE_TIME); -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (reportsFromOnyx) => { - allReports = reportsFromOnyx; - triggerUnreadUpdate(); - }, -}); - navigationRef.addListener('state', () => { triggerUnreadUpdate(); }); + +export {triggerUnreadUpdate, getUnreadReportsForUnreadIndicator}; diff --git a/src/libs/WorkspacesSettingsUtils.ts b/src/libs/WorkspacesSettingsUtils.ts index 6c57c2a6f99d..8c130c4cc886 100644 --- a/src/libs/WorkspacesSettingsUtils.ts +++ b/src/libs/WorkspacesSettingsUtils.ts @@ -10,20 +10,13 @@ import * as CurrencyUtils from './CurrencyUtils'; import type {Phrase, PhraseParameters} from './Localize'; import * as OptionsListUtils from './OptionsListUtils'; import {hasCustomUnitsError, hasEmployeeListError, hasPolicyError, hasTaxRateError} from './PolicyUtils'; +import getAllReports from './ReportConnection'; import * as ReportUtils from './ReportUtils'; type CheckingMethod = () => boolean; -let allReports: OnyxCollection; - type BrickRoad = ValueOf | undefined; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (value) => (allReports = value), -}); - let allPolicies: OnyxCollection; Onyx.connect({ @@ -100,6 +93,7 @@ function hasWorkspaceSettingsRBR(policy: Policy) { } function getChatTabBrickRoad(policyID?: string): BrickRoad | undefined { + const allReports = getAllReports(); if (!allReports) { return undefined; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9d22f2358f1c..cbb28dcc1e67 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -42,6 +42,7 @@ import Permissions from '@libs/Permissions'; import * as PhoneNumber from '@libs/PhoneNumber'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import getAllReports from '@libs/ReportConnection'; import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, TransactionDetails} from '@libs/ReportUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -166,13 +167,6 @@ Onyx.connect({ }, }); -let allReports: OnyxCollection | null = null; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (value) => (allReports = value ?? null), -}); - let allTransactions: NonNullable> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, @@ -481,7 +475,7 @@ function buildOnyxDataForMoneyRequest( if (TransactionUtils.isDistanceRequest(transaction)) { newQuickAction = CONST.QUICK_ACTIONS.REQUEST_DISTANCE; } - const existingTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingTransactionThreadReportID}`] ?? null; + const existingTransactionThreadReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingTransactionThreadReportID}`] ?? null; if (chatReport) { optimisticData.push({ @@ -1198,7 +1192,7 @@ function buildOnyxDataForTrackExpense( } else if (isDistanceRequest) { newQuickAction = CONST.QUICK_ACTIONS.TRACK_DISTANCE; } - const existingTransactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingTransactionThreadReportID}`] ?? null; + const existingTransactionThreadReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingTransactionThreadReportID}`] ?? null; if (chatReport) { optimisticData.push( @@ -1554,6 +1548,7 @@ function getDeleteTrackExpenseInformation( actionableWhisperReportActionID = '', resolution = '', ) { + const allReports = getAllReports(); // STEP 1: Get all collections we're updating const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -1894,6 +1889,7 @@ function getMoneyRequestInformation( let isNewChatReport = false; let chatReport = !isEmptyObject(parentChatReport) && parentChatReport?.reportID ? parentChatReport : null; + const allReports = getAllReports(); // If this is a policyExpenseChat, the chatReport must exist and we can get it from Onyx. // report is null if the flow is initiated from the global create menu. However, participant always stores the reportID if it exists, which is the case for policyExpenseChats if (!chatReport && isPolicyExpenseChat) { @@ -2110,7 +2106,7 @@ function getTrackExpenseInformation( // STEP 1: Get existing chat report let chatReport = !isEmptyObject(parentChatReport) && parentChatReport?.reportID ? parentChatReport : null; - + const allReports = getAllReports(); // The chatReport always exists, and we can get it from Onyx if chatReport is null. if (!chatReport) { chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.reportID}`] ?? null; @@ -2469,6 +2465,7 @@ function getUpdateMoneyRequestParams( const clearedPendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, null])); const errorFields = Object.fromEntries(Object.keys(pendingFields).map((key) => [key, {[DateUtils.getMicroseconds()]: Localize.translateLocal('iou.error.genericEditFailureMessage')}])); + const allReports = getAllReports(); // Step 2: Get all the collections being updated const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -2755,6 +2752,7 @@ function getUpdateTrackExpenseParams( const clearedPendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, null])); const errorFields = Object.fromEntries(Object.keys(pendingFields).map((key) => [key, {[DateUtils.getMicroseconds()]: Localize.translateLocal('iou.error.genericEditFailureMessage')}])); + const allReports = getAllReports(); // Step 2: Get all the collections being updated const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -2921,6 +2919,7 @@ function updateMoneyRequestDate( const transactionChanges: TransactionChanges = { created: value, }; + const allReports = getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; @@ -2961,6 +2960,7 @@ function updateMoneyRequestMerchant( const transactionChanges: TransactionChanges = { merchant: value, }; + const allReports = getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; @@ -3049,6 +3049,7 @@ function updateMoneyRequestDistance({ waypoints, routes, }; + const allReports = getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; @@ -3089,6 +3090,7 @@ function updateMoneyRequestDescription( const transactionChanges: TransactionChanges = { comment, }; + const allReports = getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; @@ -3749,7 +3751,7 @@ function getOrCreateOptimisticSplitChatReport(existingSplitChatReportID: string, const existingChatReportID = existingSplitChatReportID || participants[0].reportID; // Check if the report is available locally if we do have one - let existingSplitChatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingChatReportID}`]; + let existingSplitChatReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingChatReportID}`]; // If we do not have one locally then we will search for a chat with the same participants (only for 1:1 chats). const shouldGetOrCreateOneOneDM = participants.length < 2; @@ -4043,7 +4045,7 @@ function createSplitsAndOnyxData( } // STEP 2: Get existing IOU/Expense report and update its total OR build a new optimistic one - let oneOnOneIOUReport: OneOnOneIOUReport = oneOnOneChatReport.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] : null; + let oneOnOneIOUReport: OneOnOneIOUReport = oneOnOneChatReport.iouReportID ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] : null; const shouldCreateNewOneOnOneIOUReport = ReportUtils.shouldCreateNewMoneyRequestReport(oneOnOneIOUReport, oneOnOneChatReport); if (!oneOnOneIOUReport || shouldCreateNewOneOnOneIOUReport) { @@ -4775,6 +4777,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA let oneOnOneChatReport: OnyxTypes.Report | null; let isNewOneOnOneChatReport = false; + const allReports = getAllReports(); if (isPolicyExpenseChat) { // The workspace chat reportID is saved in the splits array when starting a split expense with a workspace oneOnOneChatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`] ?? null; @@ -4932,6 +4935,7 @@ function editRegularMoneyRequest( policyTags: OnyxTypes.PolicyTagList, policyCategories: OnyxTypes.PolicyCategories, ) { + const allReports = getAllReports(); // STEP 1: Get all collections we're updating const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -5232,6 +5236,7 @@ function updateMoneyRequestAmountAndCurrency({ taxCode, taxAmount, }; + const allReports = getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; @@ -5245,6 +5250,7 @@ function updateMoneyRequestAmountAndCurrency({ } function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) { + const allReports = getAllReports(); // STEP 1: Get all collections we're updating const iouReportID = reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? reportAction.originalMessage.IOUReportID : '-1'; const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`] ?? null; @@ -5550,7 +5556,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor function deleteTrackExpense(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) { // STEP 1: Get all collections we're updating - const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; + const chatReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; if (!ReportUtils.isSelfDM(chatReport)) { return deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView); } diff --git a/src/libs/actions/PriorityMode.ts b/src/libs/actions/PriorityMode.ts index a4561d44d5a0..3c270402f1bb 100644 --- a/src/libs/actions/PriorityMode.ts +++ b/src/libs/actions/PriorityMode.ts @@ -1,11 +1,10 @@ import debounce from 'lodash/debounce'; -import type {OnyxCollection} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; +import getAllReports from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report} from '@src/types/onyx'; /** * This actions file is used to automatically switch a user into #focus mode when they exceed a certain number of reports. We do this primarily for performance reasons. @@ -35,18 +34,6 @@ Onyx.connect({ // eslint-disable-next-line @typescript-eslint/no-use-before-define const autoSwitchToFocusMode = debounce(tryFocusModeUpdate, 300, {leading: true}); -let allReports: OnyxCollection | undefined = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (reports) => { - allReports = reports; - - // Each time a new report is added we will check to see if the user should be switched - autoSwitchToFocusMode(); - }, -}); - let isLoadingReportData = true; Onyx.connect({ key: ONYXKEYS.IS_LOADING_REPORT_DATA, @@ -87,11 +74,10 @@ function resetHasReadRequiredDataFromStorage() { resolveIsReadyPromise = resolve; }); isLoadingReportData = true; - allReports = {}; } function checkRequiredData() { - if (allReports === undefined || hasTriedFocusMode === undefined || isInFocusMode === undefined || isLoadingReportData) { + if (getAllReports() === undefined || hasTriedFocusMode === undefined || isInFocusMode === undefined || isLoadingReportData) { return; } @@ -112,6 +98,7 @@ function tryFocusModeUpdate() { } const validReports = []; + const allReports = getAllReports(); Object.keys(allReports ?? {}).forEach((key) => { const report = allReports?.[key]; if (!report) { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index b261bb0ade90..bf1791bf4415 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -69,6 +69,7 @@ import {extractPolicyIDFromPath} from '@libs/PolicyUtils'; import processReportIDDeeplink from '@libs/processReportIDDeeplink'; import * as Pusher from '@libs/Pusher/pusher'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import getAllReports from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; import type {OptimisticAddCommentReportAction} from '@libs/ReportUtils'; @@ -186,20 +187,6 @@ Onyx.connect({ }, }); -const currentReportData: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - callback: (report, key) => { - if (!key || !report) { - return; - } - const reportID = CollectionUtils.extractCollectionItemID(key); - currentReportData[reportID] = report; - // eslint-disable-next-line @typescript-eslint/no-use-before-define - handleReportChanged(report); - }, -}); - let isNetworkOffline = false; let networkStatus: NetworkStatus; Onyx.connect({ @@ -652,7 +639,7 @@ function updateGroupChatName(reportID: string, reportName: string) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { - reportName: currentReportData?.[reportID]?.reportName ?? null, + reportName: getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.reportName ?? null, errors: { reportName: Localize.translateLocal('common.genericErrorMessage'), }, @@ -689,7 +676,7 @@ function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageMani onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { - avatarUrl: currentReportData?.[reportID]?.avatarUrl ?? null, + avatarUrl: getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.avatarUrl ?? null, pendingFields: { avatar: null, }, @@ -934,7 +921,7 @@ function openReport( } } - parameters.clientLastReadTime = currentReportData?.[reportID]?.lastReadTime ?? ''; + parameters.clientLastReadTime = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.lastReadTime ?? ''; if (isFromDeepLink) { // eslint-disable-next-line rulesdir/no-api-side-effects-method @@ -2112,7 +2099,7 @@ function addPolicyReport(policyReport: ReportUtils.OptimisticChatReport) { /** Deletes a report, along with its reportActions, any linked reports, and any linked IOU report. */ function deleteReport(reportID: string) { - const report = currentReportData?.[reportID]; + const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const onyxData: Record = { [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]: null, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`]: null, @@ -2294,7 +2281,7 @@ function shouldShowReportActionNotification(reportID: string, action: ReportActi return false; } - const report = currentReportData?.[reportID]; + const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report || (report && report.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)) { Log.info(`${tag} No notification because the report does not exist or is pending deleted`, false); return false; @@ -2623,7 +2610,7 @@ function leaveGroupChat(reportID: string) { /** Leave a report by setting the state to submitted and closed */ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = false) { - const report = currentReportData?.[reportID]; + const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { return; @@ -2706,7 +2693,7 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal /** Invites people to a room */ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: InvitedEmailsToAccountIDs) { - const report = currentReportData?.[reportID]; + const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { return; } @@ -2800,7 +2787,7 @@ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: InvitedEmails } function clearAddRoomMemberError(reportID: string, invitedAccountID: string) { - const report = currentReportData?.[reportID]; + const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { pendingChatMembers: report?.pendingChatMembers?.filter((pendingChatMember) => pendingChatMember.accountID !== invitedAccountID), participants: { @@ -2862,7 +2849,7 @@ function inviteToGroupChat(reportID: string, inviteeEmailsToAccountIDs: InvitedE * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details */ function removeFromRoom(reportID: string, targetAccountIDs: number[]) { - const report = currentReportData?.[reportID]; + const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { return; } @@ -3812,4 +3799,5 @@ export { updateLoadingInitialReportAction, clearAddRoomMemberError, clearAvatarErrors, + handleReportChanged, }; diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 35eaa8f12432..297b50a62fdc 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -12,6 +12,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import getAllReports from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import CONST from '@src/CONST'; @@ -78,13 +79,6 @@ Onyx.connect({ }, }); -let allReports: OnyxCollection; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (value) => (allReports = value), -}); - /** * Clears out the task info from the store */ @@ -810,7 +804,7 @@ function getParentReport(report: OnyxEntry | EmptyObject): Ony if (!report?.parentReportID) { return {}; } - return allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`] ?? {}; + return getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`] ?? {}; } /** diff --git a/src/libs/getReportPolicyID.ts b/src/libs/getReportPolicyID.ts index 12124f24fbe7..bb1d72a1993b 100644 --- a/src/libs/getReportPolicyID.ts +++ b/src/libs/getReportPolicyID.ts @@ -1,20 +1,14 @@ -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import Onyx from 'react-native-onyx'; +import type {OnyxEntry} 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), -}); +import getAllReports from './ReportConnection'; /** * Get the report given a reportID */ function getReport(reportID: string | undefined): OnyxEntry | EmptyObject { + const allReports = getAllReports(); if (!allReports) { return {}; } diff --git a/src/libs/markAllPolicyReportsAsRead.ts b/src/libs/markAllPolicyReportsAsRead.ts index 49001a851cf5..0e0590ce7ac4 100644 --- a/src/libs/markAllPolicyReportsAsRead.ts +++ b/src/libs/markAllPolicyReportsAsRead.ts @@ -1,32 +1,21 @@ -import Onyx from 'react-native-onyx'; -import ONYXKEYS from '@src/ONYXKEYS'; import type {Report} from '@src/types/onyx'; import * as ReportActionFile from './actions/Report'; +import getAllReports from './ReportConnection'; import * as ReportUtils from './ReportUtils'; export default function markAllPolicyReportsAsRead(policyID: string) { - const connectionID = Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (allReports) => { - if (!allReports) { - return; - } + let delay = 0; + const allReports = getAllReports() ?? {}; + Object.keys(allReports).forEach((key: string) => { + const report: Report | null | undefined = allReports[key]; + if (report?.policyID !== policyID || !ReportUtils.isUnread(report)) { + return; + } - let delay = 0; - Object.keys(allReports).forEach((key: string) => { - const report: Report | null | undefined = allReports[key]; - if (report?.policyID !== policyID || !ReportUtils.isUnread(report)) { - return; - } + setTimeout(() => { + ReportActionFile.readNewestAction(report?.reportID); + }, delay); - setTimeout(() => { - ReportActionFile.readNewestAction(report?.reportID); - }, delay); - - delay += 1000; - }); - Onyx.disconnect(connectionID); - }, + delay += 1000; }); } diff --git a/src/libs/migrations/Participants.ts b/src/libs/migrations/Participants.ts index 3dbbef486d68..4aa13e6f32c3 100644 --- a/src/libs/migrations/Participants.ts +++ b/src/libs/migrations/Participants.ts @@ -1,6 +1,7 @@ import Onyx from 'react-native-onyx'; import type {NullishDeep, OnyxCollection} from 'react-native-onyx'; import Log from '@libs/Log'; +import getAllReports from '@libs/ReportConnection'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Report} from '@src/types/onyx'; import type {Participants} from '@src/types/onyx/Report'; @@ -11,14 +12,7 @@ type OldReportCollection = Record>; function getReports(): Promise> { return new Promise((resolve) => { - const connectionID = Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (reports) => { - Onyx.disconnect(connectionID); - return resolve(reports); - }, - }); + return resolve(getAllReports()); }); } From 98bc82c631575efce0e9422d432f72fedcc3cd14 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 20 Jun 2024 14:03:21 +0500 Subject: [PATCH 02/29] perf: use single policy connection --- src/libs/OptionsListUtils.ts | 20 +++----- src/libs/PolicyConnection.ts | 44 ++++++++++++++++++ src/libs/actions/Policy/Category.ts | 43 ++--------------- src/libs/actions/Policy/DistanceRate.ts | 37 +-------------- src/libs/actions/Policy/Member.ts | 41 ++-------------- src/libs/actions/Policy/Policy.ts | 62 ++++++------------------- src/libs/actions/Policy/Tag.ts | 38 +-------------- src/libs/actions/Welcome.ts | 20 -------- 8 files changed, 77 insertions(+), 228 deletions(-) create mode 100644 src/libs/PolicyConnection.ts diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index ad0cf6974f70..4b19fa52e4d5 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -51,6 +51,7 @@ import Performance from './Performance'; import Permissions from './Permissions'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import * as PhoneNumber from './PhoneNumber'; +import getAllPolicies from './PolicyConnection'; import * as PolicyUtils from './PolicyUtils'; import * as ReportActionUtils from './ReportActionsUtils'; import * as ReportUtils from './ReportUtils'; @@ -256,18 +257,6 @@ Onyx.connect({ }, }); -const policies: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - callback: (policy, key) => { - if (!policy || !key || !policy.name) { - return; - } - - policies[key] = policy; - }, -}); - const lastReportActions: ReportActions = {}; const allSortedReportActions: Record = {}; let allReportActions: OnyxCollection; @@ -1807,7 +1796,7 @@ function getOptions( report, currentReportId: topmostReportId, betas, - policies, + policies: getAllPolicies(), doesReportHaveViolations, isInFocusMode: false, excludeEmptyChats: false, @@ -1931,7 +1920,10 @@ function getOptions( reportOption.isPolicyExpenseChat && reportOption.ownerAccountID === currentUserAccountID && includeOwnedWorkspaceChats && !reportOption.isArchivedRoom; const shouldShowInvoiceRoom = - includeInvoiceRooms && ReportUtils.isInvoiceRoom(reportOption.item) && ReportUtils.isPolicyAdmin(reportOption.policyID ?? '', policies) && !reportOption.isArchivedRoom; + includeInvoiceRooms && + ReportUtils.isInvoiceRoom(reportOption.item) && + ReportUtils.isPolicyAdmin(reportOption.policyID ?? '', getAllPolicies()) && + !reportOption.isArchivedRoom; /** Exclude the report option if it doesn't meet any of the following conditions: diff --git a/src/libs/PolicyConnection.ts b/src/libs/PolicyConnection.ts new file mode 100644 index 000000000000..a70a524d0e92 --- /dev/null +++ b/src/libs/PolicyConnection.ts @@ -0,0 +1,44 @@ +import type {NullishDeep, OnyxCollection} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Policy, Report} from '@src/types/onyx'; +import * as ReportUtils from './ReportUtils'; + +const allPolicies: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + callback: (val, key) => { + if (!key) { + return; + } + if (val === null || val === undefined) { + // If we are deleting a policy, we have to check every report linked to that policy + // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. + // More info: https://github.com/Expensify/App/issues/14260 + const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); + const policyReports = ReportUtils.getAllPolicyReports(policyID); + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; + const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; + policyReports.forEach((policyReport) => { + if (!policyReport) { + return; + } + const {reportID} = policyReport; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; + }); + Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); + Onyx.multiSet(cleanUpSetQueries); + delete allPolicies[key]; + return; + } + + allPolicies[key] = val; + }, +}); + +function getAllPolicies() { + return allPolicies; +} + +export default getAllPolicies; diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts index d639c68216df..b88d48a5b6e9 100644 --- a/src/libs/actions/Policy/Category.ts +++ b/src/libs/actions/Policy/Category.ts @@ -1,5 +1,5 @@ import lodashUnion from 'lodash/union'; -import type {NullishDeep, OnyxCollection, OnyxUpdate} from 'react-native-onyx'; +import type {OnyxCollection, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type {EnablePolicyCategoriesParams, OpenPolicyCategoriesPageParams, SetPolicyDistanceRatesDefaultCategoryParams} from '@libs/API/parameters'; @@ -8,47 +8,14 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import Log from '@libs/Log'; import * as OptionsListUtils from '@libs/OptionsListUtils'; +import getAllPolicies from '@libs/PolicyConnection'; import {navigateWhenEnableFeature, removePendingFieldsFromCustomUnit} from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, PolicyCategories, PolicyCategory, RecentlyUsedCategories, Report} from '@src/types/onyx'; +import type {PolicyCategories, PolicyCategory, RecentlyUsedCategories} from '@src/types/onyx'; import type {CustomUnit} from '@src/types/onyx/Policy'; import type {OnyxData} from '@src/types/onyx/Request'; -const allPolicies: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - callback: (val, key) => { - if (!key) { - return; - } - if (val === null || val === undefined) { - // If we are deleting a policy, we have to check every report linked to that policy - // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. - // More info: https://github.com/Expensify/App/issues/14260 - const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); - const policyReports = ReportUtils.getAllPolicyReports(policyID); - const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; - const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; - policyReports.forEach((policyReport) => { - if (!policyReport) { - return; - } - const {reportID} = policyReport; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; - }); - Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); - Onyx.multiSet(cleanUpSetQueries); - delete allPolicies[key]; - return; - } - - allPolicies[key] = val; - }, -}); - let allRecentlyUsedCategories: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES, @@ -147,7 +114,7 @@ function buildOptimisticPolicyRecentlyUsedCategories(policyID?: string, category function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Record) { const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; const optimisticPolicyCategoriesData = { ...Object.keys(categoriesToUpdate).reduce((acc, key) => { acc[key] = { @@ -400,7 +367,7 @@ function clearCategoryErrors(policyID: string, categoryName: string) { } function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: string[]) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; const optimisticPolicyCategoriesData = categoryNamesToDelete.reduce>>((acc, categoryName) => { acc[categoryName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; diff --git a/src/libs/actions/Policy/DistanceRate.ts b/src/libs/actions/Policy/DistanceRate.ts index d22299e9e2fc..8ac3a36de4c8 100644 --- a/src/libs/actions/Policy/DistanceRate.ts +++ b/src/libs/actions/Policy/DistanceRate.ts @@ -1,4 +1,4 @@ -import type {NullishDeep, OnyxCollection, OnyxUpdate} from 'react-native-onyx'; +import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type { @@ -14,10 +14,8 @@ import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as ErrorUtils from '@libs/ErrorUtils'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import {navigateWhenEnableFeature, removePendingFieldsFromCustomUnit} from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, Report} from '@src/types/onyx'; import type {ErrorFields} from '@src/types/onyx/OnyxCommon'; import type {Attributes, CustomUnit, Rate} from '@src/types/onyx/Policy'; import type {OnyxData} from '@src/types/onyx/Request'; @@ -29,39 +27,6 @@ type NewCustomUnit = { rates: Rate; }; -const allPolicies: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - callback: (val, key) => { - if (!key) { - return; - } - if (val === null || val === undefined) { - // If we are deleting a policy, we have to check every report linked to that policy - // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. - // More info: https://github.com/Expensify/App/issues/14260 - const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); - const policyReports = ReportUtils.getAllPolicyReports(policyID); - const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; - const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; - policyReports.forEach((policyReport) => { - if (!policyReport) { - return; - } - const {reportID} = policyReport; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; - }); - Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); - Onyx.multiSet(cleanUpSetQueries); - delete allPolicies[key]; - return; - } - - allPolicies[key] = val; - }, -}); - /** * Takes array of customUnitRates and removes pendingFields and errorFields from each rate - we don't want to send those via API */ diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index 0da122a16bae..1503bf9669cb 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -1,5 +1,5 @@ import {ExpensiMark} from 'expensify-common'; -import type {NullishDeep, OnyxCollection, OnyxCollectionInputValue, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; +import type {OnyxCollectionInputValue, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type { @@ -14,6 +14,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import Log from '@libs/Log'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PhoneNumber from '@libs/PhoneNumber'; +import getAllPolicies from '@libs/PolicyConnection'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -44,39 +45,6 @@ type WorkspaceMembersRoleData = { role: typeof CONST.POLICY.ROLE.ADMIN | typeof CONST.POLICY.ROLE.USER; }; -const allPolicies: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - callback: (val, key) => { - if (!key) { - return; - } - if (val === null || val === undefined) { - // If we are deleting a policy, we have to check every report linked to that policy - // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. - // More info: https://github.com/Expensify/App/issues/14260 - const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); - const policyReports = ReportUtils.getAllPolicyReports(policyID); - const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; - const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; - policyReports.forEach((policyReport) => { - if (!policyReport) { - return; - } - const {reportID} = policyReport; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; - }); - Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); - Onyx.multiSet(cleanUpSetQueries); - delete allPolicies[key]; - return; - } - - allPolicies[key] = val; - }, -}); - let sessionEmail = ''; let sessionAccountID = 0; Onyx.connect({ @@ -105,6 +73,7 @@ Onyx.connect({ * Returns the policy of the report */ function getPolicy(policyID: string | undefined): Policy | EmptyObject { + const allPolicies = getAllPolicies(); if (!allPolicies || !policyID) { return {}; } @@ -309,7 +278,7 @@ function removeMembers(accountIDs: number[], policyID: string) { // If we delete all these logins then we should clear the informative messages since they are no longer relevant. if (!isEmptyObject(policy?.primaryLoginsInvited ?? {})) { // Take the current policy members and remove them optimistically - const employeeListEmails = Object.keys(allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.employeeList ?? {}); + const employeeListEmails = Object.keys(getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.employeeList ?? {}); const remainingLogins = employeeListEmails.filter((email) => !emailList.includes(email)); const invitedPrimaryToSecondaryLogins: Record = {}; @@ -359,7 +328,7 @@ function removeMembers(accountIDs: number[], policyID: string) { } function updateWorkspaceMembersRole(policyID: string, accountIDs: number[], newRole: typeof CONST.POLICY.ROLE.ADMIN | typeof CONST.POLICY.ROLE.USER) { - const previousEmployeeList = {...allPolicies?.[policyID]?.employeeList}; + const previousEmployeeList = {...getAllPolicies()?.[policyID]?.employeeList}; const memberRoles: WorkspaceMembersRoleData[] = accountIDs.reduce((result: WorkspaceMembersRoleData[], accountID: number) => { if (!allPersonalDetails?.[accountID]?.login) { return result; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index b15bcc93a6f5..71a510162aa5 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1,7 +1,7 @@ import {PUBLIC_DOMAINS, Str} from 'expensify-common'; import {escapeRegExp} from 'lodash'; import lodashClone from 'lodash/clone'; -import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import * as API from '@libs/API'; @@ -42,9 +42,11 @@ import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import Log from '@libs/Log'; import * as NumberUtils from '@libs/NumberUtils'; import * as PhoneNumber from '@libs/PhoneNumber'; +import getAllPolicies from '@libs/PolicyConnection'; import * as PolicyUtils from '@libs/PolicyUtils'; import {navigateWhenEnableFeature} from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import getAllReports from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import type {PolicySelector} from '@pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover'; @@ -87,46 +89,6 @@ type NewCustomUnit = { rates: Rate; }; -const allPolicies: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - callback: (val, key) => { - if (!key) { - return; - } - if (val === null || val === undefined) { - // If we are deleting a policy, we have to check every report linked to that policy - // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. - // More info: https://github.com/Expensify/App/issues/14260 - const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); - const policyReports = ReportUtils.getAllPolicyReports(policyID); - const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; - const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; - policyReports.forEach((policyReport) => { - if (!policyReport) { - return; - } - const {reportID} = policyReport; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; - }); - Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); - Onyx.multiSet(cleanUpSetQueries); - delete allPolicies[key]; - return; - } - - allPolicies[key] = val; - }, -}); - -let allReports: OnyxCollection; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (value) => (allReports = value), -}); - let lastAccessedWorkspacePolicyID: OnyxEntry; Onyx.connect({ key: ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, @@ -174,6 +136,7 @@ function isCurrencySupportedForDirectReimbursement(currency: string) { * Returns the policy of the report */ function getPolicy(policyID: string | undefined): Policy | EmptyObject { + const allPolicies = getAllPolicies(); if (!allPolicies || !policyID) { return {}; } @@ -184,6 +147,7 @@ function getPolicy(policyID: string | undefined): Policy | EmptyObject { * Returns a primary policy for the user */ function getPrimaryPolicy(activePolicyID?: OnyxEntry): Policy | undefined { + const allPolicies = getAllPolicies(); const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies); const primaryPolicy: Policy | null | undefined = allPolicies?.[activePolicyID ?? '-1']; @@ -226,6 +190,7 @@ function hasActiveChatEnabledPolicies(policies: Array> * Delete the workspace */ function deleteWorkspace(policyID: string, policyName: string) { + const allPolicies = getAllPolicies(); if (!allPolicies) { return; } @@ -254,7 +219,7 @@ function deleteWorkspace(policyID: string, policyName: string) { : []), ]; - const reportsToArchive = Object.values(allReports ?? {}).filter( + const reportsToArchive = Object.values(getAllReports() ?? {}).filter( (report) => report?.policyID === policyID && (ReportUtils.isChatRoom(report) || ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isTaskReport(report)), ); const finallyData: OnyxUpdate[] = []; @@ -266,7 +231,7 @@ function deleteWorkspace(policyID: string, policyName: string) { value: { stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, - oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', + oldPolicyName: getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', policyName: '', }, }); @@ -628,7 +593,7 @@ function clearWorkspaceReimbursementErrors(policyID: string) { } function leaveWorkspace(policyID: string) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; const workspaceChats = ReportUtils.getAllWorkspaceReports(policyID); const optimisticData: OnyxUpdate[] = [ @@ -920,7 +885,7 @@ function updateWorkspaceAvatar(policyID: string, file: File) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - avatarURL: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.avatarURL, + avatarURL: getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.avatarURL, }, }, ]; @@ -999,7 +964,7 @@ function clearAvatarErrors(policyID: string) { * If the response fails set a general error message. Clear the error message when updating. */ function updateGeneralSettings(policyID: string, name: string, currencyValue?: string) { - const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; if (!policy) { return; } @@ -1181,7 +1146,7 @@ function clearWorkspaceGeneralSettingsErrors(policyID: string) { } function setWorkspaceErrors(policyID: string, errors: Errors) { - if (!allPolicies?.[policyID]) { + if (!getAllPolicies()?.[policyID]) { return; } @@ -1207,7 +1172,7 @@ function clearCustomUnitErrors(policyID: string, customUnitID: string, customUni } function hideWorkspaceAlertMessage(policyID: string) { - if (!allPolicies?.[policyID]) { + if (!getAllPolicies()?.[policyID]) { return; } @@ -1366,6 +1331,7 @@ function generateDefaultWorkspaceName(email = ''): string { defaultWorkspaceName = 'My Group Workspace'; } + const allPolicies = getAllPolicies(); if (isEmptyObject(allPolicies)) { return defaultWorkspaceName; } diff --git a/src/libs/actions/Policy/Tag.ts b/src/libs/actions/Policy/Tag.ts index d8cb0ef8b028..0e0daeaa7246 100644 --- a/src/libs/actions/Policy/Tag.ts +++ b/src/libs/actions/Policy/Tag.ts @@ -1,4 +1,4 @@ -import type {NullishDeep, OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type {EnablePolicyTagsParams, OpenPolicyTagsPageParams, RenamePolicyTaglistParams, RenamePolicyTagsParams, SetPolicyTagsEnabled, SetPolicyTagsRequired} from '@libs/API/parameters'; @@ -8,11 +8,10 @@ import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import Log from '@libs/Log'; import * as PolicyUtils from '@libs/PolicyUtils'; import {navigateWhenEnableFeature} from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, PolicyTag, PolicyTagList, PolicyTags, RecentlyUsedTags, Report} from '@src/types/onyx'; +import type {PolicyTag, PolicyTagList, PolicyTags, RecentlyUsedTags} from '@src/types/onyx'; import type {OnyxValueWithOfflineFeedback} from '@src/types/onyx/OnyxCommon'; import type {Attributes, Rate} from '@src/types/onyx/Policy'; import type {OnyxData} from '@src/types/onyx/Request'; @@ -24,39 +23,6 @@ type NewCustomUnit = { rates: Rate; }; -const allPolicies: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - callback: (val, key) => { - if (!key) { - return; - } - if (val === null || val === undefined) { - // If we are deleting a policy, we have to check every report linked to that policy - // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. - // More info: https://github.com/Expensify/App/issues/14260 - const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); - const policyReports = ReportUtils.getAllPolicyReports(policyID); - const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; - const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; - policyReports.forEach((policyReport) => { - if (!policyReport) { - return; - } - const {reportID} = policyReport; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; - }); - Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); - Onyx.multiSet(cleanUpSetQueries); - delete allPolicies[key]; - return; - } - - allPolicies[key] = val; - }, -}); - let allPolicyTags: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_TAGS, diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index 3f70dc0d962d..1516bae8f651 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -1,10 +1,7 @@ -import type {OnyxCollection} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {OnboardingPurposeType} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type Onboarding from '@src/types/onyx/Onboarding'; -import type OnyxPolicy from '@src/types/onyx/Policy'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; let onboarding: Onboarding | [] | undefined; let isLoadingReportData = true; @@ -99,23 +96,6 @@ Onyx.connect({ }, }); -const allPolicies: OnyxCollection | EmptyObject = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - callback: (val, key) => { - if (!key) { - return; - } - - if (val === null || val === undefined) { - delete allPolicies[key]; - return; - } - - allPolicies[key] = {...allPolicies[key], ...val}; - }, -}); - function resetAllChecks() { isServerDataReadyPromise = new Promise((resolve) => { resolveIsReadyPromise = resolve; From 36cb17160219025f251de2c51b7d39a869386f71 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 20 Jun 2024 14:04:30 +0500 Subject: [PATCH 03/29] perf: reduce re-renders by memoizing policyMemberAccountIDs and explicitly checking whether derivedCurrentReportID is not -1 --- src/hooks/useReportIDs.tsx | 56 ++++++-------------------------------- 1 file changed, 9 insertions(+), 47 deletions(-) diff --git a/src/hooks/useReportIDs.tsx b/src/hooks/useReportIDs.tsx index d5089d9f7990..f5355811e961 100644 --- a/src/hooks/useReportIDs.tsx +++ b/src/hooks/useReportIDs.tsx @@ -2,7 +2,6 @@ import React, {createContext, useCallback, useContext, useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import {getPolicyEmployeeListByIdWithoutCurrentUser} from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -12,7 +11,6 @@ import useActiveWorkspace from './useActiveWorkspace'; import useCurrentReportID from './useCurrentReportID'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; -type ChatReportSelector = OnyxTypes.Report & {isUnreadWithMention: boolean}; type PolicySelector = Pick; type ReportActionsSelector = Array>; @@ -24,56 +22,19 @@ type ReportIDsContextProviderProps = { type ReportIDsContextValue = { orderedReportIDs: string[]; currentReportID: string; + policyMemberAccountIDs: number[]; }; const ReportIDsContext = createContext({ orderedReportIDs: [], currentReportID: '', + policyMemberAccountIDs: [], }); /** * This function (and the few below it), narrow down the data from Onyx to just the properties that we want to trigger a re-render of the component. This helps minimize re-rendering * and makes the entire component more performant because it's not re-rendering when a bunch of properties change which aren't ever used in the UI. */ -const chatReportSelector = (report: OnyxEntry): ChatReportSelector => - (report && { - reportID: report.reportID, - participants: report.participants, - isPinned: report.isPinned, - isHidden: report.isHidden, - notificationPreference: report.notificationPreference, - errorFields: { - addWorkspaceRoom: report.errorFields?.addWorkspaceRoom, - }, - lastMessageText: report.lastMessageText, - lastVisibleActionCreated: report.lastVisibleActionCreated, - iouReportID: report.iouReportID, - total: report.total, - nonReimbursableTotal: report.nonReimbursableTotal, - hasOutstandingChildRequest: report.hasOutstandingChildRequest, - isWaitingOnBankAccount: report.isWaitingOnBankAccount, - statusNum: report.statusNum, - stateNum: report.stateNum, - chatType: report.chatType, - type: report.type, - policyID: report.policyID, - visibility: report.visibility, - lastReadTime: report.lastReadTime, - // Needed for name sorting: - reportName: report.reportName, - policyName: report.policyName, - oldPolicyName: report.oldPolicyName, - // Other less obvious properites considered for sorting: - ownerAccountID: report.ownerAccountID, - currency: report.currency, - managerID: report.managerID, - // Other important less obivous properties for filtering: - parentReportActionID: report.parentReportActionID, - parentReportID: report.parentReportID, - isDeletedParentAction: report.isDeletedParentAction, - isUnreadWithMention: ReportUtils.isUnreadWithMention(report), - }) as ChatReportSelector; - const reportActionsSelector = (reportActions: OnyxEntry): ReportActionsSelector => (reportActions && Object.values(reportActions) @@ -116,7 +77,7 @@ function ReportIDsContextProvider({ currentReportIDForTests, }: ReportIDsContextProviderProps) { const [priorityMode] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE, {initialValue: CONST.PRIORITY_MODE.DEFAULT}); - const [chatReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {selector: chatReportSelector}); + const [chatReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: policySelector}); const [allReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, {selector: reportActionsSelector}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); @@ -128,7 +89,7 @@ function ReportIDsContextProvider({ const derivedCurrentReportID = currentReportIDForTests ?? currentReportIDValue?.currentReportID; const {activeWorkspaceID} = useActiveWorkspace(); - const policyMemberAccountIDs = getPolicyEmployeeListByIdWithoutCurrentUser(policies, activeWorkspaceID, accountID); + const policyMemberAccountIDs = useMemo(() => getPolicyEmployeeListByIdWithoutCurrentUser(policies, activeWorkspaceID, accountID), [policies, activeWorkspaceID, accountID]); const getOrderedReportIDs = useCallback( (currentReportID?: string) => @@ -155,15 +116,16 @@ function ReportIDsContextProvider({ // we first generate the list as if there was no current report, then we check if // the current report is missing from the list, which should very rarely happen. In this // case we re-generate the list a 2nd time with the current report included. - if (derivedCurrentReportID && !orderedReportIDs.includes(derivedCurrentReportID)) { - return {orderedReportIDs: getOrderedReportIDs(derivedCurrentReportID), currentReportID: derivedCurrentReportID ?? '-1'}; + if (derivedCurrentReportID && derivedCurrentReportID !== '-1' && orderedReportIDs.indexOf(derivedCurrentReportID) === -1) { + return {orderedReportIDs: getOrderedReportIDs(derivedCurrentReportID), currentReportID: derivedCurrentReportID ?? '-1', policyMemberAccountIDs}; } return { orderedReportIDs, currentReportID: derivedCurrentReportID ?? '-1', + policyMemberAccountIDs, }; - }, [getOrderedReportIDs, orderedReportIDs, derivedCurrentReportID]); + }, [getOrderedReportIDs, orderedReportIDs, derivedCurrentReportID, policyMemberAccountIDs]); return {children}; } @@ -173,4 +135,4 @@ function useReportIDs() { } export {ReportIDsContext, ReportIDsContextProvider, policySelector, useReportIDs}; -export type {ChatReportSelector, PolicySelector, ReportActionsSelector}; +export type {PolicySelector, ReportActionsSelector}; From 1761567a8c50c293d693f00930ede7edae81c8ce Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 20 Jun 2024 14:04:58 +0500 Subject: [PATCH 04/29] perf: use policies from useReportIDs --- src/pages/home/sidebar/SidebarLinksData.tsx | 25 +++++---------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx index 635d4df5cb58..471e0d0bf20e 100644 --- a/src/pages/home/sidebar/SidebarLinksData.tsx +++ b/src/pages/home/sidebar/SidebarLinksData.tsx @@ -2,17 +2,14 @@ import {useIsFocused} from '@react-navigation/native'; import lodashIsEqual from 'lodash/isEqual'; import React, {memo, useCallback, useEffect, useRef} from 'react'; import {View} from 'react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {EdgeInsets} from 'react-native-safe-area-context'; import type {ValueOf} from 'type-fest'; import useActiveWorkspaceFromNavigationState from '@hooks/useActiveWorkspaceFromNavigationState'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; -import type {PolicySelector} from '@hooks/useReportIDs'; -import {policySelector, useReportIDs} from '@hooks/useReportIDs'; +import {useReportIDs} from '@hooks/useReportIDs'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getPolicyEmployeeListByIdWithoutCurrentUser} from '@libs/PolicyUtils'; import * as Policy from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -24,9 +21,6 @@ type SidebarLinksDataOnyxProps = { /** The chat priority mode */ priorityMode: OnyxEntry>; - - /** The policies which the user has access to */ - policies: OnyxCollection; }; type SidebarLinksDataProps = SidebarLinksDataOnyxProps & { @@ -37,21 +31,18 @@ type SidebarLinksDataProps = SidebarLinksDataOnyxProps & { insets: EdgeInsets; }; -function SidebarLinksData({insets, isLoadingApp = true, onLinkClick, priorityMode = CONST.PRIORITY_MODE.DEFAULT, policies}: SidebarLinksDataProps) { - const {accountID} = useCurrentUserPersonalDetails(); +function SidebarLinksData({insets, isLoadingApp = true, onLinkClick, priorityMode = CONST.PRIORITY_MODE.DEFAULT}: SidebarLinksDataProps) { const isFocused = useIsFocused(); const styles = useThemeStyles(); const activeWorkspaceID = useActiveWorkspaceFromNavigationState(); const {translate} = useLocalize(); - const policyMemberAccountIDs = getPolicyEmployeeListByIdWithoutCurrentUser(policies, activeWorkspaceID, accountID); + const {orderedReportIDs, currentReportID, policyMemberAccountIDs} = useReportIDs(); // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(() => Policy.openWorkspace(activeWorkspaceID ?? '-1', policyMemberAccountIDs), [activeWorkspaceID]); const isLoading = isLoadingApp; - const {orderedReportIDs, currentReportID} = useReportIDs(); - const currentReportIDRef = useRef(currentReportID); currentReportIDRef.current = currentReportID; const isActiveReport = useCallback((reportID: string): boolean => currentReportIDRef.current === reportID, []); @@ -88,11 +79,6 @@ export default withOnyx({ key: ONYXKEYS.NVP_PRIORITY_MODE, initialValue: CONST.PRIORITY_MODE.DEFAULT, }, - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - selector: policySelector, - initialValue: {}, - }, })( /* While working on audit on the App Start App metric we noticed that by memoizing SidebarLinksData we can avoid 2 additional run of getOrderedReportIDs. @@ -105,7 +91,6 @@ More details - https://github.com/Expensify/App/issues/35234#issuecomment-192691 prevProps.isLoadingApp === nextProps.isLoadingApp && prevProps.priorityMode === nextProps.priorityMode && lodashIsEqual(prevProps.insets, nextProps.insets) && - prevProps.onLinkClick === nextProps.onLinkClick && - lodashIsEqual(prevProps.policies, nextProps.policies), + prevProps.onLinkClick === nextProps.onLinkClick, ), ); From 5ec39eb2117584f611031e1c8f659c5d6b447d3d Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 20 Jun 2024 14:05:29 +0500 Subject: [PATCH 05/29] perf: avoid translating same value repeatedly --- src/libs/PersonalDetailsUtils.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index 9a35dfc41b72..337fd6125e80 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -33,11 +33,16 @@ Onyx.connect({ }, }); +const hiddenTranslation = Localize.translateLocal('common.hidden'); +const youTranslation = Localize.translateLocal('common.you').toLowerCase(); + +const regexMergedAccount = new RegExp(CONST.REGEX.MERGED_ACCOUNT_PREFIX); + function getDisplayNameOrDefault(passedPersonalDetails?: Partial | null, defaultValue = '', shouldFallbackToHidden = true, shouldAddCurrentUserPostfix = false): string { let displayName = passedPersonalDetails?.displayName ?? ''; // If the displayName starts with the merged account prefix, remove it. - if (new RegExp(CONST.REGEX.MERGED_ACCOUNT_PREFIX).test(displayName)) { + if (regexMergedAccount.test(displayName)) { // Remove the merged account prefix from the displayName. displayName = displayName.replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); } @@ -49,7 +54,7 @@ function getDisplayNameOrDefault(passedPersonalDetails?: Partial Date: Thu, 20 Jun 2024 14:05:49 +0500 Subject: [PATCH 06/29] fix: types --- src/libs/SidebarUtils.ts | 8 +++----- tests/perf-test/SidebarUtils.perf-test.ts | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 4e5185c56cc3..6cef59755cdf 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1,7 +1,7 @@ import {Str} from 'expensify-common'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import type {ChatReportSelector, PolicySelector, ReportActionsSelector} from '@hooks/useReportIDs'; +import type {PolicySelector, ReportActionsSelector} from '@hooks/useReportIDs'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, PersonalDetailsList, ReportActions, TransactionViolation} from '@src/types/onyx'; @@ -77,7 +77,7 @@ type MiniReport = { */ function getOrderedReportIDs( currentReportId: string | null, - allReports: OnyxCollection, + allReports: OnyxCollection, betas: OnyxEntry, policies: OnyxCollection, priorityMode: OnyxEntry, @@ -116,9 +116,7 @@ function getOrderedReportIDs( return false; } - const participantAccountIDs = Object.keys(report?.participants ?? {}).map(Number); - - if (currentUserAccountID && AccountUtils.isAccountIDOddNumber(currentUserAccountID) && participantAccountIDs.includes(CONST.ACCOUNT_ID.NOTIFICATIONS)) { + if (currentUserAccountID && AccountUtils.isAccountIDOddNumber(currentUserAccountID) && report?.participants?.[CONST.ACCOUNT_ID.NOTIFICATIONS]) { return true; } diff --git a/tests/perf-test/SidebarUtils.perf-test.ts b/tests/perf-test/SidebarUtils.perf-test.ts index cceb3ae437b9..76e072ddb28d 100644 --- a/tests/perf-test/SidebarUtils.perf-test.ts +++ b/tests/perf-test/SidebarUtils.perf-test.ts @@ -2,12 +2,12 @@ import {rand} from '@ngneat/falso'; import type {OnyxCollection} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import {measureFunction} from 'reassure'; -import type {ChatReportSelector} from '@hooks/useReportIDs'; import SidebarUtils from '@libs/SidebarUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, TransactionViolation} from '@src/types/onyx'; import type Policy from '@src/types/onyx/Policy'; +import type Report from '@src/types/onyx/Report'; import type ReportAction from '@src/types/onyx/ReportAction'; import createCollection from '../utils/collections/createCollection'; import createPersonalDetails from '../utils/collections/personalDetails'; @@ -20,7 +20,7 @@ const REPORTS_COUNT = 15000; const REPORT_TRESHOLD = 5; const PERSONAL_DETAILS_LIST_COUNT = 1000; -const allReports = createCollection( +const allReports = createCollection( (item) => `${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, (index) => ({ ...createRandomReport(index), From 87bbd8b5750ea0bf8c27d36ca3ec28e9ffd1edd5 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 20 Jun 2024 14:06:15 +0500 Subject: [PATCH 07/29] fix: update imports --- tests/unit/UnreadIndicatorUpdaterTest.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/UnreadIndicatorUpdaterTest.ts b/tests/unit/UnreadIndicatorUpdaterTest.ts index 22141eee791d..9cf65bcb69d4 100644 --- a/tests/unit/UnreadIndicatorUpdaterTest.ts +++ b/tests/unit/UnreadIndicatorUpdaterTest.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import CONST from '../../src/CONST'; -import getUnreadReportsForUnreadIndicator from '../../src/libs/UnreadIndicatorUpdater'; +import * as UnreadIndicatorUpdater from '../../src/libs/UnreadIndicatorUpdater'; describe('UnreadIndicatorUpdaterTest', () => { describe('should return correct number of unread reports', () => { @@ -24,7 +24,7 @@ describe('UnreadIndicatorUpdaterTest', () => { }, 3: {reportID: '3', reportName: 'test', type: CONST.REPORT.TYPE.TASK, lastMessageText: 'test'}, }; - expect(getUnreadReportsForUnreadIndicator(reportsToBeUsed, '3').length).toBe(2); + expect(UnreadIndicatorUpdater.getUnreadReportsForUnreadIndicator(reportsToBeUsed, '3').length).toBe(2); }); it('given some reports are incomplete', () => { @@ -33,7 +33,7 @@ describe('UnreadIndicatorUpdaterTest', () => { 2: {reportID: '2', type: CONST.REPORT.TYPE.TASK, lastReadTime: '2023-02-05 09:12:05.000', lastVisibleActionCreated: '2023-02-06 07:15:44.030'}, 3: {reportID: '3', type: CONST.REPORT.TYPE.TASK}, }; - expect(getUnreadReportsForUnreadIndicator(reportsToBeUsed, '3').length).toBe(0); + expect(UnreadIndicatorUpdater.getUnreadReportsForUnreadIndicator(reportsToBeUsed, '3').length).toBe(0); }); it('given notification preference of some reports is hidden', () => { @@ -57,7 +57,7 @@ describe('UnreadIndicatorUpdaterTest', () => { }, 3: {reportID: '3', reportName: 'test', type: CONST.REPORT.TYPE.TASK, lastMessageText: 'test'}, }; - expect(getUnreadReportsForUnreadIndicator(reportsToBeUsed, '3').length).toBe(1); + expect(UnreadIndicatorUpdater.getUnreadReportsForUnreadIndicator(reportsToBeUsed, '3').length).toBe(1); }); }); }); From 839d7a24f3c456149de9779d1434f95e55991a22 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 20 Jun 2024 16:55:09 +0500 Subject: [PATCH 08/29] refactor: remove unrelated changes --- src/libs/OptionsListUtils.ts | 17 ++++++-- src/libs/PolicyConnection.ts | 44 --------------------- src/libs/actions/Policy/Category.ts | 43 +++++++++++++++++--- src/libs/actions/Policy/DistanceRate.ts | 37 +++++++++++++++++- src/libs/actions/Policy/Member.ts | 41 ++++++++++++++++--- src/libs/actions/Policy/Policy.ts | 52 +++++++++++++++++++------ src/libs/actions/Policy/Tag.ts | 38 +++++++++++++++++- 7 files changed, 200 insertions(+), 72 deletions(-) delete mode 100644 src/libs/PolicyConnection.ts diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 4b19fa52e4d5..077545822894 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -51,7 +51,6 @@ import Performance from './Performance'; import Permissions from './Permissions'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import * as PhoneNumber from './PhoneNumber'; -import getAllPolicies from './PolicyConnection'; import * as PolicyUtils from './PolicyUtils'; import * as ReportActionUtils from './ReportActionsUtils'; import * as ReportUtils from './ReportUtils'; @@ -257,6 +256,18 @@ Onyx.connect({ }, }); +const policies: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + callback: (policy, key) => { + if (!policy || !key || !policy.name) { + return; + } + + policies[key] = policy; + }, +}); + const lastReportActions: ReportActions = {}; const allSortedReportActions: Record = {}; let allReportActions: OnyxCollection; @@ -1796,7 +1807,7 @@ function getOptions( report, currentReportId: topmostReportId, betas, - policies: getAllPolicies(), + policies, doesReportHaveViolations, isInFocusMode: false, excludeEmptyChats: false, @@ -1922,7 +1933,7 @@ function getOptions( const shouldShowInvoiceRoom = includeInvoiceRooms && ReportUtils.isInvoiceRoom(reportOption.item) && - ReportUtils.isPolicyAdmin(reportOption.policyID ?? '', getAllPolicies()) && + ReportUtils.isPolicyAdmin(reportOption.policyID ?? '', policies) && !reportOption.isArchivedRoom; /** diff --git a/src/libs/PolicyConnection.ts b/src/libs/PolicyConnection.ts deleted file mode 100644 index a70a524d0e92..000000000000 --- a/src/libs/PolicyConnection.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type {NullishDeep, OnyxCollection} from 'react-native-onyx'; -import Onyx from 'react-native-onyx'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, Report} from '@src/types/onyx'; -import * as ReportUtils from './ReportUtils'; - -const allPolicies: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - callback: (val, key) => { - if (!key) { - return; - } - if (val === null || val === undefined) { - // If we are deleting a policy, we have to check every report linked to that policy - // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. - // More info: https://github.com/Expensify/App/issues/14260 - const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); - const policyReports = ReportUtils.getAllPolicyReports(policyID); - const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; - const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; - policyReports.forEach((policyReport) => { - if (!policyReport) { - return; - } - const {reportID} = policyReport; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; - cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; - }); - Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); - Onyx.multiSet(cleanUpSetQueries); - delete allPolicies[key]; - return; - } - - allPolicies[key] = val; - }, -}); - -function getAllPolicies() { - return allPolicies; -} - -export default getAllPolicies; diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts index b88d48a5b6e9..d639c68216df 100644 --- a/src/libs/actions/Policy/Category.ts +++ b/src/libs/actions/Policy/Category.ts @@ -1,5 +1,5 @@ import lodashUnion from 'lodash/union'; -import type {OnyxCollection, OnyxUpdate} from 'react-native-onyx'; +import type {NullishDeep, OnyxCollection, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type {EnablePolicyCategoriesParams, OpenPolicyCategoriesPageParams, SetPolicyDistanceRatesDefaultCategoryParams} from '@libs/API/parameters'; @@ -8,14 +8,47 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import Log from '@libs/Log'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import getAllPolicies from '@libs/PolicyConnection'; import {navigateWhenEnableFeature, removePendingFieldsFromCustomUnit} from '@libs/PolicyUtils'; +import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PolicyCategories, PolicyCategory, RecentlyUsedCategories} from '@src/types/onyx'; +import type {Policy, PolicyCategories, PolicyCategory, RecentlyUsedCategories, Report} from '@src/types/onyx'; import type {CustomUnit} from '@src/types/onyx/Policy'; import type {OnyxData} from '@src/types/onyx/Request'; +const allPolicies: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + callback: (val, key) => { + if (!key) { + return; + } + if (val === null || val === undefined) { + // If we are deleting a policy, we have to check every report linked to that policy + // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. + // More info: https://github.com/Expensify/App/issues/14260 + const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); + const policyReports = ReportUtils.getAllPolicyReports(policyID); + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; + const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; + policyReports.forEach((policyReport) => { + if (!policyReport) { + return; + } + const {reportID} = policyReport; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; + }); + Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); + Onyx.multiSet(cleanUpSetQueries); + delete allPolicies[key]; + return; + } + + allPolicies[key] = val; + }, +}); + let allRecentlyUsedCategories: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES, @@ -114,7 +147,7 @@ function buildOptimisticPolicyRecentlyUsedCategories(policyID?: string, category function setWorkspaceCategoryEnabled(policyID: string, categoriesToUpdate: Record) { const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; - const policy = getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; const optimisticPolicyCategoriesData = { ...Object.keys(categoriesToUpdate).reduce((acc, key) => { acc[key] = { @@ -367,7 +400,7 @@ function clearCategoryErrors(policyID: string, categoryName: string) { } function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: string[]) { - const policy = getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; const policyCategories = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {}; const optimisticPolicyCategoriesData = categoryNamesToDelete.reduce>>((acc, categoryName) => { acc[categoryName] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; diff --git a/src/libs/actions/Policy/DistanceRate.ts b/src/libs/actions/Policy/DistanceRate.ts index 8ac3a36de4c8..d22299e9e2fc 100644 --- a/src/libs/actions/Policy/DistanceRate.ts +++ b/src/libs/actions/Policy/DistanceRate.ts @@ -1,4 +1,4 @@ -import type {OnyxUpdate} from 'react-native-onyx'; +import type {NullishDeep, OnyxCollection, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type { @@ -14,8 +14,10 @@ import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as ErrorUtils from '@libs/ErrorUtils'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import {navigateWhenEnableFeature, removePendingFieldsFromCustomUnit} from '@libs/PolicyUtils'; +import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Policy, Report} from '@src/types/onyx'; import type {ErrorFields} from '@src/types/onyx/OnyxCommon'; import type {Attributes, CustomUnit, Rate} from '@src/types/onyx/Policy'; import type {OnyxData} from '@src/types/onyx/Request'; @@ -27,6 +29,39 @@ type NewCustomUnit = { rates: Rate; }; +const allPolicies: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + callback: (val, key) => { + if (!key) { + return; + } + if (val === null || val === undefined) { + // If we are deleting a policy, we have to check every report linked to that policy + // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. + // More info: https://github.com/Expensify/App/issues/14260 + const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); + const policyReports = ReportUtils.getAllPolicyReports(policyID); + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; + const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; + policyReports.forEach((policyReport) => { + if (!policyReport) { + return; + } + const {reportID} = policyReport; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; + }); + Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); + Onyx.multiSet(cleanUpSetQueries); + delete allPolicies[key]; + return; + } + + allPolicies[key] = val; + }, +}); + /** * Takes array of customUnitRates and removes pendingFields and errorFields from each rate - we don't want to send those via API */ diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index 1503bf9669cb..ab16196bdf58 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -1,5 +1,5 @@ import {ExpensiMark} from 'expensify-common'; -import type {OnyxCollectionInputValue, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; +import type {NullishDeep, OnyxCollection,OnyxCollectionInputValue, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type { @@ -14,7 +14,6 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import Log from '@libs/Log'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PhoneNumber from '@libs/PhoneNumber'; -import getAllPolicies from '@libs/PolicyConnection'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -45,6 +44,39 @@ type WorkspaceMembersRoleData = { role: typeof CONST.POLICY.ROLE.ADMIN | typeof CONST.POLICY.ROLE.USER; }; +const allPolicies: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + callback: (val, key) => { + if (!key) { + return; + } + if (val === null || val === undefined) { + // If we are deleting a policy, we have to check every report linked to that policy + // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. + // More info: https://github.com/Expensify/App/issues/14260 + const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); + const policyReports = ReportUtils.getAllPolicyReports(policyID); + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; + const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; + policyReports.forEach((policyReport) => { + if (!policyReport) { + return; + } + const {reportID} = policyReport; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; + }); + Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); + Onyx.multiSet(cleanUpSetQueries); + delete allPolicies[key]; + return; + } + + allPolicies[key] = val; + }, +}); + let sessionEmail = ''; let sessionAccountID = 0; Onyx.connect({ @@ -73,7 +105,6 @@ Onyx.connect({ * Returns the policy of the report */ function getPolicy(policyID: string | undefined): Policy | EmptyObject { - const allPolicies = getAllPolicies(); if (!allPolicies || !policyID) { return {}; } @@ -278,7 +309,7 @@ function removeMembers(accountIDs: number[], policyID: string) { // If we delete all these logins then we should clear the informative messages since they are no longer relevant. if (!isEmptyObject(policy?.primaryLoginsInvited ?? {})) { // Take the current policy members and remove them optimistically - const employeeListEmails = Object.keys(getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.employeeList ?? {}); + const employeeListEmails = Object.keys(allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.employeeList ?? {}); const remainingLogins = employeeListEmails.filter((email) => !emailList.includes(email)); const invitedPrimaryToSecondaryLogins: Record = {}; @@ -328,7 +359,7 @@ function removeMembers(accountIDs: number[], policyID: string) { } function updateWorkspaceMembersRole(policyID: string, accountIDs: number[], newRole: typeof CONST.POLICY.ROLE.ADMIN | typeof CONST.POLICY.ROLE.USER) { - const previousEmployeeList = {...getAllPolicies()?.[policyID]?.employeeList}; + const previousEmployeeList = {...allPolicies?.[policyID]?.employeeList}; const memberRoles: WorkspaceMembersRoleData[] = accountIDs.reduce((result: WorkspaceMembersRoleData[], accountID: number) => { if (!allPersonalDetails?.[accountID]?.login) { return result; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 71a510162aa5..787d14015e6a 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1,7 +1,7 @@ import {PUBLIC_DOMAINS, Str} from 'expensify-common'; import {escapeRegExp} from 'lodash'; import lodashClone from 'lodash/clone'; -import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; +import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import * as API from '@libs/API'; @@ -42,7 +42,6 @@ import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import Log from '@libs/Log'; import * as NumberUtils from '@libs/NumberUtils'; import * as PhoneNumber from '@libs/PhoneNumber'; -import getAllPolicies from '@libs/PolicyConnection'; import * as PolicyUtils from '@libs/PolicyUtils'; import {navigateWhenEnableFeature} from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; @@ -89,6 +88,39 @@ type NewCustomUnit = { rates: Rate; }; +const allPolicies: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + callback: (val, key) => { + if (!key) { + return; + } + if (val === null || val === undefined) { + // If we are deleting a policy, we have to check every report linked to that policy + // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. + // More info: https://github.com/Expensify/App/issues/14260 + const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); + const policyReports = ReportUtils.getAllPolicyReports(policyID); + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; + const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; + policyReports.forEach((policyReport) => { + if (!policyReport) { + return; + } + const {reportID} = policyReport; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; + }); + Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); + Onyx.multiSet(cleanUpSetQueries); + delete allPolicies[key]; + return; + } + + allPolicies[key] = val; + }, +}); + let lastAccessedWorkspacePolicyID: OnyxEntry; Onyx.connect({ key: ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, @@ -136,7 +168,6 @@ function isCurrencySupportedForDirectReimbursement(currency: string) { * Returns the policy of the report */ function getPolicy(policyID: string | undefined): Policy | EmptyObject { - const allPolicies = getAllPolicies(); if (!allPolicies || !policyID) { return {}; } @@ -147,7 +178,6 @@ function getPolicy(policyID: string | undefined): Policy | EmptyObject { * Returns a primary policy for the user */ function getPrimaryPolicy(activePolicyID?: OnyxEntry): Policy | undefined { - const allPolicies = getAllPolicies(); const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies); const primaryPolicy: Policy | null | undefined = allPolicies?.[activePolicyID ?? '-1']; @@ -190,7 +220,6 @@ function hasActiveChatEnabledPolicies(policies: Array> * Delete the workspace */ function deleteWorkspace(policyID: string, policyName: string) { - const allPolicies = getAllPolicies(); if (!allPolicies) { return; } @@ -231,7 +260,7 @@ function deleteWorkspace(policyID: string, policyName: string) { value: { stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, - oldPolicyName: getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', + oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', policyName: '', }, }); @@ -593,7 +622,7 @@ function clearWorkspaceReimbursementErrors(policyID: string) { } function leaveWorkspace(policyID: string) { - const policy = getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; const workspaceChats = ReportUtils.getAllWorkspaceReports(policyID); const optimisticData: OnyxUpdate[] = [ @@ -885,7 +914,7 @@ function updateWorkspaceAvatar(policyID: string, file: File) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - avatarURL: getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.avatarURL, + avatarURL: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.avatarURL, }, }, ]; @@ -964,7 +993,7 @@ function clearAvatarErrors(policyID: string) { * If the response fails set a general error message. Clear the error message when updating. */ function updateGeneralSettings(policyID: string, name: string, currencyValue?: string) { - const policy = getAllPolicies()?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; if (!policy) { return; } @@ -1146,7 +1175,7 @@ function clearWorkspaceGeneralSettingsErrors(policyID: string) { } function setWorkspaceErrors(policyID: string, errors: Errors) { - if (!getAllPolicies()?.[policyID]) { + if (!allPolicies?.[policyID]) { return; } @@ -1172,7 +1201,7 @@ function clearCustomUnitErrors(policyID: string, customUnitID: string, customUni } function hideWorkspaceAlertMessage(policyID: string) { - if (!getAllPolicies()?.[policyID]) { + if (!allPolicies?.[policyID]) { return; } @@ -1331,7 +1360,6 @@ function generateDefaultWorkspaceName(email = ''): string { defaultWorkspaceName = 'My Group Workspace'; } - const allPolicies = getAllPolicies(); if (isEmptyObject(allPolicies)) { return defaultWorkspaceName; } diff --git a/src/libs/actions/Policy/Tag.ts b/src/libs/actions/Policy/Tag.ts index 0e0daeaa7246..d8cb0ef8b028 100644 --- a/src/libs/actions/Policy/Tag.ts +++ b/src/libs/actions/Policy/Tag.ts @@ -1,4 +1,4 @@ -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {NullishDeep, OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type {EnablePolicyTagsParams, OpenPolicyTagsPageParams, RenamePolicyTaglistParams, RenamePolicyTagsParams, SetPolicyTagsEnabled, SetPolicyTagsRequired} from '@libs/API/parameters'; @@ -8,10 +8,11 @@ import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import Log from '@libs/Log'; import * as PolicyUtils from '@libs/PolicyUtils'; import {navigateWhenEnableFeature} from '@libs/PolicyUtils'; +import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PolicyTag, PolicyTagList, PolicyTags, RecentlyUsedTags} from '@src/types/onyx'; +import type {Policy, PolicyTag, PolicyTagList, PolicyTags, RecentlyUsedTags, Report} from '@src/types/onyx'; import type {OnyxValueWithOfflineFeedback} from '@src/types/onyx/OnyxCommon'; import type {Attributes, Rate} from '@src/types/onyx/Policy'; import type {OnyxData} from '@src/types/onyx/Request'; @@ -23,6 +24,39 @@ type NewCustomUnit = { rates: Rate; }; +const allPolicies: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + callback: (val, key) => { + if (!key) { + return; + } + if (val === null || val === undefined) { + // If we are deleting a policy, we have to check every report linked to that policy + // and unset the draft indicator (pencil icon) alongside removing any draft comments. Clearing these values will keep the newly archived chats from being displayed in the LHN. + // More info: https://github.com/Expensify/App/issues/14260 + const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); + const policyReports = ReportUtils.getAllPolicyReports(policyID); + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; + const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; + policyReports.forEach((policyReport) => { + if (!policyReport) { + return; + } + const {reportID} = policyReport; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; + cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; + }); + Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); + Onyx.multiSet(cleanUpSetQueries); + delete allPolicies[key]; + return; + } + + allPolicies[key] = val; + }, +}); + let allPolicyTags: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_TAGS, From 1ebfe983eff829f04b345b9bd75860674b538694 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 20 Jun 2024 16:55:27 +0500 Subject: [PATCH 09/29] fix: linting --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e56ad24f5de9..61080e9b82ad 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6958,7 +6958,7 @@ function isChatUsedForOnboarding(report: OnyxEntry): boolean { * Get the report (system or concierge chat) used for the user's onboarding process. */ function getChatUsedForOnboarding(): OnyxEntry { - return Object.values(allReports ?? {}).find(isChatUsedForOnboarding); + return Object.values(getAllReports() ?? {}).find(isChatUsedForOnboarding); } export { From 76ba035e2334bca7a3c9747d52d62e7e493c2553 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 20 Jun 2024 16:59:39 +0500 Subject: [PATCH 10/29] fix: prettier --- src/libs/OptionsListUtils.ts | 5 +---- src/libs/actions/Policy/Member.ts | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 077545822894..ad0cf6974f70 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1931,10 +1931,7 @@ function getOptions( reportOption.isPolicyExpenseChat && reportOption.ownerAccountID === currentUserAccountID && includeOwnedWorkspaceChats && !reportOption.isArchivedRoom; const shouldShowInvoiceRoom = - includeInvoiceRooms && - ReportUtils.isInvoiceRoom(reportOption.item) && - ReportUtils.isPolicyAdmin(reportOption.policyID ?? '', policies) && - !reportOption.isArchivedRoom; + includeInvoiceRooms && ReportUtils.isInvoiceRoom(reportOption.item) && ReportUtils.isPolicyAdmin(reportOption.policyID ?? '', policies) && !reportOption.isArchivedRoom; /** Exclude the report option if it doesn't meet any of the following conditions: diff --git a/src/libs/actions/Policy/Member.ts b/src/libs/actions/Policy/Member.ts index ab16196bdf58..0da122a16bae 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -1,5 +1,5 @@ import {ExpensiMark} from 'expensify-common'; -import type {NullishDeep, OnyxCollection,OnyxCollectionInputValue, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; +import type {NullishDeep, OnyxCollection, OnyxCollectionInputValue, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type { From 11bef46bddaa5d625a080b3e44b278a25c86a667 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 20 Jun 2024 17:05:09 +0500 Subject: [PATCH 11/29] fix: prettier --- src/libs/migrations/Participants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/migrations/Participants.ts b/src/libs/migrations/Participants.ts index 4aa13e6f32c3..4c8905ba81be 100644 --- a/src/libs/migrations/Participants.ts +++ b/src/libs/migrations/Participants.ts @@ -12,7 +12,7 @@ type OldReportCollection = Record>; function getReports(): Promise> { return new Promise((resolve) => { - return resolve(getAllReports()); + resolve(getAllReports()); }); } From c22fceca686b1dd3b06b76f5d953a354da04c049 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 21 Jun 2024 14:07:51 +0500 Subject: [PATCH 12/29] fix: unit tests --- src/libs/ReportConnection.ts | 3 +++ src/libs/ReportUtils.ts | 23 ++++++++--------------- src/libs/UnreadIndicatorUpdater/index.ts | 2 +- tests/unit/SidebarOrderTest.ts | 2 +- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/libs/ReportConnection.ts b/src/libs/ReportConnection.ts index 8c28f354afc8..75148474a9da 100644 --- a/src/libs/ReportConnection.ts +++ b/src/libs/ReportConnection.ts @@ -20,6 +20,9 @@ Onyx.connect({ // Each time a new report is added we will check to see if the user should be switched PriorityModeActions.autoSwitchToFocusMode(); + if (!value) { + return; + } Object.values(value).forEach((report) => { if (!report) { return; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 61080e9b82ad..5030addcbe12 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3270,13 +3270,6 @@ function getInvoicesChatName(report: OnyxEntry): string { return getPolicyName(report, false, allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${invoiceReceiverPolicyID}`]); } -const archivedTranslation = Localize.translateLocal('common.archived'); -const deletedMessageTranslation = Localize.translateLocal('parentReportAction.deletedMessage'); -const attachmentTranslation = `[${Localize.translateLocal('common.attachment')}]`; -const hiddenMessageTranslation = Localize.translateLocal('parentReportAction.hiddenMessage'); -const deletedTaskTranslation = Localize.translateLocal('parentReportAction.deletedTask'); -const deletedReportTranslation = Localize.translateLocal('parentReportAction.deletedReport'); - /** * Get the title for a report. */ @@ -3287,32 +3280,32 @@ function getReportName(report: OnyxEntry, policy?: OnyxEntry): s if (!isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction)) { formattedName = getTransactionReportName(parentReportAction); if (isArchivedRoom(report)) { - formattedName += ` (${archivedTranslation})`; + formattedName += ` (${Localize.translateLocal('common.archived')})`; } return formatReportLastMessageText(formattedName); } if (parentReportAction?.message?.[0]?.isDeletedParentAction) { - return deletedMessageTranslation; + return Localize.translateLocal('parentReportAction.deletedMessage'); } const isAttachment = ReportActionsUtils.isReportActionAttachment(!isEmptyObject(parentReportAction) ? parentReportAction : undefined); const parentReportActionMessage = getReportActionMessage(parentReportAction, report?.parentReportID, report?.reportID ?? '').replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { - return attachmentTranslation; + return `[${Localize.translateLocal('common.attachment')}]`; } if ( parentReportAction?.message?.[0]?.moderationDecision?.decision === CONST.MODERATION.MODERATOR_DECISION_PENDING_HIDE || parentReportAction?.message?.[0]?.moderationDecision?.decision === CONST.MODERATION.MODERATOR_DECISION_HIDDEN || parentReportAction?.message?.[0]?.moderationDecision?.decision === CONST.MODERATION.MODERATOR_DECISION_PENDING_REMOVE ) { - return hiddenMessageTranslation; + return Localize.translateLocal('parentReportAction.hiddenMessage'); } if (isAdminRoom(report) || isUserCreatedPolicyRoom(report)) { return getAdminRoomInvitedParticipants(parentReportAction, parentReportActionMessage); } if (parentReportActionMessage && isArchivedRoom(report)) { - return `${parentReportActionMessage} (${archivedTranslation})`; + return `${parentReportActionMessage} (${Localize.translateLocal('common.archived')})`; } if (ReportActionsUtils.isModifiedExpenseAction(parentReportAction)) { return ModifiedExpenseMessage.getForReportAction(report?.reportID, parentReportAction); @@ -3326,11 +3319,11 @@ function getReportName(report: OnyxEntry, policy?: OnyxEntry): s } if (isClosedExpenseReportWithNoExpenses(report)) { - return deletedReportTranslation; + return Localize.translateLocal('parentReportAction.deletedReport'); } if (isTaskReport(report) && isCanceledTaskReport(report, parentReportAction)) { - return deletedTaskTranslation; + return Localize.translateLocal('parentReportAction.deletedTask'); } if (isGroupChat(report)) { @@ -3354,7 +3347,7 @@ function getReportName(report: OnyxEntry, policy?: OnyxEntry): s } if (isArchivedRoom(report)) { - formattedName += ` (${archivedTranslation})`; + formattedName += ` (${Localize.translateLocal('common.archived')})`; } if (isSelfDM(report)) { diff --git a/src/libs/UnreadIndicatorUpdater/index.ts b/src/libs/UnreadIndicatorUpdater/index.ts index 62c5ddc84ac6..23a087acddc4 100644 --- a/src/libs/UnreadIndicatorUpdater/index.ts +++ b/src/libs/UnreadIndicatorUpdater/index.ts @@ -45,7 +45,7 @@ const triggerUnreadUpdate = debounce(() => { updateUnread(unreadReports.length); }, CONST.TIMING.UNREAD_UPDATE_DEBOUNCE_TIME); -navigationRef.addListener('state', () => { +navigationRef?.addListener('state', () => { triggerUnreadUpdate(); }); diff --git a/tests/unit/SidebarOrderTest.ts b/tests/unit/SidebarOrderTest.ts index 98120a53b6d4..c1396e3f2659 100644 --- a/tests/unit/SidebarOrderTest.ts +++ b/tests/unit/SidebarOrderTest.ts @@ -875,13 +875,13 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() + .then(() => Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, LHNTestUtils.fakePersonalDetails)) .then(() => LHNTestUtils.getDefaultRenderedSidebarLinks()) // Given the sidebar is rendered in #focus mode (hides read chats) // with all reports having unread comments .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, - [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, }), From 2b8154cb6e5c1d78078e93abfa406778b2f8b162 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 21 Jun 2024 15:30:00 +0500 Subject: [PATCH 13/29] fix: typecheck --- src/libs/actions/Task.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index aa4e49e82f6d..af9de57c45f1 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -1008,7 +1008,7 @@ function canModifyTask(taskReport: OnyxEntry, sessionAccountID } function clearTaskErrors(reportID: string) { - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; // Delete the task preview in the parent report if (report?.pendingFields?.createChat === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { From cf111d9f6727a928cd41184b96f5906839b812f0 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 21 Jun 2024 15:34:47 +0500 Subject: [PATCH 14/29] fix: unit test --- src/libs/UnreadIndicatorUpdater/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/UnreadIndicatorUpdater/index.ts b/src/libs/UnreadIndicatorUpdater/index.ts index 23a087acddc4..3273cd0ef517 100644 --- a/src/libs/UnreadIndicatorUpdater/index.ts +++ b/src/libs/UnreadIndicatorUpdater/index.ts @@ -37,7 +37,7 @@ function getUnreadReportsForUnreadIndicator(reports: OnyxCollection, cur const memoizedGetUnreadReportsForUnreadIndicator = memoize(getUnreadReportsForUnreadIndicator); const triggerUnreadUpdate = debounce(() => { - const currentReportID = navigationRef.isReady() ? Navigation.getTopmostReportId() ?? '-1' : '-1'; + const currentReportID = navigationRef?.isReady() ? Navigation.getTopmostReportId() ?? '-1' : '-1'; // We want to keep notification count consistent with what can be accessed from the LHN list const unreadReports = memoizedGetUnreadReportsForUnreadIndicator(getAllReports(), currentReportID); From aa4195919ff7297e1b1241519f1af1a5f84f173b Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 21 Jun 2024 16:53:27 +0500 Subject: [PATCH 15/29] fix: failing reassure test --- src/libs/UnreadIndicatorUpdater/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/UnreadIndicatorUpdater/index.ts b/src/libs/UnreadIndicatorUpdater/index.ts index 3273cd0ef517..2cfc5bc7dc8f 100644 --- a/src/libs/UnreadIndicatorUpdater/index.ts +++ b/src/libs/UnreadIndicatorUpdater/index.ts @@ -37,7 +37,7 @@ function getUnreadReportsForUnreadIndicator(reports: OnyxCollection, cur const memoizedGetUnreadReportsForUnreadIndicator = memoize(getUnreadReportsForUnreadIndicator); const triggerUnreadUpdate = debounce(() => { - const currentReportID = navigationRef?.isReady() ? Navigation.getTopmostReportId() ?? '-1' : '-1'; + const currentReportID = navigationRef?.isReady?.() ? Navigation.getTopmostReportId() ?? '-1' : '-1'; // We want to keep notification count consistent with what can be accessed from the LHN list const unreadReports = memoizedGetUnreadReportsForUnreadIndicator(getAllReports(), currentReportID); @@ -45,7 +45,7 @@ const triggerUnreadUpdate = debounce(() => { updateUnread(unreadReports.length); }, CONST.TIMING.UNREAD_UPDATE_DEBOUNCE_TIME); -navigationRef?.addListener('state', () => { +navigationRef?.addListener?.('state', () => { triggerUnreadUpdate(); }); From ba9b00cd84a2fcfb4bd17e2b938bece106da65f1 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 21 Jun 2024 16:53:56 +0500 Subject: [PATCH 16/29] fix: add support for testing dynamic imports --- .github/workflows/reassurePerformanceTests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reassurePerformanceTests.yml b/.github/workflows/reassurePerformanceTests.yml index a695c0acf942..a2aadc331f19 100644 --- a/.github/workflows/reassurePerformanceTests.yml +++ b/.github/workflows/reassurePerformanceTests.yml @@ -48,12 +48,12 @@ jobs: git fetch origin "$BASELINE_BRANCH" --no-tags --depth=1 git switch "$BASELINE_BRANCH" npm install --force - npx reassure --baseline + NODE_OPTIONS=--experimental-vm-modules npx reassure --baseline git switch --force --detach - git merge --no-commit --allow-unrelated-histories "$BASELINE_BRANCH" -X ours git checkout --ours . npm install --force - npx reassure --branch + NODE_OPTIONS=--experimental-vm-modules npx reassure --branch - name: Validate output.json id: validateReassureOutput From 58c81a01b72bc9fb6305a8cf8f1b0f5962cac470 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 24 Jun 2024 13:37:32 +0500 Subject: [PATCH 17/29] refactor: update imports --- src/libs/DistanceRequestUtils.ts | 4 +- src/libs/ReportActionsUtils.ts | 11 ++-- src/libs/ReportUtils.ts | 80 ++++++++++++------------ src/libs/TaskUtils.ts | 4 +- src/libs/TransactionUtils.ts | 4 +- src/libs/UnreadIndicatorUpdater/index.ts | 4 +- src/libs/WorkspacesSettingsUtils.ts | 4 +- src/libs/actions/IOU.ts | 42 +++++++------ src/libs/actions/Policy/Policy.ts | 4 +- src/libs/actions/PriorityMode.ts | 6 +- src/libs/actions/Report.ts | 24 +++---- src/libs/actions/Task.ts | 6 +- src/libs/markAllPolicyReportsAsRead.ts | 4 +- src/libs/migrations/Participants.ts | 4 +- 14 files changed, 102 insertions(+), 99 deletions(-) diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 041214623e14..af704dcdb6a6 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -11,7 +11,7 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; -import getAllReports from './ReportConnection'; +import * as ReportConnection from './ReportConnection'; import * as ReportUtils from './ReportUtils'; type MileageRate = { @@ -246,7 +246,7 @@ function convertToDistanceInMeters(distance: number, unit: Unit): number { * Returns custom unit rate ID for the distance transaction */ function getCustomUnitRateID(reportID: string) { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; const policy = PolicyUtils.getPolicy(report?.policyID ?? parentReport?.policyID ?? '-1'); diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 7d92501c4e96..188b8d5e014b 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -22,7 +22,7 @@ import * as Localize from './Localize'; import Log from './Log'; import type {MessageElementBase, MessageTextElement} from './MessageElement'; import * as PersonalDetailsUtils from './PersonalDetailsUtils'; -import getAllReports from './ReportConnection'; +import * as ReportConnection from './ReportConnection'; import type {OptimisticIOUReportAction, PartialReportAction} from './ReportUtils'; import StringUtils from './StringUtils'; import * as TransactionUtils from './TransactionUtils'; @@ -373,7 +373,7 @@ function getCombinedReportActions(reportActions: ReportAction[], transactionThre // Filter out the created action from the transaction thread report actions, since we already have the parent report's created action in `reportActions` const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED); - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const isSelfDM = report?.chatType === CONST.REPORT.CHAT_TYPE.SELF_DM; // Filter out request and send money request actions because we don't want to show any preview actions for one transaction reports const filteredReportActions = [...reportActions, ...filteredTransactionThreadReportActions].filter((action) => { @@ -880,7 +880,7 @@ function getMostRecentReportActionLastModified(): string { // We might not have actions so we also look at the report objects to see if any have a lastVisibleActionLastModified that is more recent. We don't need to get // any reports that have been updated before either a recently updated report or reportAction as we should be up to date on these - Object.values(getAllReports() ?? {}).forEach((report) => { + Object.values(ReportConnection.getAllReports() ?? {}).forEach((report) => { const reportLastVisibleActionLastModified = report?.lastVisibleActionLastModified ?? report?.lastVisibleActionCreated; if (!reportLastVisibleActionLastModified || reportLastVisibleActionLastModified < mostRecentReportActionLastModified) { return; @@ -951,7 +951,7 @@ function isTaskAction(reportAction: OnyxEntry): boolean { */ function getOneTransactionThreadReportID(reportID: string, reportActions: OnyxEntry | ReportAction[], isOffline: boolean | undefined = undefined): string | undefined { // If the report is not an IOU, Expense report, or Invoice, it shouldn't be treated as one-transaction report. - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (report?.type !== CONST.REPORT.TYPE.IOU && report?.type !== CONST.REPORT.TYPE.EXPENSE && report?.type !== CONST.REPORT.TYPE.INVOICE) { return; } @@ -1123,14 +1123,13 @@ function getReportActionHtml(reportAction: PartialReportAction): string { return getReportActionMessage(reportAction)?.html ?? ''; } +const parser = new ExpensiMark(); function getReportActionText(reportAction: PartialReportAction): string { const html = getReportActionHtml(reportAction); - const parser = new ExpensiMark(); return html ? parser.htmlToText(html) : ''; } function getTextFromHtml(html?: string): string { - const parser = new ExpensiMark(); return html ? parser.htmlToText(html) : ''; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 4033eb09976a..f5f4af0ac4ff 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -70,7 +70,7 @@ import * as PhoneNumber from './PhoneNumber'; import * as PolicyUtils from './PolicyUtils'; import type {LastVisibleMessage} from './ReportActionsUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; -import getAllReports from './ReportConnection'; +import * as ReportConnection from './ReportConnection'; import StringUtils from './StringUtils'; import * as SubscriptionUtils from './SubscriptionUtils'; import * as TransactionUtils from './TransactionUtils'; @@ -560,7 +560,7 @@ function getChatType(report: OnyxInputOrEntry | Participant | EmptyObjec * Get the report or draft report given a reportID */ function getReportOrDraftReport(reportID: string | undefined): OnyxEntry { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); if (!allReports && !allReportsDraft) { return undefined; } @@ -587,7 +587,7 @@ function getParentReport(report: OnyxEntry | EmptyObject): OnyxEntry | EmptyObject): boolea * Checks if a report is an IOU report using report or reportID */ function isIOUReport(reportOrID: OnyxInputOrEntry | string | EmptyObject): boolean { - const report = typeof reportOrID === 'string' ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return report?.type === CONST.REPORT.TYPE.IOU; } @@ -748,7 +748,7 @@ function isReportManager(report: OnyxEntry): boolean { * Checks if the supplied report has been approved */ function isReportApproved(reportOrID: OnyxInputOrEntry | string | EmptyObject): boolean { - const report = typeof reportOrID === 'string' ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return report?.stateNum === CONST.REPORT.STATE_NUM.APPROVED && report?.statusNum === CONST.REPORT.STATUS_NUM.APPROVED; } @@ -782,7 +782,7 @@ function hasParticipantInArray(report: OnyxEntry, memberAccountIDs: numb * Whether the Money Request report is settled */ function isSettled(reportID: string | undefined): boolean { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); if (!allReports || !reportID) { return false; } @@ -804,7 +804,7 @@ function isSettled(reportID: string | undefined): boolean { * Whether the current user is the submitter of the report */ function isCurrentUserSubmitter(reportID: string): boolean { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); if (!allReports) { return false; } @@ -867,7 +867,7 @@ function isInvoiceRoom(report: OnyxEntry | EmptyObject): boolean { function isInvoiceRoomWithID(reportID?: string): boolean { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`]; return isInvoiceRoom(report); } @@ -987,7 +987,7 @@ function isWorkspaceTaskReport(report: OnyxEntry): boolean { if (!isTaskReport(report)) { return false; } - const parentReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; + const parentReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; return isPolicyExpenseChat(parentReport); } @@ -1033,7 +1033,7 @@ function isConciergeChatReport(report: OnyxInputOrEntry): boolean { } function findSelfDMReportID(): string | undefined { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); if (!allReports) { return; } @@ -1224,7 +1224,7 @@ function isArchivedRoom(report: OnyxInputOrEntry | EmptyObject, reportNa */ function isArchivedRoomWithID(reportID?: string) { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`]; return isArchivedRoom(report); } @@ -1356,7 +1356,7 @@ function isChildReport(report: OnyxEntry): boolean { function isExpenseRequest(report: OnyxInputOrEntry): boolean { if (isThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); - const parentReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; + const parentReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; return isExpenseReport(parentReport) && !isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction); } return false; @@ -1369,7 +1369,7 @@ function isExpenseRequest(report: OnyxInputOrEntry): boolean { function isIOURequest(report: OnyxInputOrEntry): boolean { if (isThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); - const parentReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; + const parentReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; return isIOUReport(parentReport) && !isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction); } return false; @@ -1391,7 +1391,7 @@ function isTrackExpenseReport(report: OnyxInputOrEntry): boolean { * Checks if a report is an IOU or expense request. */ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { - const report = typeof reportOrID === 'string' ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return isIOURequest(report) || isExpenseRequest(report); } @@ -1399,7 +1399,7 @@ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { * Checks if a report is an IOU or expense report. */ function isMoneyRequestReport(reportOrID: OnyxInputOrEntry | EmptyObject | string): boolean { - const report = typeof reportOrID === 'string' ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return isIOUReport(report) || isExpenseReport(report); } @@ -1605,7 +1605,7 @@ function getReportRecipientAccountIDs(report: OnyxEntry, currentLoginAcc // In 1:1 chat threads, the participants will be the same as parent report. If a report is specifically a 1:1 chat thread then we will // get parent report and use its participants array. if (isThread(report) && !(isTaskReport(report) || isMoneyRequestReport(report))) { - const parentReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; + const parentReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`]; if (isOneOnOneChat(parentReport)) { finalReport = parentReport; } @@ -2154,7 +2154,7 @@ function getReimbursementQueuedActionMessage(reportAction: OnyxEntry, allReportsDict?: OnyxCollection): SpendBreakdown { - const allAvailableReports = allReportsDict ?? getAllReports(); + const allAvailableReports = allReportsDict ?? ReportConnection.getAllReports(); let moneyRequestReport; if (isMoneyRequestReport(report) || isInvoiceReport(report)) { moneyRequestReport = report; @@ -2671,7 +2671,7 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxInputOrEntry } const iouMessage = ReportActionsUtils.getOriginalMessage(reportAction); - const moneyRequestReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${iouMessage?.IOUReportID}`] ?? ({} as Report); + const moneyRequestReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${iouMessage?.IOUReportID}`] ?? ({} as Report); const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${iouMessage?.IOUTransactionID}`] ?? ({} as Transaction); if (isSettled(String(moneyRequestReport.reportID)) || isReportApproved(String(moneyRequestReport.reportID))) { @@ -2918,7 +2918,7 @@ function getReportPreviewMessage( isForListPreview = false, originalReportAction: OnyxInputOrEntry | EmptyObject = iouReportAction, ): string { - const report = typeof reportOrID === 'string' ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; + const report = typeof reportOrID === 'string' ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; const reportActionMessage = ReportActionsUtils.getReportActionHtml(iouReportAction); if (isEmptyObject(report) || !report?.reportID || isEmptyObject(iouReportAction)) { @@ -3881,7 +3881,7 @@ function buildOptimisticInvoiceReport(chatReportID: string, policyID: string, re function buildOptimisticExpenseReport(chatReportID: string, policyID: string, payeeAccountID: number, total: number, currency: string, reimbursable = true): OptimisticExpenseReport { // The amount for Expense reports are stored as negative value in the database const storedTotal = total * -1; - const policyName = getPolicyName(getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`]); + const policyName = getPolicyName(ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`]); const formattedTotal = CurrencyUtils.convertToDisplayString(storedTotal, currency); const policy = getPolicy(policyID); @@ -5203,7 +5203,7 @@ function isUnread(report: OnyxEntry): boolean { } function isIOUOwnedByCurrentUser(report: OnyxEntry, allReportsDict?: OnyxCollection): boolean { - const allAvailableReports = allReportsDict ?? getAllReports(); + const allAvailableReports = allReportsDict ?? ReportConnection.getAllReports(); if (!report || !allAvailableReports) { return false; } @@ -5472,7 +5472,7 @@ function shouldReportBeInOptionList({ * Returns the system report from the list of reports. */ function getSystemChat(): OnyxEntry { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); if (!allReports) { return undefined; } @@ -5483,7 +5483,7 @@ function getSystemChat(): OnyxEntry { /** * Attempts to find a report in onyx with the provided list of participants. Does not include threads, task, expense, room, and policy expense chat. */ -function getChatByParticipants(newParticipantList: number[], reports: OnyxCollection = getAllReports()): OnyxEntry { +function getChatByParticipants(newParticipantList: number[], reports: OnyxCollection = ReportConnection.getAllReports()): OnyxEntry { const sortedNewParticipantList = newParticipantList.sort(); return Object.values(reports ?? {}).find((report) => { const participantAccountIDs = Object.keys(report?.participants ?? {}); @@ -5511,7 +5511,7 @@ function getChatByParticipants(newParticipantList: number[], reports: OnyxCollec /** * Attempts to find an invoice chat report in onyx with the provided policyID and receiverID. */ -function getInvoiceChatByParticipants(policyID: string, receiverID: string | number, reports: OnyxCollection = getAllReports()): OnyxEntry { +function getInvoiceChatByParticipants(policyID: string, receiverID: string | number, reports: OnyxCollection = ReportConnection.getAllReports()): OnyxEntry { return Object.values(reports ?? {}).find((report) => { if (!report || !isInvoiceRoom(report)) { return false; @@ -5530,7 +5530,7 @@ function getInvoiceChatByParticipants(policyID: string, receiverID: string | num * Attempts to find a policy expense report in onyx that is owned by ownerAccountID in a given policy */ function getPolicyExpenseChat(ownerAccountID: number, policyID: string): OnyxEntry { - return Object.values(getAllReports() ?? {}).find((report: OnyxEntry) => { + return Object.values(ReportConnection.getAllReports() ?? {}).find((report: OnyxEntry) => { // If the report has been deleted, then skip it if (!report) { return false; @@ -5541,7 +5541,7 @@ function getPolicyExpenseChat(ownerAccountID: number, policyID: string): OnyxEnt } function getAllPolicyReports(policyID: string): Array> { - return Object.values(getAllReports() ?? {}).filter((report) => report?.policyID === policyID); + return Object.values(ReportConnection.getAllReports() ?? {}).filter((report) => report?.policyID === policyID); } /** @@ -5554,7 +5554,7 @@ function chatIncludesChronos(report: OnyxInputOrEntry | EmptyObject): bo function chatIncludesChronosWithID(reportID?: string): boolean { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID || -1}`]; return chatIncludesChronos(report); } @@ -5704,7 +5704,7 @@ function getReportIDFromLink(url: string | null): string { */ function hasIOUWaitingOnCurrentUserBankAccount(chatReport: OnyxInputOrEntry): boolean { if (chatReport?.iouReportID) { - const iouReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReport?.iouReportID}`]; + const iouReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReport?.iouReportID}`]; if (iouReport?.isWaitingOnBankAccount && iouReport?.ownerAccountID === currentUserAccountID) { return true; } @@ -6025,7 +6025,7 @@ function shouldReportShowSubscript(report: OnyxEntry): boolean { * Return true if reports data exists */ function isReportDataReady(): boolean { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); return !isEmptyObject(allReports) && Object.keys(allReports ?? {}).some((key) => allReports?.[key]?.reportID); } @@ -6049,7 +6049,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' ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; + const report = typeof reportOrID === 'string' ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] : reportOrID; if (!isMoneyRequestReport(report)) { return false; } @@ -6077,7 +6077,9 @@ function getOriginalReportID(reportID: string, reportAction: OnyxInputOrEntry, policy: OnyxEntry, } function getWorkspaceChats(policyID: string, accountIDs: number[]): Array> { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); return Object.values(allReports ?? {}).filter((report) => isPolicyExpenseChat(report) && (report?.policyID ?? '-1') === policyID && accountIDs.includes(report?.ownerAccountID ?? -1)); } @@ -6121,7 +6123,7 @@ function getWorkspaceChats(policyID: string, accountIDs: number[]): Array> { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); return Object.values(allReports ?? {}).filter((report) => (report?.policyID ?? '-1') === policyID); } @@ -6418,7 +6420,7 @@ function shouldUseFullTitleToDisplay(report: OnyxEntry): boolean { } function getRoom(type: ValueOf, policyID: string): OnyxEntry | undefined { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); const room = Object.values(allReports ?? {}).find((report) => report?.policyID === policyID && report?.chatType === type && !isThread(report)); return room; } @@ -6782,7 +6784,7 @@ function shouldCreateNewMoneyRequestReport(existingIOUReport: OnyxInputOrEntry report && report?.[reportFieldToCompare] === tripRoomReportID) .map((report) => report?.reportID); return tripTransactionReportIDs.flatMap((reportID) => TransactionUtils.getAllReportTransactions(reportID)); @@ -6970,7 +6972,7 @@ function canReportBeMentionedWithinPolicy(report: OnyxEntry, policyID: s } function shouldShowMerchantColumn(transactions: Transaction[]) { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); return transactions.some((transaction) => isExpenseReport(allReports?.[transaction.reportID] ?? {})); } @@ -6985,7 +6987,7 @@ function isChatUsedForOnboarding(report: OnyxEntry): boolean { * Get the report (system or concierge chat) used for the user's onboarding process. */ function getChatUsedForOnboarding(): OnyxEntry { - return Object.values(getAllReports() ?? {}).find(isChatUsedForOnboarding); + return Object.values(ReportConnection.getAllReports() ?? {}).find(isChatUsedForOnboarding); } export { diff --git a/src/libs/TaskUtils.ts b/src/libs/TaskUtils.ts index 37b0282f3002..bd0bd10cd83e 100644 --- a/src/libs/TaskUtils.ts +++ b/src/libs/TaskUtils.ts @@ -5,7 +5,7 @@ import type {Message} from '@src/types/onyx/ReportAction'; import type ReportAction from '@src/types/onyx/ReportAction'; import * as Localize from './Localize'; import {getReportActionHtml, getReportActionText} from './ReportActionsUtils'; -import getAllReports from './ReportConnection'; +import * as ReportConnection from './ReportConnection'; /** * Given the Task reportAction name, return the appropriate message to be displayed and copied to clipboard. @@ -29,7 +29,7 @@ function getTaskReportActionMessage(action: OnyxEntry): Pick = {}; Onyx.connect({ @@ -188,7 +188,7 @@ function isCreatedMissing(transaction: OnyxEntry) { } function areRequiredFieldsEmpty(transaction: OnyxEntry): boolean { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`] ?? null; const isFromExpenseReport = parentReport?.type === CONST.REPORT.TYPE.EXPENSE; const isSplitPolicyExpenseChat = !!transaction?.comment?.splits?.some((participant) => allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`]?.isOwnPolicyExpenseChat); diff --git a/src/libs/UnreadIndicatorUpdater/index.ts b/src/libs/UnreadIndicatorUpdater/index.ts index 2cfc5bc7dc8f..2546225bd6ea 100644 --- a/src/libs/UnreadIndicatorUpdater/index.ts +++ b/src/libs/UnreadIndicatorUpdater/index.ts @@ -1,7 +1,7 @@ import debounce from 'lodash/debounce'; import memoize from 'lodash/memoize'; import type {OnyxCollection} from 'react-native-onyx'; -import getAllReports from '@libs/ReportConnection'; +import * as ReportConnection from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import Navigation, {navigationRef} from '@navigation/Navigation'; import CONST from '@src/CONST'; @@ -40,7 +40,7 @@ const triggerUnreadUpdate = debounce(() => { const currentReportID = navigationRef?.isReady?.() ? Navigation.getTopmostReportId() ?? '-1' : '-1'; // We want to keep notification count consistent with what can be accessed from the LHN list - const unreadReports = memoizedGetUnreadReportsForUnreadIndicator(getAllReports(), currentReportID); + const unreadReports = memoizedGetUnreadReportsForUnreadIndicator(ReportConnection.getAllReports(), currentReportID); updateUnread(unreadReports.length); }, CONST.TIMING.UNREAD_UPDATE_DEBOUNCE_TIME); diff --git a/src/libs/WorkspacesSettingsUtils.ts b/src/libs/WorkspacesSettingsUtils.ts index 8c130c4cc886..271af7a4b077 100644 --- a/src/libs/WorkspacesSettingsUtils.ts +++ b/src/libs/WorkspacesSettingsUtils.ts @@ -10,7 +10,7 @@ import * as CurrencyUtils from './CurrencyUtils'; import type {Phrase, PhraseParameters} from './Localize'; import * as OptionsListUtils from './OptionsListUtils'; import {hasCustomUnitsError, hasEmployeeListError, hasPolicyError, hasTaxRateError} from './PolicyUtils'; -import getAllReports from './ReportConnection'; +import * as ReportConnection from './ReportConnection'; import * as ReportUtils from './ReportUtils'; type CheckingMethod = () => boolean; @@ -93,7 +93,7 @@ function hasWorkspaceSettingsRBR(policy: Policy) { } function getChatTabBrickRoad(policyID?: string): BrickRoad | undefined { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); if (!allReports) { return undefined; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 559f164ed4c0..c8740f84148a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -42,7 +42,7 @@ import Permissions from '@libs/Permissions'; import * as PhoneNumber from '@libs/PhoneNumber'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import getAllReports from '@libs/ReportConnection'; +import * as ReportConnection from '@libs/ReportConnection'; import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, TransactionDetails} from '@libs/ReportUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -289,7 +289,7 @@ Onyx.connect({ * Get the report or draft report given a reportID */ function getReportOrDraftReport(reportID: string | undefined): OnyxEntry { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); if (!allReports && !allReportsDraft) { return undefined; } @@ -497,7 +497,7 @@ function buildOnyxDataForMoneyRequest( if (TransactionUtils.isDistanceRequest(transaction)) { newQuickAction = CONST.QUICK_ACTIONS.REQUEST_DISTANCE; } - const existingTransactionThreadReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingTransactionThreadReportID}`] ?? null; + const existingTransactionThreadReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingTransactionThreadReportID}`] ?? null; if (chatReport) { optimisticData.push({ @@ -1214,7 +1214,7 @@ function buildOnyxDataForTrackExpense( } else if (isDistanceRequest) { newQuickAction = CONST.QUICK_ACTIONS.TRACK_DISTANCE; } - const existingTransactionThreadReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingTransactionThreadReportID}`] ?? null; + const existingTransactionThreadReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingTransactionThreadReportID}`] ?? null; if (chatReport) { optimisticData.push( @@ -1570,7 +1570,7 @@ function getDeleteTrackExpenseInformation( actionableWhisperReportActionID = '', resolution = '', ) { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); // STEP 1: Get all collections we're updating const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -1911,7 +1911,7 @@ function getMoneyRequestInformation( let isNewChatReport = false; let chatReport = !isEmptyObject(parentChatReport) && parentChatReport?.reportID ? parentChatReport : null; - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); // If this is a policyExpenseChat, the chatReport must exist and we can get it from Onyx. // report is null if the flow is initiated from the global create menu. However, participant always stores the reportID if it exists, which is the case for policyExpenseChats if (!chatReport && isPolicyExpenseChat) { @@ -2128,7 +2128,7 @@ function getTrackExpenseInformation( // STEP 1: Get existing chat report let chatReport = !isEmptyObject(parentChatReport) && parentChatReport?.reportID ? parentChatReport : null; - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); // The chatReport always exists, and we can get it from Onyx if chatReport is null. if (!chatReport) { chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.reportID}`] ?? null; @@ -2487,7 +2487,7 @@ function getUpdateMoneyRequestParams( const clearedPendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, null])); const errorFields = Object.fromEntries(Object.keys(pendingFields).map((key) => [key, {[DateUtils.getMicroseconds()]: Localize.translateLocal('iou.error.genericEditFailureMessage')}])); - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); // Step 2: Get all the collections being updated const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -2774,7 +2774,7 @@ function getUpdateTrackExpenseParams( const clearedPendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, null])); const errorFields = Object.fromEntries(Object.keys(pendingFields).map((key) => [key, {[DateUtils.getMicroseconds()]: Localize.translateLocal('iou.error.genericEditFailureMessage')}])); - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); // Step 2: Get all the collections being updated const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -2941,7 +2941,7 @@ function updateMoneyRequestDate( const transactionChanges: TransactionChanges = { created: value, }; - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; @@ -2982,7 +2982,7 @@ function updateMoneyRequestMerchant( const transactionChanges: TransactionChanges = { merchant: value, }; - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; @@ -3071,7 +3071,7 @@ function updateMoneyRequestDistance({ waypoints, routes, }; - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; @@ -3112,7 +3112,7 @@ function updateMoneyRequestDescription( const transactionChanges: TransactionChanges = { comment, }; - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; @@ -3777,7 +3777,7 @@ function getOrCreateOptimisticSplitChatReport(existingSplitChatReportID: string, const existingChatReportID = existingSplitChatReportID || participants[0].reportID; // Check if the report is available locally if we do have one - let existingSplitChatReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingChatReportID}`]; + let existingSplitChatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingChatReportID}`]; // If we do not have one locally then we will search for a chat with the same participants (only for 1:1 chats). const shouldGetOrCreateOneOneDM = participants.length < 2; @@ -4071,7 +4071,9 @@ function createSplitsAndOnyxData( } // STEP 2: Get existing IOU/Expense report and update its total OR build a new optimistic one - let oneOnOneIOUReport: OneOnOneIOUReport = oneOnOneChatReport.iouReportID ? getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] : null; + let oneOnOneIOUReport: OneOnOneIOUReport = oneOnOneChatReport.iouReportID + ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] + : null; const shouldCreateNewOneOnOneIOUReport = ReportUtils.shouldCreateNewMoneyRequestReport(oneOnOneIOUReport, oneOnOneChatReport); if (!oneOnOneIOUReport || shouldCreateNewOneOnOneIOUReport) { @@ -4803,7 +4805,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA let oneOnOneChatReport: OnyxTypes.Report | null; let isNewOneOnOneChatReport = false; - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); if (isPolicyExpenseChat) { // The workspace chat reportID is saved in the splits array when starting a split expense with a workspace oneOnOneChatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`] ?? null; @@ -4961,7 +4963,7 @@ function editRegularMoneyRequest( policyTags: OnyxTypes.PolicyTagList, policyCategories: OnyxTypes.PolicyCategories, ) { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); // STEP 1: Get all collections we're updating const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -5262,7 +5264,7 @@ function updateMoneyRequestAmountAndCurrency({ taxCode, taxAmount, }; - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; @@ -5276,7 +5278,7 @@ function updateMoneyRequestAmountAndCurrency({ } function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) { - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); // STEP 1: Get all collections we're updating const iouReportID = ReportActionsUtils.isMoneyRequestAction(reportAction) ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUReportID : '-1'; const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`] ?? null; @@ -5588,7 +5590,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor function deleteTrackExpense(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) { // STEP 1: Get all collections we're updating - const chatReport = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; + const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null; if (!ReportUtils.isSelfDM(chatReport)) { return deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView); } diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 77fe519d5252..a9ce784e1955 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -45,7 +45,7 @@ import * as PhoneNumber from '@libs/PhoneNumber'; import * as PolicyUtils from '@libs/PolicyUtils'; import {navigateWhenEnableFeature} from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import getAllReports from '@libs/ReportConnection'; +import * as ReportConnection from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import type {PolicySelector} from '@pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover'; @@ -248,7 +248,7 @@ function deleteWorkspace(policyID: string, policyName: string) { : []), ]; - const reportsToArchive = Object.values(getAllReports() ?? {}).filter( + const reportsToArchive = Object.values(ReportConnection.getAllReports() ?? {}).filter( (report) => report?.policyID === policyID && (ReportUtils.isChatRoom(report) || ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isTaskReport(report)), ); const finallyData: OnyxUpdate[] = []; diff --git a/src/libs/actions/PriorityMode.ts b/src/libs/actions/PriorityMode.ts index 3c270402f1bb..beec327a2e40 100644 --- a/src/libs/actions/PriorityMode.ts +++ b/src/libs/actions/PriorityMode.ts @@ -1,7 +1,7 @@ import debounce from 'lodash/debounce'; import Onyx from 'react-native-onyx'; import Log from '@libs/Log'; -import getAllReports from '@libs/ReportConnection'; +import * as ReportConnection from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -77,7 +77,7 @@ function resetHasReadRequiredDataFromStorage() { } function checkRequiredData() { - if (getAllReports() === undefined || hasTriedFocusMode === undefined || isInFocusMode === undefined || isLoadingReportData) { + if (ReportConnection.getAllReports() === undefined || hasTriedFocusMode === undefined || isInFocusMode === undefined || isLoadingReportData) { return; } @@ -98,7 +98,7 @@ function tryFocusModeUpdate() { } const validReports = []; - const allReports = getAllReports(); + const allReports = ReportConnection.getAllReports(); Object.keys(allReports ?? {}).forEach((key) => { const report = allReports?.[key]; if (!report) { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 3bfecb96e8c6..5a981f92020d 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -69,7 +69,7 @@ import {extractPolicyIDFromPath} from '@libs/PolicyUtils'; import processReportIDDeeplink from '@libs/processReportIDDeeplink'; import * as Pusher from '@libs/Pusher/pusher'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import getAllReports from '@libs/ReportConnection'; +import * as ReportConnection from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import {doesReportBelongToWorkspace} from '@libs/ReportUtils'; import type {OptimisticAddCommentReportAction} from '@libs/ReportUtils'; @@ -465,7 +465,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { lastReadTime: currentTime, }; - const report = getAllReports()?.[reportID]; + const report = ReportConnection.getAllReports()?.[reportID]; if (!isEmptyObject(report) && ReportUtils.getReportNotificationPreference(report) === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { optimisticReport.notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS; @@ -639,7 +639,7 @@ function updateGroupChatName(reportID: string, reportName: string) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { - reportName: getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.reportName ?? null, + reportName: ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.reportName ?? null, errors: { reportName: Localize.translateLocal('common.genericErrorMessage'), }, @@ -676,7 +676,7 @@ function updateGroupChatAvatar(reportID: string, file?: File | CustomRNImageMani onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { - avatarUrl: getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.avatarUrl ?? null, + avatarUrl: ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.avatarUrl ?? null, pendingFields: { avatar: null, }, @@ -921,7 +921,7 @@ function openReport( } } - parameters.clientLastReadTime = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.lastReadTime ?? ''; + parameters.clientLastReadTime = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.lastReadTime ?? ''; if (isFromDeepLink) { // eslint-disable-next-line rulesdir/no-api-side-effects-method @@ -2099,7 +2099,7 @@ function addPolicyReport(policyReport: ReportUtils.OptimisticChatReport) { /** Deletes a report, along with its reportActions, any linked reports, and any linked IOU report. */ function deleteReport(reportID: string) { - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const onyxData: Record = { [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]: null, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`]: null, @@ -2281,7 +2281,7 @@ function shouldShowReportActionNotification(reportID: string, action: ReportActi return false; } - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report || (report && report.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)) { Log.info(`${tag} No notification because the report does not exist or is pending deleted`, false); return false; @@ -2582,7 +2582,7 @@ function joinRoom(report: OnyxEntry) { } function leaveGroupChat(reportID: string) { - const report = getAllReports()?.[reportID]; + const report = ReportConnection.getAllReports()?.[reportID]; if (!report) { Log.warn('Attempting to leave Group Chat that does not existing locally'); return; @@ -2610,7 +2610,7 @@ function leaveGroupChat(reportID: string) { /** Leave a report by setting the state to submitted and closed */ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = false) { - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { return; @@ -2693,7 +2693,7 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal /** Invites people to a room */ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: InvitedEmailsToAccountIDs) { - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { return; } @@ -2787,7 +2787,7 @@ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: InvitedEmails } function clearAddRoomMemberError(reportID: string, invitedAccountID: string) { - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { pendingChatMembers: report?.pendingChatMembers?.filter((pendingChatMember) => pendingChatMember.accountID !== invitedAccountID), participants: { @@ -2849,7 +2849,7 @@ function inviteToGroupChat(reportID: string, inviteeEmailsToAccountIDs: InvitedE * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details */ function removeFromRoom(reportID: string, targetAccountIDs: number[]) { - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { return; } diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index af9de57c45f1..c1a0182099dd 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -12,7 +12,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import getAllReports from '@libs/ReportConnection'; +import * as ReportConnection from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import CONST from '@src/CONST'; @@ -804,7 +804,7 @@ function getParentReport(report: OnyxEntry | EmptyObject): Ony if (!report?.parentReportID) { return {}; } - return getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`] ?? {}; + return ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`] ?? {}; } /** @@ -1008,7 +1008,7 @@ function canModifyTask(taskReport: OnyxEntry, sessionAccountID } function clearTaskErrors(reportID: string) { - const report = getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${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/markAllPolicyReportsAsRead.ts b/src/libs/markAllPolicyReportsAsRead.ts index 0e0590ce7ac4..259a5e426d89 100644 --- a/src/libs/markAllPolicyReportsAsRead.ts +++ b/src/libs/markAllPolicyReportsAsRead.ts @@ -1,11 +1,11 @@ import type {Report} from '@src/types/onyx'; import * as ReportActionFile from './actions/Report'; -import getAllReports from './ReportConnection'; +import * as ReportConnection from './ReportConnection'; import * as ReportUtils from './ReportUtils'; export default function markAllPolicyReportsAsRead(policyID: string) { let delay = 0; - const allReports = getAllReports() ?? {}; + const allReports = ReportConnection.getAllReports() ?? {}; Object.keys(allReports).forEach((key: string) => { const report: Report | null | undefined = allReports[key]; if (report?.policyID !== policyID || !ReportUtils.isUnread(report)) { diff --git a/src/libs/migrations/Participants.ts b/src/libs/migrations/Participants.ts index 4c8905ba81be..eccaa0662f2f 100644 --- a/src/libs/migrations/Participants.ts +++ b/src/libs/migrations/Participants.ts @@ -1,7 +1,7 @@ import Onyx from 'react-native-onyx'; import type {NullishDeep, OnyxCollection} from 'react-native-onyx'; import Log from '@libs/Log'; -import getAllReports from '@libs/ReportConnection'; +import * as ReportConnection from '@libs/ReportConnection'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Report} from '@src/types/onyx'; import type {Participants} from '@src/types/onyx/Report'; @@ -12,7 +12,7 @@ type OldReportCollection = Record>; function getReports(): Promise> { return new Promise((resolve) => { - resolve(getAllReports()); + resolve(ReportConnection.getAllReports()); }); } From c7b03abf04cb0fbe19a31d56b70347c46d90fbea Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 24 Jun 2024 13:38:29 +0500 Subject: [PATCH 18/29] perf: use single connection for report --- src/libs/ModifiedExpenseMessage.ts | 12 +++-------- src/libs/Navigation/Navigation.ts | 12 ++--------- .../subscribePushNotification/index.ts | 13 +++--------- src/libs/OnyxAwareParser.ts | 21 +++++++------------ src/libs/OptionsListUtils.ts | 9 ++------ src/libs/actions/ReportActions.ts | 12 +++-------- 6 files changed, 20 insertions(+), 59 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index efcba4c23204..38562edb7704 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -2,12 +2,13 @@ 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, Report, ReportAction} from '@src/types/onyx'; +import type {PolicyTagList, ReportAction} from '@src/types/onyx'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; import * as PolicyUtils from './PolicyUtils'; import * as ReportActionsUtils from './ReportActionsUtils'; +import * as ReportConnection from './ReportConnection'; import * as TransactionUtils from './TransactionUtils'; let allPolicyTags: OnyxCollection = {}; @@ -23,13 +24,6 @@ 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. */ @@ -116,7 +110,7 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr return ''; } const reportActionOriginalMessage = ReportActionsUtils.getOriginalMessage(reportAction); - const policyID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.policyID ?? '-1'; + const policyID = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.policyID ?? '-1'; const removalFragments: string[] = []; const setFragments: string[] = []; diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 6389876a1858..42c6aa35ff14 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -1,9 +1,8 @@ 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 ReportConnection from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; @@ -34,13 +33,6 @@ 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. */ @@ -68,7 +60,7 @@ const dismissModal = (reportID?: string, ref = navigationRef) => { originalDismissModal(ref); return; } - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${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 18099f157d6a..d60e66f8d535 100644 --- a/src/libs/Notification/PushNotification/subscribePushNotification/index.ts +++ b/src/libs/Notification/PushNotification/subscribePushNotification/index.ts @@ -1,5 +1,4 @@ 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'; @@ -7,13 +6,14 @@ 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 * as ReportConnection from '@libs/ReportConnection'; 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, Report} from '@src/types/onyx'; +import type {OnyxUpdatesFromServer} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import PushNotification from '..'; @@ -28,13 +28,6 @@ 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({ @@ -82,7 +75,7 @@ function navigateToReport({reportID, reportActionID}: ReportActionPushNotificati Log.info('[PushNotification] Navigating to report', false, {reportID, reportActionID}); const policyID = lastVisitedPath && extractPolicyIDFromPath(lastVisitedPath); - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const policyEmployeeAccountIDs = policyID ? getPolicyEmployeeAccountIDs(policyID) : []; const reportBelongsToWorkspace = policyID && !isEmptyObject(report) && doesReportBelongToWorkspace(report, policyEmployeeAccountIDs, policyID); diff --git a/src/libs/OnyxAwareParser.ts b/src/libs/OnyxAwareParser.ts index c058775341c2..51ea39ef972a 100644 --- a/src/libs/OnyxAwareParser.ts +++ b/src/libs/OnyxAwareParser.ts @@ -1,23 +1,12 @@ import {ExpensiMark} from 'expensify-common'; import Onyx from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; +import * as ReportConnection from './ReportConnection'; const parser = new ExpensiMark(); -const reportIDToNameMap: Record = {}; const accountIDToNameMap: Record = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - callback: (report) => { - if (!report) { - return; - } - - reportIDToNameMap[report.reportID] = report.reportName ?? report.displayName ?? report.reportID; - }, -}); - Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (personalDetailsList) => { @@ -37,7 +26,11 @@ function parseHtmlToMarkdown( accountIDToName?: Record, cacheVideoAttributes?: (videoSource: string, videoAttrs: string) => void, ): string { - return parser.htmlToMarkdown(html, {reportIDToName: reportIDToName ?? reportIDToNameMap, accountIDToName: accountIDToName ?? accountIDToNameMap, cacheVideoAttributes}); + return parser.htmlToMarkdown(html, { + reportIDToName: reportIDToName ?? ReportConnection.getAllReportsNameMap(), + accountIDToName: accountIDToName ?? accountIDToNameMap, + cacheVideoAttributes, + }); } function parseHtmlToText( @@ -46,7 +39,7 @@ function parseHtmlToText( accountIDToName?: Record, cacheVideoAttributes?: (videoSource: string, videoAttrs: string) => void, ): string { - return parser.htmlToText(html, {reportIDToName: reportIDToName ?? reportIDToNameMap, accountIDToName: accountIDToName ?? accountIDToNameMap, cacheVideoAttributes}); + return parser.htmlToText(html, {reportIDToName: reportIDToName ?? ReportConnection.getAllReportsNameMap(), accountIDToName: accountIDToName ?? accountIDToNameMap, cacheVideoAttributes}); } export {parseHtmlToMarkdown, parseHtmlToText}; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index ae1c1f795d82..a723f8b6d224 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -53,6 +53,7 @@ import * as PersonalDetailsUtils from './PersonalDetailsUtils'; import * as PhoneNumber from './PhoneNumber'; import * as PolicyUtils from './PolicyUtils'; import * as ReportActionUtils from './ReportActionsUtils'; +import * as ReportConnection from './ReportConnection'; import * as ReportUtils from './ReportUtils'; import * as TaskUtils from './TaskUtils'; import * as TransactionUtils from './TransactionUtils'; @@ -335,13 +336,6 @@ 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, @@ -353,6 +347,7 @@ Onyx.connect({ * Get the report or draft report given a reportID */ function getReportOrDraftReport(reportID: string | undefined): OnyxEntry { + const allReports = ReportConnection.getAllReports(); if (!allReports && !allReportsDraft) { return undefined; } diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts index 395c99fc4b26..b3718079441f 100644 --- a/src/libs/actions/ReportActions.ts +++ b/src/libs/actions/ReportActions.ts @@ -1,10 +1,11 @@ import type {OnyxCollection} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as ReportActionUtils from '@libs/ReportActionsUtils'; +import * as ReportConnection from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report as OnyxReportType, ReportActions} from '@src/types/onyx'; +import type {ReportActions} from '@src/types/onyx'; import type ReportAction from '@src/types/onyx/ReportAction'; import * as Report from './Report'; @@ -17,13 +18,6 @@ Onyx.connect({ callback: (value) => (allReportActions = value), }); -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); @@ -85,7 +79,7 @@ function clearAllRelatedReportActionErrors(reportID: string, reportAction: Repor clearReportActionErrors(reportID, reportAction, keys); - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${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)); From 10644c444f676d6419b779b84923b3feaebec4ee Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 24 Jun 2024 13:38:53 +0500 Subject: [PATCH 19/29] feat: add support for reports name map --- src/libs/ReportConnection.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportConnection.ts b/src/libs/ReportConnection.ts index 75148474a9da..b9aa2dbd0484 100644 --- a/src/libs/ReportConnection.ts +++ b/src/libs/ReportConnection.ts @@ -8,6 +8,7 @@ import * as ReportHelperActions from './actions/Report'; // Dynamic Import to avoid circular dependency const UnreadIndicatorUpdaterHelper = () => import('./UnreadIndicatorUpdater'); +const reportIDToNameMap: Record = {}; let allReports: OnyxCollection; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, @@ -27,6 +28,7 @@ Onyx.connect({ if (!report) { return; } + reportIDToNameMap[report.reportID] = report.reportName ?? report.displayName ?? report.reportID; ReportHelperActions.handleReportChanged(report); }); }, @@ -36,4 +38,8 @@ function getAllReports() { return allReports; } -export default getAllReports; +function getAllReportsNameMap() { + return reportIDToNameMap; +} + +export {getAllReports, getAllReportsNameMap}; From 36e76887ecf492898a4922011cf58aafc83ce067 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 24 Jun 2024 13:42:14 +0500 Subject: [PATCH 20/29] feat: add jsdocs --- src/libs/ReportConnection.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/ReportConnection.ts b/src/libs/ReportConnection.ts index b9aa2dbd0484..86e73229e84b 100644 --- a/src/libs/ReportConnection.ts +++ b/src/libs/ReportConnection.ts @@ -34,10 +34,12 @@ Onyx.connect({ }, }); +// This function is used to get all reports function getAllReports() { return allReports; } +// This function is used to get all reports name map function getAllReportsNameMap() { return reportIDToNameMap; } From 613768621ef2bd8a414925b511329889dfe86c38 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 24 Jun 2024 13:53:01 +0500 Subject: [PATCH 21/29] fix: typecheck --- src/libs/actions/Report.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 5a981f92020d..ae4d8da74150 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -225,7 +225,6 @@ Onyx.connect({ callback: (value) => (reportMetadata = value), }); -const allReports: OnyxCollection = {}; const typingWatchTimers: Record = {}; let reportIDDeeplinkedFromOldDot: string | undefined; @@ -739,7 +738,7 @@ function openReport( const optimisticReport = reportActionsExist(reportID) ? {} : { - reportName: allReports?.[reportID]?.reportName ?? CONST.REPORT.DEFAULT_REPORT_NAME, + reportName: ReportConnection.getAllReports()?.[reportID]?.reportName ?? CONST.REPORT.DEFAULT_REPORT_NAME, }; const optimisticData: OnyxUpdate[] = [ @@ -1008,7 +1007,7 @@ function navigateToAndOpenChildReport(childReportID = '-1', parentReportAction: Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID)); } else { const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction.actorAccountID)])]; - const parentReport = allReports?.[parentReportID]; + const parentReport = ReportConnection.getAllReports()?.[parentReportID]; // Threads from DMs and selfDMs don't have a chatType. All other threads inherit the chatType from their parent const childReportChatType = parentReport && ReportUtils.isSelfDM(parentReport) ? undefined : parentReport?.chatType; const newChat = ReportUtils.buildOptimisticChatReport( @@ -1182,7 +1181,8 @@ function markCommentAsUnread(reportID: string, reportActionCreated: string) { }, null); // If no action created date is provided, use the last action's from other user - const actionCreationTime = reportActionCreated || (latestReportActionFromOtherUsers?.created ?? allReports?.[reportID]?.lastVisibleActionCreated ?? DateUtils.getDBTime(0)); + const actionCreationTime = + reportActionCreated || (latestReportActionFromOtherUsers?.created ?? ReportConnection.getAllReports()?.[reportID]?.lastVisibleActionCreated ?? DateUtils.getDBTime(0)); // We subtract 1 millisecond so that the lastReadTime is updated to just before a given reportAction's created date // For example, if we want to mark a report action with ID 100 and created date '2014-04-01 16:07:02.999' unread, we set the lastReadTime to '2014-04-01 16:07:02.998' @@ -1284,6 +1284,7 @@ function handleReportChanged(report: OnyxEntry) { return; } + const allReports = ReportConnection.getAllReports(); if (allReports && report?.reportID) { allReports[report.reportID] = report; @@ -1686,7 +1687,7 @@ function toggleSubscribeToChildReport(childReportID = '-1', parentReportAction: } } else { const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction?.actorAccountID)])]; - const parentReport = allReports?.[parentReportID]; + const parentReport = ReportConnection.getAllReports()?.[parentReportID]; const newChat = ReportUtils.buildOptimisticChatReport( participantAccountIDs, ReportActionsUtils.getReportActionText(parentReportAction), @@ -2263,7 +2264,7 @@ function shouldShowReportActionNotification(reportID: string, action: ReportActi } // We don't want to send a local notification if the user preference is daily, mute or hidden. - const notificationPreference = allReports?.[reportID]?.notificationPreference ?? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS; + const notificationPreference = ReportConnection.getAllReports()?.[reportID]?.notificationPreference ?? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS; if (notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS) { Log.info(`${tag} No notification because user preference is to be notified: ${notificationPreference}`); return false; @@ -2315,7 +2316,7 @@ function showReportActionNotification(reportID: string, reportAction: ReportActi Log.info('[LocalNotification] Creating notification'); - const report = allReports?.[reportID] ?? null; + const report = ReportConnection.getAllReports()?.[reportID] ?? null; if (!report) { Log.hmmm("[LocalNotification] couldn't show report action notification because the report wasn't found", {reportID, reportActionID: reportAction.reportActionID}); return; @@ -2538,7 +2539,7 @@ function getCurrentUserAccountID(): number { function navigateToMostRecentReport(currentReport: OnyxEntry) { const reportID = currentReport?.reportID; - const sortedReportsByLastRead = ReportUtils.sortReportsByLastRead(Object.values(allReports ?? {}) as Report[], reportMetadata); + const sortedReportsByLastRead = ReportUtils.sortReportsByLastRead(Object.values(ReportConnection.getAllReports() ?? {}) as Report[], reportMetadata); // We want to filter out the current report, hidden reports and empty chats const filteredReportsByLastRead = sortedReportsByLastRead.filter( From 942e38c824dd3d7adb75ad0f366f398536487a07 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 24 Jun 2024 15:50:50 +0500 Subject: [PATCH 22/29] fix: use getAllReports --- src/libs/ReportActionsUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index f0fbc4f91cf6..0ee7c3f3310e 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1348,7 +1348,7 @@ function wasActionTakenByCurrentUser(reportAction: OnyxInputOrEntry { - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; const reportActions = getAllReportActions(report?.reportID ?? ''); const action = Object.values(reportActions ?? {})?.find((reportAction) => { const IOUTransactionID = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction)?.IOUTransactionID : -1; From 6441df081d2e6cd24e00f4b50fef5255f65abc10 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 25 Jun 2024 16:13:58 +0500 Subject: [PATCH 23/29] refactor: remove unused code --- src/libs/actions/Report.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 08ed521440ee..54e5eda00243 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1299,10 +1299,7 @@ function handleReportChanged(report: OnyxEntry) { return; } - const allReports = ReportConnection.getAllReports(); - if (allReports && report?.reportID) { - allReports[report.reportID] = report; - + if (report?.reportID) { if (ReportUtils.isConciergeChatReport(report)) { conciergeChatReportID = report.reportID; } From 06fee84a6956dcfd4ae2ed620be8ca33752cc4d3 Mon Sep 17 00:00:00 2001 From: Muhammad Hur Ali Date: Tue, 25 Jun 2024 18:21:20 +0500 Subject: [PATCH 24/29] refactor: use shorthand condition Co-authored-by: Hans --- src/libs/ReportActionsUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 856edb982c1b..018f3e3ba130 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -714,7 +714,7 @@ function replaceBaseURLInPolicyChangeLogAction(reportAction: ReportAction): Repo function getLastVisibleAction(reportID: string, actionsToMerge: OnyxCollection | OnyxCollectionInputValue = {}): OnyxEntry { let reportActions: Array = []; - if (_.isEmpty(actionsToMerge) === false) { + if (!_.isEmpty(actionsToMerge)) { reportActions = Object.values(fastMerge(allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {}, actionsToMerge ?? {}, true)); } else { reportActions = Object.values(allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? {}); From a4695359f87622a8a92648c08bb65f5891fc7836 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 26 Jun 2024 12:37:23 +0500 Subject: [PATCH 25/29] fix: prettier --- src/libs/Navigation/Navigation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index a0971a6a1715..729d0294aee6 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -2,8 +2,8 @@ import {findFocusedRoute} from '@react-navigation/core'; import type {EventArg, NavigationContainerEventMap} from '@react-navigation/native'; import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native'; import Log from '@libs/Log'; -import * as ReportConnection from '@libs/ReportConnection'; import isCentralPaneName from '@libs/NavigationUtils'; +import * as ReportConnection from '@libs/ReportConnection'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; From 9180ec0963b6c58170e9e411b35f6314a1f7acd6 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 26 Jun 2024 15:07:24 +0500 Subject: [PATCH 26/29] fix: use reportID with collection identifier --- src/libs/actions/Report.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 54e5eda00243..58d13e5a3787 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2328,9 +2328,10 @@ function showReportActionNotification(reportID: string, reportAction: ReportActi Log.info('[LocalNotification] Creating notification'); - const report = ReportConnection.getAllReports()?.[reportID] ?? null; + const localReportID = `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; + const report = ReportConnection.getAllReports()?.[localReportID] ?? null; if (!report) { - Log.hmmm("[LocalNotification] couldn't show report action notification because the report wasn't found", {reportID, reportActionID: reportAction.reportActionID}); + Log.hmmm("[LocalNotification] couldn't show report action notification because the report wasn't found", {localReportID, reportActionID: reportAction.reportActionID}); return; } From 611e585596cde3944499230bb044deb06027a4e6 Mon Sep 17 00:00:00 2001 From: Muhammad Hur Ali Date: Fri, 5 Jul 2024 19:12:52 +0500 Subject: [PATCH 27/29] fix: use correct reportID Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/libs/actions/Report.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 34b8e032281d..f3f5bbb3a449 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -741,7 +741,7 @@ function openReport( const optimisticReport = reportActionsExist(reportID) ? {} : { - reportName: ReportConnection.getAllReports()?.[reportID]?.reportName ?? CONST.REPORT.DEFAULT_REPORT_NAME, + reportName: ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.reportName ?? CONST.REPORT.DEFAULT_REPORT_NAME, }; const optimisticData: OnyxUpdate[] = [ @@ -1024,7 +1024,7 @@ function navigateToAndOpenChildReport(childReportID = '-1', parentReportAction: Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID)); } else { const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction.actorAccountID)])]; - const parentReport = ReportConnection.getAllReports()?.[parentReportID]; + const parentReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`]; // Threads from DMs and selfDMs don't have a chatType. All other threads inherit the chatType from their parent const childReportChatType = parentReport && ReportUtils.isSelfDM(parentReport) ? undefined : parentReport?.chatType; const newChat = ReportUtils.buildOptimisticChatReport( @@ -1199,7 +1199,7 @@ function markCommentAsUnread(reportID: string, reportActionCreated: string) { // If no action created date is provided, use the last action's from other user const actionCreationTime = - reportActionCreated || (latestReportActionFromOtherUsers?.created ?? ReportConnection.getAllReports()?.[reportID]?.lastVisibleActionCreated ?? DateUtils.getDBTime(0)); + reportActionCreated || (latestReportActionFromOtherUsers?.created ?? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.lastVisibleActionCreated ?? DateUtils.getDBTime(0)); // We subtract 1 millisecond so that the lastReadTime is updated to just before a given reportAction's created date // For example, if we want to mark a report action with ID 100 and created date '2014-04-01 16:07:02.999' unread, we set the lastReadTime to '2014-04-01 16:07:02.998' @@ -1702,7 +1702,7 @@ function toggleSubscribeToChildReport(childReportID = '-1', parentReportAction: } } else { const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction?.actorAccountID)])]; - const parentReport = ReportConnection.getAllReports()?.[parentReportID]; + const parentReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`] const newChat = ReportUtils.buildOptimisticChatReport( participantAccountIDs, ReportActionsUtils.getReportActionText(parentReportAction), @@ -2267,7 +2267,7 @@ function shouldShowReportActionNotification(reportID: string, action: ReportActi } // We don't want to send a local notification if the user preference is daily, mute or hidden. - const notificationPreference = ReportConnection.getAllReports()?.[reportID]?.notificationPreference ?? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS; + const notificationPreference = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.notificationPreference ?? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS; if (notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS) { Log.info(`${tag} No notification because user preference is to be notified: ${notificationPreference}`); return false; @@ -2577,7 +2577,7 @@ function joinRoom(report: OnyxEntry) { } function leaveGroupChat(reportID: string) { - const report = ReportConnection.getAllReports()?.[reportID]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { Log.warn('Attempting to leave Group Chat that does not existing locally'); return; From 8a1736928d97b5e34c2fc38ed69a69ccb6c374b7 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 5 Jul 2024 19:23:23 +0500 Subject: [PATCH 28/29] fix: apply prettier --- src/libs/actions/Report.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7229e238c70b..933c00c7ec56 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1239,7 +1239,8 @@ function markCommentAsUnread(reportID: string, reportActionCreated: string) { // If no action created date is provided, use the last action's from other user const actionCreationTime = - reportActionCreated || (latestReportActionFromOtherUsers?.created ?? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.lastVisibleActionCreated ?? DateUtils.getDBTime(0)); + reportActionCreated || + (latestReportActionFromOtherUsers?.created ?? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.lastVisibleActionCreated ?? DateUtils.getDBTime(0)); // We subtract 1 millisecond so that the lastReadTime is updated to just before a given reportAction's created date // For example, if we want to mark a report action with ID 100 and created date '2014-04-01 16:07:02.999' unread, we set the lastReadTime to '2014-04-01 16:07:02.998' @@ -1742,7 +1743,7 @@ function toggleSubscribeToChildReport(childReportID = '-1', parentReportAction: } } else { const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction?.actorAccountID)])]; - const parentReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`] + const parentReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`]; const newChat = ReportUtils.buildOptimisticChatReport( participantAccountIDs, ReportActionsUtils.getReportActionText(parentReportAction), From bdba40b191afe8224d0f95594612e91dcc92df5e Mon Sep 17 00:00:00 2001 From: Muhammad Hur Ali Date: Mon, 8 Jul 2024 14:32:26 +0500 Subject: [PATCH 29/29] fix: use correct reportID Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 933c00c7ec56..4d674726540a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -479,7 +479,7 @@ function addActions(reportID: string, text = '', file?: FileObject) { lastReadTime: currentTime, }; - const report = ReportConnection.getAllReports()?.[reportID]; + const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!isEmptyObject(report) && ReportUtils.getReportNotificationPreference(report) === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { optimisticReport.notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS;