Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Navigation] Fix goBack in IOU steps #56876

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ function MoneyRequestConfirmationList({
}

const newIOUType = iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.TRACK ? CONST.IOU.TYPE.CREATE : iouType;
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(newIOUType, transactionID, transaction.reportID));
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(newIOUType, transactionID, transaction.reportID, Navigation.getActiveRoute()));
};

/**
Expand Down
2 changes: 1 addition & 1 deletion src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1100,7 +1100,7 @@ type MoneyRequestNavigatorParamList = {
iouType: Exclude<IOUType, typeof CONST.IOU.TYPE.REQUEST | typeof CONST.IOU.TYPE.SEND>;
transactionID: string;
reportID: string;
backTo: string;
backTo: Routes;
};
[SCREENS.MONEY_REQUEST.STEP_DATE]: {
action: IOUAction;
Expand Down
11 changes: 11 additions & 0 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ function getActivePolicies(policies: OnyxCollection<Policy> | null, currentUserL
!!policy && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !!policy.name && !!policy.id && !!getPolicyRole(policy, currentUserLogin),
);
}

function getPerDiemCustomUnits(policies: OnyxCollection<Policy> | null, email: string | undefined): Array<{policyID: string; customUnit: CustomUnit}> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function getPerDiemCustomUnits(policies: OnyxCollection<Policy> | null, email: string | undefined): Array<{policyID: string; customUnit: CustomUnit}> {
function getPerDiemCustomUnits(policies: OnyxCollection<Policy> | null, email: string | undefined): Array<{policyID: string; customUnit: CustomUnit | undefined}> {

By doing this way, we don't need to cast the result

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I assume that as a result type we want to have array of objects that have a defined value of CustomUnit

return (
getActivePolicies(policies, email)
.map((mappedPolicy) => ({policyID: mappedPolicy.id, customUnit: getPerDiemCustomUnit(mappedPolicy)}))
// We filter out custom units that are undefine but ts cant' figure it out.
.filter(({customUnit}) => !isEmptyObject(customUnit) && !!customUnit.enabled) as Array<{policyID: string; customUnit: CustomUnit}>
);
}

/**
* Checks if the current user is an admin of the policy.
*/
Expand Down Expand Up @@ -1338,6 +1348,7 @@ export {
extractPolicyIDFromPath,
escapeTagName,
getActivePolicies,
getPerDiemCustomUnits,
getAllSelfApprovers,
getAdminEmployees,
getCleanedTagName,
Expand Down
7 changes: 2 additions & 5 deletions src/pages/iou/request/IOURequestStartPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import Navigation from '@libs/Navigation/Navigation';
import OnyxTabNavigator, {TabScreenWithFocusTrapWrapper, TopTab} from '@libs/Navigation/OnyxTabNavigator';
import {getActivePolicies, getPerDiemCustomUnit} from '@libs/PolicyUtils';
import {getPerDiemCustomUnit, getPerDiemCustomUnits} from '@libs/PolicyUtils';
import {getPayeeName} from '@libs/ReportUtils';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import type {IOURequestType} from '@userActions/IOU';
Expand Down Expand Up @@ -103,10 +103,7 @@ function IOURequestStartPage({
return [headerWithBackBtnContainerElement, tabBarContainerElement, activeTabContainerElement].filter((element) => !!element) as HTMLElement[];
}, [headerWithBackBtnContainerElement, tabBarContainerElement, activeTabContainerElement]);

const perDiemCustomUnits = getActivePolicies(allPolicies, session?.email)
.map((mappedPolicy) => ({policyID: mappedPolicy.id, customUnit: getPerDiemCustomUnit(mappedPolicy)}))
.filter(({customUnit}) => !isEmptyObject(customUnit) && !!customUnit.enabled);

const perDiemCustomUnits = getPerDiemCustomUnits(allPolicies, session?.email);
const doesPerDiemPolicyExist = perDiemCustomUnits.length > 0;

const moreThanOnePerDiemExist = perDiemCustomUnits.length > 1;
Expand Down
32 changes: 26 additions & 6 deletions src/pages/iou/request/step/IOURequestStepConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,35 @@ function IOURequestStepConfirmation({
Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_SUBRATE.getRoute(action, iouType, transactionID, reportID));
return;
}
// If there is not a report attached to the IOU with a reportID, then the participants were manually selected and the user needs taken
// back to the participants step
if (!transaction?.participantsAutoAssigned && participantsAutoAssignedFromRoute !== 'true') {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, transaction?.reportID || reportID, undefined, action), {compareParams: false});

