diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 63c43d4fa7fa7..476739c04fe43 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -7,7 +7,6 @@ import Onyx from 'react-native-onyx'; 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'; @@ -65,6 +64,14 @@ type MemberChangeMessageRoomReferenceElement = { type MemberChangeMessageElement = MessageTextElement | MemberChangeMessageUserMentionElement | MemberChangeMessageRoomReferenceElement; +function isPolicyExpenseChat(report: OnyxInputOrEntry): 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; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 34fc05e140456..c2f630a0d8be6 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -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'; @@ -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'; @@ -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]); @@ -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) { @@ -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]); @@ -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; } @@ -606,6 +612,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail isTaskActionable, isRootGroupChat, leaveChat, + showLastMemberLeavingModal, isSmallScreenWidth, isRestrictedToPreferredPolicy, preferredPolicyID, @@ -900,6 +907,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail isSingleTransactionView, moneyRequestReport, removeTransaction, + allTransactionViolations, isMoneyRequestReportArchived, iouReport, chatIOUReport, @@ -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(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(); + }, [showConfirmModal, translate, caseID, navigateToTargetUrl, deleteTransaction]); const mentionReportContextValue = useMemo(() => ({currentReportID: report.reportID, exactlyMatch: true}), [report.reportID]); @@ -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} /> )} - { - setIsLastMemberLeavingGroupModalVisible(false); - leaveChat(); - }} - onCancel={() => setIsLastMemberLeavingGroupModalVisible(false)} - prompt={translate('groupChat.lastMemberWarning')} - confirmText={translate('common.leave')} - cancelText={translate('common.cancel')} - /> - { - 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 - /> ); diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index 33123c30d3487..fe7c6521554a9 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -1,6 +1,6 @@ import {useIsFocused} from '@react-navigation/native'; import reportsSelector from '@selectors/Attributes'; -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import type {ValueOf} from 'type-fest'; import Badge from '@components/Badge'; @@ -8,16 +8,17 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView import Button from '@components/Button'; import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu'; import type {DropdownOption, WorkspaceMemberBulkActionType} from '@components/ButtonWithDropdownMenu/types'; -import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; // eslint-disable-next-line no-restricted-imports import {Plus} from '@components/Icon/Expensicons'; +import {ModalActions} from '@components/Modal/Global/ModalContext'; import ScreenWrapper from '@components/ScreenWrapper'; import TableListItem from '@components/SelectionList/ListItem/TableListItem'; import type {ListItem, SelectionListHandle} from '@components/SelectionList/types'; import SelectionListWithModal from '@components/SelectionListWithModal'; import Text from '@components/Text'; import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; +import useConfirmModal from '@hooks/useConfirmModal'; import useFilteredSelection from '@hooks/useFilteredSelection'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -65,8 +66,8 @@ type ReportParticipantsPageProps = WithReportOrNotFoundProps & PlatformStackScre function ReportParticipantsPage({report, route}: ReportParticipantsPageProps) { const backTo = route.params.backTo; const icons = useMemoizedLazyExpensifyIcons(['User', 'MakeAdmin', 'RemoveMembers', 'FallbackAvatar']); - const [removeMembersConfirmModalVisible, setRemoveMembersConfirmModalVisible] = useState(false); const {translate, formatPhoneNumber, localeCompare} = useLocalize(); + const {showConfirmModal} = useConfirmModal(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -84,31 +85,24 @@ function ReportParticipantsPage({report, route}: ReportParticipantsPageProps) { const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const currentUserAccountID = Number(session?.accountID); const isCurrentUserAdmin = isGroupChatAdmin(report, currentUserAccountID); - const isGroupChat = useMemo(() => isGroupChatUtils(report), [report]); + const isGroupChat = isGroupChatUtils(report); const isCurrentUserGroupChatAdmin = isGroupChat && isCurrentUserAdmin; const isFocused = useIsFocused(); const {isOffline} = useNetwork(); const canSelectMultiple = isGroupChat && isCurrentUserAdmin && (isSmallScreenWidth ? isMobileSelectionModeEnabled : true); const [searchValue, setSearchValue] = useState(''); - const {chatParticipants, personalDetailsParticipants} = useMemo( - () => getReportPersonalDetailsParticipants(report, personalDetails, reportMetadata), - [report, personalDetails, reportMetadata], - ); - - const filterParticipants = useCallback( - (participant?: PersonalDetails) => { - if (!participant) { - return false; - } - const isInParticipants = chatParticipants.includes(participant.accountID); - const pendingChatMember = reportMetadata?.pendingChatMembers?.find((member) => member.accountID === participant.accountID.toString()); + const {chatParticipants, personalDetailsParticipants} = getReportPersonalDetailsParticipants(report, personalDetails, reportMetadata); - const isPendingDelete = pendingChatMember?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; - return isInParticipants && !isPendingDelete; - }, - [chatParticipants, reportMetadata?.pendingChatMembers], - ); + const filterParticipants = (participant?: PersonalDetails) => { + if (!participant) { + return false; + } + const isInParticipants = chatParticipants.includes(participant.accountID); + const pendingChatMember = reportMetadata?.pendingChatMembers?.find((member) => member.accountID === participant.accountID.toString()); + const isPendingDelete = pendingChatMember?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + return isInParticipants && !isPendingDelete; + }; const [selectedMembers, setSelectedMembers] = useFilteredSelection(personalDetailsParticipants, filterParticipants); @@ -121,6 +115,7 @@ function ReportParticipantsPage({report, route}: ReportParticipantsPageProps) { if (!personalDetails?.[accountID]) { return false; } + // When offline, we want to include the pending members with delete action as they are displayed in the list as well return !pendingMember || isOffline || pendingMember.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; }); @@ -152,101 +147,32 @@ function ReportParticipantsPage({report, route}: ReportParticipantsPageProps) { }, }); - const getParticipants = () => { - let result: MemberOption[] = []; - - for (const accountID of chatParticipants) { - const role = reportParticipants?.[accountID].role; - const details = personalDetails?.[accountID]; - - // If search value is provided, filter out members that don't match the search value - if (!details || (searchValue.trim() && !isSearchStringMatchUserDetails(details, searchValue))) { - continue; - } - - const pendingChatMember = pendingChatMembers?.findLast((member) => member.accountID === accountID.toString()); - const isSelected = selectedMembers.includes(accountID) && canSelectMultiple; - const isAdmin = role === CONST.REPORT.ROLE.ADMIN; - let roleBadge = null; - if (isAdmin) { - roleBadge = ; - } - - const pendingAction = pendingChatMember?.pendingAction ?? reportParticipants?.[accountID]?.pendingAction; - - result.push({ - keyForList: `${accountID}`, - accountID, - isSelected, - isDisabledCheckbox: accountID === currentUserAccountID, - isDisabled: pendingChatMember?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || details?.isOptimisticPersonalDetail, - text: formatPhoneNumber(getDisplayNameOrDefault(details)), - alternateText: formatPhoneNumber(details?.login ?? ''), - rightElement: roleBadge, - pendingAction, - icons: [ - { - source: details?.avatar ?? icons.FallbackAvatar, - name: formatPhoneNumber(details?.login ?? ''), - type: CONST.ICON_TYPE_AVATAR, - id: accountID, - }, - ], - }); + const toggleUser = (user: MemberOption) => { + if (user.accountID === currentUserAccountID) { + return; } - result = result.sort((a, b) => localeCompare((a.text ?? '').toLowerCase(), (b.text ?? '').toLowerCase())); - return result; + if (selectedMembers.includes(user.accountID)) { + setSelectedMembers((prevSelected) => prevSelected.filter((id) => id !== user.accountID)); + } else { + setSelectedMembers((prevSelected) => [...prevSelected, user.accountID]); + } }; - const participants = getParticipants(); - - /** - * Add user from the selectedMembers list - */ - const addUser = useCallback((accountID: number) => setSelectedMembers((prevSelected) => [...prevSelected, accountID]), [setSelectedMembers]); - - /** - * Add or remove all users passed from the selectedEmployees list - */ const toggleAllUsers = (memberList: MemberOption[]) => { const enabledAccounts = memberList.filter((member) => !member.isDisabled && !member.isDisabledCheckbox); const someSelected = enabledAccounts.some((member) => selectedMembers.includes(member.accountID)); if (someSelected) { setSelectedMembers([]); } else { - const everyAccountId = enabledAccounts.map((member) => member.accountID); - setSelectedMembers(everyAccountId); + setSelectedMembers(enabledAccounts.map((member) => member.accountID)); } }; - /** - * Remove user from the selectedMembers list - */ - const removeUser = useCallback( - (accountID: number) => { - setSelectedMembers((prevSelected) => prevSelected.filter((id) => id !== accountID)); - }, - [setSelectedMembers], - ); - - /** - * Open the modal to invite a user - */ - const inviteUser = useCallback(() => { - Navigation.navigate(ROUTES.REPORT_PARTICIPANTS_INVITE.getRoute(report.reportID, backTo)); - }, [report.reportID, backTo]); - - /** - * Remove selected users from the workspace - * Please see https://github.com/Expensify/App/blob/main/README.md#Security for more details - */ const removeUsers = () => { - // Remove the admin from the list const accountIDsToRemove = selectedMembers.filter((id) => id !== currentUserAccountID); removeFromGroupChat(report.reportID, accountIDsToRemove); setSearchValue(''); - setRemoveMembersConfirmModalVisible(false); // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => { setSelectedMembers([]); @@ -254,156 +180,143 @@ function ReportParticipantsPage({report, route}: ReportParticipantsPageProps) { }); }; - const changeUserRole = useCallback( - (role: ValueOf) => { - const accountIDsToUpdate = selectedMembers.filter((id) => report.participants?.[id].role !== role); - updateGroupChatMemberRoles(report.reportID, accountIDsToUpdate, role); - setSelectedMembers([]); - }, - [report, selectedMembers, setSelectedMembers], - ); - - /** - * Toggle user from the selectedMembers list - */ - const toggleUser = useCallback( - (user: MemberOption) => { - if (user.accountID === currentUserAccountID) { - return; - } - - // Add or remove the user if the checkbox is enabled - if (selectedMembers.includes(user.accountID)) { - removeUser(user.accountID); - } else { - addUser(user.accountID); - } - }, - [selectedMembers, addUser, removeUser, currentUserAccountID], - ); - - const customListHeader = useMemo(() => { - const header = ( - - - {translate('common.member')} - - {isGroupChat && ( - - {translate('common.role')} - - )} - - ); + const showRemoveMembersModal = async () => { + const {action} = await showConfirmModal({ + title: translate('workspace.people.removeMembersTitle', {count: selectedMembers.length}), + prompt: translate('workspace.people.removeMembersPrompt', { + count: selectedMembers.length, + memberName: formatPhoneNumber(getPersonalDetailsByIDs({accountIDs: selectedMembers, currentUserAccountID}).at(0)?.displayName ?? ''), + }), + confirmText: translate('common.remove'), + cancelText: translate('common.cancel'), + danger: true, + }); - if (canSelectMultiple) { - return header; + if (textInputRef.current) { + textInputRef.current.focus(); } - return {header}; - }, [styles, translate, isGroupChat, shouldShowTextInput, StyleUtils, canSelectMultiple]); - - const bulkActionsButtonOptions = useMemo(() => { - const options: Array> = [ - { - text: translate('workspace.people.removeMembersTitle', {count: selectedMembers.length}), - value: CONST.POLICY.MEMBERS_BULK_ACTION_TYPES.REMOVE, - icon: icons.RemoveMembers, - onSelected: () => setRemoveMembersConfirmModalVisible(true), - }, - ]; - - const isAtLeastOneAdminSelected = selectedMembers.some((accountId) => report.participants?.[accountId]?.role === CONST.REPORT.ROLE.ADMIN); - - if (isAtLeastOneAdminSelected) { - options.push({ - text: translate('workspace.people.makeMember', {count: selectedMembers.length}), - value: CONST.POLICY.MEMBERS_BULK_ACTION_TYPES.MAKE_MEMBER, - icon: icons.User, - onSelected: () => changeUserRole(CONST.REPORT.ROLE.MEMBER), - }); + if (action === ModalActions.CONFIRM) { + removeUsers(); } + }; - const isAtLeastOneMemberSelected = selectedMembers.some((accountId) => report.participants?.[accountId]?.role === CONST.REPORT.ROLE.MEMBER); + const changeUserRole = (role: ValueOf) => { + const accountIDsToUpdate = selectedMembers.filter((id) => report.participants?.[id].role !== role); + updateGroupChatMemberRoles(report.reportID, accountIDsToUpdate, role); + setSelectedMembers([]); + }; - if (isAtLeastOneMemberSelected) { - options.push({ - text: translate('workspace.people.makeAdmin', {count: selectedMembers.length}), - value: CONST.POLICY.MEMBERS_BULK_ACTION_TYPES.MAKE_ADMIN, - icon: icons.MakeAdmin, - onSelected: () => changeUserRole(CONST.REPORT.ROLE.ADMIN), - }); + const openMemberDetails = (item: MemberOption) => { + if (isGroupChat && isCurrentUserAdmin) { + Navigation.navigate(ROUTES.REPORT_PARTICIPANTS_DETAILS.getRoute(report.reportID, item.accountID, backTo)); + return; } + Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID, Navigation.getActiveRoute())); + }; - return options; - }, [icons.RemoveMembers, icons.User, icons.MakeAdmin, changeUserRole, translate, setRemoveMembersConfirmModalVisible, selectedMembers, report.participants]); + // Build participants list + let participants: MemberOption[] = []; + for (const accountID of chatParticipants) { + const role = reportParticipants?.[accountID].role; + const details = personalDetails?.[accountID]; - const headerButtons = useMemo(() => { - if (!isGroupChat) { - return; + if (!details || (searchValue.trim() && !isSearchStringMatchUserDetails(details, searchValue))) { + continue; } - return ( - - {(isSmallScreenWidth ? canSelectMultiple : selectedMembers.length > 0) ? ( - - shouldAlwaysShowDropdownMenu - pressOnEnter - customText={translate('workspace.common.selected', {count: selectedMembers.length})} - buttonSize={CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} - onPress={() => null} - isSplitButton={false} - options={bulkActionsButtonOptions} - style={[shouldUseNarrowLayout && styles.flexGrow1]} - isDisabled={!selectedMembers.length} - /> - ) : ( -