Skip to content

Commit 0d1df3c

Browse files
authored
Merge pull request Expensify#66067 from software-mansion-labs/fix/help-panel-expenses
Fix the logic used to display help content for IOU expenses
2 parents 427a1f2 + ca2df0f commit 0d1df3c

4 files changed

Lines changed: 50 additions & 32 deletions

File tree

src/components/SidePanel/HelpComponents/HelpContent.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
1414
import useRootNavigationState from '@hooks/useRootNavigationState';
1515
import useThemeStyles from '@hooks/useThemeStyles';
1616
import {normalizedConfigs} from '@libs/Navigation/linkingConfig/config';
17-
import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
17+
import {getOneTransactionThreadReportAction, getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
1818
import {getHelpPaneReportType} from '@libs/ReportUtils';
1919
import {getExpenseType} from '@libs/TransactionUtils';
2020
import CONST from '@src/CONST';
@@ -43,14 +43,22 @@ function HelpContent({closeSidePanel}: HelpContentProps) {
4343
});
4444

4545
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${params?.reportID || String(CONST.DEFAULT_NUMBER_ID)}`, {canBeMissing: true});
46-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
47-
const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`, {
48-
canEvict: false,
46+
const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`, {canBeMissing: true});
47+
const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`, {canBeMissing: true});
48+
const [parentIOUReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`, {
4949
canBeMissing: true,
50+
selector: (actions) =>
51+
Object.values(actions ?? {})
52+
.filter((action) => action.reportActionID === report?.parentReportActionID)
53+
.filter(isMoneyRequestAction)
54+
.at(0),
5055
});
51-
const parentReportAction = report?.parentReportActionID ? parentReportActions?.[report.parentReportActionID] : undefined;
52-
const linkedTransactionID = useMemo(() => (isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction)?.IOUTransactionID : undefined), [parentReportAction]);
53-
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${linkedTransactionID}`, {canBeMissing: true});
56+
57+
const transactionID = useMemo(() => {
58+
const transactionThreadReportAction = getOneTransactionThreadReportAction(report, chatReport, reportActions ?? []);
59+
return getOriginalMessage(parentIOUReportAction ?? transactionThreadReportAction)?.IOUTransactionID;
60+
}, [report, chatReport, reportActions, parentIOUReportAction]);
61+
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {canBeMissing: true});
5462

