Skip to content

Commit cad02de

Browse files
authored
Merge pull request Expensify#91048 from callstack-internal/decompose/ral-3
decompose ReportActionsList: 3
2 parents cc634d6 + 71c2e57 commit cad02de

4 files changed

Lines changed: 131 additions & 83 deletions

File tree

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import {getAllNonDeletedTransactions} from '@libs/MoneyRequestReportUtils';
2+
import {isCreatedAction, isDeletedParentAction, isIOUActionMatchingTransactionList, isReportActionVisible} from '@libs/ReportActionsUtils';
3+
import {isConciergeChatReport} from '@libs/ReportUtils';
4+
import CONST from '@src/CONST';
5+
import ONYXKEYS from '@src/ONYXKEYS';
6+
import type {ReportAction} from '@src/types/onyx';
7+
import useConciergeSidePanelReportActions from './useConciergeSidePanelReportActions';
8+
import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails';
9+
import useIsInSidePanel from './useIsInSidePanel';
10+
import useLocalize from './useLocalize';
11+
import useNetwork from './useNetwork';
12+
import useOnyx from './useOnyx';
13+
import useSidePanelState from './useSidePanelState';
14+
import useTransactionsAndViolationsForReport from './useTransactionsAndViolationsForReport';
15+
16+
type UseReportActionsVisibilityParams = {
17+
reportID: string | undefined;
18+
reportActions: ReportAction[];
19+
allReportActions: ReportAction[];
20+
canPerformWriteAction: boolean;
21+
hasOlderActions: boolean;
22+
loadOlderChats: (force?: boolean) => void;
23+
};
24+
25+
type UseReportActionsVisibilityResult = {
26+
sortedReportActions: ReportAction[];
27+
sortedVisibleReportActions: ReportAction[];
28+
isConciergeSidePanel: boolean;
29+
showConciergeSidePanelWelcome: boolean;
30+
showFullHistory: boolean;
31+
hasPreviousMessages: boolean;
32+
handleShowPreviousMessages: () => void;
33+
};
34+
35+
function useReportActionsVisibility({
36+
reportID,
37+
reportActions,
38+
allReportActions,
39+
canPerformWriteAction,
40+
hasOlderActions,
41+
loadOlderChats,
42+
}: UseReportActionsVisibilityParams): UseReportActionsVisibilityResult {
43+
const {isOffline} = useNetwork();
44+
const {translate} = useLocalize();
45+
const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
46+
47+
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
48+
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
49+
const [visibleReportActionsData] = useOnyx(ONYXKEYS.DERIVED.VISIBLE_REPORT_ACTIONS);
50+
51+
const isInSidePanel = useIsInSidePanel();
52+
const isConciergeSidePanel = isInSidePanel && isConciergeChatReport(report, conciergeReportID);
53+
54+
const {sessionStartTime} = useSidePanelState();
55+
56+
const hasUserSentMessage =
57+
isConciergeSidePanel && sessionStartTime
58+
? allReportActions.some((action) => !isCreatedAction(action) && action.actorAccountID === currentUserAccountID && action.created >= sessionStartTime)
59+
: false;
60+
61+
const {transactions: reportTransactions, isLoaded: areTransactionsLoaded} = useTransactionsAndViolationsForReport(reportID);
62+
// When transactions haven't loaded yet, pass undefined to skip IOU filtering entirely
63+
// (undefined = "don't filter" in isIOUActionMatchingTransactionList).
64+
// Once loaded, filter normally — even if transactions is empty (genuinely no transactions).
65+
const reportTransactionIDs = areTransactionsLoaded ? getAllNonDeletedTransactions(reportTransactions, allReportActions ?? []).map((transaction) => transaction.transactionID) : undefined;
66+
67+
const visibleReportActions = reportActions.filter((reportAction) => {
68+
const passesOfflineCheck = isOffline || isDeletedParentAction(reportAction) || reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || reportAction.errors;
69+
70+
if (!passesOfflineCheck) {
71+
return false;
72+
}
73+
74+
const actionReportID = reportAction.reportID ?? reportID;
75+
if (!isReportActionVisible(reportAction, actionReportID, canPerformWriteAction, visibleReportActionsData)) {
76+
return false;
77+
}
78+
79+
if (!isIOUActionMatchingTransactionList(reportAction, reportTransactionIDs)) {
80+
return false;
81+
}
82+
83+
return true;
84+
});
85+
86+
const {filteredVisibleActions, filteredReportActions, showConciergeSidePanelWelcome, showFullHistory, hasPreviousMessages, handleShowPreviousMessages} =
87+
useConciergeSidePanelReportActions({
88+
report,
89+
reportActions,
90+
visibleReportActions,
91+
isConciergeSidePanel,
92+
hasUserSentMessage,
93+
hasOlderActions,
94+
sessionStartTime,
95+
currentUserAccountID,
96+
greetingText: translate('common.concierge.sidePanelGreeting'),
97+
loadOlderChats,
98+
});
99+
100+
return {
101+
sortedReportActions: filteredReportActions,
102+
sortedVisibleReportActions: filteredVisibleActions,
103+
isConciergeSidePanel,
104+
showConciergeSidePanelWelcome,
105+
showFullHistory,
106+
hasPreviousMessages,
107+
handleShowPreviousMessages,
108+
};
109+
}
110+
111+
export default useReportActionsVisibility;

