Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
4659765
fixed prettier issues
abbasifaizan70 Dec 18, 2025
6d169ce
Merge branch 'main' into 76695-refactored-confirmation-modal-PR-2
abbasifaizan70 Dec 23, 2025
7981b0b
Fixed lint issues
abbasifaizan70 Dec 23, 2025
c6c1ae6
Merge branch 'Expensify:main' into 76695-refactored-confirmation-moda…
abbasifaizan70 Jan 5, 2026
4f99d79
Merge branch 'main' into 76695-refactored-confirmation-modal-PR-2
abbasifaizan70 Jan 6, 2026
76ae50f
fixed lint issues
abbasifaizan70 Jan 6, 2026
ff4f5e1
Fixed confirmation modal actions
abbasifaizan70 Jan 7, 2026
8e96ddc
remove redundent code
abbasifaizan70 Jan 7, 2026
62a2db4
Merge branch 'Expensify:main' into 76695-refactored-confirmation-moda…
abbasifaizan70 Jan 7, 2026
5b13c8a
reverted code in Globl modal
abbasifaizan70 Jan 7, 2026
c4e1d84
reverted pacage.json file
abbasifaizan70 Jan 7, 2026
8c278c6
Merge main and resolve conflicts
abbasifaizan70 Jan 8, 2026
549067c
reverted the navigation code
abbasifaizan70 Jan 8, 2026
db923f8
refactor: use async/await for showConfirmModal and move ref check ins…
abbasifaizan70 Jan 8, 2026
155d8ec
Merge main into 76695-refactored-confirmation-modal-PR-2
abbasifaizan70 Jan 14, 2026
9b794cb
resolved feedbacks
abbasifaizan70 Jan 14, 2026
4793ade
resolved conflicts with main
abbasifaizan70 Jan 14, 2026
33387e4
fixed EsLint issues
abbasifaizan70 Jan 14, 2026
960a140
fixed prettier issues
abbasifaizan70 Jan 14, 2026
a8cd81e
fix: update leaveGroupChat and leaveRoom calls to match upstream sign…
abbasifaizan70 Jan 14, 2026
3e736b8
Merge upstream/main and resolve conflicts in ReportDetailsPage.tsx
abbasifaizan70 Jan 14, 2026
15ae414
Merge branch 'main' into 76695-refactored-confirmation-modal-PR-2
abbasifaizan70 Jan 15, 2026
886fedd
fixed lint issue for react compiler
abbasifaizan70 Jan 16, 2026
7b584b2
Merge branch '76695-refactored-confirmation-modal-PR-2' of https://gi…
abbasifaizan70 Jan 16, 2026
e02c483
fixed lint issue for react compiler
abbasifaizan70 Jan 16, 2026
b23ebe4
Added missing text focus logic
abbasifaizan70 Jan 16, 2026
652f686
Merge branch 'Expensify:main' into 76695-refactored-confirmation-moda…
abbasifaizan70 Jan 16, 2026
a9e31a1
Merge branch 'Expensify:main' into 76695-refactored-confirmation-moda…
abbasifaizan70 Jan 16, 2026
beb4892
fixed lint & delete task modal issue
abbasifaizan70 Jan 19, 2026
a38b67a
Merge branch '76695-refactored-confirmation-modal-PR-2' of https://gi…
abbasifaizan70 Jan 19, 2026
0cfed45
Merge branch 'Expensify:main' into 76695-refactored-confirmation-moda…
abbasifaizan70 Jan 20, 2026
defb232
Merge branch 'Expensify:main' into 76695-refactored-confirmation-moda…
abbasifaizan70 Jan 20, 2026
b6792cf
resolved feedback
abbasifaizan70 Jan 20, 2026
9bdaac6
Merge branch '76695-refactored-confirmation-modal-PR-2' of https://gi…
abbasifaizan70 Jan 20, 2026
d8810da
Merge branch 'Expensify:main' into 76695-refactored-confirmation-moda…
abbasifaizan70 Jan 20, 2026
681d505
fixed lint issue
abbasifaizan70 Jan 20, 2026
b9cd36d
Merge branch '76695-refactored-confirmation-modal-PR-2' of https://gi…
abbasifaizan70 Jan 20, 2026
d4d1e56
fixed prettier issue
abbasifaizan70 Jan 20, 2026
6d54751
Merge branch 'Expensify:main' into 76695-refactored-confirmation-moda…
abbasifaizan70 Jan 21, 2026
f532c08
fixed lint issues
abbasifaizan70 Jan 21, 2026
042f228
Merge branch '76695-refactored-confirmation-modal-PR-2' of https://gi…
abbasifaizan70 Jan 21, 2026
71bb9da
Fixed lint issue
abbasifaizan70 Jan 21, 2026
84599bb
Fix React Compiler regressions in ReportParticipantsPage
abbasifaizan70 Jan 26, 2026
a0291cb
Merge main into branch
abbasifaizan70 Jan 26, 2026
8cccd3d
fixed navigation issue
abbasifaizan70 Jan 26, 2026
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
9 changes: 8 additions & 1 deletion src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import type {ValueOf} from 'type-fest';
import type {LocaleContextProps, LocalizedTranslate} from '@components/LocaleContextProvider';
import usePrevious from '@hooks/usePrevious';
import {isHarvestCreatedExpenseReport, isPolicyExpenseChat} from '@libs/ReportUtils';
import CONST from '@src/CONST';
import IntlStore from '@src/languages/IntlStore';
import type {TranslationPaths} from '@src/languages/types';
Expand Down Expand Up @@ -65,8 +64,16 @@

