Skip to content

Commit

Permalink
Merge pull request #37 from software-mansion-labs/ideal-nav/dismiss-m…
Browse files Browse the repository at this point in the history
…odal

Add dismissModalWithReport
  • Loading branch information
adamgrzybowski authored Jan 26, 2024
2 parents e6202e2 + 3310097 commit 652f13f
Show file tree
Hide file tree
Showing 17 changed files with 127 additions and 75 deletions.
2 changes: 1 addition & 1 deletion src/components/AttachmentModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ function AttachmentModal(props) {
const deleteAndCloseModal = useCallback(() => {
IOU.detachReceipt(props.transaction.transactionID, props.report.reportID);
setIsDeleteReceiptConfirmModalVisible(false);
Navigation.dismissModal(props.report.reportID);
Navigation.dismissModalWithReportID(props.report.reportID);
}, [props.transaction, props.report]);

/**
Expand Down
17 changes: 16 additions & 1 deletion src/libs/Navigation/Navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import {findFocusedRoute} from '@react-navigation/core';
import type {EventArg, NavigationContainerEventMap} from '@react-navigation/native';
import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native';
import Log from '@libs/Log';
import {getReport} from '@libs/ReportUtils';
import CONST from '@src/CONST';
import NAVIGATORS from '@src/NAVIGATORS';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
import {PROTECTED_SCREENS} from '@src/SCREENS';
import type {Report} from '@src/types/onyx';
import type {EmptyObject} from '@src/types/utils/EmptyObject';
import originalDismissModal from './dismissModal';
import originalDismissModalWithReport from './dismissModalWithReport';
import originalGetTopmostReportActionId from './getTopmostReportActionID';
import originalGetTopmostReportId from './getTopmostReportId';
import linkingConfig from './linkingConfig';
Expand Down Expand Up @@ -47,8 +51,17 @@ const getTopmostReportId = (state = navigationRef.getState()) => originalGetTopm
const getTopmostReportActionId = (state = navigationRef.getState()) => originalGetTopmostReportActionId(state);

// Re-exporting the dismissModal here to fill in default value for navigationRef. The dismissModal isn't defined in this file to avoid cyclic dependencies.
const dismissModal = (targetReportId = '', ref = navigationRef) => originalDismissModal(targetReportId, ref);
const dismissModal = (ref = navigationRef) => originalDismissModal(ref);

// Re-exporting the dismissModalWithReport here to fill in default value for navigationRef. The dismissModalWithReport isn't defined in this file to avoid cyclic dependencies.
// This method is needed because it allows to dismiss the modal and then open the report. Within this method is checked whether the report belongs to a specific workspace. Sometimes the report we want to check, hasn't been added to the Onyx yet.
// Then we can pass the report as a param without getting it from the Onyx.
const dismissModalWithReport = (report: Report | EmptyObject, ref = navigationRef) => originalDismissModalWithReport(report, ref);

const dismissModalWithReportID = (reportID: string, ref = navigationRef) => {
const report = getReport(reportID);
originalDismissModalWithReport({reportID, ...report}, ref);
};
/** Method for finding on which index in stack we are. */
function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number | undefined {
if ('routes' in stateOrRoute && stateOrRoute.routes) {
Expand Down Expand Up @@ -323,6 +336,8 @@ export default {
navigate,
setParams,
dismissModal,
dismissModalWithReport,
dismissModalWithReportID,
isActiveRoute,
getActiveRoute,
getActiveRouteWithoutParams,
Expand Down
43 changes: 3 additions & 40 deletions src/libs/Navigation/dismissModal.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
import {getActionFromState} from '@react-navigation/core';
import type {NavigationContainerRef} from '@react-navigation/native';
import {StackActions} from '@react-navigation/native';
import {findLastIndex} from 'lodash';
import Log from '@libs/Log';
import getPolicyMemberAccountIDs from '@libs/PolicyMembersUtils';
import {doesReportBelongToWorkspace, getReport} from '@libs/ReportUtils';
import NAVIGATORS from '@src/NAVIGATORS';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import getPolicyIdFromState from './getPolicyIdFromState';
import getStateFromPath from './getStateFromPath';
import getTopmostReportId from './getTopmostReportId';
import linkingConfig from './linkingConfig';
import switchPolicyID from './switchPolicyID';
import type {RootStackParamList, StackNavigationAction, State} from './types';
import type {RootStackParamList} from './types';

// This function is in a separate file than Navigation.js to avoid cyclic dependency.

Expand All @@ -23,7 +12,7 @@ import type {RootStackParamList, StackNavigationAction, State} from './types';
*
* @param targetReportID - The reportID to navigate to after dismissing the modal
*/
function dismissModal(targetReportID: string, navigationRef: NavigationContainerRef<RootStackParamList>) {
function dismissModal(navigationRef: NavigationContainerRef<RootStackParamList>) {
if (!navigationRef.isReady()) {
return;
}
Expand All @@ -36,33 +25,7 @@ function dismissModal(targetReportID: string, navigationRef: NavigationContainer
case NAVIGATORS.RIGHT_MODAL_NAVIGATOR:
case SCREENS.NOT_FOUND:
case SCREENS.REPORT_ATTACHMENTS:
// if we are not in the target report, we need to navigate to it after dismissing the modal
if (targetReportID && targetReportID !== getTopmostReportId(state)) {
const reportState = getStateFromPath(ROUTES.REPORT_WITH_ID.getRoute(targetReportID));
const policyID = getPolicyIdFromState(state as State<RootStackParamList>);
const policyMemberAccountIDs = getPolicyMemberAccountIDs(policyID);
const targetReport = getReport(targetReportID);
// If targetReport is an empty object, it means that it's a new report, so it can't belong to any workspace
const shouldOpenAllWorkspace = isEmptyObject(targetReport) ? true : !doesReportBelongToWorkspace(targetReport, policyMemberAccountIDs, policyID);
if (shouldOpenAllWorkspace) {
switchPolicyID(navigationRef, {route: ROUTES.HOME});
} else {
switchPolicyID(navigationRef, {policyID, route: ROUTES.HOME});
}

const action: StackNavigationAction = getActionFromState(reportState, linkingConfig.config);
if (action) {
action.type = 'REPLACE';
navigationRef.dispatch(action);
}
// If not-found page is in the route stack, we need to close it
} else if (targetReportID && state.routes.some((route) => route.name === SCREENS.NOT_FOUND)) {
const lastRouteIndex = state.routes.length - 1;
const centralRouteIndex = findLastIndex(state.routes, (route) => route.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR);
navigationRef.dispatch({...StackActions.pop(lastRouteIndex - centralRouteIndex), target: state.key});
} else {
navigationRef.dispatch({...StackActions.pop(), target: state.key});
}
navigationRef.dispatch({...StackActions.pop(), target: state.key});
break;
default: {
Log.hmmm('[Navigation] dismissModal failed because there is no modal stack to dismiss');
Expand Down
74 changes: 74 additions & 0 deletions src/libs/Navigation/dismissModalWithReport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {getActionFromState} from '@react-navigation/core';
import type {NavigationContainerRef} from '@react-navigation/native';
import {StackActions} from '@react-navigation/native';
import {findLastIndex} from 'lodash';
import Log from '@libs/Log';
import getPolicyMemberAccountIDs from '@libs/PolicyMembersUtils';
import {doesReportBelongToWorkspace} from '@libs/ReportUtils';
import NAVIGATORS from '@src/NAVIGATORS';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import type {Report} from '@src/types/onyx';
import type {EmptyObject} from '@src/types/utils/EmptyObject';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import getPolicyIdFromState from './getPolicyIdFromState';
import getStateFromPath from './getStateFromPath';
import getTopmostReportId from './getTopmostReportId';
import linkingConfig from './linkingConfig';
import switchPolicyID from './switchPolicyID';
import type {RootStackParamList, StackNavigationAction, State} from './types';

// This function is in a separate file than Navigation.js to avoid cyclic dependency.

/**
* Dismisses the last modal stack if there is any
*
* @param targetReportID - The reportID to navigate to after dismissing the modal
*/
function dismissModalWithReport(targetReport: Report | EmptyObject, navigationRef: NavigationContainerRef<RootStackParamList>) {
if (!navigationRef.isReady()) {
return;
}

const state = navigationRef.getState();
const lastRoute = state.routes.at(-1);
switch (lastRoute?.name) {
case NAVIGATORS.FULL_SCREEN_NAVIGATOR:
case NAVIGATORS.LEFT_MODAL_NAVIGATOR:
case NAVIGATORS.RIGHT_MODAL_NAVIGATOR:
case SCREENS.NOT_FOUND:
case SCREENS.REPORT_ATTACHMENTS:
// if we are not in the target report, we need to navigate to it after dismissing the modal
if (targetReport.reportID !== getTopmostReportId(state)) {
const reportState = getStateFromPath(ROUTES.REPORT_WITH_ID.getRoute(targetReport.reportID));
const policyID = getPolicyIdFromState(state as State<RootStackParamList>);
const policyMemberAccountIDs = getPolicyMemberAccountIDs(policyID);
const shouldOpenAllWorkspace = isEmptyObject(targetReport) ? true : !doesReportBelongToWorkspace(targetReport, policyMemberAccountIDs, policyID);

if (shouldOpenAllWorkspace) {
switchPolicyID(navigationRef, {route: ROUTES.HOME});
} else {
switchPolicyID(navigationRef, {policyID, route: ROUTES.HOME});
}

const action: StackNavigationAction = getActionFromState(reportState, linkingConfig.config);
if (action) {
action.type = 'REPLACE';
navigationRef.dispatch(action);
}
// If not-found page is in the route stack, we need to close it
} else if (state.routes.some((route) => route.name === SCREENS.NOT_FOUND)) {
const lastRouteIndex = state.routes.length - 1;
const centralRouteIndex = findLastIndex(state.routes, (route) => route.name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR);
navigationRef.dispatch({...StackActions.pop(lastRouteIndex - centralRouteIndex), target: state.key});
} else {
navigationRef.dispatch({...StackActions.pop(), target: state.key});
}
break;
default: {
Log.hmmm('[Navigation] dismissModalWithReport failed because there is no modal stack to dismiss');
}
}
}

export default dismissModalWithReport;
2 changes: 1 addition & 1 deletion src/libs/Navigation/linkTo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export default function linkTo(navigation: NavigationContainerRef<RootStackParam
// If this action is navigating to the ModalNavigator and the last route on the root navigator is not already opened ModalNavigator then push
} else if (isModalNavigator(action.payload.name) && !isTargetNavigatorOnTop) {
if (isModalNavigator(topRouteName)) {
dismissModal('', navigation);
dismissModal(navigation);
}
action.type = CONST.NAVIGATION.ACTION_TYPE.PUSH;

Expand Down
16 changes: 8 additions & 8 deletions src/libs/actions/IOU.js
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ function createDistanceRequest(report, participant, comment, created, category,
},
onyxData,
);
Navigation.dismissModal(isMoneyRequestReport ? report.reportID : chatReport.reportID);
Navigation.dismissModalWithReportID(isMoneyRequestReport ? report.reportID : chatReport.reportID);
Report.notifyNewAction(chatReport.reportID, userAccountID);
}

Expand Down Expand Up @@ -1340,7 +1340,7 @@ function requestMoney(
onyxData,
);
resetMoneyRequestInfo();
Navigation.dismissModal(activeReportID);
Navigation.dismissModalWithReportID(activeReportID);
Report.notifyNewAction(activeReportID, payeeAccountID);
}

Expand Down Expand Up @@ -1782,7 +1782,7 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou
);

resetMoneyRequestInfo();
Navigation.dismissModal(splitData.chatReportID);
Navigation.dismissModalWithReportID(splitData.chatReportID);
Report.notifyNewAction(splitData.chatReportID, currentUserAccountID);
}

Expand Down Expand Up @@ -2042,7 +2042,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co
);

resetMoneyRequestInfo();
Navigation.dismissModal(splitChatReport.reportID);
Navigation.dismissModalWithReport(splitChatReport);
Report.notifyNewAction(splitChatReport.chatReportID, currentUserAccountID);
}

Expand Down Expand Up @@ -2261,7 +2261,7 @@ function completeSplitBill(chatReportID, reportAction, updatedTransaction, sessi
},
{optimisticData, successData, failureData},
);
Navigation.dismissModal(chatReportID);
Navigation.dismissModalWithReportID(chatReportID);
Report.notifyNewAction(chatReportID, sessionAccountID);
}

