Skip to content

Commit 5f8f5a4

Browse files
authored
Merge pull request #88343 from dukenv0307/fix/66407-part-7
refactor getReasonAndReportActionThatHasRedBrickRoad to use isOffline from useNetwork
2 parents 97bf507 + 25dcd2a commit 5f8f5a4

8 files changed

Lines changed: 277 additions & 13 deletions

File tree

src/libs/DebugUtils.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1487,10 +1487,21 @@ function getReasonAndReportActionForRBRInLHNRow(
14871487
transactionViolations: OnyxCollection<TransactionViolation[]>,
14881488
hasViolations: boolean,
14891489
reportErrors: Errors,
1490+
isOffline: boolean,
14901491
isArchivedReport = false,
14911492
): RBRReasonAndReportAction | null {
14921493
const {reason, reportAction} =
1493-
SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(report, chatReport, reportActions, hasViolations, reportErrors, transactions, transactionViolations, isArchivedReport) ?? {};
1494+
SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(
1495+
report,
1496+
chatReport,
1497+
reportActions,
1498+
hasViolations,
1499+
reportErrors,
1500+
transactions,
1501+
isOffline,
1502+
transactionViolations,
1503+
isArchivedReport,
1504+
) ?? {};
14941505

14951506
if (reason) {
14961507
return {reason: `debug.reasonRBR.${reason}`, reportAction};

src/libs/ReportUtils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9418,9 +9418,16 @@ function getAllReportErrors(
94189418
return allReportErrors;
94199419
}
94209420

9421-
function getReceiptUploadErrorReason(report: Report, chatReport: OnyxEntry<Report>, reportActions: OnyxEntry<ReportActions>, transactions: OnyxCollection<Transaction>) {
9421+
function getReceiptUploadErrorReason(
9422+
report: Report,
9423+
chatReport: OnyxEntry<Report>,
9424+
reportActions: OnyxEntry<ReportActions>,
9425+
transactions: OnyxCollection<Transaction>,
9426+
// We'll make it required eventually. Refactor issue: https://github.com/Expensify/App/issues/66407
9427+
isOffline?: boolean,
9428+
) {
94229429
const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID);
9423-
const transactionThreadReportAction = getOneTransactionThreadReportAction(report, chatReport, reportActions ?? []);
9430+
const transactionThreadReportAction = getOneTransactionThreadReportAction(report, chatReport, reportActions ?? [], isOffline);
94249431
if (transactionThreadReportAction) {
94259432
const transactionID = isMoneyRequestAction(transactionThreadReportAction) ? getOriginalMessage(transactionThreadReportAction)?.IOUTransactionID : undefined;
94269433
const transaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`];

src/libs/SidebarUtils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@ function getReasonAndReportActionThatHasRedBrickRoad(
695695
hasViolations: boolean,
696696
reportErrors: Errors,
697697
transactions: OnyxCollection<Transaction>,
698+
isOffline: boolean,
698699
transactionViolations?: OnyxCollection<TransactionViolation[]>,
699700
isReportArchived = false,
700701
reports?: OnyxCollection<Report>,
@@ -729,7 +730,7 @@ function getReasonAndReportActionThatHasRedBrickRoad(
729730
};
730731
}
731732

732-
return getReceiptUploadErrorReason(report, chatReport, reportActions, transactions);
733+
return getReceiptUploadErrorReason(report, chatReport, reportActions, transactions, isOffline);
733734
}
734735

735736
/**

src/libs/actions/OnyxDerived/configs/reportAttributes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
2+
import {getIsOffline} from '@libs/NetworkState';
23
import {computeReportName} from '@libs/ReportNameUtils';
34
import {generateIsEmptyReport, generateReportAttributes, hasVisibleReportFieldViolations, isArchivedReport, isValidReport} from '@libs/ReportUtils';
45
import SidebarUtils from '@libs/SidebarUtils';
@@ -89,11 +90,14 @@ export default createOnyxDerivedValueConfig({
8990
ONYXKEYS.COLLECTION.POLICY_TAGS,
9091
ONYXKEYS.COLLECTION.REPORT_METADATA,
9192
ONYXKEYS.CONCIERGE_REPORT_ID,
93+
ONYXKEYS.NETWORK,
9294
],
9395
compute: (
9496
[reports, preferredLocale, transactionViolations, reportActions, reportNameValuePairs, transactions, personalDetails, session, policies, policyTags],
9597
{currentValue, sourceValues},
9698
) => {
99+
// Read the in-memory offline state directly (NETWORK is a dependency so recompute still fires when it changes).
100+
const isOffline = getIsOffline();
97101
// Check if display names changed when personal details are updated
98102
let displayNamesChanged = false;
99103
if (hasKeyTriggeredCompute(ONYXKEYS.PERSONAL_DETAILS_LIST, sourceValues)) {
@@ -284,6 +288,7 @@ export default createOnyxDerivedValueConfig({
284288
hasAnyViolations || hasFieldViolations,
285289
reportErrors,
286290
transactions,
291+
isOffline,
287292
transactionViolations,
288293
!!isReportArchived,
289294
reports,

src/pages/Debug/Report/DebugReportPage.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Text from '@components/Text';
99
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
1010
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
1111
import useLocalize from '@hooks/useLocalize';
12+
import useNetwork from '@hooks/useNetwork';
1213
import useOnyx from '@hooks/useOnyx';
1314
import useReportIsArchived from '@hooks/useReportIsArchived';
1415
import useStyleUtils from '@hooks/useStyleUtils';
@@ -60,6 +61,7 @@ function DebugReportPage({
6061
const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`);
6162
const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION);
6263
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
64+
const {isOffline} = useNetwork();
6365
const reportAttributesSelector = useCallback((attributes: OnyxEntry<ReportAttributesDerivedValue>) => attributes?.reports?.[reportID], [reportID]);
6466
const [reportAttributes] = useOnyx(
6567
ONYXKEYS.DERIVED.REPORT_ATTRIBUTES,
@@ -95,6 +97,7 @@ function DebugReportPage({
9597
transactionViolations,
9698
hasViolations,
9799
reportAttributes?.reportErrors ?? {},
100+
isOffline,
98101
isReportArchived,
99102
) ?? {};
100103
const hasRBR = !!reasonRBR;
@@ -153,7 +156,7 @@ function DebugReportPage({
153156
: undefined,
154157
},
155158
];
156-
}, [report, transactionViolations, isReportArchived, chatReport, reportActions, transactions, reportAttributes?.reportErrors, betas, priorityMode, draftComment, translate]);
159+
}, [report, transactionViolations, isReportArchived, chatReport, reportActions, transactions, reportAttributes?.reportErrors, betas, priorityMode, draftComment, translate, isOffline]);
157160

