Skip to content

Commit 6637833

Browse files
authored
Merge pull request Expensify#65744 from callstack-internal/Update-the-transaction-thread-report-loading-to-show-skeleton
Update the transaction thread report loading to show skeleton
2 parents c061e81 + 6b86334 commit 6637833

9 files changed

Lines changed: 346 additions & 210 deletions

File tree

src/ROUTES.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,16 @@ const ROUTES = {
8787
getRoute: ({
8888
reportID,
8989
reportActionID,
90+
parentReportID,
91+
parentReportActionID,
9092
backTo,
9193
moneyRequestReportActionID,
9294
transactionID,
9395
}: {
9496
reportID: string | undefined;
9597
reportActionID?: string;
98+
parentReportID?: string;
99+
parentReportActionID?: string;
96100
backTo?: string;
97101
moneyRequestReportActionID?: string;
98102
transactionID?: string;
@@ -112,6 +116,14 @@ const ROUTES = {
112116
queryParams.push(`moneyRequestReportActionID=${moneyRequestReportActionID}`);
113117
}
114118

119+
if (parentReportID) {
120+
queryParams.push(`parentReportID=${parentReportID}`);
121+
}
122+
123+
if (parentReportActionID) {
124+
queryParams.push(`parentReportActionID=${parentReportActionID}`);
125+
}
126+
115127
const queryString = queryParams.length > 0 ? (`${baseRoute}?${queryParams.join('&')}` as const) : baseRoute;
116128
return getUrlWithBackToParam(queryString, backTo);
117129
},

src/components/MoneyRequestHeader.tsx

Lines changed: 169 additions & 97 deletions
Large diffs are not rendered by default.

src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,9 @@ function MoneyRequestReportTransactionList({
200200
// to display prev/next arrows in RHP for navigation
201201
const sortedSiblingTransactionReportIDs = getThreadReportIDsForTransactions(reportActions, sortedTransactions);
202202
setActiveTransactionThreadIDs(sortedSiblingTransactionReportIDs).then(() => {
203-
Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: reportIDToNavigate, backTo}));
203+
Navigation.navigate(
204+
ROUTES.SEARCH_REPORT.getRoute({reportID: reportIDToNavigate, backTo, parentReportID: activeTransaction.reportID, parentReportActionID: iouAction?.reportActionID}),
205+
);
204206
});
205207
},
206208
[reportActions, sortedTransactions],

src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,37 @@ import getEmptyArray from '@src/types/utils/getEmptyArray';
1212

1313
type MoneyRequestReportRHPNavigationButtonsProps = {
1414
currentReportID: string;
15+
16+
/** The ID of the parent report that contains the transaction threads */
17+
parentReportID?: string;
18+
19+
/** The route to navigate back to when the user clicks on the back button */
20+
backTo?: string;
1521
};
1622

