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
67 changes: 35 additions & 32 deletions src/pages/EditReportFieldPage.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {Str} from 'expensify-common';
import React, {useState} from 'react';
import React, {useCallback} from 'react';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import ConfirmModal from '@components/ConfirmModal';
import type {FormOnyxValues} from '@components/Form/types';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';

Check warning on line 6 in src/pages/EditReportFieldPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'@components/Icon/Expensicons' import is restricted from being used by a pattern. Direct imports from Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details

Check warning on line 6 in src/pages/EditReportFieldPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'@components/Icon/Expensicons' import is restricted from being used. Direct imports from @components/Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details
import {ModalActions} from '@components/Modal/Global/ModalContext';
import {useSession} from '@components/OnyxListItemProvider';
import type {PopoverMenuItem} from '@components/PopoverMenu';
import ScreenWrapper from '@components/ScreenWrapper';
import useConfirmModal from '@hooks/useConfirmModal';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import usePermissions from '@hooks/usePermissions';
Expand Down Expand Up @@ -50,13 +51,43 @@
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true});
const hasViolations = hasViolationsReportUtils(report?.reportID, transactionViolations, session?.accountID ?? CONST.DEFAULT_NUMBER_ID, session?.email ?? '');

const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
const {translate} = useLocalize();
const {showConfirmModal} = useConfirmModal();
const isReportFieldTitle = isReportFieldOfTypeTitle(reportField);
const reportFieldsEnabled = ((isPaidGroupPolicyExpenseReport(report) || isInvoiceReport(report)) && !!policy?.areReportFieldsEnabled) || isReportFieldTitle;
const hasOtherViolations =
report?.fieldList && Object.entries(report.fieldList).some(([key, field]) => key !== fieldKey && field.value === '' && !isReportFieldDisabled(report, reportField, policy));

const goBack = useCallback(() => {
if (isReportFieldTitle && report?.reportID) {
Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID, backTo));
return;
}
Navigation.goBack(backTo);
}, [isReportFieldTitle, report?.reportID, backTo]);

const showDeleteModal = useCallback(() => {
if (!report || !reportField) {
return;
}
showConfirmModal({
title: translate('workspace.reportFields.delete'),
prompt: translate('workspace.reportFields.deleteConfirmation'),
confirmText: translate('common.delete'),
cancelText: translate('common.cancel'),
danger: true,
shouldEnableNewFocusManagement: true,
}).then(({action}) => {
if (action !== ModalActions.CONFIRM) {
return;
}
goBack();
setTimeout(() => {
deleteReportField(report.reportID, reportField);
}, CONST.ANIMATED_TRANSITION);
});
}, [showConfirmModal, translate, goBack, report, reportField]);

if (!reportFieldsEnabled || !reportField || !policyField || !report || isDisabled) {
return (
<ScreenWrapper
Expand All @@ -69,22 +100,6 @@
);
}

const goBack = () => {
if (isReportFieldTitle) {
Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID, backTo));
return;
}
Navigation.goBack(backTo);
};

const handleReportFieldDelete = () => {
setIsDeleteModalVisible(false);
goBack();
setTimeout(() => {
deleteReportField(report.reportID, reportField);
}, CONST.ANIMATED_TRANSITION);
};

const fieldValue = isReportFieldTitle ? (report.reportName ?? '') : (reportField.value ?? reportField.defaultValue);

const handleReportFieldChange = (form: FormOnyxValues<typeof ONYXKEYS.FORMS.REPORT_FIELDS_EDIT_FORM>) => {
Expand Down Expand Up @@ -121,7 +136,7 @@
const isReportFieldDeletable = reportField.deletable && reportField?.fieldID !== CONST.REPORT_FIELD_TITLE_FIELD_ID;

if (isReportFieldDeletable) {
menuItems.push({icon: Expensicons.Trashcan, text: translate('common.delete'), onSelected: () => setIsDeleteModalVisible(true), shouldCallAfterModalHide: true});
menuItems.push({icon: Expensicons.Trashcan, text: translate('common.delete'), onSelected: showDeleteModal, shouldCallAfterModalHide: true});
}

const fieldName = Str.UCFirst(reportField.name);
Expand All @@ -139,18 +154,6 @@
onBackButtonPress={goBack}
/>

<ConfirmModal
title={translate('workspace.reportFields.delete')}
isVisible={isDeleteModalVisible}
onConfirm={handleReportFieldDelete}
onCancel={() => setIsDeleteModalVisible(false)}
prompt={translate('workspace.reportFields.deleteConfirmation')}
confirmText={translate('common.delete')}
cancelText={translate('common.cancel')}
danger
shouldEnableNewFocusManagement
/>

{(reportField.type === CONST.REPORT_FIELD_TYPES.TEXT || isReportFieldTitle) && (
<EditReportFieldText
fieldName={reportField.name}
Expand Down
85 changes: 41 additions & 44 deletions src/pages/ReportDetailsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import reportsSelector from '@selectors/Attributes';
import {Str} from 'expensify-common';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import AvatarWithImagePicker from '@components/AvatarWithImagePicker';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import ConfirmModal from '@components/ConfirmModal';
import DisplayNames from '@components/DisplayNames';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import MentionReportContext from '@components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext';
import MenuItem from '@components/MenuItem';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import {ModalActions} from '@components/Modal/Global/ModalContext';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ParentNavigationSubtitle from '@components/ParentNavigationSubtitle';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
Expand All @@ -23,6 +23,7 @@ import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import {useSearchContext} from '@components/Search/SearchContext';
import useAncestors from '@hooks/useAncestors';
import useConfirmModal from '@hooks/useConfirmModal';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useDeleteTransactions from '@hooks/useDeleteTransactions';
import useDuplicateTransactionsAndViolations from '@hooks/useDuplicateTransactionsAndViolations';
Expand Down Expand Up @@ -182,8 +183,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false});
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {canBeMissing: true});
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const [isLastMemberLeavingGroupModalVisible, setIsLastMemberLeavingGroupModalVisible] = useState(false);
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
const {showConfirmModal} = useConfirmModal();
const isPolicyAdmin = useMemo(() => isPolicyAdminUtil(policy), [policy]);
const isPolicyEmployee = useMemo(() => isPolicyEmployeeUtil(report?.policyID, policy), [report?.policyID, policy]);
const isPolicyExpenseChat = useMemo(() => isPolicyExpenseChatUtil(report), [report]);
Expand Down Expand Up @@ -307,14 +307,6 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {canBeMissing: true, selector: reportsSelector});
const isWorkspaceChat = useMemo(() => isWorkspaceChatUtil(report?.chatType ?? ''), [report?.chatType]);

