Skip to content

Commit

Permalink
Merge pull request Expensify#33482 from bernhardoj/fix/32912-split-ba…
Browse files Browse the repository at this point in the history
…sed-on-route

Detect split request based on the route params
  • Loading branch information
youssef-lr authored Feb 28, 2024
2 parents 98461f9 + 07ebdf0 commit e5b3eb2
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 20 deletions.
27 changes: 26 additions & 1 deletion src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type {ParamListBase, StackNavigationState} from '@react-navigation/native';
import type {StackScreenProps} from '@react-navigation/stack';
import {format} from 'date-fns';
import fastMerge from 'expensify-common/lib/fastMerge';
Expand Down Expand Up @@ -43,7 +44,7 @@ import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUR
import * as TransactionUtils from '@libs/TransactionUtils';
import * as UserUtils from '@libs/UserUtils';
import ViolationsUtils from '@libs/Violations/ViolationsUtils';
import type {MoneyRequestNavigatorParamList} from '@navigation/types';
import type {MoneyRequestNavigatorParamList, NavigationPartialRoute} from '@navigation/types';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand Down Expand Up @@ -260,6 +261,28 @@ function clearMoneyRequest(transactionID: string) {
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, null);
}

/**
* Update money request-related pages IOU type params
*/
function updateMoneyRequestTypeParams(routes: StackNavigationState<ParamListBase>['routes'] | NavigationPartialRoute[], newIouType: string, tab: string) {
routes.forEach((route) => {
const tabList = [CONST.TAB_REQUEST.DISTANCE, CONST.TAB_REQUEST.MANUAL, CONST.TAB_REQUEST.SCAN] as string[];
if (!route.name.startsWith('Money_Request_') && !tabList.includes(route.name)) {
return;
}
const newParams: Record<string, unknown> = {iouType: newIouType};
if (route.name === 'Money_Request_Create') {
// Both screen and nested params are needed to properly update the nested tab navigator
newParams.params = {...newParams};
newParams.screen = tab;
}
Navigation.setParams(newParams, route.key ?? '');

// Recursively update nested money request tab params
updateMoneyRequestTypeParams(route.state?.routes ?? [], newIouType, tab);
});
}

// eslint-disable-next-line @typescript-eslint/naming-convention
function startMoneyRequest_temporaryForRefactor(iouType: ValueOf<typeof CONST.IOU.TYPE>, reportID: string) {
clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID);
Expand Down Expand Up @@ -4234,6 +4257,8 @@ export {
initMoneyRequest,
startMoneyRequest_temporaryForRefactor,
resetMoneyRequestInfo,
clearMoneyRequest,
updateMoneyRequestTypeParams,
setMoneyRequestAmount_temporaryForRefactor,
setMoneyRequestBillable_temporaryForRefactor,
setMoneyRequestCreated,
Expand Down
8 changes: 6 additions & 2 deletions src/pages/iou/request/IOURequestStartPage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {useFocusEffect} from '@react-navigation/native';
import {useFocusEffect, useNavigation} from '@react-navigation/native';
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useRef, useState} from 'react';
Expand Down Expand Up @@ -71,6 +71,7 @@ function IOURequestStartPage({
}) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const navigation = useNavigation();
const [isDraggingOver, setIsDraggingOver] = useState(false);
const tabTitles = {
[CONST.IOU.TYPE.REQUEST]: translate('iou.requestMoney'),
Expand Down Expand Up @@ -120,10 +121,13 @@ function IOURequestStartPage({
if (newIouType === previousIOURequestType) {
return;
}
if (iouType === CONST.IOU.TYPE.SPLIT && transaction.isFromGlobalCreate) {
IOU.updateMoneyRequestTypeParams(navigation.getState().routes, CONST.IOU.TYPE.REQUEST, newIouType);
}
IOU.initMoneyRequest(reportID, isFromGlobalCreate, newIouType);
transactionRequestType.current = newIouType;
},
[previousIOURequestType, reportID, isFromGlobalCreate],
[previousIOURequestType, reportID, isFromGlobalCreate, iouType, navigation, transaction.isFromGlobalCreate],
);

if (!transaction.transactionID) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({
];
}

onParticipantsAdded(newSelectedOptions);
onParticipantsAdded(newSelectedOptions, newSelectedOptions.length !== 0 ? CONST.IOU.TYPE.SPLIT : undefined);
},
[participants, onParticipantsAdded],
);
Expand Down Expand Up @@ -263,7 +263,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({
return;
}

onFinish();
onFinish(CONST.IOU.TYPE.SPLIT);
}, [shouldShowSplitBillErrorMessage, onFinish]);