type MemberChangeMessageElement = MessageTextElement | MemberChangeMessageUserMentionElement | MemberChangeMessageRoomReferenceElement;

function isPolicyExpenseChat(report: OnyxInputOrEntry<Report>): boolean {
return report?.chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT || !!(report && typeof report === 'object' && 'isPolicyExpenseChat' in report && report.isPolicyExpenseChat);
}

function isHarvestCreatedExpenseReport(origin?: string, originalID?: string): boolean {
return !!originalID && origin === 'harvest';
}

let allReportActions: OnyxCollection<ReportActions>;
Onyx.connect({

Check warning on line 76 in src/libs/ReportActionsUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand All @@ -78,7 +85,7 @@
});

let allReports: OnyxCollection<Report>;
Onyx.connect({

Check warning on line 88 in src/libs/ReportActionsUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -87,13 +94,13 @@
});

let isNetworkOffline = false;
Onyx.connect({

Check warning on line 97 in src/libs/ReportActionsUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.NETWORK,
callback: (val) => (isNetworkOffline = val?.isOffline ?? false),
});

let deprecatedCurrentUserAccountID: number | undefined;
Onyx.connect({

Check warning on line 103 in src/libs/ReportActionsUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
// When signed out, value is undefined
Expand Down
108 changes: 45 additions & 63 deletions src/pages/ReportDetailsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import {StackActions} from '@react-navigation/native';
import reportsSelector from '@selectors/Attributes';
import {Str} from 'expensify-common';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import React, {useCallback, useEffect, useMemo} 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 @@ -26,6 +26,7 @@ import {useSearchContext} from '@components/Search/SearchContext';
import {SUPER_WIDE_RIGHT_MODALS} from '@components/WideRHPContextProvider/WIDE_RIGHT_MODALS';
import useActivePolicy from '@hooks/useActivePolicy';
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 @@ -189,8 +190,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
const [allTransactionDrafts] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {canBeMissing: true});
const [allTransactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {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 @@ -314,14 +314,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 @@ -332,16 +324,30 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
}, [report?.reportID, isOffline, isPrivateNotesFetchTriggered, isSelfDM]);

const leaveChat = useCallback(() => {
Navigation.isNavigationReady().then(() => {
if (isRootGroupChat) {
leaveGroupChat(report, quickAction?.chatReportID?.toString() === report.reportID, currentUserPersonalDetails.accountID);
return;
}
const isWorkspaceMemberLeavingWorkspaceRoom = isWorkspaceMemberLeavingWorkspaceRoomUtil(report, isPolicyEmployee, isPolicyAdmin);
leaveRoom(report, currentUserPersonalDetails.accountID, isWorkspaceMemberLeavingWorkspaceRoom);
});
if (isRootGroupChat) {
leaveGroupChat(report, quickAction?.chatReportID?.toString() === report.reportID, currentUserPersonalDetails.accountID);
return;
}

const isWorkspaceMemberLeavingWorkspaceRoom = isWorkspaceMemberLeavingWorkspaceRoomUtil(report, isPolicyEmployee, isPolicyAdmin);
leaveRoom(report, currentUserPersonalDetails.accountID, isWorkspaceMemberLeavingWorkspaceRoom);
}, [isRootGroupChat, isPolicyEmployee, isPolicyAdmin, quickAction?.chatReportID, report, currentUserPersonalDetails.accountID]);

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

const shouldShowLeaveButton = canLeaveChat(report, policy, !!reportNameValuePairs?.private_isArchived);
const shouldShowGoToWorkspace = shouldShowPolicy(policy, false, currentUserPersonalDetails?.email) && !policy?.isJoinRequestPending;
const reportForHeader = useMemo(() => getReportForHeader(report), [report]);
Expand Down Expand Up @@ -552,7 +558,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 @@ -606,6 +612,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
isTaskActionable,
isRootGroupChat,
leaveChat,
showLastMemberLeavingModal,
isSmallScreenWidth,
isRestrictedToPreferredPolicy,
preferredPolicyID,
Expand Down Expand Up @@ -900,6 +907,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
isSingleTransactionView,
moneyRequestReport,
removeTransaction,
allTransactionViolations,
isMoneyRequestReportArchived,
iouReport,
chatIOUReport,
Expand Down Expand Up @@ -979,20 +987,21 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
}
}, [requestParentReportAction, route.params.reportID, moneyRequestReport, iouTransactionID, iouReport, chatIOUReport, isChatIOUReportArchived, isSingleTransactionView]);

// 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.
if (!isTransactionDeleted.current) {
return;
}
isTransactionDeleted.current = false;
navigateToTargetUrl();
deleteTransaction();
};
}, [deleteTransaction, navigateToTargetUrl]);
const showDeleteModal = useCallback(async () => {
const {action} = await 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,
});
if (action !== ModalActions.CONFIRM) {
return;
}
navigateToTargetUrl();
deleteTransaction();
Copy link
Contributor

Choose a reason for hiding this comment

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

And just checked
Looks like now we have an issue that the useEffect fixed
@abbasifaizan70
Could you please experiment a little to see how we can fix this visual glitch?

2026-01-21.17.50.36.mov

Copy link
Contributor Author

@abbasifaizan70 abbasifaizan70 Jan 21, 2026

Choose a reason for hiding this comment

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

@ZhenjaHorbach Can you check now

Screen.Recording.2026-01-22.at.1.58.10.AM.mov

}, [showConfirmModal, translate, caseID, navigateToTargetUrl, deleteTransaction]);

const mentionReportContextValue = useMemo(() => ({currentReportID: report.reportID, exactlyMatch: true}), [report.reportID]);

Expand Down Expand Up @@ -1075,37 +1084,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
Loading
Loading