useEffect(() => {
if (canDeleteRequest) {
return;
}

setIsDeleteModalVisible(false);
}, [canDeleteRequest]);

useEffect(() => {
// Do not fetch private notes if isLoadingPrivateNotes is already defined, or if the network is offline, or if the report is a self DM.
if (isPrivateNotesFetchTriggered || isOffline || isSelfDM) {
Expand All @@ -336,6 +328,40 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
});
}, [isRootGroupChat, isPolicyEmployee, isPolicyAdmin, quickAction?.chatReportID, report]);

const showLastMemberLeavingModal = useCallback(() => {
showConfirmModal({
title: translate('groupChat.lastMemberTitle'),
prompt: translate('groupChat.lastMemberWarning'),
confirmText: translate('common.leave'),
cancelText: translate('common.cancel'),
danger: true,
}).then(({action}) => {
if (action !== ModalActions.CONFIRM) {
return;
}
leaveChat();
});
}, [showConfirmModal, translate, leaveChat]);

// A flag to indicate whether the user chose to delete the transaction or not
const isTransactionDeleted = useRef<boolean>(false);

const showDeleteModal = useCallback(() => {
showConfirmModal({
title: caseID === CASES.DEFAULT ? translate('task.deleteTask') : translate('iou.deleteExpense', {count: 1}),
prompt: caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation', {count: 1}),
confirmText: translate('common.delete'),
cancelText: translate('common.cancel'),
danger: true,
shouldEnableNewFocusManagement: true,
}).then(({action}) => {
if (action !== ModalActions.CONFIRM) {
return;
}
isTransactionDeleted.current = true;
});
}, [showConfirmModal, translate, caseID]);

const shouldShowLeaveButton = canLeaveChat(report, policy, !!reportNameValuePairs?.private_isArchived);
const shouldShowGoToWorkspace = shouldShowPolicy(policy, false, currentUserPersonalDetails?.email) && !policy?.isJoinRequestPending;

Expand Down Expand Up @@ -526,7 +552,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
isAnonymousAction: true,
action: () => {
if (getParticipantsAccountIDsForDisplay(report, false, true).length === 1 && isRootGroupChat) {
setIsLastMemberLeavingGroupModalVisible(true);
showLastMemberLeavingModal();
return;
}

Expand Down Expand Up @@ -580,6 +606,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
isTaskActionable,
isRootGroupChat,
leaveChat,
showLastMemberLeavingModal,
isSmallScreenWidth,
isRestrictedToPreferredPolicy,
preferredPolicyID,
Expand Down Expand Up @@ -918,9 +945,6 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
}
}, [iouTransactionID, requestParentReportAction, isSingleTransactionView, moneyRequestReport, isChatIOUReportArchived, iouReport, chatIOUReport]);

// A flag to indicate whether the user chose to delete the transaction or not
const isTransactionDeleted = useRef<boolean>(false);