const footerContent = useMemo(
Expand Down
5 changes: 0 additions & 5 deletions src/pages/iou/request/step/IOURequestStepConfirmation.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,6 @@ function IOURequestStepConfirmation({
// 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) {
// When going back to the participants step, if the iou is a "request" (not a split), then the participants need to be cleared from the
// transaction so that the participant can be selected again.
if (iouType === CONST.IOU.TYPE.REQUEST) {
IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, []);
}
Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, reportID));
return;
}
Expand Down
61 changes: 51 additions & 10 deletions src/pages/iou/request/step/IOURequestStepParticipants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {useNavigation} from '@react-navigation/native';
import lodashGet from 'lodash/get';
import React, {useCallback, useEffect, useRef} from 'react';
import _ from 'underscore';
import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
import compose from '@libs/compose';
Expand Down Expand Up @@ -36,13 +38,16 @@ function IOURequestStepParticipants({
transaction: {participants = []},
}) {
const {translate} = useLocalize();
const navigation = useNavigation();
const selectedReportID = useRef(reportID);
const numberOfParticipants = useRef(participants.length);
const iouRequestType = TransactionUtils.getRequestType(transaction);
const headerTitle = translate(TransactionUtils.getHeaderTitleTranslationKey(transaction));
const isSplitRequest = iouType === CONST.IOU.TYPE.SPLIT;
const headerTitle = isSplitRequest ? translate('iou.split') : translate(TransactionUtils.getHeaderTitleTranslationKey(transaction));
const receiptFilename = lodashGet(transaction, 'filename');
const receiptPath = lodashGet(transaction, 'receipt.source');
const receiptType = lodashGet(transaction, 'receipt.type');
const newIouType = useRef();

// When the component mounts, if there is a receipt, see if the image can be read from the disk. If not, redirect the user to the starting step of the flow.
// This is because until the request is saved, the receipt file is only stored in the browsers memory as a blob:// and if the browser is refreshed, then
Expand All @@ -51,8 +56,40 @@ function IOURequestStepParticipants({
IOU.navigateToStartStepIfScanFileCannotBeRead(receiptFilename, receiptPath, () => {}, iouRequestType, iouType, transactionID, reportID, receiptType);
}, [receiptType, receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID]);

const updateRouteParams = useCallback(() => {
IOU.updateMoneyRequestTypeParams(navigation.getState().routes, newIouType.current);
}, [navigation]);

useEffect(() => {
if (!newIouType.current) {
return;
}
// Participants can be added as normal or split participants. We want to wait for the participants' data to be updated before
// updating the money request type route params reducing the overhead of the thread and preventing possible jitters in UI.
updateRouteParams();
newIouType.current = null;
}, [participants, updateRouteParams]);

const addParticipant = useCallback(
(val) => {
(val, selectedIouType) => {
const isSplit = selectedIouType === CONST.IOU.TYPE.SPLIT;
// It's only possible to switch between REQUEST and SPLIT.
// We want to update the IOU type only if it's not updated yet to prevent unnecessary updates.
if (isSplit && iouType !== CONST.IOU.TYPE.SPLIT) {
newIouType.current = CONST.IOU.TYPE.SPLIT;
} else if (!isSplit && iouType === CONST.IOU.TYPE.SPLIT) {
// Non-split can be either REQUEST or SEND. Instead of checking whether
// the current IOU type is not a REQUEST (true for SEND), we check whether the current IOU type is a SPLIT.
newIouType.current = CONST.IOU.TYPE.REQUEST;
}

// If the Onyx participants has the same items as the selected participants (val), Onyx won't update it
// thus this component won't rerender, so we can immediately update the route params.
if (newIouType.current && _.isEqual(participants, val)) {
updateRouteParams();
newIouType.current = null;
}

IOU.setMoneyRequestParticipants_temporaryForRefactor(transactionID, val);
numberOfParticipants.current = val.length;

Expand All @@ -66,15 +103,19 @@ function IOURequestStepParticipants({
// When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step.
selectedReportID.current = lodashGet(val, '[0].reportID', reportID);
},
[reportID, transactionID],
[reportID, transactionID, iouType, participants, updateRouteParams],
);

const goToNextStep = useCallback(() => {
const nextStepIOUType = numberOfParticipants.current === 1 ? iouType : CONST.IOU.TYPE.SPLIT;
IOU.setMoneyRequestTag(transactionID, '');
IOU.setMoneyRequestCategory(transactionID, '');
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(nextStepIOUType, transactionID, selectedReportID.current || reportID));
}, [iouType, transactionID, reportID]);
const goToNextStep = useCallback(
(selectedIouType) => {
const isSplit = selectedIouType === CONST.IOU.TYPE.SPLIT;
const nextStepIOUType = !isSplit && iouType !== CONST.IOU.TYPE.REQUEST ? CONST.IOU.TYPE.REQUEST : iouType;
IOU.setMoneyRequestTag(transactionID, '');
IOU.setMoneyRequestCategory(transactionID, '');
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(nextStepIOUType, transactionID, selectedReportID.current || reportID));
},
[iouType, transactionID, reportID],
);

const navigateBack = useCallback(() => {
IOUUtils.navigateToStartMoneyRequestStep(iouRequestType, iouType, transactionID, reportID);
Expand All @@ -90,7 +131,7 @@ function IOURequestStepParticipants({
>
{({didScreenTransitionEnd}) => (
<MoneyRequestParticipantsSelector
participants={participants}
participants={isSplitRequest ? participants : []}
onParticipantsAdded={addParticipant}
onFinish={goToNextStep}
iouType={iouType}
Expand Down

0 comments on commit e5b3eb2

Please sign in to comment.