Skip to content
16 changes: 8 additions & 8 deletions src/components/ReportActionItem/TripDetailsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@ import * as TripReservationUtils from '@src/libs/TripReservationUtils';
import ROUTES from '@src/ROUTES';
import type {Reservation, ReservationTimeDetails} from '@src/types/onyx/Transaction';

type TripDetailsViewProps = {
/** The active tripRoomReportID, used for Onyx subscription */
tripRoomReportID: string;

/** Whether we should display the horizontal rule below the component */
shouldShowHorizontalRule: boolean;
};

type ReservationViewProps = {
reservation: Reservation;
transactionID: string;
Expand Down Expand Up @@ -142,6 +134,14 @@ function ReservationView({reservation, transactionID, tripRoomReportID, reservat
);
}

type TripDetailsViewProps = {
/** The active tripRoomReportID, used for Onyx subscription */
tripRoomReportID: string;

/** Whether we should display the horizontal rule below the component */
shouldShowHorizontalRule: boolean;
};

function TripDetailsView({tripRoomReportID, shouldShowHorizontalRule}: TripDetailsViewProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
Expand Down
3 changes: 2 additions & 1 deletion src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7359,8 +7359,9 @@ function canApproveIOU(
isTransactionBeingScanned = true;
}
}
const isPayAtEndExpenseReport = isPayAtEndExpenseReportReportUtils(iouReport?.reportID, reportTransactions);

return isCurrentUserManager && !isOpenExpenseReport && !isApproved && !iouSettled && !isArchivedExpenseReport && !isTransactionBeingScanned;
return isCurrentUserManager && !isOpenExpenseReport && !isApproved && !iouSettled && !isArchivedExpenseReport && !isTransactionBeingScanned && !isPayAtEndExpenseReport;
}