Expand Down Expand Up @@ -3217,7 +3217,7 @@ function sendMoneyElsewhere(report, amount, currency, comment, managerID, recipi
API.write('SendMoneyElsewhere', params, {optimisticData, successData, failureData});

resetMoneyRequestInfo();
Navigation.dismissModal(params.chatReportID);
Navigation.dismissModalWithReportID(params.chatReportID);
Report.notifyNewAction(params.chatReportID, managerID);
}

Expand All @@ -3235,7 +3235,7 @@ function sendMoneyWithWallet(report, amount, currency, comment, managerID, recip
API.write('SendMoneyWithWallet', params, {optimisticData, successData, failureData});

resetMoneyRequestInfo();
Navigation.dismissModal(params.chatReportID);
Navigation.dismissModalWithReportID(params.chatReportID);
Report.notifyNewAction(params.chatReportID, managerID);
}

Expand Down Expand Up @@ -3440,7 +3440,7 @@ function payMoneyRequest(paymentType, chatReport, iouReport) {
const apiCommand = paymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY ? 'PayMoneyRequestWithWallet' : 'PayMoneyRequest';

API.write(apiCommand, params, {optimisticData, successData, failureData});
Navigation.dismissModal(chatReport.reportID);
Navigation.dismissModalWithReport(chatReport);
}

