Skip to content

Commit

Permalink
Merge pull request Expensify#52740 from wildan-m/wildan/fix/45576-fix…
Browse files Browse the repository at this point in the history
…-not-found-decouple-2

Fix: Expense - Not here page shows up briefly when deleting the expense
  • Loading branch information
luacmartins authored Dec 4, 2024
2 parents a7adbb1 + fd39c99 commit c049ac7
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 77 deletions.
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ const ONYXKEYS = {
/** The NVP containing all information related to educational tooltip in workspace chat */
NVP_WORKSPACE_TOOLTIP: 'workspaceTooltip',

/** The NVP containing the target url to navigate to when deleting a transaction */
NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL: 'nvp_deleteTransactionNavigateBackURL',

/** Whether to show save search rename tooltip */
SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP: 'shouldShowSavedSearchRenameTooltip',

Expand Down Expand Up @@ -1012,6 +1015,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED]: number;
[ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END]: number;
[ONYXKEYS.NVP_WORKSPACE_TOOLTIP]: OnyxTypes.WorkspaceTooltip;
[ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL]: string | undefined;
[ONYXKEYS.NVP_SHOULD_HIDE_GBR_TOOLTIP]: boolean;
[ONYXKEYS.NVP_PRIVATE_CANCELLATION_DETAILS]: OnyxTypes.CancellationDetails[];
[ONYXKEYS.ROOM_MEMBERS_USER_SEARCH_PHRASE]: string;
Expand Down
2 changes: 1 addition & 1 deletion src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
isVisible={isDeleteRequestModalVisible}
onConfirm={deleteTransaction}
onCancel={() => setIsDeleteRequestModalVisible(false)}
onModalHide={() => ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current)}
onModalHide={() => ReportUtils.navigateBackOnDeleteTransaction(navigateBackToAfterDelete.current)}
prompt={translate('iou.deleteConfirmation', {count: 1})}
confirmText={translate('common.delete')}
cancelText={translate('common.cancel')}
Expand Down
4 changes: 2 additions & 2 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4208,7 +4208,7 @@ function goBackToDetailsPage(report: OnyxEntry<Report>, backTo?: string) {
Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report?.reportID ?? '-1', backTo));
}

function navigateBackAfterDeleteTransaction(backRoute: Route | undefined, isFromRHP?: boolean) {
function navigateBackOnDeleteTransaction(backRoute: Route | undefined, isFromRHP?: boolean) {
if (!backRoute) {
return;
}
Expand Down Expand Up @@ -8720,7 +8720,7 @@ export {
canWriteInReport,
navigateToDetailsPage,
navigateToPrivateNotes,
navigateBackAfterDeleteTransaction,
navigateBackOnDeleteTransaction,
parseReportRouteParams,
parseReportActionHtmlToText,
requiresAttentionFromCurrentUser,
Expand Down
147 changes: 117 additions & 30 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import type {IOUAction, IOUType} from '@src/CONST';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Route} from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import type {Attendee, Participant, Split} from '@src/types/onyx/IOU';
import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon';
Expand Down Expand Up @@ -1795,10 +1796,21 @@ function getDeleteTrackExpenseInformation(

if (shouldDeleteTransactionThread) {
optimisticData.push(
// Use merge instead of set to avoid deleting the report too quickly, which could cause a brief "not found" page to appear.
// The remaining parts of the report object will be removed after the API call is successful.
{
onyxMethod: Onyx.METHOD.SET,
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`,
value: null,
value: {
reportID: null,
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
statusNum: CONST.REPORT.STATUS_NUM.CLOSED,
participants: {
[userAccountID]: {
notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN,
},
},
},
},
{
onyxMethod: Onyx.METHOD.SET,
Expand Down Expand Up @@ -1838,6 +1850,19 @@ function getDeleteTrackExpenseInformation(
},
];

// Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response.
// This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function.
if (shouldDeleteTransactionThread && transactionThread) {
successData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`,
value: Object.keys(transactionThread).reduce<Record<string, null>>((acc, key) => {
acc[key] = null;
return acc;
}, {}),
});
}

const failureData: OnyxUpdate[] = [];

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

// STEP 5: Calculate the url that the user will be navigated back to
// This depends on which page they are on and which resources were deleted
let reportIDToNavigateBack: string | undefined;
if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) {
reportIDToNavigateBack = iouReport.reportID;
}

if (iouReport?.chatReportID && shouldDeleteIOUReport) {
reportIDToNavigateBack = iouReport.chatReportID;
}

const urlToNavigateBack = reportIDToNavigateBack ? ROUTES.REPORT_WITH_ID.getRoute(reportIDToNavigateBack) : undefined;

return {
shouldDeleteTransactionThread,
shouldDeleteIOUReport,
Expand All @@ -5558,10 +5569,59 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT
transactionViolations,
reportPreviewAction,
iouReport,
urlToNavigateBack,
};
}

/**
* Calculate the URL to navigate to after a money request deletion
* @param transactionID - The ID of the money request being deleted
* @param reportAction - The report action associated with the money request
* @param isSingleTransactionView - whether we are in the transaction thread report
* @returns The URL to navigate to
*/
function getNavigationUrlOnMoneyRequestDelete(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): Route | undefined {
const {shouldDeleteTransactionThread, shouldDeleteIOUReport, iouReport} = prepareToCleanUpMoneyRequest(transactionID, reportAction);

// Determine which report to navigate back to
if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) {
return ROUTES.REPORT_WITH_ID.getRoute(iouReport.reportID);
}

if (iouReport?.chatReportID && shouldDeleteIOUReport) {
return ROUTES.REPORT_WITH_ID.getRoute(iouReport.chatReportID);
}

return undefined;
}