function canIOUBePaid(
Expand Down
87 changes: 37 additions & 50 deletions src/pages/home/report/ReportActionItemParentAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import TripDetailsView from '@components/ReportActionItem/TripDetailsView';
import useNetwork from '@hooks/useNetwork';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import onyxSubscribe from '@libs/onyxSubscribe';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as Report from '@userActions/Report';
import {shouldReportActionBeVisible} from '@libs/ReportActionsUtils';
import type {Ancestor} from '@libs/ReportUtils';
import {canCurrentUserOpenReport, canUserPerformWriteAction as canUserPerformWriteActionReportUtils, getAllAncestorReportActionIDs, getAllAncestorReportActions} from '@libs/ReportUtils';
import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
Expand Down Expand Up @@ -65,9 +65,9 @@ function ReportActionItemParentAction({
}: ReportActionItemParentActionProps) {
const styles = useThemeStyles();
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
const ancestorIDs = useRef(ReportUtils.getAllAncestorReportActionIDs(report));
const ancestorIDs = useRef(getAllAncestorReportActionIDs(report));
const ancestorReports = useRef<Record<string, OnyxEntry<OnyxTypes.Report>>>({});
const [allAncestors, setAllAncestors] = useState<ReportUtils.Ancestor[]>([]);
const [allAncestors, setAllAncestors] = useState<Ancestor[]>([]);
const {isOffline} = useNetwork();

useEffect(() => {
Expand All @@ -83,15 +83,15 @@ function ReportActionItemParentAction({
// holds the report collection. However, allReports is not updated by the time this current callback is called.
// Therefore we need to pass the up-to-date report to getAllAncestorReportActions so that it uses the up-to-date report value
// to calculate, for instance, unread marker.
setAllAncestors(ReportUtils.getAllAncestorReportActions(report, val));
setAllAncestors(getAllAncestorReportActions(report, val));
},
}),
);
unsubscribeReportActions.push(
onyxSubscribe({
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${ancestorReportID}`,
callback: () => {
setAllAncestors(ReportUtils.getAllAncestorReportActions(report));
setAllAncestors(getAllAncestorReportActions(report));
},
}),
);
Expand All @@ -110,60 +110,47 @@ function ReportActionItemParentAction({
{/* eslint-disable-next-line react-compiler/react-compiler */}
{allAncestors.map((ancestor) => {
const ancestorReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${ancestor.report.reportID}`];
const canUserPerformWriteAction = ReportUtils.canUserPerformWriteAction(ancestorReport);
const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(ancestorReport);
return (
<OfflineWithFeedback
key={ancestor.reportAction.reportActionID}
shouldDisableOpacity={!!ancestor.reportAction?.pendingAction}
pendingAction={ancestor.report?.pendingFields?.addWorkspaceRoom ?? ancestor.report?.pendingFields?.createChat}
errors={ancestor.report?.errorFields?.addWorkspaceRoom ?? ancestor.report?.errorFields?.createChat}
errorRowStyles={[styles.ml10, styles.mr2]}
onClose={() => Report.navigateToConciergeChatAndDeleteReport(ancestor.report.reportID)}
onClose={() => navigateToConciergeChatAndDeleteReport(ancestor.report.reportID)}
>
<ThreadDivider
ancestor={ancestor}
isLinkDisabled={!ReportUtils.canCurrentUserOpenReport(ancestorReports.current?.[ancestor?.report?.reportID ?? '-1'])}
isLinkDisabled={!canCurrentUserOpenReport(ancestorReports.current?.[ancestor?.report?.reportID])}
/>
{ReportActionsUtils.isTripPreview(ancestor?.reportAction) ? (
<OfflineWithFeedback pendingAction={ancestor.reportAction.pendingAction}>
<TripDetailsView
tripRoomReportID={ReportActionsUtils.getOriginalMessage(ancestor.reportAction)?.linkedReportID ?? '-1'}
shouldShowHorizontalRule={false}
/>
</OfflineWithFeedback>
) : (
<ReportActionItem
onPress={
ReportUtils.canCurrentUserOpenReport(ancestorReports.current?.[ancestor?.report?.reportID ?? '-1'])
? () => {
const isVisibleAction = ReportActionsUtils.shouldReportActionBeVisible(
ancestor.reportAction,
ancestor.reportAction.reportActionID ?? '-1',
canUserPerformWriteAction,
);
// Pop the thread report screen before navigating to the chat report.
Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.reportID ?? '-1'));
if (isVisibleAction && !isOffline) {
// Pop the chat report screen before navigating to the linked report action.
Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.reportID ?? '-1', ancestor.reportAction.reportActionID));
}
<ReportActionItem
onPress={
canCurrentUserOpenReport(ancestorReports.current?.[ancestor?.report?.reportID])
? () => {
const isVisibleAction = shouldReportActionBeVisible(ancestor.reportAction, ancestor.reportAction.reportActionID, canUserPerformWriteAction);
// Pop the thread report screen before navigating to the chat report.
Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.reportID));
if (isVisibleAction && !isOffline) {
// Pop the chat report screen before navigating to the linked report action.
Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.reportID, ancestor.reportAction.reportActionID));
}
: undefined
}
parentReportAction={parentReportAction}
report={ancestor.report}
reportActions={reportActions}
transactionThreadReport={transactionThreadReport}
action={ancestor.reportAction}
displayAsGroup={false}
isMostRecentIOUReportAction={false}
shouldDisplayNewMarker={ancestor.shouldDisplayNewMarker}
index={index}
isFirstVisibleReportAction={isFirstVisibleReportAction}
shouldUseThreadDividerLine={shouldUseThreadDividerLine}
isThreadReportParentAction
/>
)}
}
: undefined
}
parentReportAction={parentReportAction}
report={ancestor.report}
reportActions={reportActions}
transactionThreadReport={transactionThreadReport}
action={ancestor.reportAction}
displayAsGroup={false}
isMostRecentIOUReportAction={false}
shouldDisplayNewMarker={ancestor.shouldDisplayNewMarker}
index={index}
isFirstVisibleReportAction={isFirstVisibleReportAction}
shouldUseThreadDividerLine={shouldUseThreadDividerLine}
isThreadReportParentAction
/>
</OfflineWithFeedback>
);
})}
Expand Down
55 changes: 33 additions & 22 deletions src/pages/home/report/ReportActionsListItemRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, {memo, useMemo} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import {getOriginalMessage, isSentMoneyReportAction, isTransactionThread} from '@libs/ReportActionsUtils';
import {getTripTransactions, isChatThread, isInvoiceRoom, isPolicyExpenseChat, isTripRoom} from '@libs/ReportUtils';
import CONST from '@src/CONST';
import type {Report, ReportAction} from '@src/types/onyx';
import ReportActionItem from './ReportActionItem';
import ReportActionItemParentAction from './ReportActionItemParentAction';
import TripSummary from './TripSummary';