5563
const route = useMemo(() => {
5664
const path = normalizedConfigs[routeName]?.path;
@@ -61,13 +69,14 @@ function HelpContent({closeSidePanel}: HelpContentProps) {
6169

6270
const cleanedPath = path.replaceAll('?', '');
6371
const expenseType = getExpenseType(transaction);
72+
const reportType = getHelpPaneReportType(report);
6473

65-
if (expenseType) {
74+
if (expenseType && reportType !== CONST.REPORT.HELP_TYPE.EXPENSE_REPORT) {
6675
return cleanedPath.replaceAll(':reportID', `:${CONST.REPORT.HELP_TYPE.EXPENSE}/:${expenseType}`);
6776
}
6877

69-
if (report) {
70-
return cleanedPath.replaceAll(':reportID', `:${getHelpPaneReportType(report)}`);
78+
if (reportType) {
79+
return cleanedPath.replaceAll(':reportID', `:${reportType}`);
7180
}
7281

7382
return cleanedPath;

src/hooks/useRootNavigationState.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ function useRootNavigationState<T>(selector: Selector<T>): T {
2020
});
2121

2222
useEffect(() => {
23-
const unsubscribe = navigationRef.addListener('state', (e) => {
24-
setResult(selectorRef.current(e.data.state as NavigationState));
23+
const unsubscribe = navigationRef.addListener('state', () => {
24+
// State from the event data may be incomplete. (defined params but no nested state for the route)
25+
setResult(selectorRef.current(navigationRef.getRootState()));
2526
});
2627

2728
return unsubscribe;

src/libs/ReportActionsUtils.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,7 @@ function isTagModificationAction(actionName: string): boolean {
11921192
* Used for Send Money flow, which is a special case where we have no IOU create action and only one IOU pay action.
11931193
* In other reports, pay actions do not count as a transactions, but this is an exception to this rule.
11941194
*/
1195-
function getSendMoneyFlowOneTransactionThreadID(actions: OnyxEntry<ReportActions> | ReportAction[], chatReport: OnyxEntry<Report>) {
1195+
function getSendMoneyFlowAction(actions: OnyxEntry<ReportActions> | ReportAction[], chatReport: OnyxEntry<Report>): ReportAction<'IOU'> | undefined {
11961196
if (!chatReport) {
11971197
return undefined;
11981198
}
@@ -1212,7 +1212,7 @@ function getSendMoneyFlowOneTransactionThreadID(actions: OnyxEntry<ReportActions
12121212
// ...and can only be triggered on DM chats
12131213
const isDM = type === CONST.REPORT.TYPE.CHAT && !chatType && !(parentReportID && parentReportActionID);
12141214

1215-
return isFirstActionPay && isDM ? iouActions.at(0)?.childReportID : undefined;
1215+
return isFirstActionPay && isDM ? iouActions.at(0) : undefined;
12161216
}
12171217

12181218
/** Whether action has no linked report by design */
@@ -1247,16 +1247,16 @@ const isIOUActionMatchingTransactionList = (
12471247
};
12481248

12491249
/**
1250-
* Gets the reportID for the transaction thread associated with a report by iterating over the reportActions and identifying the IOU report actions.
1251-
* Returns a reportID if there is exactly one transaction thread for the report, and null otherwise.
1250+
* Gets the report action for the transaction thread associated with a report by iterating over the reportActions and identifying the IOU report actions.
1251+
* Returns a report action if there is exactly one transaction thread for the report, and undefined otherwise.
12521252
*/
1253-
function getOneTransactionThreadReportID(
1253+
function getOneTransactionThreadReportAction(
12541254
report: OnyxEntry<Report>,
12551255
chatReport: OnyxEntry<Report>,
12561256
reportActions: OnyxEntry<ReportActions> | ReportAction[],
12571257
isOffline: boolean | undefined = undefined,
12581258
reportTransactionIDs?: string[],
1259-
): string | undefined {
1259+
): ReportAction<'IOU'> | undefined {
12601260
// If the report is not an IOU, Expense report, or Invoice, it shouldn't be treated as one-transaction report.
12611261
if (report?.type !== CONST.REPORT.TYPE.IOU && report?.type !== CONST.REPORT.TYPE.EXPENSE && report?.type !== CONST.REPORT.TYPE.INVOICE) {
12621262
return;
@@ -1267,10 +1267,10 @@ function getOneTransactionThreadReportID(
12671267
return;
12681268
}
12691269

1270-
const sendMoneyFlowID = getSendMoneyFlowOneTransactionThreadID(reportActions, chatReport);
1270+
const sendMoneyFlow = getSendMoneyFlowAction(reportActions, chatReport);
12711271

1272-
if (sendMoneyFlowID) {
1273-
return sendMoneyFlowID;
1272+
if (sendMoneyFlow?.childReportID) {
1273+
return sendMoneyFlow;
12741274
}
12751275

12761276
const iouRequestActions = [];
@@ -1314,8 +1314,15 @@ function getOneTransactionThreadReportID(
13141314
return;
13151315
}
13161316

1317-
// Ensure we have a childReportID associated with the IOU report action
1318-
return singleAction?.childReportID;
1317+
return singleAction;
1318+
}
1319+
1320+
/**
1321+
* Gets the reportID for the transaction thread associated with a report by iterating over the reportActions and identifying the IOU report actions.
1322+
* Returns a reportID if there is exactly one transaction thread for the report, and undefined otherwise.
1323+
*/
1324+
function getOneTransactionThreadReportID(...args: Parameters<typeof getOneTransactionThreadReportAction>): string | undefined {
1325+
return getOneTransactionThreadReportAction(...args)?.childReportID;
13191326
}
13201327

13211328
/**
@@ -2961,6 +2968,7 @@ export {
29612968
getMessageOfOldDotReportAction,
29622969
getMostRecentIOURequestActionID,
29632970
getNumberOfMoneyRequests,
2971+
getOneTransactionThreadReportAction,
29642972
getOneTransactionThreadReportID,
29652973
getOriginalMessage,
29662974
getAddedApprovalRuleMessage,
@@ -3083,7 +3091,7 @@ export {
30833091
getWorkspaceDescriptionUpdatedMessage,
30843092
getWorkspaceReportFieldAddMessage,
30853093
getWorkspaceCustomUnitRateAddedMessage,
3086-
getSendMoneyFlowOneTransactionThreadID,
3094+
getSendMoneyFlowAction,
30873095
getWorkspaceTagUpdateMessage,
30883096
getWorkspaceReportFieldUpdateMessage,
30893097
getWorkspaceReportFieldDeleteMessage,

tests/unit/ReportActionsUtilsTest.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {actionR14932 as mockIOUAction, originalMessageR14932 as mockOriginalMess
66
import {chatReportR14932 as mockChatReport, iouReportR14932 as mockIOUReport} from '../../__mocks__/reportData/reports';
77
import CONST from '../../src/CONST';
88
import * as ReportActionsUtils from '../../src/libs/ReportActionsUtils';
9-
import {getOneTransactionThreadReportID, getOriginalMessage, getSendMoneyFlowOneTransactionThreadID, isIOUActionMatchingTransactionList} from '../../src/libs/ReportActionsUtils';
9+
import {getOneTransactionThreadReportID, getOriginalMessage, getSendMoneyFlowAction, isIOUActionMatchingTransactionList} from '../../src/libs/ReportActionsUtils';
1010
import ONYXKEYS from '../../src/ONYXKEYS';
1111
import type {Report, ReportAction} from '../../src/types/onyx';
1212
import {createRandomReport} from '../utils/collections/reports';
@@ -856,7 +856,7 @@ describe('ReportActionsUtils', () => {
856856
});
857857
});
858858

859-
describe('getSendMoneyFlowOneTransactionThreadID', () => {
859+
describe('getSendMoneyFlowAction', () => {
860860
const mockChatReportID = `${ONYXKEYS.COLLECTION.REPORT}REPORT` as const;
861861
const mockDMChatReportID = `${ONYXKEYS.COLLECTION.REPORT}REPORT_DM` as const;
862862
const childReportID = `${ONYXKEYS.COLLECTION.REPORT}childReport123` as const;
@@ -891,27 +891,27 @@ describe('ReportActionsUtils', () => {
891891
};
892892

893893
it('should return undefined for a single non-IOU action', () => {
894-
expect(getSendMoneyFlowOneTransactionThreadID([nonIOUAction], mockedReports[mockDMChatReportID])).toBeUndefined();
894+
expect(getSendMoneyFlowAction([nonIOUAction], mockedReports[mockDMChatReportID])?.childReportID).toBeUndefined();
895895
});
896896

897897
it('should return undefined for multiple IOU actions regardless of type', () => {
898-
expect(getSendMoneyFlowOneTransactionThreadID([payAction, payAction], mockedReports[mockDMChatReportID])).toBeUndefined();
898+
expect(getSendMoneyFlowAction([payAction, payAction], mockedReports[mockDMChatReportID])?.childReportID).toBeUndefined();
899899
});
900900

901901
it('should return undefined for a single IOU action that is not `Pay`', () => {
902-
expect(getSendMoneyFlowOneTransactionThreadID([createAction], mockedReports[mockDMChatReportID])).toBeUndefined();
902+
expect(getSendMoneyFlowAction([createAction], mockedReports[mockDMChatReportID])?.childReportID).toBeUndefined();
903903
});
904904

905905
it('should return the appropriate childReportID for a valid single `Pay` IOU action in DM chat', () => {
906-
expect(getSendMoneyFlowOneTransactionThreadID([payAction], mockedReports[mockDMChatReportID])).toEqual(childReportID);
906+
expect(getSendMoneyFlowAction([payAction], mockedReports[mockDMChatReportID])?.childReportID).toEqual(childReportID);
907907
});
908908

909909
it('should return undefined for a valid single `Pay` IOU action in a chat that is not DM', () => {
910-
expect(getSendMoneyFlowOneTransactionThreadID([payAction], mockedReports[mockChatReportID])).toBeUndefined();
910+
expect(getSendMoneyFlowAction([payAction], mockedReports[mockChatReportID])?.childReportID).toBeUndefined();
911911
});
912912

913913
it('should return undefined for a valid `Pay` IOU action in DM chat that has also a create IOU action', () => {
914-
expect(getSendMoneyFlowOneTransactionThreadID([payAction, createAction], mockedReports[mockDMChatReportID])).toBeUndefined();
914+
expect(getSendMoneyFlowAction([payAction, createAction], mockedReports[mockDMChatReportID])?.childReportID).toBeUndefined();
915915
});
916916
});
917917

0 commit comments

Comments
 (0)