src/hooks/useTransactionsAndViolationsForReport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function useTransactionsAndViolationsForReport(reportID?: string) {
2323
filteredViolations[transactionViolationKey] = getTransactionViolations(transaction, violations, currentUserDetails.email ?? '', currentUserDetails.accountID, report, policy) ?? [];
2424
}
2525

26-
return {transactions, violations: filteredViolations};
26+
return {transactions, violations: filteredViolations, isLoaded: allReportsTransactionsAndViolations !== undefined};
2727
}
2828

2929
export default useTransactionsAndViolationsForReport;

src/pages/inbox/report/ReportActionsView.tsx

Lines changed: 18 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,20 @@ import {useRoute} from '@react-navigation/native';
22
import React, {useEffect, useMemo, useRef} from 'react';
33
import type {LayoutChangeEvent} from 'react-native';
44
import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
5-
import useConciergeSidePanelReportActions from '@hooks/useConciergeSidePanelReportActions';
65
import useCopySelectionHelper from '@hooks/useCopySelectionHelper';
7-
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
8-
import useIsInSidePanel from '@hooks/useIsInSidePanel';
96
import useLoadReportActions from '@hooks/useLoadReportActions';
10-
import useLocalize from '@hooks/useLocalize';
117
import useNetwork from '@hooks/useNetwork';
128
import useOnyx from '@hooks/useOnyx';
139
import useParentReportAction from '@hooks/useParentReportAction';
1410
import usePendingConciergeResponse from '@hooks/usePendingConciergeResponse';
1511
import useReportActionsPagination from '@hooks/useReportActionsPagination';
12+
import useReportActionsVisibility from '@hooks/useReportActionsVisibility';
1613
import useReportIsArchived from '@hooks/useReportIsArchived';
17-
import useSidePanelState from '@hooks/useSidePanelState';
18-
import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport';
1914
import {getReportPreviewAction} from '@libs/actions/IOU/MoneyRequestBuilder';
2015
import {updateLoadingInitialReportAction} from '@libs/actions/Report';
21-
import {getAllNonDeletedTransactions} from '@libs/MoneyRequestReportUtils';
22-
import {isCreatedAction, isDeletedParentAction, isIOUActionMatchingTransactionList, isReportActionVisible} from '@libs/ReportActionsUtils';
23-
import {canUserPerformWriteAction, isConciergeChatReport, isReportTransactionThread as isReportTransactionThreadUtil, isUnread} from '@libs/ReportUtils';
16+
import {canUserPerformWriteAction, isReportTransactionThread as isReportTransactionThreadUtil, isUnread} from '@libs/ReportUtils';
2417
import markOpenReportEnd from '@libs/telemetry/markOpenReportEnd';
2518
import type ReportScreenNavigationProps from '@pages/inbox/types';
26-
import CONST from '@src/CONST';
2719
import ONYXKEYS from '@src/ONYXKEYS';
2820
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
2921
import ReportActionsList from './ReportActionsList';
@@ -42,9 +34,7 @@ function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) {
4234
const reportActionIDFromRoute = route?.params?.reportActionID;
4335

4436
useCopySelectionHelper();
45-
const {translate} = useLocalize();
4637
usePendingConciergeResponse(reportID);
47-
const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
4838
const {isOffline} = useNetwork();
4939

5040
const [report, reportResult] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
@@ -71,39 +61,19 @@ function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) {
7161
const isLoadingInitialReportActions = reportLoadingState?.isLoadingInitialReportActions;
7262
const hasOnceLoadedReportActions = reportLoadingState?.hasOnceLoadedReportActions;
7363

74-
const isInSidePanel = useIsInSidePanel();
75-
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
76-
const isConciergeSidePanel = isInSidePanel && isConciergeChatReport(report, conciergeReportID);
77-
78-
const {sessionStartTime} = useSidePanelState();
79-
80-
const hasUserSentMessage = useMemo(() => {
81-
if (!isConciergeSidePanel || !sessionStartTime) {
82-
return false;
83-
}
84-
return allReportActions.some((action) => !isCreatedAction(action) && action.actorAccountID === currentUserAccountID && action.created >= sessionStartTime);
85-
}, [isConciergeSidePanel, allReportActions, currentUserAccountID, sessionStartTime]);
86-
8764
const isReportTransactionThread = isReportTransactionThreadUtil(report);
8865

8966
const isReportArchived = useReportIsArchived(reportID);
9067
const canPerformWriteAction = !!canUserPerformWriteAction(report, isReportArchived);
9168

9269
const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP);
93-
const [visibleReportActionsData] = useOnyx(ONYXKEYS.DERIVED.VISIBLE_REPORT_ACTIONS);
9470
const reportPreviewAction = useMemo(() => getReportPreviewAction(report?.chatReportID, report?.reportID), [report?.chatReportID, report?.reportID]);
9571
const didLayout = useRef(false);
9672

