diff --git a/src/pages/inbox/AccountManagerBanner.tsx b/src/pages/inbox/AccountManagerBanner.tsx index aca7344274ee..7e54ba010b42 100644 --- a/src/pages/inbox/AccountManagerBanner.tsx +++ b/src/pages/inbox/AccountManagerBanner.tsx @@ -6,7 +6,6 @@ import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Navigation from '@libs/Navigation/Navigation'; -import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import {getParticipantsAccountIDsForDisplay, isConciergeChatReport} from '@libs/ReportUtils'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -23,16 +22,15 @@ function AccountManagerBanner({reportID}: AccountManagerBannerProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(reportID)}`); const [accountManagerReportID] = useOnyx(ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID); const [accountManagerReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(accountManagerReportID)}`); - const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const accountManagerAccountID = getParticipantsAccountIDsForDisplay(accountManagerReport, false, true)?.at(0) ?? -1; + const [participantPersonalDetail] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, { + selector: (details) => details?.[accountManagerAccountID], + }); const [isBannerVisible, setIsBannerVisible] = useState(true); if (!accountManagerReportID || !isConciergeChatReport(report) || !isBannerVisible) { return null; } - - const participants = getParticipantsAccountIDsForDisplay(accountManagerReport, false, true); - const participantPersonalDetails = getPersonalDetailsForAccountIDs([participants?.at(0) ?? -1], personalDetails); - const participantPersonalDetail = Object.values(participantPersonalDetails).at(0); const displayName = getDisplayNameOrDefault(participantPersonalDetail); const login = participantPersonalDetail?.login; const chatWithAccountManagerText = displayName && login ? translate('common.chatWithAccountManager', `${displayName} (${login})`) : ''; diff --git a/src/pages/inbox/ReportNotFoundGuard.tsx b/src/pages/inbox/ReportNotFoundGuard.tsx index c5ce99b3becf..861a421fb93b 100644 --- a/src/pages/inbox/ReportNotFoundGuard.tsx +++ b/src/pages/inbox/ReportNotFoundGuard.tsx @@ -1,6 +1,7 @@ import {useRoute} from '@react-navigation/native'; import type {ReactNode} from 'react'; import React, {useEffect} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -15,23 +16,31 @@ import {getParentReportActionDeletionStatus} from '@libs/TransactionNavigationUt import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isLoadingInitialReportActionsSelector} from '@src/selectors/ReportMetaData'; +import type * as OnyxTypes from '@src/types/onyx'; type ReportNotFoundGuardProps = { children: ReactNode; }; +/** + * Two-level gate: the outer guard subscribes to lightweight keys to determine + * whether the report clearly exists. When it does (and the report is not a + * transaction thread), children render directly without the cost of + * parentReportMetadata / useParentReportAction subscriptions. + * + * The inner gate mounts only when the "not found" path is plausible: + * the report is missing, the path is invalid, or the report is a transaction + * thread whose parent action may have been deleted. + */ // eslint-disable-next-line rulesdir/no-negated-variables function ReportNotFoundGuard({children}: ReportNotFoundGuardProps) { - const styles = useThemeStyles(); const route = useRoute(); const routeParams = route.params as {reportID?: string} | undefined; const reportIDFromRoute = getNonEmptyStringOnyxID(routeParams?.reportID); const {isOffline} = useNetwork(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`); - const [parentReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${report?.parentReportID}`); const [userLeavingStatus = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportIDFromRoute}`); const [isLoadingInitialReportActions = true] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportIDFromRoute}`, { selector: isLoadingInitialReportActionsSelector, @@ -40,9 +49,81 @@ function ReportNotFoundGuard({children}: ReportNotFoundGuardProps) { const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); const [deleteTransactionNavigateBackUrl] = useOnyx(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL); - const parentReportAction = useParentReportAction(report); const reportID = report?.reportID; const isOptimisticDelete = report?.statusNum === CONST.REPORT.STATUS_NUM.CLOSED; + const isInvalidReportPath = !!routeParams?.reportID && !isValidReportIDFromPath(routeParams.reportID); + const isLoading = isLoadingApp !== false || isLoadingReportData || (!isOffline && !!isLoadingInitialReportActions); + const mayBeTransactionThread = isReportTransactionThread(report); + + // Fast path: skip the expensive inner guard (parentReportMetadata, useParentReportAction) + // when we can determine shouldShowNotFoundPage is definitely false. + // + // shouldShowNotFoundPage is false when: + // - deleteTransactionNavigateBackUrl is set (always suppresses not-found), OR + // - path is valid AND report is not a transaction thread AND (still loading OR report exists) + const reportClearlyExists = !!reportID || isOptimisticDelete || userLeavingStatus; + const canSkipInnerGuard = !!deleteTransactionNavigateBackUrl || (!isInvalidReportPath && !mayBeTransactionThread && (isLoading || reportClearlyExists)); + if (canSkipInnerGuard) { + return children; + } + + return ( + + {children} + + ); +} + +type ReportNotFoundInnerGuardProps = { + report: OnyxEntry; + reportIDFromPath: string | undefined; + reportID: string | undefined; + isOptimisticDelete: boolean; + isInvalidReportPath: boolean; + isLoading: boolean; + isLoadingApp: OnyxEntry; + isLoadingReportData: boolean; + isLoadingInitialReportActions: boolean; + isOffline: boolean; + userLeavingStatus: boolean; + deleteTransactionNavigateBackUrl: OnyxEntry; + children: ReactNode; +}; + +// eslint-disable-next-line rulesdir/no-negated-variables +function ReportNotFoundInnerGuard({ + report, + reportIDFromPath, + reportID, + isOptimisticDelete, + isInvalidReportPath, + isLoading, + isLoadingApp, + isLoadingReportData, + isLoadingInitialReportActions, + isOffline, + userLeavingStatus, + deleteTransactionNavigateBackUrl, + children, +}: ReportNotFoundInnerGuardProps) { + const styles = useThemeStyles(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); + + const [parentReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${report?.parentReportID}`); + const parentReportAction = useParentReportAction(report); const {isParentActionMissingAfterLoad, isParentActionDeleted} = getParentReportActionDeletionStatus({ parentReportID: report?.parentReportID, @@ -53,8 +134,6 @@ function ReportNotFoundGuard({children}: ReportNotFoundGuardProps) { }); const isDeletedTransactionThread = isReportTransactionThread(report) && (isParentActionDeleted || isParentActionMissingAfterLoad); - const isInvalidReportPath = !!routeParams?.reportID && !isValidReportIDFromPath(routeParams.reportID); - const isLoading = isLoadingApp !== false || isLoadingReportData || (!isOffline && !!isLoadingInitialReportActions); const reportExists = !!reportID || (!isDeletedTransactionThread && isOptimisticDelete) || userLeavingStatus; // eslint-disable-next-line rulesdir/no-negated-variables @@ -73,7 +152,7 @@ function ReportNotFoundGuard({children}: ReportNotFoundGuardProps) { reportID, isOptimisticDelete, userLeavingStatus, - reportIDFromPath: routeParams?.reportID, + reportIDFromPath, deleteTransactionNavigateBackUrl, isDeletedTransactionThread, isParentActionDeleted, @@ -88,7 +167,7 @@ function ReportNotFoundGuard({children}: ReportNotFoundGuardProps) { reportID, isOptimisticDelete, userLeavingStatus, - routeParams?.reportID, + reportIDFromPath, deleteTransactionNavigateBackUrl, isDeletedTransactionThread, isParentActionDeleted,