Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/pages/inbox/ChatReportActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/** Keep chat effects unmounted during app loading, except for the Concierge welcome screen. */
import {useIsAppLoadPending} from '@hooks/useInFlightRequests';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';

import {isConciergeChatReport} from '@libs/ReportUtils';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Report} from '@src/types/onyx';

import React from 'react';

import ReportActionsList from './report/ReportActionsList';
import ReportActionsLoadingSkeleton from './report/ReportActionsLoadingSkeleton';
import UserTypingEventListener from './report/UserTypingEventListener';

type ChatReportActionsProps = {
report: Report;
};

function ChatReportActions({report}: ChatReportActionsProps) {
const {isOffline} = useNetwork();
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const isAppLoadPending = useIsAppLoadPending();
const isConciergeMainDM = isConciergeChatReport(report, conciergeReportID);

if (isAppLoadPending && !isOffline && !isConciergeMainDM) {
return (
<ReportActionsLoadingSkeleton
reportID={report.reportID}
skeletonName={CONST.TELEMETRY.CANCELED_BY_SKELETON.REPORT_ACTIONS_APP_LOAD}
/>
);
}

return (
<>
<ReportActionsList
key={report.reportID}
reportID={report.reportID}
/>
<UserTypingEventListener report={report} />
</>
);
}

export default ChatReportActions;
59 changes: 59 additions & 0 deletions src/pages/inbox/MoneyRequestReportActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/** Only money-request and invoice reports need transactions to select their actions layout. */
import MoneyRequestReportActionsList from '@components/MoneyRequestReportView/MoneyRequestReportActionsList';

import {useIsReportLoadPending} from '@hooks/useInFlightRequests';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePaginatedReportActions from '@hooks/usePaginatedReportActions';
import useReportTransactionsCollection from '@hooks/useReportTransactionsCollection';

import {getAllNonDeletedTransactions, shouldDisplayReportTableView, shouldWaitForTransactions as shouldWaitForTransactionsUtil} from '@libs/MoneyRequestReportUtils';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Report} from '@src/types/onyx';

import React from 'react';

import ChatReportActions from './ChatReportActions';
import ReportActionsLoadingSkeleton from './report/ReportActionsLoadingSkeleton';

const defaultReportLoadingState = {
hasOnceLoadedReportActions: false,
isLoadingInitialReportActions: true,
isLoadingOlderReportActions: false,
hasLoadingOlderReportActionsError: false,
isLoadingNewerReportActions: false,
hasLoadingNewerReportActionsError: false,
};

type MoneyRequestReportActionsProps = {
report: Report;
};

function MoneyRequestReportActions({report}: MoneyRequestReportActionsProps) {
const {isOffline} = useNetwork();
const [reportLoadingState = defaultReportLoadingState] = useOnyx(`${ONYXKEYS.COLLECTION.RAM_ONLY_REPORT_LOADING_STATE}${report.reportID}`);
const isReportLoadPending = useIsReportLoadPending(report.reportID);
const {reportActions} = usePaginatedReportActions(report.reportID);
const allReportTransactions = useReportTransactionsCollection(report.reportID);
const reportTransactions = getAllNonDeletedTransactions(allReportTransactions, reportActions, isOffline, true);
const shouldWaitForTransactions = shouldWaitForTransactionsUtil(report, reportTransactions, reportLoadingState, isReportLoadPending, isOffline);

if (shouldWaitForTransactions) {
return (
<ReportActionsLoadingSkeleton
reportID={report.reportID}
skeletonName={CONST.TELEMETRY.CANCELED_BY_SKELETON.REPORT_ACTIONS_REPORT_DATA_LOADING}
/>
);
}

if (shouldDisplayReportTableView(report, reportTransactions)) {
return <MoneyRequestReportActionsList />;
}

return <ChatReportActions report={report} />;
}

export default MoneyRequestReportActions;
110 changes: 8 additions & 102 deletions src/pages/inbox/ReportActions.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import MoneyRequestReportActionsList from '@components/MoneyRequestReportView/MoneyRequestReportActionsList';
import NavigationDeferredMount from '@components/NavigationDeferredMount';