9773
useEffect(() => {
9874
didLayout.current = false;
9975
}, [reportID]);
10076

101-
const {transactions: reportTransactions} = useTransactionsAndViolationsForReport(reportID);
102-
const reportTransactionIDs = useMemo(
103-
() => getAllNonDeletedTransactions(reportTransactions, allReportActions ?? []).map((transaction) => transaction.transactionID),
104-
[reportTransactions, allReportActions],
105-
);
106-
10777
useEffect(() => {
10878
// When we linked to message - we do not need to wait for initial actions - they already exists
10979
if (!reportActionIDFromRoute || !isOffline) {
@@ -115,35 +85,6 @@ function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) {
11585
// Remount the list when the deep-linked message or unread anchor changes (scroll positioning), or when the report changes.
11686
const listID = [reportID, reportActionIDFromRoute, hasOnceLoadedReportActions ? undefined : oldestUnreadReportAction?.reportActionID].join(':');
11787

118-
const visibleReportActions = useMemo(
119-
() =>
120-
reportActions.filter((reportAction) => {
121-
const passesOfflineCheck =
122-
isOffline || isDeletedParentAction(reportAction) || reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || reportAction.errors;
123-
124-
if (!passesOfflineCheck) {
125-
return false;
126-
}
127-
128-
const actionReportID = reportAction.reportID ?? reportID;
129-
if (!isReportActionVisible(reportAction, actionReportID, canPerformWriteAction, visibleReportActionsData)) {
130-
return false;
131-
}
132-
133-
if (!isIOUActionMatchingTransactionList(reportAction, reportTransactionIDs)) {
134-
return false;
135-
}
136-
137-
return true;
138-
}),
139-
[canPerformWriteAction, isOffline, reportActions, reportID, reportTransactionIDs, visibleReportActionsData],
140-
);
141-
142-
const isSingleExpenseReport = reportPreviewAction?.childMoneyRequestCount === 1;
143-
const isMissingTransactionThreadReportID = !transactionThreadReport?.reportID;
144-
const isReportDataIncomplete = isSingleExpenseReport && isMissingTransactionThreadReportID;
145-
const isMissingReportActions = visibleReportActions.length === 0;
146-
14788
const {loadOlderChats, loadNewerChats} = useLoadReportActions({
14889
reportID,
14990
reportActions,
@@ -153,25 +94,20 @@ function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) {
15394
hasNewerActions,
15495
});
15596

156-
const {
157-
filteredVisibleActions: conciergeSidePanelFilteredVisibleActions,
158-
filteredReportActions: conciergeSidePanelFilteredReportActions,
159-
showConciergeSidePanelWelcome,
160-
showFullHistory,
161-
hasPreviousMessages,
162-
handleShowPreviousMessages,
163-
} = useConciergeSidePanelReportActions({
164-
report,
165-
reportActions,
166-
visibleReportActions,
167-
isConciergeSidePanel,
168-
hasUserSentMessage,
169-
hasOlderActions,
170-
sessionStartTime,
171-
currentUserAccountID,
172-
greetingText: translate('common.concierge.sidePanelGreeting'),
173-
loadOlderChats,
174-
});
97+
const {sortedReportActions, sortedVisibleReportActions, isConciergeSidePanel, showConciergeSidePanelWelcome, showFullHistory, hasPreviousMessages, handleShowPreviousMessages} =
98+
useReportActionsVisibility({
99+
reportID,
100+
reportActions,
101+
allReportActions,
102+
canPerformWriteAction,
103+
hasOlderActions,
104+
loadOlderChats,
105+
});
106+
107+
const isSingleExpenseReport = reportPreviewAction?.childMoneyRequestCount === 1;
108+
const isMissingTransactionThreadReportID = !transactionThreadReport?.reportID;
109+
const isReportDataIncomplete = isSingleExpenseReport && isMissingTransactionThreadReportID;
110+
const isMissingReportActions = sortedVisibleReportActions.length === 0;
175111

176112
/**
177113
* Runs when the FlatList finishes laying out
@@ -243,8 +179,8 @@ function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) {
243179
parentReportAction={parentReportAction}
244180
parentReportActionForTransactionThread={parentReportActionForTransactionThread}
245181
onLayout={recordTimeToMeasureItemLayout}
246-
sortedReportActions={conciergeSidePanelFilteredReportActions}
247-
sortedVisibleReportActions={conciergeSidePanelFilteredVisibleActions}
182+
sortedReportActions={sortedReportActions}
183+
sortedVisibleReportActions={sortedVisibleReportActions}
248184
loadOlderChats={loadOlderChats}
249185
loadNewerChats={loadNewerChats}
250186
hasNewerActions={hasNewerActions}

tests/ui/ReportActionsViewTest.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ describe('ReportActionsView', () => {
182182
mockUseTransactionsAndViolationsForReport.mockReturnValue({
183183
transactions: {},
184184
violations: {},
185+
isLoaded: true,
185186
});
186187

187188
mockUsePaginatedReportActions.mockReturnValue({

0 commit comments

Comments
 (0)