useEffect(() => {
return () => {
// Perform the actual deletion after the details page is unmounted. This prevents the [Deleted ...] text from briefly appearing when dismissing the modal.
Expand Down Expand Up @@ -1014,37 +1038,10 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
key={CONST.REPORT_DETAILS_MENU_ITEM.DELETE}
icon={expensifyIcons.Trashcan}
title={caseID === CASES.DEFAULT ? translate('common.delete') : translate('reportActionContextMenu.deleteAction', {action: requestParentReportAction})}
onPress={() => setIsDeleteModalVisible(true)}
onPress={showDeleteModal}
/>
)}
</ScrollView>
<ConfirmModal
danger
title={translate('groupChat.lastMemberTitle')}
isVisible={isLastMemberLeavingGroupModalVisible}
onConfirm={() => {
setIsLastMemberLeavingGroupModalVisible(false);
leaveChat();
}}
onCancel={() => setIsLastMemberLeavingGroupModalVisible(false)}
prompt={translate('groupChat.lastMemberWarning')}
confirmText={translate('common.leave')}
cancelText={translate('common.cancel')}
/>
<ConfirmModal
title={caseID === CASES.DEFAULT ? translate('task.deleteTask') : translate('iou.deleteExpense', {count: 1})}
isVisible={isDeleteModalVisible}
onConfirm={() => {
setIsDeleteModalVisible(false);
isTransactionDeleted.current = true;
}}
onCancel={() => setIsDeleteModalVisible(false)}
prompt={caseID === CASES.DEFAULT ? translate('task.deleteConfirmation') : translate('iou.deleteConfirmation', {count: 1})}
confirmText={translate('common.delete')}
cancelText={translate('common.cancel')}
danger
shouldEnableNewFocusManagement
/>
</FullPageNotFoundView>
</ScreenWrapper>
);
Expand Down
55 changes: 27 additions & 28 deletions src/pages/ReportParticipantDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import React, {useCallback} from 'react';
import {View} from 'react-native';
import Avatar from '@components/Avatar';
import Button from '@components/Button';
import ConfirmModal from '@components/ConfirmModal';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import MenuItem from '@components/MenuItem';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import {ModalActions} from '@components/Modal/Global/ModalContext';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import useConfirmModal from '@hooks/useConfirmModal';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -39,8 +40,7 @@ function ReportParticipantDetails({report, route}: ReportParticipantDetailsPageP
const StyleUtils = useStyleUtils();
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false});
const currentUserPersonalDetails = useCurrentUserPersonalDetails();

const [isRemoveMemberConfirmModalVisible, setIsRemoveMemberConfirmModalVisible] = React.useState(false);
const {showConfirmModal} = useConfirmModal();

const accountID = Number(route.params.accountID);
const backTo = ROUTES.REPORT_PARTICIPANTS.getRoute(report?.reportID, route.params.backTo);
Expand All @@ -51,11 +51,22 @@ function ReportParticipantDetails({report, route}: ReportParticipantDetailsPageP
const displayName = formatPhoneNumber(getDisplayNameOrDefault(details));
const isCurrentUserAdmin = isGroupChatAdmin(report, currentUserPersonalDetails?.accountID);
const isSelectedMemberCurrentUser = accountID === currentUserPersonalDetails?.accountID;
const removeUser = useCallback(() => {
setIsRemoveMemberConfirmModalVisible(false);
removeFromGroupChat(report?.reportID, [accountID]);
Navigation.goBack(backTo);
}, [backTo, report?.reportID, accountID]);

const showRemoveUserModal = useCallback(() => {
showConfirmModal({
danger: true,
title: translate('workspace.people.removeGroupMemberButtonTitle'),
prompt: translate('workspace.people.removeMemberPrompt', {memberName: displayName}),
confirmText: translate('common.remove'),
cancelText: translate('common.cancel'),
}).then(({action}) => {
if (action !== ModalActions.CONFIRM) {
return;
}
removeFromGroupChat(report?.reportID, [accountID]);
Navigation.goBack(backTo);
});
}, [showConfirmModal, translate, displayName, report?.reportID, accountID, backTo]);

const navigateToProfile = useCallback(() => {
Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()));
Expand Down Expand Up @@ -95,26 +106,14 @@ function ReportParticipantDetails({report, route}: ReportParticipantDetailsPageP
</Text>
)}
{isCurrentUserAdmin && (
<>
<Button
text={translate('workspace.people.removeGroupMemberButtonTitle')}
onPress={() => setIsRemoveMemberConfirmModalVisible(true)}
isDisabled={isSelectedMemberCurrentUser}
icon={icons.RemoveMembers}
iconStyles={StyleUtils.getTransformScaleStyle(0.8)}
style={styles.mv5}
/>
<ConfirmModal
danger
title={translate('workspace.people.removeGroupMemberButtonTitle')}
isVisible={isRemoveMemberConfirmModalVisible}
onConfirm={removeUser}
onCancel={() => setIsRemoveMemberConfirmModalVisible(false)}
prompt={translate('workspace.people.removeMemberPrompt', {memberName: displayName})}
confirmText={translate('common.remove')}
cancelText={translate('common.cancel')}
/>
</>
<Button
text={translate('workspace.people.removeGroupMemberButtonTitle')}
onPress={showRemoveUserModal}
isDisabled={isSelectedMemberCurrentUser}
icon={icons.RemoveMembers}
iconStyles={StyleUtils.getTransformScaleStyle(0.8)}
style={styles.mv5}
/>
)}
</View>
<View style={styles.w100}>
Expand Down
Loading
Loading