/**
* Calculate the URL to navigate to after a track expense deletion
* @param chatReportID - The ID of the chat report containing the track expense
* @param transactionID - The ID of the track expense being deleted
* @param reportAction - The report action associated with the track expense
* @param isSingleTransactionView - Whether we're in single transaction view
* @returns The URL to navigate to
*/
function getNavigationUrlAfterTrackExpenseDelete(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false): Route | undefined {
const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null;

// If not a self DM, handle it as a regular money request
if (!ReportUtils.isSelfDM(chatReport)) {
return getNavigationUrlOnMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView);
}

const transactionThreadID = reportAction.childReportID;
const shouldDeleteTransactionThread = transactionThreadID ? (reportAction?.childVisibleActionCount ?? 0) === 0 : false;

// Only navigate if in single transaction view and the thread will be deleted
if (isSingleTransactionView && shouldDeleteTransactionThread && chatReport?.reportID) {
// Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report.
return ROUTES.REPORT_WITH_ID.getRoute(chatReport.reportID);
}

return undefined;
}

/**
*
* @param transactionID - The transactionID of IOU
Expand All @@ -5580,9 +5640,9 @@ function cleanUpMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repo
chatReport,
iouReport,
reportPreviewAction,
urlToNavigateBack,
} = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView);
} = prepareToCleanUpMoneyRequest(transactionID, reportAction);

const urlToNavigateBack = getNavigationUrlOnMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView);
// build Onyx data

// Onyx operations to delete the transaction, update the IOU report action and chat report action
Expand Down Expand Up @@ -5726,8 +5786,9 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor
transactionViolations,
iouReport,
reportPreviewAction,
urlToNavigateBack,
} = prepareToCleanUpMoneyRequest(transactionID, reportAction, isSingleTransactionView);
} = prepareToCleanUpMoneyRequest(transactionID, reportAction);

const urlToNavigateBack = getNavigationUrlOnMoneyRequestDelete(transactionID, reportAction, isSingleTransactionView);

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

if (shouldDeleteTransactionThread) {
optimisticData.push(
// Use merge instead of set to avoid deleting the report too quickly, which could cause a brief "not found" page to appear.
// The remaining parts of the report object will be removed after the API call is successful.
{
onyxMethod: Onyx.METHOD.SET,
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`,
value: null,
value: {
reportID: null,
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
statusNum: CONST.REPORT.STATUS_NUM.CLOSED,
participants: {
[userAccountID]: {
notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN,
},
},
},
},
{
onyxMethod: Onyx.METHOD.SET,
Expand Down Expand Up @@ -5848,6 +5920,19 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor
},
];

// Ensure that any remaining data is removed upon successful completion, even if the server sends a report removal response.
// This is done to prevent the removal update from lingering in the applyHTTPSOnyxUpdates function.
if (shouldDeleteTransactionThread && transactionThread) {
successData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`,
value: Object.keys(transactionThread).reduce<Record<string, null>>((acc, key) => {
acc[key] = null;
return acc;
}, {}),
});
}

if (shouldDeleteIOUReport) {
successData.push({
onyxMethod: Onyx.METHOD.SET,
Expand Down Expand Up @@ -5951,15 +6036,18 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor
}

function deleteTrackExpense(chatReportID: string, transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) {
const urlToNavigateBack = getNavigationUrlAfterTrackExpenseDelete(chatReportID, transactionID, reportAction, isSingleTransactionView);

// STEP 1: Get all collections we're updating
const chatReport = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`] ?? null;
if (!ReportUtils.isSelfDM(chatReport)) {
return deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView);
deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView);
return urlToNavigateBack;
}

const whisperAction = ReportActionsUtils.getTrackExpenseActionableWhisper(transactionID, chatReportID);
const actionableWhisperReportActionID = whisperAction?.reportActionID;
const {parameters, optimisticData, successData, failureData, shouldDeleteTransactionThread} = getDeleteTrackExpenseInformation(
const {parameters, optimisticData, successData, failureData} = getDeleteTrackExpenseInformation(
chatReportID,
transactionID,
reportAction,
Expand All @@ -5974,10 +6062,7 @@ function deleteTrackExpense(chatReportID: string, transactionID: string, reportA
CachedPDFPaths.clearByKey(transactionID);

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

/**
Expand Down Expand Up @@ -8585,5 +8670,7 @@ export {
updateLastLocationPermissionPrompt,
resolveDuplicates,
getIOUReportActionToApproveOrPay,
getNavigationUrlOnMoneyRequestDelete,
getNavigationUrlAfterTrackExpenseDelete,
};
export type {GPSPoint as GpsPoint, IOURequestType};
12 changes: 12 additions & 0 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2917,6 +2917,8 @@ function leaveGroupChat(reportID: string) {
});
}

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

function setDeleteTransactionNavigateBackUrl(url: string) {
Onyx.set(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, url);
}

function clearDeleteTransactionNavigateBackUrl() {
Onyx.merge(ONYXKEYS.NVP_DELETE_TRANSACTION_NAVIGATE_BACK_URL, null);
}

export type {Video};

export {
Expand Down Expand Up @@ -4490,4 +4500,6 @@ export {
updateReportName,
updateRoomVisibility,
updateWriteCapability,
setDeleteTransactionNavigateBackUrl,
clearDeleteTransactionNavigateBackUrl,
};
42 changes: 34 additions & 8 deletions src/libs/actions/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,36 @@ function getParentReport(report: OnyxEntry<OnyxTypes.Report>): OnyxEntry<OnyxTyp
return ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`];
}

/**
* Calculate the URL to navigate to after a task deletion
* @param report - The task report being deleted
* @returns The URL to navigate to
*/
function getNavigationUrlOnTaskDelete(report: OnyxEntry<OnyxTypes.Report>): string | undefined {
if (!report) {
return undefined;
}

const shouldDeleteTaskReport = !ReportActionsUtils.doesReportHaveVisibleActions(report.reportID ?? '-1');
if (!shouldDeleteTaskReport) {
return undefined;
}

// First try to navigate to parent report
const parentReport = getParentReport(report);
if (parentReport?.reportID) {
return ROUTES.REPORT_WITH_ID.getRoute(parentReport.reportID);
}

// If no parent report, try to navigate to most recent report
const mostRecentReportID = Report.getMostRecentReportID(report);
if (mostRecentReportID) {
return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID);
}