import {useIsAppLoadPending, useIsReportLoadPending} from '@hooks/useInFlightRequests';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePaginatedReportActions from '@hooks/usePaginatedReportActions';
import useReportTransactionsCollection from '@hooks/useReportTransactionsCollection';

import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import {getAllNonDeletedTransactions, shouldDisplayReportTableView, shouldWaitForTransactions as shouldWaitForTransactionsUtil} from '@libs/MoneyRequestReportUtils';
import {isConciergeChatReport, isInvoiceReport, isMoneyRequestReport} from '@libs/ReportUtils';
import {isInvoiceReport, isMoneyRequestReport} from '@libs/ReportUtils';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand All @@ -19,55 +11,17 @@ import React from 'react';

import type ReportScreenNavigationProps from './types';

import ReportActionsList from './report/ReportActionsList';
import ChatReportActions from './ChatReportActions';
import MoneyRequestReportActions from './MoneyRequestReportActions';
import ReportActionsLoadingSkeleton from './report/ReportActionsLoadingSkeleton';
import UserTypingEventListener from './report/UserTypingEventListener';

const defaultReportLoadingState = {
hasOnceLoadedReportActions: false,
isLoadingInitialReportActions: true,
isLoadingOlderReportActions: false,
hasLoadingOlderReportActionsError: false,
isLoadingNewerReportActions: false,
hasLoadingNewerReportActionsError: false,
};