function detachReceipt(transactionID) {
Expand Down
16 changes: 8 additions & 8 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -718,15 +718,15 @@ function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true
if (!chat) {
newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs);
}
const reportID = chat ? chat.reportID : newChat.reportID;
const report = chat ?? newChat;

// We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server
openReport(reportID, userLogins, newChat);
openReport(report.reportID, userLogins, newChat);
if (shouldDismissModal) {
Navigation.dismissModal(reportID);
Navigation.dismissModalWithReport(report);
} else {
Navigation.navigateWithSwitchPolicyID({route: ROUTES.HOME});
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(reportID));
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report.reportID));
}
}

Expand All @@ -741,11 +741,11 @@ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[])
if (!chat) {
newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs);
}
const reportID = chat ? chat.reportID : newChat.reportID;
const report = chat ?? newChat;

// We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server
openReport(reportID, [], newChat, '0', false, participantAccountIDs);
Navigation.dismissModal(reportID);
openReport(report.reportID, [], newChat, '0', false, participantAccountIDs);
Navigation.dismissModalWithReport(report);
}

/**
Expand Down Expand Up @@ -1701,7 +1701,7 @@ function addPolicyReport(policyReport: ReportUtils.OptimisticChatReport) {
};

API.write('AddWorkspaceRoom', parameters, {optimisticData, successData, failureData});
Navigation.dismissModal(policyReport.reportID);
Navigation.dismissModalWithReportID(policyReport.reportID);
}

/** Deletes a report, along with its reportActions, any linked reports, and any linked IOU report. */
Expand Down
2 changes: 1 addition & 1 deletion src/libs/actions/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ function createTaskAndNavigate(

API.write('CreateTask', parameters, {optimisticData, successData, failureData});

Navigation.dismissModal(parentReportID);
Navigation.dismissModalWithReportID(parentReportID);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/libs/actions/TeachersUnite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function referTeachersUniteVolunteer(partnerUserID: string, firstName: string, l
};

API.write('ReferTeachersUniteVolunteer', parameters, {optimisticData});
Navigation.dismissModal(publicRoomReportID);
Navigation.dismissModalWithReportID(publicRoomReportID);
}