return undefined;
}

/**
* Cancels a task by setting the report state to SUBMITTED and status to CLOSED
*/
Expand Down Expand Up @@ -1117,15 +1147,10 @@ function deleteTask(report: OnyxEntry<OnyxTypes.Report>) {
API.write(WRITE_COMMANDS.CANCEL_TASK, parameters, {optimisticData, successData, failureData});
Report.notifyNewAction(report.reportID, currentUserAccountID);

if (shouldDeleteTaskReport) {
const urlToNavigateBack = getNavigationUrlOnTaskDelete(report);
if (urlToNavigateBack) {
Navigation.goBack();
if (parentReport?.reportID) {
return ROUTES.REPORT_WITH_ID.getRoute(parentReport.reportID);
}
const mostRecentReportID = Report.getMostRecentReportID(report);
if (mostRecentReportID) {
return ROUTES.REPORT_WITH_ID.getRoute(mostRecentReportID);
}
return urlToNavigateBack;
}
}

Expand Down Expand Up @@ -1239,6 +1264,7 @@ export {
canModifyTask,
canActionTask,
setNewOptimisticAssignee,
getNavigationUrlOnTaskDelete,
};

export type {PolicyValue, Assignee, ShareDestination};
Loading

0 comments on commit c049ac7

Please sign in to comment.