Skip to content

Commit c049ac7

Browse files
authored
Merge pull request #52740 from wildan-m/wildan/fix/45576-fix-not-found-decouple-2
Fix: Expense - Not here page shows up briefly when deleting the expense
2 parents a7adbb1 + fd39c99 commit c049ac7

File tree

9 files changed

+249
-77
lines changed

9 files changed

+249
-77
lines changed

src/ONYXKEYS.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,9 @@ const ONYXKEYS = {
219219
/** The NVP containing all information related to educational tooltip in workspace chat */
220220
NVP_WORKSPACE_TOOLTIP: 'workspaceTooltip',
221221

222+
/** The NVP containing the target url to navigate to when deleting a transaction */
223+
NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL: 'nvp_deleteTransactionNavigateBackURL',
224+
222225
/** Whether to show save search rename tooltip */
223226
SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP: 'shouldShowSavedSearchRenameTooltip',
224227

@@ -1012,6 +1015,7 @@ type OnyxValuesMapping = {
10121015
[ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED]: number;
10131016
[ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END]: number;
10141017
[ONYXKEYS.NVP_WORKSPACE_TOOLTIP]: OnyxTypes.WorkspaceTooltip;
1018+
[ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL]: string | undefined;
10151019
[ONYXKEYS.NVP_SHOULD_HIDE_GBR_TOOLTIP]: boolean;
10161020
[ONYXKEYS.NVP_PRIVATE_CANCELLATION_DETAILS]: OnyxTypes.CancellationDetails[];
10171021
[ONYXKEYS.ROOM_MEMBERS_USER_SEARCH_PHRASE]: string;

src/components/MoneyReportHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
502502
isVisible={isDeleteRequestModalVisible}
503503
onConfirm={deleteTransaction}
504504
onCancel={() => setIsDeleteRequestModalVisible(false)}
505-
onModalHide={() => ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current)}
505+
onModalHide={() => ReportUtils.navigateBackOnDeleteTransaction(navigateBackToAfterDelete.current)}
506506
prompt={translate('iou.deleteConfirmation', {count: 1})}
507507
confirmText={translate('common.delete')}
508508
cancelText={translate('common.cancel')}

src/libs/ReportUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4208,7 +4208,7 @@ function goBackToDetailsPage(report: OnyxEntry<Report>, backTo?: string) {
42084208
Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report?.reportID ?? '-1', backTo));
42094209
}
42104210

