Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
import type {NavigationAction} from '@react-navigation/native';
import {usePreventRemove} from '@react-navigation/native';
import React, {memo, useCallback, useRef, useState} from 'react';
import ConfirmModal from '@components/ConfirmModal';
import useBeforeRemove from '@hooks/useBeforeRemove';
import useLocalize from '@hooks/useLocalize';
import navigationRef from '@libs/Navigation/navigationRef';
import type DiscardChangesConfirmationProps from './types';

function DiscardChangesConfirmation({getHasUnsavedChanges}: DiscardChangesConfirmationProps) {
const {translate} = useLocalize();
const [isVisible, setIsVisible] = useState(false);
const shouldAllowNavigation = useRef(false);
const blockedNavigationAction = useRef<NavigationAction | undefined>(undefined);

useBeforeRemove(
useCallback(
(e) => {
if (!getHasUnsavedChanges()) {
return;
}
const hasUnsavedChanges = getHasUnsavedChanges();
const shouldPrevent = hasUnsavedChanges && !shouldAllowNavigation.current;

e.preventDefault();
blockedNavigationAction.current = e.data.action;
setIsVisible(true);
},
[getHasUnsavedChanges],
),
usePreventRemove(
shouldPrevent,
useCallback(({data}) => {
blockedNavigationAction.current = data.action;
setIsVisible(true);
}, []),
);

return (
Expand All @@ -36,11 +33,18 @@ function DiscardChangesConfirmation({getHasUnsavedChanges}: DiscardChangesConfir
cancelText={translate('common.cancel')}
onConfirm={() => {
setIsVisible(false);
shouldAllowNavigation.current = true;
if (blockedNavigationAction.current) {
navigationRef.current?.dispatch(blockedNavigationAction.current);
blockedNavigationAction.current = undefined;
} else {
navigationRef.current?.goBack();
}
}}
onCancel={() => setIsVisible(false)}
onCancel={() => {
setIsVisible(false);
blockedNavigationAction.current = undefined;
}}
shouldHandleNavigationBack
/>
);
Expand Down
37 changes: 23 additions & 14 deletions src/pages/iou/request/step/IOURequestStepDescription.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import lodashIsEmpty from 'lodash/isEmpty';
import React, {useCallback, useMemo, useRef} from 'react';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {InteractionManager, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import FormProvider from '@components/Form/FormProvider';
Expand Down Expand Up @@ -69,8 +69,9 @@ function IOURequestStepDescription({
return isEditingSplit && !lodashIsEmpty(splitDraftTransaction) ? (splitDraftTransaction?.comment?.comment ?? '') : (transaction?.comment?.comment ?? '');
}, [isTransactionDraft, iouType, isEditingSplit, splitDraftTransaction, transaction?.comment?.comment]);

const descriptionRef = useRef(currentDescriptionInMarkdown);
const isSavedRef = useRef(false);
const [currentDescription, setCurrentDescription] = useState(currentDescriptionInMarkdown);
const [isSaved, setIsSaved] = useState(false);
const shouldNavigateAfterSaveRef = useRef(false);
useRestartOnReceiptFailure(transaction, reportID, iouType, action);
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const currentUserAccountIDParam = currentUserPersonalDetails.accountID;
Expand Down Expand Up @@ -116,32 +117,39 @@ function IOURequestStepDescription({
[isDescriptionRequired, translate],
);

const navigateBack = () => {
const navigateBack = useCallback(() => {
Navigation.goBack(backTo);
};
}, [backTo]);

useEffect(() => {
if (!isSaved || !shouldNavigateAfterSaveRef.current) {
return;
}
shouldNavigateAfterSaveRef.current = false;
navigateBack();
}, [isSaved, navigateBack]);

const updateDescriptionRef = (value: string) => {
descriptionRef.current = value;
setCurrentDescription(value);
};

const updateComment = (value: FormOnyxValues<typeof ONYXKEYS.FORMS.MONEY_REQUEST_DESCRIPTION_FORM>) => {
if (!transaction?.transactionID) {
return;
}

isSavedRef.current = true;
const newComment = value.moneyRequestComment.trim();

// Only update comment if it has changed
if (newComment === currentDescriptionInMarkdown) {
navigateBack();
setIsSaved(true);
shouldNavigateAfterSaveRef.current = true;
return;
}

// In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value
if (isEditingSplit) {
setDraftSplitTransaction(transaction?.transactionID, splitDraftTransaction, {comment: newComment});
navigateBack();
setIsSaved(true);
shouldNavigateAfterSaveRef.current = true;
return;
}

Expand All @@ -161,7 +169,8 @@ function IOURequestStepDescription({
);
}

navigateBack();
setIsSaved(true);
shouldNavigateAfterSaveRef.current = true;
};

// eslint-disable-next-line rulesdir/no-negated-variables
Expand Down Expand Up @@ -218,10 +227,10 @@ function IOURequestStepDescription({
});
}}
getHasUnsavedChanges={() => {
if (isSavedRef.current) {
if (isSaved) {
return false;
}
return descriptionRef.current !== currentDescriptionInMarkdown;
return currentDescription !== currentDescriptionInMarkdown;
}}
/>
</StepScreenWrapper>
Expand Down
39 changes: 23 additions & 16 deletions src/pages/iou/request/step/IOURequestStepMerchant.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useCallback, useRef} from 'react';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {InteractionManager, View} from 'react-native';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
Expand Down Expand Up @@ -56,8 +56,9 @@ function IOURequestStepMerchant({
const merchant = getTransactionDetails(isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction)?.merchant;
const isEmptyMerchant = merchant === '' || merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
const initialMerchant = isEmptyMerchant ? '' : merchant;
const merchantRef = useRef(initialMerchant);
const isSavedRef = useRef(false);
const [currentMerchant, setCurrentMerchant] = useState(initialMerchant);
const [isSaved, setIsSaved] = useState(false);
const shouldNavigateAfterSaveRef = useRef(false);
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const currentUserAccountIDParam = currentUserPersonalDetails.accountID;
const currentUserEmailParam = currentUserPersonalDetails.login ?? '';
Expand All @@ -66,9 +67,17 @@ function IOURequestStepMerchant({

const isMerchantRequired = isPolicyExpenseChat(report) || isExpenseRequest(report) || transaction?.participants?.some((participant) => !!participant.isPolicyExpenseChat);

const navigateBack = () => {
const navigateBack = useCallback(() => {
Navigation.goBack(backTo);
};
}, [backTo]);

useEffect(() => {
if (!isSaved || !shouldNavigateAfterSaveRef.current) {
return;
}
shouldNavigateAfterSaveRef.current = false;
navigateBack();
}, [isSaved, navigateBack]);

const validate = useCallback(
(value: FormOnyxValues<typeof ONYXKEYS.FORMS.MONEY_REQUEST_MERCHANT_FORM>) => {
Expand All @@ -92,27 +101,24 @@ function IOURequestStepMerchant({
);

const updateMerchantRef = (value: string) => {
merchantRef.current = value;
setCurrentMerchant(value);
};

const updateMerchant = (value: FormOnyxValues<typeof ONYXKEYS.FORMS.MONEY_REQUEST_MERCHANT_FORM>) => {
isSavedRef.current = true;
const newMerchant = value.moneyRequestMerchant?.trim();

// In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value
if (isEditingSplitBill) {
setDraftSplitTransaction(transactionID, splitDraftTransaction, {merchant: newMerchant});
navigateBack();
setIsSaved(true);
shouldNavigateAfterSaveRef.current = true;
return;
}

// In case the merchant hasn't been changed, do not make the API request.
// In case the merchant has been set to empty string while current merchant is partial, do nothing too.
if (newMerchant === merchant || (newMerchant === '' && merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT)) {
navigateBack();
setIsSaved(true);
shouldNavigateAfterSaveRef.current = true;
return;
}
// When creating/editing an expense, newMerchant can be blank so we fall back on PARTIAL_TRANSACTION_MERCHANT
setMoneyRequestMerchant(transactionID, newMerchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, !isEditing);
if (isEditing) {
updateMoneyRequestMerchant(
Expand All @@ -127,7 +133,8 @@ function IOURequestStepMerchant({
isASAPSubmitBetaEnabled,
);
}
navigateBack();
setIsSaved(true);
shouldNavigateAfterSaveRef.current = true;
};

return (
Expand Down Expand Up @@ -171,10 +178,10 @@ function IOURequestStepMerchant({
});
}}
getHasUnsavedChanges={() => {
if (isSavedRef.current) {
if (isSaved) {
return false;
}
return merchantRef.current !== initialMerchant;
return currentMerchant !== initialMerchant;
}}
/>
</StepScreenWrapper>
Expand Down
Loading