/**
* Route-only orchestrator for the report actions surface. It owns the coarse branching only —
* skeleton vs. money-request table view vs. the chat list — and subscribes to just what those
* branches need. All heavy data derivation lives inside the hook-driven `ReportActionsList` body,
* which is mounted only for the chat path so its hooks/effects never run while a skeleton shows.
*/
/** Choose the report surface without mounting transaction-based layout selection for ordinary chats. */
function ReportActions() {
const route = useRoute<ReportScreenNavigationProps['route']>();
const reportIDFromRoute = getNonEmptyStringOnyxID(route.params?.reportID);

const {isOffline} = useNetwork();

const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`);
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const [reportLoadingState = defaultReportLoadingState] = useOnyx(`${ONYXKEYS.COLLECTION.RAM_ONLY_REPORT_LOADING_STATE}${reportIDFromRoute}`);
const isAppLoadPending = useIsAppLoadPending();
const isReportLoadPending = useIsReportLoadPending(reportIDFromRoute);
const {reportActions} = usePaginatedReportActions(reportIDFromRoute);

const allReportTransactions = useReportTransactionsCollection(reportIDFromRoute);
const reportTransactions = getAllNonDeletedTransactions(allReportTransactions, reportActions, isOffline, true);

const isMoneyRequestOrInvoiceReport = isMoneyRequestReport(report) || isInvoiceReport(report);
const shouldWaitForTransactions = shouldWaitForTransactionsUtil(report, reportTransactions, reportLoadingState, isReportLoadPending, isOffline);
const shouldDisplayMoneyRequestActionsList = isMoneyRequestOrInvoiceReport && shouldDisplayReportTableView(report, reportTransactions);

// The app-load skeleton is hoisted out of the body so the body's data hooks/effects never run
// during app boot. It only applies on the chat path (after the skeleton and money-request
// branches below) — matching the previous behavior, where this skeleton lived inside the
// chat-only ReportActionsView.
//
// Concierge is excluded so its welcome screen shows while the app loads.
const isConciergeMainDM = isConciergeChatReport(report, conciergeReportID);
const shouldShowAppLoadSkeleton = isAppLoadPending && !isOffline && !!report && !shouldWaitForTransactions && !shouldDisplayMoneyRequestActionsList && !isConciergeMainDM;

if (!report || shouldWaitForTransactions) {
if (!report) {
return (
<ReportActionsLoadingSkeleton
reportID={reportIDFromRoute}
Expand All @@ -76,59 +30,11 @@ function ReportActions() {
);
}

if (shouldDisplayMoneyRequestActionsList) {
return <MoneyRequestReportActionsList />;
}

if (shouldShowAppLoadSkeleton) {
return (
<ReportActionsLoadingSkeleton
reportID={reportIDFromRoute}
skeletonName={CONST.TELEMETRY.CANCELED_BY_SKELETON.REPORT_ACTIONS_APP_LOAD}
/>
);
}

return (
<>
<ReportActionsList
key={report.reportID}
reportID={report.reportID}
/>
<UserTypingEventListener report={report} />
</>
);
}

type ReportActionsWithInboxTabDeferredMountProps = {
/** The report ID used by the deferred loading skeleton */
reportID: string | undefined;

/** Whether to defer mounting report actions during the initial Inbox tab navigation */
shouldDefer: boolean;
};

function ReportActionsWithInboxTabDeferredMount({reportID, shouldDefer}: ReportActionsWithInboxTabDeferredMountProps) {
if (!shouldDefer) {
return <ReportActions />;
if (isMoneyRequestReport(report) || isInvoiceReport(report)) {
return <MoneyRequestReportActions report={report} />;
}

return (
<NavigationDeferredMount
waitForUpcomingTransition={false}
placeholder={
// Deferral, not a data wait. Closing the span here would time the defer and tag a cached report cold.
<ReportActionsLoadingSkeleton
reportID={reportID}
skeletonName={CONST.TELEMETRY.CANCELED_BY_SKELETON.INBOX_TAB_DEFER}
shouldMarkOpenReportEnd={false}
/>
}
>
<ReportActions />
</NavigationDeferredMount>
);
return <ChatReportActions report={report} />;
}

export {ReportActionsWithInboxTabDeferredMount};
export default ReportActions;
41 changes: 41 additions & 0 deletions src/pages/inbox/ReportActionsWithInboxTabDeferredMount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/** Defer mounting report actions until the initial Inbox tab transition settles. */
import NavigationDeferredMount from '@components/NavigationDeferredMount';

import CONST from '@src/CONST';

import React from 'react';

import ReportActionsLoadingSkeleton from './report/ReportActionsLoadingSkeleton';
import ReportActions from './ReportActions';

type ReportActionsWithInboxTabDeferredMountProps = {
/** The report ID used by the deferred loading skeleton */
reportID: string | undefined;

/** Whether to defer mounting report actions during the initial Inbox tab navigation */
shouldDefer: boolean;
};

function ReportActionsWithInboxTabDeferredMount({reportID, shouldDefer}: ReportActionsWithInboxTabDeferredMountProps) {
if (!shouldDefer) {
return <ReportActions />;
}

return (
<NavigationDeferredMount
waitForUpcomingTransition={false}
placeholder={
// Deferral, not a data wait. Closing the span here would time the defer and tag a cached report cold.
<ReportActionsLoadingSkeleton
reportID={reportID}
skeletonName={CONST.TELEMETRY.CANCELED_BY_SKELETON.INBOX_TAB_DEFER}
shouldMarkOpenReportEnd={false}
/>
}
>
<ReportActions />
</NavigationDeferredMount>
);
}

export default ReportActionsWithInboxTabDeferredMount;
2 changes: 1 addition & 1 deletion src/pages/inbox/ReportScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import ReportActionCompose from './report/ReportActionCompose/ReportActionCompos
import {ReportActionEditMessageContextProvider, ReportScreenEditMessageProviderWithTransactionThread} from './report/ReportActionEditMessageContext';
import ReportFooter from './report/ReportFooter';
import useClearReportActionDraftsOnReportChange from './report/useClearReportActionDraftsOnReportChange';
import {ReportActionsWithInboxTabDeferredMount} from './ReportActions';
import ReportActionsWithInboxTabDeferredMount from './ReportActionsWithInboxTabDeferredMount';
import ReportDragAndDropProvider from './ReportDragAndDropProvider';
import ReportFetchHandler from './ReportFetchHandler';
import ReportHeader from './ReportHeader';
Expand Down
Loading
Loading