if (transaction?.isFromGlobalCreate) {
// If the participants weren't automatically added to the transaction, then we should go back to the IOURequestStepParticipants.
if (!transaction?.participantsAutoAssigned && participantsAutoAssignedFromRoute !== 'true') {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, transaction?.reportID || reportID, undefined, action), {compareParams: false});
return;
}

// If the participant was auto-assigned, we need to keep the reportID that is already on the stack.
// This will allow the user to edit the participant field after going back and forward.
Navigation.goBack();
return;
}

// This has selected the participants from the beginning and the participant field shouldn't be editable.
navigateToStartMoneyRequestStep(requestType, iouType, transactionID, reportID, action);
}, [action, isPerDiemRequest, transaction?.participantsAutoAssigned, transaction?.reportID, participantsAutoAssignedFromRoute, requestType, iouType, transactionID, reportID]);
}, [
action,
isPerDiemRequest,
transaction?.participantsAutoAssigned,
transaction?.isFromGlobalCreate,
transaction?.reportID,
participantsAutoAssignedFromRoute,
requestType,
iouType,
transactionID,
reportID,
]);

const navigateToAddReceipt = useCallback(() => {
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_SCAN.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRouteWithoutParams()));
Expand Down
4 changes: 2 additions & 2 deletions src/pages/iou/request/step/IOURequestStepDestination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ function IOURequestStepDestination({
if (backTo) {
navigateBack();
} else if (explicitPolicyID) {
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TIME.getRoute(action, iouType, transactionID, policyExpenseReport?.reportID ?? reportID, Navigation.getActiveRoute()));
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TIME.getRoute(action, iouType, transactionID, policyExpenseReport?.reportID ?? reportID));
} else {
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TIME.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute()));
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TIME.getRoute(action, iouType, transactionID, reportID));
}
};

Expand Down
59 changes: 39 additions & 20 deletions src/pages/iou/request/step/IOURequestStepParticipants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
} from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {Participant} from '@src/types/onyx/IOU';
Expand All @@ -42,7 +41,7 @@ type IOURequestStepParticipantsProps = WithWritableReportOrNotFoundProps<typeof

function IOURequestStepParticipants({
route: {
params: {iouType, reportID, transactionID, action},
params: {iouType, reportID, transactionID, action, backTo},
},
transaction,
}: IOURequestStepParticipantsProps) {
Expand Down Expand Up @@ -109,6 +108,19 @@ function IOURequestStepParticipants({
resetDraftTransactionsCustomUnit(transactionID);
}, [isFocused, isMovingTransactionFromTrackExpense, transactionID]);

const waitForKeyboardDismiss = useCallback(
(callback: () => void) => {
if (isAndroidNative || isMobileSafari) {
KeyboardUtils.dismiss().then(() => {
callback();
});
} else {
callback();
}
},
[isAndroidNative, isMobileSafari],
);

const trackExpense = useCallback(() => {
// If coming from the combined submit/track flow and the user proceeds to just track the expense,
// we will use the track IOU type in the confirmation flow.
Expand All @@ -120,8 +132,16 @@ function IOURequestStepParticipants({
setCustomUnitRateID(transactionID, rateID);
setMoneyRequestParticipantsFromReport(transactionID, selfDMReport);
const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, CONST.IOU.TYPE.TRACK, transactionID, selfDMReportID);
Navigation.navigate(iouConfirmationPageRoute);
}, [action, selfDMReport, selfDMReportID, transactionID]);
waitForKeyboardDismiss(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need waitForKeyboardDismiss here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we type a participant from the keyboard and select it, we want to first close the keyboard and then navigate

// If the backTo parameter is set, we should navigate back to the confirmation screen that is already on the stack.
if (backTo) {
// We don't want to compare params because we just changed the participants.
Navigation.goBack(iouConfirmationPageRoute, {compareParams: false});
} else {
Navigation.navigate(iouConfirmationPageRoute);
}
});
}, [action, backTo, selfDMReport, selfDMReportID, transactionID, waitForKeyboardDismiss]);