158161
const icons = useMemoizedLazyExpensifyIcons(['Eye']);
159162

tests/unit/DebugUtilsTest.ts

Lines changed: 104 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {renderHook} from '@testing-library/react-native';
2-
import type {OnyxCollection} from 'react-native-onyx';
2+
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
33
import Onyx from 'react-native-onyx';
44
import useReportIsArchived from '@hooks/useReportIsArchived';
55
import DateUtils from '@libs/DateUtils';
@@ -1197,6 +1197,7 @@ describe('DebugUtils', () => {
11971197
undefined,
11981198
false,
11991199
{},
1200+
false,
12001201
) ?? {};
12011202
expect(reportAction).toBeUndefined();
12021203
});
@@ -1260,6 +1261,7 @@ describe('DebugUtils', () => {
12601261
undefined,
12611262
false,
12621263
{},
1264+
false,
12631265
) ?? {};
12641266
expect(reportAction).toBe(undefined);
12651267
});
@@ -1327,8 +1329,16 @@ describe('DebugUtils', () => {
13271329
};
13281330
const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS, mockTransactions);
13291331
const {reportAction} =
1330-
DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, chatReportR14932, MOCK_CHAT_REPORT_ACTIONS, mockTransactions, undefined, false, reportErrors) ??
1331-
{};
1332+
DebugUtils.getReasonAndReportActionForRBRInLHNRow(
1333+
MOCK_CHAT_REPORT,
1334+
chatReportR14932,
1335+
MOCK_CHAT_REPORT_ACTIONS,
1336+
mockTransactions,
1337+
undefined,
1338+
false,
1339+
reportErrors,
1340+
false,
1341+
) ?? {};
13321342
expect(reportAction).toMatchObject(MOCK_CHAT_REPORT_ACTIONS['1']);
13331343
});
13341344
it('returns correct report action which is a split bill and has an error', async () => {
@@ -1400,7 +1410,8 @@ describe('DebugUtils', () => {
14001410
};
14011411
const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS, mockTransactions);
14021412
const {reportAction} =
1403-
DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, chatReportR14932, MOCK_REPORT_ACTIONS, mockTransactions, undefined, false, reportErrors) ?? {};
1413+
DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, chatReportR14932, MOCK_REPORT_ACTIONS, mockTransactions, undefined, false, reportErrors, false) ??
1414+
{};
14041415
expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']);
14051416
});
14061417
});
@@ -1458,6 +1469,7 @@ describe('DebugUtils', () => {
14581469
undefined,
14591470
false,
14601471
reportErrors,
1472+
false,
14611473
) ?? {};
14621474
expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['1']);
14631475
});
@@ -1486,7 +1498,8 @@ describe('DebugUtils', () => {
14861498

14871499
const reportErrors = getAllReportErrors(mockedReport, mockedReportActions, sharedAllTransactions);
14881500
const {reason} =
1489-
DebugUtils.getReasonAndReportActionForRBRInLHNRow(mockedReport, chatReportR14932, mockedReportActions, sharedAllTransactions, undefined, false, reportErrors) ?? {};
1501+
DebugUtils.getReasonAndReportActionForRBRInLHNRow(mockedReport, chatReportR14932, mockedReportActions, sharedAllTransactions, undefined, false, reportErrors, false) ??
1502+
{};
14901503
expect(reason).toBe('debug.reasonRBR.hasErrors');
14911504
});
14921505
it('returns correct reason when there are violations', () => {
@@ -1501,6 +1514,7 @@ describe('DebugUtils', () => {
15011514
undefined,
15021515
true,
15031516
{},
1517+
false,
15041518
) ?? {};
15051519
expect(reason).toBe('debug.reasonRBR.hasViolations');
15061520
});
@@ -1516,6 +1530,7 @@ describe('DebugUtils', () => {
15161530
undefined,
15171531
true,
15181532
{},
1533+
false,
15191534
true,
15201535
) ?? {};
15211536
expect(reason).toBe(undefined);
@@ -1572,9 +1587,92 @@ describe('DebugUtils', () => {
15721587
reportID: '1',
15731588
} as Transaction,
15741589
};
1575-
const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, chatReportR14932, {}, violationTransactions, transactionViolations, false, {}) ?? {};
1590+
const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, chatReportR14932, {}, violationTransactions, transactionViolations, false, {}, false) ?? {};
15761591
expect(reason).toBe('debug.reasonRBR.hasTransactionThreadViolations');
15771592
});
1593+
it('forwards isOffline through to SidebarUtils so the live IOU transaction-thread receipt error surfaces only when isOffline=false excludes the deleted pending-delete action', () => {
1594+
// Given: an expense report with two IOU actions — one live (with a receipt-errored transaction) and one deleted-with-pending-delete.
1595+
const OFFLINE_EXPENSE_REPORT: Report = {
1596+
reportID: '1',
1597+
type: CONST.REPORT.TYPE.EXPENSE,
1598+
chatReportID: '2',
1599+
};
1600+
const OFFLINE_CHAT_REPORT: Report = {
1601+
reportID: '2',
1602+
};
1603+
const liveTransactionID = 'tx-debug-live';
1604+
const deletedTransactionID = 'tx-debug-deleted';
1605+
const OFFLINE_REPORT_ACTIONS: OnyxEntry<ReportActions> = {
1606+
// eslint-disable-next-line @typescript-eslint/naming-convention
1607+
'1': {
1608+
reportActionID: '1',
1609+
actionName: CONST.REPORT.ACTIONS.TYPE.IOU,
1610+
actorAccountID: 12345,
1611+
created: '2024-08-08 18:20:44.171',
1612+
message: [{type: 'TEXT', text: 'live'}],
1613+
originalMessage: {
1614+
type: CONST.IOU.REPORT_ACTION_TYPE.CREATE,
1615+
IOUTransactionID: liveTransactionID,
1616+
amount: 10,
1617+
currency: CONST.CURRENCY.USD,
1618+
},
1619+
} as ReportAction,
1620+
// eslint-disable-next-line @typescript-eslint/naming-convention
1621+
'2': {
1622+
reportActionID: '2',
1623+
actionName: CONST.REPORT.ACTIONS.TYPE.IOU,
1624+
actorAccountID: 12345,
1625+
created: '2024-08-08 18:25:44.171',
1626+
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
1627+
message: [{type: 'TEXT', text: '', html: ''}],
1628+
originalMessage: {
1629+
type: CONST.IOU.REPORT_ACTION_TYPE.CREATE,
1630+
IOUTransactionID: deletedTransactionID,
1631+
amount: 20,
1632+
currency: CONST.CURRENCY.USD,
1633+
},
1634+
} as ReportAction,
1635+
};
1636+
const OFFLINE_TRANSACTIONS: OnyxCollection<Transaction> = {
1637+
[`${ONYXKEYS.COLLECTION.TRANSACTION}${liveTransactionID}`]: {
1638+
transactionID: liveTransactionID,
1639+
amount: 10,
1640+
errors: {
1641+
someErrorKey: {error: CONST.IOU.RECEIPT_ERROR},
1642+
},
1643+
} as unknown as Transaction,
1644+
[`${ONYXKEYS.COLLECTION.TRANSACTION}${deletedTransactionID}`]: {
1645+
transactionID: deletedTransactionID,
1646+
amount: 20,
1647+
} as unknown as Transaction,
1648+
};
1649+
1650+
const offline = DebugUtils.getReasonAndReportActionForRBRInLHNRow(
1651+
OFFLINE_EXPENSE_REPORT,
1652+
OFFLINE_CHAT_REPORT,
1653+
OFFLINE_REPORT_ACTIONS,
1654+
OFFLINE_TRANSACTIONS,
1655+
{},
1656+
false,
1657+
{},
1658+
true,
1659+
);
1660+
const online = DebugUtils.getReasonAndReportActionForRBRInLHNRow(
1661+
OFFLINE_EXPENSE_REPORT,
1662+
OFFLINE_CHAT_REPORT,
1663+
OFFLINE_REPORT_ACTIONS,
1664+
OFFLINE_TRANSACTIONS,
1665+
{},
1666+
false,
1667+
{},
1668+
false,
1669+
);
1670+
1671+
// Online: deleted pending-delete is skipped → 1 IOU thread → receipt error surfaces.
1672+
expect(online?.reason).toBe('debug.reasonRBR.hasErrors');
1673+
// Offline: deleted pending-delete is counted → 2 IOU actions → no single thread → no receipt error via that path.
1674+
expect(offline).toBeNull();
1675+
});
15781676
});
15791677
});
15801678
});

tests/unit/OnyxDerivedTest.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ describe('OnyxDerived', () => {
126126
const transaction = createRandomTransaction(1);
127127

128128
// When the report attributes are recomputed with both report and transaction updates
129-
reportAttributes.compute([reports, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined], {});
129+
reportAttributes.compute([reports, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined], {});
130130
const reportAttributesComputedValue = reportAttributes.compute(
131-
[reports, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined],
131+
[reports, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined],
132132
{
133133
sourceValues: {
134134
[ONYXKEYS.COLLECTION.REPORT]: {

0 commit comments

Comments
 (0)