type ReportActionsListItemRendererProps = {
/** All the data of the action item */
Expand Down Expand Up @@ -71,12 +72,8 @@ function ReportActionsListItemRenderer({
shouldUseThreadDividerLine = false,
parentReportActionForTransactionThread,
}: ReportActionsListItemRendererProps) {
const shouldDisplayParentAction =
reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED &&
ReportUtils.isChatThread(report) &&
(!ReportActionsUtils.isTransactionThread(parentReportAction) || ReportActionsUtils.isSentMoneyReportAction(parentReportAction));
const originalMessage = useMemo(() => getOriginalMessage(reportAction), [reportAction]);

const originalMessage = useMemo(() => ReportActionsUtils.getOriginalMessage(reportAction), [reportAction]);
/**
* Create a lightweight ReportAction so as to keep the re-rendering as light as possible by
* passing in only the required props.
Expand Down Expand Up @@ -145,20 +142,34 @@ function ReportActionsListItemRenderer({
],
);

return shouldDisplayParentAction ? (
<ReportActionItemParentAction
shouldHideThreadDividerLine={shouldDisplayParentAction && shouldHideThreadDividerLine}
shouldDisplayReplyDivider={shouldDisplayReplyDivider}
parentReportAction={parentReportAction}
reportID={report.reportID}
report={report}
reportActions={reportActions}
transactionThreadReport={transactionThreadReport}
index={index}
isFirstVisibleReportAction={isFirstVisibleReportAction}
shouldUseThreadDividerLine={shouldUseThreadDividerLine}
/>
) : (
const shouldDisplayParentAction =
reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && (!isTransactionThread(parentReportAction) || isSentMoneyReportAction(parentReportAction));

const tripTransactions = getTripTransactions(report?.reportID);
const shouldDisplayTripSummary = shouldDisplayParentAction && isTripRoom(report) && tripTransactions.length > 0;

if (shouldDisplayTripSummary) {
return <TripSummary report={report} />;
}

if (shouldDisplayParentAction && isChatThread(report)) {
return (
<ReportActionItemParentAction
shouldHideThreadDividerLine={shouldDisplayParentAction && shouldHideThreadDividerLine}
shouldDisplayReplyDivider={shouldDisplayReplyDivider}
parentReportAction={parentReportAction}
reportID={report.reportID}
report={report}
reportActions={reportActions}
transactionThreadReport={transactionThreadReport}
index={index}
isFirstVisibleReportAction={isFirstVisibleReportAction}
shouldUseThreadDividerLine={shouldUseThreadDividerLine}
/>
);
}

return (
<ReportActionItem
shouldHideThreadDividerLine={shouldHideThreadDividerLine}
parentReportAction={parentReportAction}
Expand All @@ -171,7 +182,7 @@ function ReportActionsListItemRenderer({
displayAsGroup={displayAsGroup}
shouldDisplayNewMarker={shouldDisplayNewMarker}
shouldShowSubscriptAvatar={
(ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isInvoiceRoom(report)) &&
(isPolicyExpenseChat(report) || isInvoiceRoom(report)) &&
[
CONST.REPORT.ACTIONS.TYPE.IOU,
CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW,
Expand Down
39 changes: 39 additions & 0 deletions src/pages/home/report/TripSummary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import TripDetailsView from '@components/ReportActionItem/TripDetailsView';
import useThemeStyles from '@hooks/useThemeStyles';
import type * as OnyxTypes from '@src/types/onyx';
import AnimatedEmptyStateBackground from './AnimatedEmptyStateBackground';
import RepliesDivider from './RepliesDivider';

type TripSummaryProps = {
/** The current report is displayed */
report: OnyxEntry<OnyxTypes.Report>;
};

function TripSummary({report}: TripSummaryProps) {
const styles = useThemeStyles();

if (!report?.reportID) {
return null;
}

return (
<View style={[styles.pRelative]}>
<AnimatedEmptyStateBackground />
<OfflineWithFeedback pendingAction={report.pendingAction}>
<TripDetailsView
tripRoomReportID={report.reportID}
shouldShowHorizontalRule={false}
/>
</OfflineWithFeedback>
<RepliesDivider shouldHideThreadDividerLine={false} />
</View>
);
}

TripSummary.displayName = 'TripSummary';

export default TripSummary;