const addParticipant = useCallback(
(val: Participant[]) => {
Expand Down Expand Up @@ -159,19 +179,6 @@ function IOURequestStepParticipants({
[iouType, reportID, trackExpense, transactionID, isMovingTransactionFromTrackExpense],
);

const handleNavigation = useCallback(
(route: Route) => {
if (isAndroidNative || isMobileSafari) {
KeyboardUtils.dismiss().then(() => {
Navigation.navigate(route);
});
} else {
Navigation.navigate(route);
}
},
[isAndroidNative, isMobileSafari],
);

const goToNextStep = useCallback(() => {
const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE;
const isShareAction = action === CONST.IOU.ACTION.SHARE;
Expand Down Expand Up @@ -202,12 +209,24 @@ function IOURequestStepParticipants({
? ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, selectedReportID.current || reportID, iouConfirmationPageRoute)
: iouConfirmationPageRoute;

handleNavigation(route);
}, [action, participants, iouType, transaction, transactionID, reportID, handleNavigation]);
waitForKeyboardDismiss(() => {
// If the backTo parameter is set, we should navigate back to the confirmation screen that is already on the stack.
if (backTo) {
// We don't want to compare params because we just changed the participants.
Navigation.goBack(route, {compareParams: false});
} else {
Navigation.navigate(route);
}
});
}, [action, participants, iouType, transaction, transactionID, reportID, waitForKeyboardDismiss, backTo]);

const navigateBack = useCallback(() => {
if (backTo) {
Navigation.goBack(backTo);
return;
}
navigateToStartMoneyRequestStep(iouRequestType, iouType, transactionID, reportID, action);
}, [iouRequestType, iouType, transactionID, reportID, action]);
}, [backTo, iouRequestType, iouType, transactionID, reportID, action]);

useEffect(() => {
const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE;
Expand Down
19 changes: 18 additions & 1 deletion src/pages/iou/request/step/IOURequestStepTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import DateUtils from '@libs/DateUtils';
import {addErrorMessage} from '@libs/ErrorUtils';
import {isValidMoneyRequestType} from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
import {getPerDiemCustomUnits} from '@libs/PolicyUtils';
import {getIOURequestPolicyID, setMoneyRequestDateAttribute} from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand Down Expand Up @@ -44,6 +45,8 @@ function IOURequestStepTime({
}: IOURequestStepTimeProps) {
const styles = useThemeStyles();
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getIOURequestPolicyID(transaction, report)}`);
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const [session] = useOnyx(ONYXKEYS.SESSION);
const {translate} = useLocalize();
const currentDateAttributes = transaction?.comment?.customUnit?.attributes?.dates;
const currentStartDate = currentDateAttributes?.start ? DateUtils.extractDate(currentDateAttributes.start) : undefined;
Expand All @@ -52,6 +55,9 @@ function IOURequestStepTime({
const shouldShowNotFound = !isValidMoneyRequestType(iouType) || isEmptyObject(transaction?.comment?.customUnit) || isEmptyObject(policy);
const isEditPage = name === SCREENS.MONEY_REQUEST.STEP_TIME_EDIT;

const perDiemCustomUnits = getPerDiemCustomUnits(allPolicies, session?.email);
const moreThanOnePerDiemExist = perDiemCustomUnits.length > 1;

const navigateBack = () => {
if (isEditPage) {
Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, reportID));
Expand All @@ -62,7 +68,18 @@ function IOURequestStepTime({
Navigation.goBack(backTo);
return;
}
Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_DESTINATION.getRoute(action, iouType, transactionID, reportID));

if (transaction?.isFromGlobalCreate) {
if (moreThanOnePerDiemExist) {
Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_DESTINATION.getRoute(action, iouType, transactionID, reportID));
return;
}

// If there is only one per diem policy, we can't override the reportID that is already on the stack to make sure we go back to the right screen.
Navigation.goBack();
}

Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_PER_DIEM.getRoute(action, iouType, transactionID, reportID));
};

const validate = (value: FormOnyxValues<typeof ONYXKEYS.FORMS.MONEY_REQUEST_TIME_FORM>) => {
Expand Down