17-
function MoneyRequestReportTransactionsNavigation({currentReportID}: MoneyRequestReportRHPNavigationButtonsProps) {
23+
function MoneyRequestReportTransactionsNavigation({currentReportID, parentReportID, backTo}: MoneyRequestReportRHPNavigationButtonsProps) {
1824
const [reportIDsList = getEmptyArray<string>()] = useOnyx(ONYXKEYS.TRANSACTION_THREAD_NAVIGATION_REPORT_IDS, {
1925
canBeMissing: true,
2026
});
27+
const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, {
28+
canBeMissing: true,
29+
selector: (reportActions) => Object.values(reportActions ?? {}),
30+
});
2131

22-
const {prevReportID, nextReportID} = useMemo(() => {
32+
const {prevReportID, prevParentReportActionID, nextReportID, nextParentReportActionID} = useMemo(() => {
2333
if (!reportIDsList || reportIDsList.length < 2) {
24-
return {prevReportID: undefined, nextReportID: undefined};
34+
return {prevReportID: undefined, prevParentReportActionID: undefined, nextReportID: undefined, nextParentReportActionID: undefined};
2535
}
2636

2737
const currentReportIndex = reportIDsList.findIndex((id) => id === currentReportID);
2838

2939
const prevID = currentReportIndex > 0 ? reportIDsList.at(currentReportIndex - 1) : undefined;
3040
const nextID = currentReportIndex <= reportIDsList.length - 1 ? reportIDsList.at(currentReportIndex + 1) : undefined;
41+
const prevReportActionID = currentReportIndex > 0 ? parentReportActions?.find((action) => action.childReportID === prevID)?.reportActionID : undefined;
42+
const nextReportActionID = currentReportIndex <= reportIDsList.length - 1 ? parentReportActions?.find((action) => action.childReportID === nextID)?.reportActionID : undefined;
3143

32-
return {prevReportID: prevID, nextReportID: nextID};
33-
}, [currentReportID, reportIDsList]);
44+
return {prevReportID: prevID, prevParentReportActionID: prevReportActionID, nextReportID: nextID, nextParentReportActionID: nextReportActionID};
45+
}, [currentReportID, parentReportActions, reportIDsList]);
3446

3547
/**
3648
* We clear the sibling transactionThreadIDs when unmounting this component
@@ -55,14 +67,22 @@ function MoneyRequestReportTransactionsNavigation({currentReportID}: MoneyReques
5567
isPrevButtonDisabled={!prevReportID}
5668
isNextButtonDisabled={!nextReportID}
5769
onNext={(e) => {
58-
const backTo = Navigation.getActiveRoute();
5970
e?.preventDefault();
60-
Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: nextReportID, backTo}), {forceReplace: true});
71+
Navigation.navigate(
72+
ROUTES.SEARCH_REPORT.getRoute({reportID: nextReportID, parentReportActionID: nextParentReportActionID, parentReportID, backTo: backTo ?? Navigation.getActiveRoute()}),
73+
{
74+
forceReplace: true,
75+
},
76+
);
6177
}}
6278
onPrevious={(e) => {
63-
const backTo = Navigation.getActiveRoute();
6479
e?.preventDefault();
65-
Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: prevReportID, backTo}), {forceReplace: true});
80+
Navigation.navigate(
81+
ROUTES.SEARCH_REPORT.getRoute({reportID: prevReportID, parentReportActionID: prevParentReportActionID, parentReportID, backTo: backTo ?? Navigation.getActiveRoute()}),
82+
{
83+
forceReplace: true,
84+
},
85+
);
6686
}}
6787
/>
6888
);

src/components/MoneyRequestReportView/MoneyRequestReportView.tsx

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -133,20 +133,22 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe
133133
const isEmptyTransactionReport = visibleTransactions && visibleTransactions.length === 0 && transactionThreadReportID === undefined;
134134
const shouldDisplayMoneyRequestActionsList = !!isEmptyTransactionReport || shouldDisplayReportTableView(report, visibleTransactions ?? []);
135135

136+
const handleBackButtonPress = useCallback(() => {
137+
if (!backToRoute) {
138+
goBackFromSearchMoneyRequest();
139+
return;
140+
}
141+
Navigation.goBack(backToRoute);
142+
}, [backToRoute]);
143+
136144
const reportHeaderView = useMemo(
137145
() =>
138146
isTransactionThreadView ? (
139147
<MoneyRequestHeader
140148
report={report}
141149
policy={policy}
142150
parentReportAction={parentReportAction}
143-
onBackButtonPress={() => {
144-
if (!backToRoute) {
145-
goBackFromSearchMoneyRequest();
146-
return;
147-
}
148-
Navigation.goBack(backToRoute);
149-
}}
151+
onBackButtonPress={handleBackButtonPress}
150152
/>
151153
) : (
152154
<MoneyReportHeader
@@ -156,16 +158,10 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe
156158
transactionThreadReportID={transactionThreadReportID}
157159
isLoadingInitialReportActions={isLoadingInitialReportActions}
158160
shouldDisplayBackButton
159-
onBackButtonPress={() => {
160-
if (!backToRoute) {
161-
goBackFromSearchMoneyRequest();
162-
return;
163-
}
164-
Navigation.goBack(backToRoute);
165-
}}
161+
onBackButtonPress={handleBackButtonPress}
166162
/>
167163
),
168-
[backToRoute, isLoadingInitialReportActions, isTransactionThreadView, parentReportAction, policy, report, reportActions, transactionThreadReportID],
164+
[handleBackButtonPress, isLoadingInitialReportActions, isTransactionThreadView, parentReportAction, policy, report, reportActions, transactionThreadReportID],
169165
);
170166

171167
if (!!(isLoadingInitialReportActions && reportActions.length === 0 && !isOffline) || shouldWaitForTransactions) {

src/libs/Navigation/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,6 +1768,8 @@ type ReportsSplitNavigatorParamList = {
17681768
[SCREENS.REPORT]: {
17691769
reportID: string;
17701770
reportActionID?: string;
1771+
parentReportID?: string;
1772+
parentReportActionID?: string;
17711773
openOnAdminRoom?: boolean;
17721774
referrer?: string;
17731775
backTo?: Routes;

src/pages/home/ReportScreen.tsx

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
137137
const {translate} = useLocalize();
138138
const reportIDFromRoute = getNonEmptyStringOnyxID(route.params?.reportID);
139139
const reportActionIDFromRoute = route?.params?.reportActionID;
140+
const parentReportIDFromRoute = route?.params?.parentReportID;
141+
const parentReportActionIDFromRoute = route?.params?.parentReportActionID;
140142
const isFocused = useIsFocused();
141143
const prevIsFocused = usePrevious(isFocused);
142144
const firstRenderRef = useRef(true);
@@ -157,9 +159,9 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
157159
const [reportNameValuePairsOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportIDFromRoute}`, {allowStaleData: true, canBeMissing: true});
158160
const [reportMetadata = defaultReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportIDFromRoute}`, {canBeMissing: true, allowStaleData: true});
159161
const [policies = getEmptyObject<NonNullable<OnyxCollection<OnyxTypes.Policy>>>()] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {allowStaleData: true, canBeMissing: false});
160-
const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(reportOnyx?.parentReportID)}`, {
162+
const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(reportOnyx?.parentReportID ?? parentReportIDFromRoute)}`, {
161163
canEvict: false,
162-
selector: (parentReportActions) => getParentReportAction(parentReportActions, reportOnyx?.parentReportActionID),
164+
selector: (parentReportActions) => getParentReportAction(parentReportActions, reportOnyx?.parentReportActionID ?? parentReportActionIDFromRoute),
163165
canBeMissing: true,
164166
});
165167
const deletedParentAction = isDeletedParentAction(parentReportAction);
@@ -205,6 +207,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
205207
return '';
206208
}, [accountManagerReportID, accountManagerReport, personalDetails, translate]);
207209

210+
const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportIDFromRoute}`, {canBeMissing: true});
211+
208212
/**
209213
* Create a lightweight Report so as to keep the re-rendering as light as possible by
210214
* passing in only the required props.
@@ -215,48 +219,58 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
215219
*/
216220
const report = useMemo(
217221
() =>
218-
reportOnyx && {
219-
lastReadTime: reportOnyx.lastReadTime,
220-
reportID: reportOnyx.reportID,
221-
policyID: reportOnyx.policyID,
222-
lastVisibleActionCreated: reportOnyx.lastVisibleActionCreated,
223-
statusNum: reportOnyx.statusNum,
224-
stateNum: reportOnyx.stateNum,
225-
writeCapability: reportOnyx.writeCapability,
226-
type: reportOnyx.type,
227-
errorFields: reportOnyx.errorFields,
228-
parentReportID: reportOnyx.parentReportID,
229-
parentReportActionID: reportOnyx.parentReportActionID,
230-
chatType: reportOnyx.chatType,
231-
pendingFields: reportOnyx.pendingFields,
232-
isDeletedParentAction: reportOnyx.isDeletedParentAction,
233-
reportName: reportOnyx.reportName,
234-
description: reportOnyx.description,
235-
managerID: reportOnyx.managerID,
236-
total: reportOnyx.total,
237-
nonReimbursableTotal: reportOnyx.nonReimbursableTotal,
238-
fieldList: reportOnyx.fieldList,
239-
ownerAccountID: reportOnyx.ownerAccountID,
240-
currency: reportOnyx.currency,
241-
unheldTotal: reportOnyx.unheldTotal,
242-
unheldNonReimbursableTotal: reportOnyx.unheldNonReimbursableTotal,
243-
participants: reportOnyx.participants,
244-
isWaitingOnBankAccount: reportOnyx.isWaitingOnBankAccount,
245-
iouReportID: reportOnyx.iouReportID,
246-
isOwnPolicyExpenseChat: reportOnyx.isOwnPolicyExpenseChat,
247-
isPinned: reportOnyx.isPinned,
248-
chatReportID: reportOnyx.chatReportID,
249-
visibility: reportOnyx.visibility,
250-
oldPolicyName: reportOnyx.oldPolicyName,
251-
policyName: reportOnyx.policyName,
252-
private_isArchived: reportNameValuePairsOnyx?.private_isArchived,
253-
lastMentionedTime: reportOnyx.lastMentionedTime,
254-
avatarUrl: reportOnyx.avatarUrl,
255-
permissions,
256-
invoiceReceiver: reportOnyx.invoiceReceiver,
257-
policyAvatar: reportOnyx.policyAvatar,
258-
},
259-
[reportOnyx, reportNameValuePairsOnyx, permissions],
222+
// This is required to get the transaction data from the parent report, to render the optimistic transaction thread
223+
parentReportIDFromRoute && parentReportActionIDFromRoute
224+
? ({
225+
...reportOnyx,
226+
reportID: reportIDFromRoute,
227+
parentReportID: parentReportIDFromRoute,
228+
parentReportActionID: parentReportActionIDFromRoute,
229+
policyID: parentReport?.policyID,
230+
type: CONST.REPORT.TYPE.CHAT,
231+
} as OnyxTypes.Report)
232+
: reportOnyx && {
233+
lastReadTime: reportOnyx.lastReadTime,
234+
reportID: reportOnyx.reportID,
235+
policyID: reportOnyx.policyID,
236+
lastVisibleActionCreated: reportOnyx.lastVisibleActionCreated,
237+
statusNum: reportOnyx.statusNum,
238+
stateNum: reportOnyx.stateNum,
239+
writeCapability: reportOnyx.writeCapability,
240+
type: reportOnyx.type,
241+
errorFields: reportOnyx.errorFields,
242+
parentReportID: reportOnyx.parentReportID,
243+
parentReportActionID: reportOnyx.parentReportActionID,
244+
chatType: reportOnyx.chatType,
245+
pendingFields: reportOnyx.pendingFields,
246+
isDeletedParentAction: reportOnyx.isDeletedParentAction,
247+
reportName: reportOnyx.reportName,
248+
description: reportOnyx.description,
249+
managerID: reportOnyx.managerID,
250+
total: reportOnyx.total,
251+
nonReimbursableTotal: reportOnyx.nonReimbursableTotal,
252+
fieldList: reportOnyx.fieldList,
253+
ownerAccountID: reportOnyx.ownerAccountID,
254+
currency: reportOnyx.currency,
255+
unheldTotal: reportOnyx.unheldTotal,
256+
unheldNonReimbursableTotal: reportOnyx.unheldNonReimbursableTotal,
257+
participants: reportOnyx.participants,
258+
isWaitingOnBankAccount: reportOnyx.isWaitingOnBankAccount,
259+
iouReportID: reportOnyx.iouReportID,
260+
isOwnPolicyExpenseChat: reportOnyx.isOwnPolicyExpenseChat,
261+
isPinned: reportOnyx.isPinned,
262+
chatReportID: reportOnyx.chatReportID,
263+
visibility: reportOnyx.visibility,
264+
oldPolicyName: reportOnyx.oldPolicyName,
265+
policyName: reportOnyx.policyName,
266+
private_isArchived: reportNameValuePairsOnyx?.private_isArchived,
267+
lastMentionedTime: reportOnyx.lastMentionedTime,
268+
avatarUrl: reportOnyx.avatarUrl,
269+
permissions,
270+
invoiceReceiver: reportOnyx.invoiceReceiver,
271+
policyAvatar: reportOnyx.policyAvatar,
272+
},
273+
[parentReportIDFromRoute, parentReportActionIDFromRoute, reportOnyx, reportIDFromRoute, parentReport?.policyID, reportNameValuePairsOnyx?.private_isArchived, permissions],
260274
);
261275
const reportID = report?.reportID;
262276

@@ -308,7 +322,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
308322
});
309323
const combinedReportActions = getCombinedReportActions(reportActions, transactionThreadReportID ?? null, Object.values(transactionThreadReportActions));
310324
const lastReportAction = [...combinedReportActions, parentReportAction].find((action) => canEditReportAction(action) && !isMoneyRequestAction(action));
311-
const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`];
325+
const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID ?? parentReport?.policyID}`];
312326
const isTopMostReportId = currentReportIDValue?.currentReportID === reportIDFromRoute;
313327
const didSubscribeToReportLeavingEvents = useRef(false);
314328
const isTransactionThreadView = isReportTransactionThread(report);
@@ -370,6 +384,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
370384
<MoneyRequestHeader
371385
report={report}
372386
policy={policy}
387+
backTo={backTo}
373388
parentReportAction={parentReportAction}
374389
onBackButtonPress={onBackButtonPress}
375390
/>
@@ -817,11 +832,12 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
817832
<ReportActionsView
818833
report={report}
819834
reportActions={reportActions}
820-
isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions}
835+
isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions && !isTransactionThreadView}
821836
hasNewerActions={hasNewerActions}
822837
hasOlderActions={hasOlderActions}
823838
parentReportAction={parentReportAction}
824839
transactionThreadReportID={transactionThreadReportID}
840+
isReportTransactionThread={isTransactionThreadView}
825841
/>
826842
) : null}
827843
{!!report && shouldDisplayMoneyRequestActionsList && !shouldWaitForTransactions ? (

0 commit comments

Comments
 (0)