diff --git a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx index c0f47562371a..ef946f75eba4 100644 --- a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx +++ b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx @@ -2,19 +2,20 @@ import {Str} from 'expensify-common'; import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import {InteractionManager, Keyboard} from 'react-native'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import ConfirmModal from '@components/ConfirmModal'; import {useDelegateNoAccessActions, useDelegateNoAccessState} from '@components/DelegateNoAccessModalProvider'; import ErrorMessageRow from '@components/ErrorMessageRow'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import {LockedAccountContext} from '@components/LockedAccountModalProvider'; import MenuItem from '@components/MenuItem'; +import {ModalActions} from '@components/Modal/Global/ModalContext'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import ValidateCodeActionForm from '@components/ValidateCodeActionForm'; import type {ValidateCodeFormHandle} from '@components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm'; +import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -67,7 +68,6 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) { const themeStyles = useThemeStyles(); const icons = useMemoizedLazyExpensifyIcons(['Star', 'Trashcan']); - const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const validateCodeFormRef = useRef(null); const backTo = route.params.backTo; @@ -121,29 +121,6 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) { return !securityGroups?.[`${ONYXKEYS.COLLECTION.SECURITY_GROUP}${primaryDomainSecurityGroupID}`]?.enableRestrictedPrimaryLogin; }, [isDefaultContactMethod, loginData?.validatedDate, session?.email, myDomainSecurityGroups, securityGroups]); - /** - * Toggle delete confirm modal visibility - */ - const toggleDeleteModal = useCallback((isOpen: boolean) => { - if (canUseTouchScreen() && isOpen) { - // eslint-disable-next-line @typescript-eslint/no-deprecated - InteractionManager.runAfterInteractions(() => { - setIsDeleteModalOpen(isOpen); - }); - Keyboard.dismiss(); - } else { - setIsDeleteModalOpen(isOpen); - } - }, []); - - /** - * Delete the contact method and hide the modal - */ - const confirmDeleteAndHideModal = useCallback(() => { - toggleDeleteModal(false); - deleteContactMethod(contactMethod, loginList ?? {}, backTo); - }, [contactMethod, loginList, toggleDeleteModal, backTo]); - const prevValidatedDate = usePrevious(loginData?.validatedDate); useEffect(() => { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -169,17 +146,56 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) { // eslint-disable-next-line react-hooks/exhaustive-deps -- The prevPendingDeletedLogin is a ref, so no need to add it to dependencies. }, [contactMethod, loginData?.partnerUserID, loginData?.validatedDate]); + const {showConfirmModal} = useConfirmModal(); + const showRemoveContactMethodModal = useCallback(() => { + return showConfirmModal({ + title: translate('contacts.removeContactMethod'), + prompt: translate('contacts.removeAreYouSure'), + confirmText: translate('common.yesContinue'), + cancelText: translate('common.cancel'), + shouldShowCancelButton: true, + danger: true, + }); + }, [showConfirmModal, translate]); + + /** + * Toggle delete confirm modal visibility + */ + const turnOnDeleteModal = useCallback(() => { + const openDeleteModal = async () => { + const result = await showRemoveContactMethodModal(); + // eslint-disable-next-line @typescript-eslint/no-deprecated + InteractionManager.runAfterInteractions(() => { + validateCodeFormRef.current?.focusLastSelected?.(); + }); + if (result.action !== ModalActions.CONFIRM) { + return; + } + + deleteContactMethod(contactMethod, loginList ?? {}, backTo); + }; + + if (canUseTouchScreen()) { + // eslint-disable-next-line @typescript-eslint/no-deprecated + InteractionManager.runAfterInteractions(openDeleteModal); + Keyboard.dismiss(); + return; + } + + openDeleteModal(); + }, [contactMethod, loginList, backTo, showRemoveContactMethodModal]); + const getThreeDotsMenuItems = useCallback(() => { const menuItems = []; if (isValidateCodeFormVisible && !isDefaultContactMethod) { menuItems.push({ icon: icons.Trashcan, text: translate('common.remove'), - onSelected: () => close(() => toggleDeleteModal(true)), + onSelected: () => close(turnOnDeleteModal), }); } return menuItems; - }, [isValidateCodeFormVisible, translate, toggleDeleteModal, isDefaultContactMethod, icons.Trashcan]); + }, [isValidateCodeFormVisible, translate, turnOnDeleteModal, isDefaultContactMethod, icons.Trashcan]); if (isLoadingOnyxValues || (isLoadingReportData && isEmptyObject(loginList))) { return ; @@ -205,25 +221,6 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) { const isFailedRemovedContactMethod = !!loginData.errorFields?.deletedLogin; const shouldSkipInitialValidation = route.params?.shouldSkipInitialValidation === 'true'; - const getDeleteConfirmationModal = () => ( - toggleDeleteModal(false)} - onModalHide={() => { - // eslint-disable-next-line @typescript-eslint/no-deprecated - InteractionManager.runAfterInteractions(() => { - validateCodeFormRef.current?.focusLastSelected?.(); - }); - }} - prompt={translate('contacts.removeAreYouSure')} - confirmText={translate('common.yesContinue')} - cancelText={translate('common.cancel')} - isVisible={isDeleteModalOpen && !isDefaultContactMethod} - danger - /> - ); - const getMenuItems = () => ( <> {canChangeDefaultContactMethod ? ( @@ -263,7 +260,7 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) { showDelegateNoAccessModal(); return; } - toggleDeleteModal(true); + turnOnDeleteModal(); }} /> @@ -356,7 +353,6 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) { )} {!isValidateCodeFormVisible && !!loginData.validatedDate && getMenuItems()} - {getDeleteConfirmationModal()} ); diff --git a/src/pages/settings/Profile/CustomStatus/VacationDelegatePage.tsx b/src/pages/settings/Profile/CustomStatus/VacationDelegatePage.tsx index 3608e8a7e13e..e197d6f838c2 100644 --- a/src/pages/settings/Profile/CustomStatus/VacationDelegatePage.tsx +++ b/src/pages/settings/Profile/CustomStatus/VacationDelegatePage.tsx @@ -1,7 +1,8 @@ -import React, {useCallback, useState} from 'react'; +import React, {useCallback} from 'react'; import BaseVacationDelegateSelectionComponent from '@components/BaseVacationDelegateSelectionComponent'; -import ConfirmModal from '@components/ConfirmModal'; +import {ModalActions} from '@components/Modal/Global/ModalContext'; import ScreenWrapper from '@components/ScreenWrapper'; +import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -15,12 +16,32 @@ import type {Participant} from '@src/types/onyx/IOU'; function VacationDelegatePage() { const {translate} = useLocalize(); - const [isWarningModalVisible, setIsWarningModalVisible] = useState(false); - const [newVacationDelegate, setNewVacationDelegate] = useState(''); const {login: currentUserLogin = ''} = useCurrentUserPersonalDetails(); + const {showConfirmModal} = useConfirmModal(); const [vacationDelegate] = useOnyx(ONYXKEYS.NVP_PRIVATE_VACATION_DELEGATE, {canBeMissing: true}); + const showWarningModal = useCallback( + async (delegateLogin: string) => { + const result = await showConfirmModal({ + title: translate('common.headsUp'), + prompt: translate('statusPage.vacationDelegateWarning', {nameOrEmail: getPersonalDetailByEmail(delegateLogin)?.displayName ?? delegateLogin}), + confirmText: translate('common.confirm'), + cancelText: translate('common.cancel'), + shouldShowCancelButton: true, + }); + + if (result.action === ModalActions.CONFIRM) { + await setVacationDelegate(currentUserLogin, delegateLogin, true, vacationDelegate?.delegate); + Navigation.goBack(ROUTES.SETTINGS_STATUS); + return; + } + + clearVacationDelegateError(vacationDelegate?.previousDelegate); + }, + [showConfirmModal, translate, currentUserLogin, vacationDelegate?.previousDelegate, vacationDelegate?.delegate], + ); + const onSelectRow = useCallback( (option: Participant) => { if (option?.login === vacationDelegate?.delegate) { @@ -36,48 +57,30 @@ function VacationDelegatePage() { } if (response.jsonCode === CONST.JSON_CODE.POLICY_DIFF_WARNING) { - setIsWarningModalVisible(true); - setNewVacationDelegate(option?.login ?? ''); + showWarningModal(option?.login ?? ''); return; } Navigation.goBack(ROUTES.SETTINGS_STATUS); }); }, - [currentUserLogin, vacationDelegate], + [currentUserLogin, vacationDelegate, showWarningModal], ); return ( - <> - - Navigation.goBack(ROUTES.SETTINGS_STATUS)} - cannotSetDelegateMessage={translate('statusPage.cannotSetVacationDelegate')} - includeCurrentUser={false} - /> - - { - setIsWarningModalVisible(false); - setVacationDelegate(currentUserLogin, newVacationDelegate, true, vacationDelegate?.delegate).then(() => Navigation.goBack(ROUTES.SETTINGS_STATUS)); - }} - onCancel={() => { - setIsWarningModalVisible(false); - clearVacationDelegateError(vacationDelegate?.previousDelegate); - }} - confirmText={translate('common.confirm')} - cancelText={translate('common.cancel')} + + Navigation.goBack(ROUTES.SETTINGS_STATUS)} + cannotSetDelegateMessage={translate('statusPage.cannotSetVacationDelegate')} + includeCurrentUser={false} /> - + ); } diff --git a/src/pages/settings/Report/VisibilityPage.tsx b/src/pages/settings/Report/VisibilityPage.tsx index 3287d52e9f61..4cbbe57f3ab6 100644 --- a/src/pages/settings/Report/VisibilityPage.tsx +++ b/src/pages/settings/Report/VisibilityPage.tsx @@ -1,13 +1,15 @@ import {useRoute} from '@react-navigation/native'; -import React, {useCallback, useMemo, useRef, useState} from 'react'; +import React, {useMemo} from 'react'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {ModalActions} from '@components/Modal/Global/ModalContext'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import RadioListItem from '@components/SelectionList/ListItem/RadioListItem'; +import useConfirmModal from '@hooks/useConfirmModal'; import useLocalize from '@hooks/useLocalize'; import useReportIsArchived from '@hooks/useReportIsArchived'; +import setNavigationActionToMicrotaskQueue from '@libs/Navigation/helpers/setNavigationActionToMicrotaskQueue'; import type {PlatformStackRouteProp, PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportSettingsNavigatorParamList} from '@libs/Navigation/types'; import {goBackToDetailsPage, isArchivedNonExpenseReport} from '@libs/ReportUtils'; @@ -22,12 +24,12 @@ type VisibilityProps = WithReportOrNotFoundProps & PlatformStackScreenProps>(); - const [showConfirmModal, setShowConfirmModal] = useState(false); - const shouldGoBackToDetailsPage = useRef(false); const isReportArchived = useReportIsArchived(report?.reportID); const shouldDisableVisibility = isArchivedNonExpenseReport(report, isReportArchived); const {translate} = useLocalize(); + const {showConfirmModal} = useConfirmModal(); + const visibilityOptions = useMemo( () => Object.values(CONST.REPORT.VISIBILITY) @@ -42,28 +44,32 @@ function VisibilityPage({report}: VisibilityProps) { [translate, report?.visibility], ); - const goBack = useCallback(() => { + const goBack = () => { goBackToDetailsPage(report, route.params.backTo); - }, [report, route.params.backTo]); + }; - const changeVisibility = useCallback( - (newVisibility: RoomVisibility) => { - if (!report) { - return; - } - updateRoomVisibility(report.reportID, report.visibility, newVisibility); - if (showConfirmModal) { - shouldGoBackToDetailsPage.current = true; - } else { - goBack(); - } - }, - [report, showConfirmModal, goBack], - ); + const changeVisibility = (newVisibility: RoomVisibility) => { + if (!report) { + return; + } + updateRoomVisibility(report.reportID, report.visibility, newVisibility); + setNavigationActionToMicrotaskQueue(goBack); + }; - const hideModal = useCallback(() => { - setShowConfirmModal(false); - }, []); + const showPublicVisibilityModal = async () => { + const result = await showConfirmModal({ + title: translate('common.areYouSure'), + prompt: translate('newRoomPage.publicDescription'), + confirmText: translate('common.yes'), + cancelText: translate('common.no'), + shouldShowCancelButton: true, + danger: true, + }); + if (result.action !== ModalActions.CONFIRM) { + return; + } + changeVisibility(CONST.REPORT.VISIBILITY.PUBLIC); + }; return ( { if (option.value === CONST.REPORT.VISIBILITY.PUBLIC) { - setShowConfirmModal(true); + showPublicVisibilityModal(); return; } changeVisibility(option.value); @@ -89,26 +95,6 @@ function VisibilityPage({report}: VisibilityProps) { initiallyFocusedItemKey={visibilityOptions.find((visibility) => visibility.isSelected)?.keyForList} ListItem={RadioListItem} /> - { - changeVisibility(CONST.REPORT.VISIBILITY.PUBLIC); - hideModal(); - }} - onModalHide={() => { - if (!shouldGoBackToDetailsPage.current) { - return; - } - shouldGoBackToDetailsPage.current = false; - goBack(); - }} - onCancel={hideModal} - title={translate('common.areYouSure')} - prompt={translate('newRoomPage.publicDescription')} - confirmText={translate('common.yes')} - cancelText={translate('common.no')} - danger - /> ); diff --git a/src/pages/settings/Wallet/WalletPage/index.tsx b/src/pages/settings/Wallet/WalletPage/index.tsx index 4b21f3b778d6..ae5a58adb99c 100644 --- a/src/pages/settings/Wallet/WalletPage/index.tsx +++ b/src/pages/settings/Wallet/WalletPage/index.tsx @@ -5,7 +5,6 @@ import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} fr import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import ActivityIndicator from '@components/ActivityIndicator'; -import ConfirmModal from '@components/ConfirmModal'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; @@ -93,7 +92,6 @@ function WalletPage() { const {showConfirmModal} = useConfirmModal(); const [shouldShowLoadingSpinner, setShouldShowLoadingSpinner] = useState(false); const paymentMethodButtonRef = useRef(null); - const [showConfirmDeleteModal, setShowConfirmDeleteModal] = useState(false); const [selectedCard, setSelectedCard] = useState(undefined); const [shouldShowShareButton, setShouldShowShareButton] = useState(false); const [shouldShowUnshareButton, setShouldShowUnshareButton] = useState(false); @@ -240,7 +238,6 @@ function WalletPage() { } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD && fundID) { deletePaymentCard(fundID); } - setShowConfirmDeleteModal(false); resetSelectedPaymentMethodData(); }, [ paymentMethod.selectedPaymentMethod.bankAccountID, @@ -354,6 +351,22 @@ function WalletPage() { [paymentMethod.formattedSelectedPaymentMethod, styles.mb4, styles.ph5, styles.pt3], ); + const showDeleteAccountModal = useCallback(async () => { + const result = await showConfirmModal({ + title: translate('walletPage.deleteAccount'), + prompt: translate('walletPage.deleteConfirmation'), + confirmText: translate('common.delete'), + cancelText: translate('common.cancel'), + shouldShowCancelButton: true, + danger: true, + }); + resetSelectedPaymentMethodData(); + if (result.action !== ModalActions.CONFIRM) { + return; + } + deletePaymentMethod(); + }, [showConfirmModal, translate, resetSelectedPaymentMethodData, deletePaymentMethod]); + const threeDotMenuItems = useMemo( () => [ ...(shouldUseNarrowLayout ? [bottomMountItem] : []), @@ -411,7 +424,9 @@ function WalletPage() { closeModal(() => showLockedAccountModal()); return; } - closeModal(() => setShowConfirmDeleteModal(true)); + closeModal(() => { + showDeleteAccountModal(); + }); }, }, ...(shouldShowEnableGlobalReimbursementsButton @@ -448,6 +463,7 @@ function WalletPage() { makeDefaultPaymentMethod, showLockedAccountModal, paymentMethod.selectedPaymentMethod.bankAccountID, + showDeleteAccountModal, ], ); @@ -723,18 +739,6 @@ function WalletPage() { - setShowConfirmDeleteModal(false)} - title={translate('walletPage.deleteAccount')} - prompt={translate('walletPage.deleteConfirmation')} - confirmText={translate('common.delete')} - cancelText={translate('common.cancel')} - shouldShowCancelButton - danger - onModalHide={resetSelectedPaymentMethodData} - /> ); }