/**
Expand Down Expand Up @@ -194,7 +194,7 @@ function addSchoolPrincipal(firstName: string, partnerUserID: string, lastName:
};

API.write('AddSchoolPrincipal', parameters, {optimisticData, successData, failureData});
Navigation.dismissModal(expenseChatReportID);
Navigation.dismissModalWithReportID(expenseChatReportID);
}

export default {referTeachersUniteVolunteer, addSchoolPrincipal};
2 changes: 1 addition & 1 deletion src/pages/AddPersonalBankAccountPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function AddPersonalBankAccountPage({personalBankAccount, plaidData}) {
const onSuccessFallbackRoute = lodashGet(personalBankAccount, 'onSuccessFallbackRoute', '');

if (exitReportID) {
Navigation.dismissModal(exitReportID);
Navigation.dismissModalWithReportID(exitReportID);
} else if (shouldContinue && onSuccessFallbackRoute) {
PaymentMethods.continueSetup(onSuccessFallbackRoute);
} else {
Expand Down
6 changes: 3 additions & 3 deletions src/pages/EditRequestDistancePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function EditRequestDistancePage({report, route, transaction, transactionBackup}
// When the loading goes from true to false, then we know the transaction has just been
// saved to the server. Check for errors. If there are no errors, then the modal can be closed.
if (prevIsLoading && !transaction.isLoading && !hasWaypointError.current) {
Navigation.dismissModal(report.reportID);
Navigation.dismissModalWithReportID(report.reportID);
}
}, [transaction, prevIsLoading, report]);

Expand All @@ -75,7 +75,7 @@ function EditRequestDistancePage({report, route, transaction, transactionBackup}
const oldAddresses = _.mapObject(oldWaypoints, (waypoint) => _.pick(waypoint, 'address'));
const addresses = _.mapObject(waypoints, (waypoint) => _.pick(waypoint, 'address'));
if (_.isEqual(oldAddresses, addresses)) {
Navigation.dismissModal(report.reportID);
Navigation.dismissModalWithReportID(report.reportID);
return;
}

Expand All @@ -84,7 +84,7 @@ function EditRequestDistancePage({report, route, transaction, transactionBackup}
// If the client is offline, then the modal can be closed as well (because there are no errors or other feedback to show them
// until they come online again and sync with the server).
if (isOffline) {
Navigation.dismissModal(report.reportID);
Navigation.dismissModalWithReportID(report.reportID);
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/pages/SearchPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ function SearchPage({betas, personalDetails, reports, isSearchingForReports, nav
return;
}
if (option.reportID) {
Navigation.dismissModal(option.reportID);
Navigation.dismissModalWithReportID(option.reportID);
} else {
Report.navigateToAndOpenReport([option.login]);
}
Expand Down
Loading

0 comments on commit 652f13f

Please sign in to comment.