4211-
function navigateBackAfterDeleteTransaction(backRoute: Route | undefined, isFromRHP?: boolean) {
4211+
function navigateBackOnDeleteTransaction(backRoute: Route | undefined, isFromRHP?: boolean) {
42124212
if (!backRoute) {
42134213
return;
42144214
}
@@ -8720,7 +8720,7 @@ export {
87208720
canWriteInReport,
87218721
navigateToDetailsPage,
87228722
navigateToPrivateNotes,
8723-
navigateBackAfterDeleteTransaction,
8723+
navigateBackOnDeleteTransaction,
87248724
parseReportRouteParams,
87258725
parseReportActionHtmlToText,
87268726
requiresAttentionFromCurrentUser,

src/libs/actions/IOU.ts

Lines changed: 117 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import type {IOUAction, IOUType} from '@src/CONST';
6161
import CONST from '@src/CONST';
6262
import ONYXKEYS from '@src/ONYXKEYS';
6363
import ROUTES from '@src/ROUTES';
64+
import type {Route} from '@src/ROUTES';
6465
import type * as OnyxTypes from '@src/types/onyx';
6566
import type {Attendee, Participant, Split} from '@src/types/onyx/IOU';
6667
import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon';
@@ -1795,10 +1796,21 @@ function getDeleteTrackExpenseInformation(
17951796

17961797
if (shouldDeleteTransactionThread) {
17971798
optimisticData.push(
1799+
// Use merge instead of set to avoid deleting the report too quickly, which could cause a brief "not found" page to appear.
1800+
// The remaining parts of the report object will be removed after the API call is successful.
17981801
{
1799-
onyxMethod: Onyx.METHOD.SET,
1802+
onyxMethod: Onyx.METHOD.MERGE,
18001803
key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`,
1801-
value: null,
1804+
value: {
1805+
reportID: null,
1806+
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
1807+
statusNum: CONST.REPORT.STATUS_NUM.CLOSED,
1808+
participants: {
1809+
[userAccountID]: {
1810+
notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN,
1811+
},
1812+
},
1813+
},
18021814
},
18031815
{
18041816
onyxMethod: Onyx.METHOD.SET,
@@ -1838,6 +1850,19 @@ function getDeleteTrackExpenseInformation(
18381850
},
18391851
];
18401852

1853+
// Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response.
1854+
// This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function.
1855+
if (shouldDeleteTransactionThread && transactionThread) {
1856+
successData.push({
1857+
onyxMethod: Onyx.METHOD.MERGE,
1858+
key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`,
1859+
value: Object.keys(transactionThread).reduce<Record<string, null>>((acc, key) => {
1860+
acc[key] = null;
1861+
return acc;
1862+
}, {}),
1863+
});
1864+
}
1865+
18411866
const failureData: OnyxUpdate[] = [];
18421867

18431868
if (shouldDeleteTransactionFromOnyx) {
@@ -5409,10 +5434,9 @@ function updateMoneyRequestAmountAndCurrency({
54095434
*
54105435
* @param transactionID - The transactionID of IOU
54115436
* @param reportAction - The reportAction of the transaction in the IOU report
5412-
* @param isSingleTransactionView - whether we are in the transaction thread report
54135437
* @return the url to navigate back once the money request is deleted
54145438
*/
5415-
function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) {
5439+
function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.ReportAction) {
54165440
// STEP 1: Get all collections we're updating
54175441
const allReports = ReportConnection.getAllReports();
54185442
const iouReportID = ReportActionsUtils.isMoneyRequestAction(reportAction) ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUReportID : '-1';
@@ -5532,19 +5556,6 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT
55325556
updatedReportPreviewAction.childMoneyRequestCount = reportPreviewAction.childMoneyRequestCount - 1;
55335557
}
55345558

5535-
// STEP 5: Calculate the url that the user will be navigated back to
5536-
// This depends on which page they are on and which resources were deleted
5537-
let reportIDToNavigateBack: string | undefined;
5538-
if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) {
5539-
reportIDToNavigateBack = iouReport.reportID;
5540-
}
5541-
5542-
if (iouReport?.chatReportID && shouldDeleteIOUReport) {
5543-
reportIDToNavigateBack = iouReport.chatReportID;
5544-
}
5545-
5546-
const urlToNavigateBack = reportIDToNavigateBack ? ROUTES.REPORT_WITH_ID.getRoute(reportIDToNavigateBack) : undefined;
5547-
55485559
return {
55495560
shouldDeleteTransactionThread,
55505561
shouldDeleteIOUReport,
@@ -5558,10 +5569,59 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT
55585569
transactionViolations,
55595570
reportPreviewAction,
55605571
iouReport,
5561-
urlToNavigateBack,
55625572
};
55635573
}
55645574

5575+
/**
5576+
* Calculate the URL to navigate to after a money request deletion
5577+
* @param transactionID - The ID of the money request being deleted
5578+
* @param reportAction - The report action associated with the money request
5579+
* @param isSingleTransactionView - whether we are in the transaction thread report
5580+
* @returns The URL to navigate to
5581+
*/
5582+
function getNavigationUrlOnMoneyRequestDelete(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): Route | undefined {
5583+
const {shouldDeleteTransactionThread, shouldDeleteIOUReport, iouReport} = prepareToCleanUpMoneyRequest(transactionID, reportAction);
5584+
5585+
// Determine which report to navigate back to
5586+
if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) {
5587+
return ROUTES.REPORT_WITH_ID.getRoute(iouReport.reportID);
5588+
}
5589+
5590+
if (iouReport?.chatReportID && shouldDeleteIOUReport) {
5591+
return ROUTES.REPORT_WITH_ID.getRoute(iouReport.chatReportID);
5592+
}
5593+
5594+
return undefined;
5595+
}
5596+
5597+
/**
5598+
* Calculate the URL to navigate to after a track expense deletion
5599+
* @param chatReportID - The ID of the chat report containing the track expense
5600+
* @param transactionID - The ID of the track expense being deleted
5601+
* @param reportAction - The report action associated with the track expense
5602+
* @param isSingleTransactionView - Whether we're in single transaction view
5603+
* @returns The URL to navigate to
5604+
*/
5605+
function getNavigationUrlAfterTrackExpenseDelete(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): Route | undefined {
5606+
const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null;
5607+
5608+
// If not a self DM, handle it as a regular money request
5609+
if (!ReportUtils.isSelfDM(chatReport)) {
5610+
return getNavigationUrlOnMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView);
5611+
}
5612+
5613+
const transactionThreadID = reportAction.childReportID;
5614+
const shouldDeleteTransactionThread = transactionThreadID ? (reportAction?.childVisibleActionCount ?? 0) === 0 : false;
5615+
5616+
// Only navigate if in single transaction view and the thread will be deleted
5617+
if (isSingleTransactionView && shouldDeleteTransactionThread && chatReport?.reportID) {
5618+
// Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report.
5619+
return ROUTES.REPORT_WITH_ID.getRoute(chatReport.reportID);
5620+
}
5621+
5622+
return undefined;
5623+
}
5624+
55655625
/**
55665626
*
55675627
* @param transactionID - The transactionID of IOU
@@ -5580,9 +5640,9 @@ function cleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repo
55805640
chatReport,
55815641
iouReport,
55825642
reportPreviewAction,
5583-
urlToNavigateBack,
5584-
} = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView);
5643+
} = prepareToCleanUpMoneyRequest(transactionID, reportAction);
55855644

5645+
const urlToNavigateBack = getNavigationUrlOnMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView);
55865646
// build Onyx data
55875647

55885648
// Onyx operations to delete the transaction, update the IOU report action and chat report action
@@ -5726,8 +5786,9 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor
57265786
transactionViolations,
57275787
iouReport,
57285788
reportPreviewAction,
5729-
urlToNavigateBack,
5730-
} = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView);
5789+
} = prepareToCleanUpMoneyRequest(transactionID, reportAction);
5790+
5791+
const urlToNavigateBack = getNavigationUrlOnMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView);
57315792

57325793
// STEP 2: Build Onyx data
57335794
// The logic mostly resembles the cleanUpMoneyRequest function
@@ -5747,10 +5808,21 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor
57475808

57485809
if (shouldDeleteTransactionThread) {
57495810
optimisticData.push(
5811+
// Use merge instead of set to avoid deleting the report too quickly, which could cause a brief "not found" page to appear.
5812+
// The remaining parts of the report object will be removed after the API call is successful.
57505813
{
5751-
onyxMethod: Onyx.METHOD.SET,
5814+
onyxMethod: Onyx.METHOD.MERGE,
57525815
key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`,
5753-
value: null,
5816+
value: {
5817+
reportID: null,
5818+
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
5819+
statusNum: CONST.REPORT.STATUS_NUM.CLOSED,
5820+
participants: {
5821+
[userAccountID]: {
5822+
notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN,
5823+
},
5824+
},
5825+
},
57545826
},
57555827
{
57565828
onyxMethod: Onyx.METHOD.SET,
@@ -5848,6 +5920,19 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor
58485920
},
58495921
];
58505922

5923+
// Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response.
5924+
// This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function.
5925+
if (shouldDeleteTransactionThread && transactionThread) {
5926+
successData.push({
5927+
onyxMethod: Onyx.METHOD.MERGE,
5928+
key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`,
5929+
value: Object.keys(transactionThread).reduce<Record<string, null>>((acc, key) => {
5930+
acc[key] = null;
5931+
return acc;
5932+
}, {}),
5933+
});
5934+
}
5935+
58515936
if (shouldDeleteIOUReport) {
58525937
successData.push({
58535938
onyxMethod: Onyx.METHOD.SET,
@@ -5951,15 +6036,18 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor
59516036
}
59526037

59536038
function deleteTrackExpense(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) {
6039+
const urlToNavigateBack = getNavigationUrlAfterTrackExpenseDelete(chatReportID, transactionID, reportAction, isSingleTransactionView);
6040+
59546041
// STEP 1: Get all collections we're updating
59556042
const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null;
59566043
if (!ReportUtils.isSelfDM(chatReport)) {
5957-
return deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView);
6044+
deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView);
6045+
return urlToNavigateBack;
59586046
}
59596047

59606048
const whisperAction = ReportActionsUtils.getTrackExpenseActionableWhisper(transactionID, chatReportID);
59616049
const actionableWhisperReportActionID = whisperAction?.reportActionID;
5962-
const {parameters, optimisticData, successData, failureData, shouldDeleteTransactionThread} = getDeleteTrackExpenseInformation(
6050+
const {parameters, optimisticData, successData, failureData} = getDeleteTrackExpenseInformation(
59636051
chatReportID,
59646052
transactionID,
59656053
reportAction,
@@ -5974,10 +6062,7 @@ function deleteTrackExpense(chatReportID: string, transactionID: string, reportA
59746062
CachedPDFPaths.clearByKey(transactionID);
59756063

59766064
// STEP 7: Navigate the user depending on which page they are on and which resources were deleted
5977-
if (isSingleTransactionView && shouldDeleteTransactionThread) {
5978-
// Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report.
5979-
return ROUTES.REPORT_WITH_ID.getRoute(chatReport?.reportID ?? '-1');
5980-
}
6065+
return urlToNavigateBack;
59816066
}
59826067

59836068
/**
@@ -8585,5 +8670,7 @@ export {
85858670
updateLastLocationPermissionPrompt,
85868671
resolveDuplicates,
85878672
getIOUReportActionToApproveOrPay,
8673+
getNavigationUrlOnMoneyRequestDelete,
8674+
getNavigationUrlAfterTrackExpenseDelete,
85888675
};
85898676
export type {GPSPoint as GpsPoint, IOURequestType};

src/libs/actions/Report.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2917,6 +2917,8 @@ function leaveGroupChat(reportID: string) {
29172917
});
29182918
}
29192919

2920+
// Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response.
2921+
// This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function.
29202922
const successData: OnyxUpdate[] = [
29212923
{
29222924
onyxMethod: Onyx.METHOD.MERGE,
@@ -4401,6 +4403,14 @@ function exportReportToCSV({reportID, transactionIDList}: ExportReportCSVParams,
44014403
fileDownload(ApiUtils.getCommandURL({command: WRITE_COMMANDS.EXPORT_REPORT_TO_CSV}), 'Expensify.csv', '', false, formData, CONST.NETWORK.METHOD.POST, onDownloadFailed);
44024404
}
44034405

4406+
function setDeleteTransactionNavigateBackUrl(url: string) {
4407+
Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, url);
4408+
}
4409+
4410+
function clearDeleteTransactionNavigateBackUrl() {
4411+
Onyx.merge(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, null);
4412+
}
4413+
44044414
export type {Video};
44054415

44064416
export {
@@ -4490,4 +4500,6 @@ export {
44904500
updateReportName,
44914501
updateRoomVisibility,
44924502
updateWriteCapability,
4503+
setDeleteTransactionNavigateBackUrl,
4504+
clearDeleteTransactionNavigateBackUrl,
44934505
};

src/libs/actions/Task.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,36 @@ function getParentReport(report: OnyxEntry<OnyxTypes.Report>): OnyxEntry<OnyxTyp
961961
return ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`];
962962
}
963963

964+
/**
965+
* Calculate the URL to navigate to after a task deletion
966+
* @param report - The task report being deleted
967+
* @returns The URL to navigate to
968+
*/
969+
function getNavigationUrlOnTaskDelete(report: OnyxEntry<OnyxTypes.Report>): string | undefined {
970+
if (!report) {
971+
return undefined;
972+
}
973+
974+
const shouldDeleteTaskReport = !ReportActionsUtils.doesReportHaveVisibleActions(report.reportID ?? '-1');
975+
if (!shouldDeleteTaskReport) {
976+
return undefined;
977+
}
978+
979+
// First try to navigate to parent report
980+
const parentReport = getParentReport(report);
981+
if (parentReport?.reportID) {
982+
return ROUTES.REPORT_WITH_ID.getRoute(parentReport.reportID);
983+
}
984+
985+
// If no parent report, try to navigate to most recent report
986+
const mostRecentReportID = Report.getMostRecentReportID(report);
987+
if (mostRecentReportID) {
988+
return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID);
989+
}
990+
991+
return undefined;
992+
}
993+
964994
/**
965995
* Cancels a task by setting the report state to SUBMITTED and status to CLOSED
966996
*/
@@ -1117,15 +1147,10 @@ function deleteTask(report: OnyxEntry<OnyxTypes.Report>) {
11171147
API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData});
11181148
Report.notifyNewAction(report.reportID, currentUserAccountID);
11191149

1120-
if (shouldDeleteTaskReport) {
1150+
const urlToNavigateBack = getNavigationUrlOnTaskDelete(report);
1151+
if (urlToNavigateBack) {
11211152
Navigation.goBack();
1122-
if (parentReport?.reportID) {
1123-
return ROUTES.REPORT_WITH_ID.getRoute(parentReport.reportID);
1124-
}
1125-
const mostRecentReportID = Report.getMostRecentReportID(report);
1126-
if (mostRecentReportID) {
1127-
return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID);
1128-
}
1153+
return urlToNavigateBack;
11291154
}
11301155
}
11311156

@@ -1239,6 +1264,7 @@ export {
12391264
canModifyTask,
12401265
canActionTask,
12411266
setNewOptimisticAssignee,
1267+
getNavigationUrlOnTaskDelete,
12421268
};
12431269

12441270
export type {PolicyValue, Assignee, ShareDestination};

0 commit comments

Comments
 (0)