import type { FC } from '../../lib/teact/teact'; import React, { memo, useEffect, useMemo, useState, } from '../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../global'; import type { ApiChat, ApiChatMember } from '../../api/types'; import type { IRadioOption } from '../ui/CheckboxGroup'; import { getHasAdminRight, getPrivateChatUserId, getUserFirstOrLastName, getUserFullName, isChatBasicGroup, isChatChannel, isChatSuperGroup, isUserId, } from '../../global/helpers'; import { selectCanDeleteSelectedMessages, selectChatFullInfo, selectCurrentChat, selectCurrentMessageIds, selectCurrentMessageList, selectSenderFromMessage, selectSendersFromSelectedMessages, selectTabState, selectUser, } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import { buildCollectionByCallback } from '../../util/iteratees'; import renderText from '../common/helpers/renderText'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; import useOldLang from '../../hooks/useOldLang'; import usePrevious from '../../hooks/usePrevious'; import useManagePermissions from '../right/hooks/useManagePermissions'; import Avatar from '../common/Avatar'; import AvatarList from '../common/AvatarList'; import Icon from '../common/icons/Icon'; import PermissionCheckboxList from '../main/PermissionCheckboxList'; import Button from '../ui/Button'; import CheckboxGroup from '../ui/CheckboxGroup'; import ListItem from '../ui/ListItem'; import Modal from '../ui/Modal'; import styles from './DeleteSelectedMessageModal.module.scss'; export type OwnProps = { isOpen: boolean; isSchedule: boolean; onClose: () => void; }; type StateProps = { chat?: ApiChat; isGroup?: boolean; isChannel?: boolean; isSuperGroup?: boolean; selectedMessageIds?: number[]; canDeleteForAll?: boolean; contactName?: string; currentUserId?: string; willDeleteForCurrentUserOnly?: boolean; willDeleteForAll?: boolean; messageIds: number[] | undefined; adminMembersById?: Record; canBanUsers?: boolean; }; const DeleteSelectedMessageModal: FC = ({ chat, isChannel, isGroup, isSuperGroup, isOpen, isSchedule, currentUserId, selectedMessageIds, canDeleteForAll, contactName, willDeleteForCurrentUserOnly, willDeleteForAll, messageIds, onClose, adminMembersById, canBanUsers, }) => { const { deleteMessages, reportMessages, deleteChatMember, deleteScheduledMessages, exitMessageSelectMode, updateChatMemberBannedRights, } = getActions(); const prevIsOpen = usePrevious(isOpen); const oldLang = useOldLang(); const lang = useLang(); const { permissions, havePermissionChanged, handlePermissionChange, resetPermissions, } = useManagePermissions(chat?.defaultBannedRights); const [chosenDeleteOption, setChosenDeleteOption] = useState(undefined); const [chosenBanOption, setChosenBanOptions] = useState(undefined); const [chosenSpanOption, setChosenSpanOptions] = useState(undefined); const [isMediaDropdownOpen, setIsMediaDropdownOpen] = useState(false); const [isAdditionalOptionsVisible, setIsAdditionalOptionsVisible] = useState(false); const senderList = useMemo(() => { if (isChannel) { return undefined; } return selectSendersFromSelectedMessages(getGlobal(), chat); // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps }, [chat, isChannel, isOpen]); const isSenderOwner = useMemo(() => { if (!senderList) { return undefined; } return senderList.some((sender) => sender && adminMembersById && adminMembersById[sender.id] && adminMembersById[sender.id].isOwner); }, [senderList, adminMembersById]); const userList = useMemo(() => { const usersById = getGlobal().users.byId; if (!senderList || isSchedule) return []; const uniqueUserIds = new Set(senderList.map((user) => user!.id)); return Array.from(uniqueUserIds) .map((id) => usersById[id]) .filter(Boolean); }, [isSchedule, senderList]); const nestedOptionsWithAvatarList = useLastCallback(() => { return userList.map((user) => ({ value: `${user.id}`, label: getUserFullName(user) || '', leftElement: , })); }); const showAdditionalOptions = useMemo(() => { return !userList.some((user) => user.id === currentUserId); }, [userList, currentUserId]); const userNames = useMemo(() => { const usersById = getGlobal().users.byId; if (!senderList || isSchedule) return {}; const uniqueUserIds = new Set(senderList.map((user) => user!.id)); const userIds = Array.from(uniqueUserIds).map((userId) => usersById[userId]); return buildCollectionByCallback(userIds, (user) => [user.id, getUserFullName(user)]); }, [isSchedule, senderList]); const ACTION_SPAM_OPTION: IRadioOption[] = useMemo(() => { return [ { value: selectedMessageIds && userList.length >= 2 ? 'spam' : userList?.[0]?.id, label: oldLang('ReportSpamTitle'), nestedOptions: selectedMessageIds && userList.length >= 2 ? [ ...nestedOptionsWithAvatarList(), ] : undefined, }, ]; }, [oldLang, selectedMessageIds, userList]); const ACTION_DELETE_OPTION: IRadioOption[] = useMemo(() => { return [ { value: selectedMessageIds && userList.length >= 2 ? 'delete_all' : userList?.[0]?.id, label: selectedMessageIds && userList.length >= 2 ? oldLang('DeleteAllFromUsers') : oldLang('DeleteAllFrom', Object.values(userNames)[0]), nestedOptions: selectedMessageIds && userList.length >= 2 ? [ ...nestedOptionsWithAvatarList(), ] : undefined, }, ]; }, [oldLang, selectedMessageIds, userList, userNames]); const ACTION_BAN_OPTION: IRadioOption[] = useMemo(() => { return [ { value: selectedMessageIds && userList.length >= 2 ? 'ban' : userList?.[0]?.id, label: selectedMessageIds && userList.length >= 2 ? (isAdditionalOptionsVisible ? oldLang('DeleteRestrictUsers') : oldLang('DeleteBanUsers')) : (isAdditionalOptionsVisible ? oldLang('KickFromSupergroup') : oldLang('DeleteBan', Object.values(userNames)[0])), nestedOptions: selectedMessageIds && userList.length >= 2 ? [ ...nestedOptionsWithAvatarList(), ] : undefined, }, ]; }, [isAdditionalOptionsVisible, oldLang, selectedMessageIds, userList, userNames]); const toggleAdditionalOptions = useLastCallback(() => { setIsAdditionalOptionsVisible((prev) => !prev); }); const handleDeleteMessageForAll = useLastCallback(() => { onClose(); deleteMessages({ messageIds: selectedMessageIds!, shouldDeleteForAll: true }); }); const filterMessageIdByUserId = useLastCallback((userIds: string[], selectedMessageIdList: number[]) => { return selectedMessageIdList.filter((msgId) => { const sender = selectSenderFromMessage(getGlobal(), chat, msgId); return sender && userIds.includes(sender.id); }); }); const handleDeleteMessages = useLastCallback((filteredMessageIdList: number[]) => { if (filteredMessageIdList && filteredMessageIdList.length) { deleteMessages({ messageIds: filteredMessageIdList, shouldDeleteForAll: true }); } }); const handleDeleteMember = useLastCallback((filteredUserIdList: string[]) => { filteredUserIdList.forEach((userId) => { deleteChatMember({ chatId: chat!.id, userId }); }); }); const handleUpdateChatMemberBannedRights = useLastCallback((filteredUserIdList: string[]) => { filteredUserIdList.forEach((userId) => { updateChatMemberBannedRights({ chatId: chat!.id, userId, bannedRights: permissions, }); }); }); const handleDeleteMessageForSelf = useLastCallback(() => { if (isSchedule) { deleteScheduledMessages({ messageIds: selectedMessageIds! }); } else if (!isSenderOwner && (chosenSpanOption || chosenDeleteOption || chosenBanOption) && (isGroup || isSuperGroup)) { if (chosenSpanOption) { const userIdList = chosenSpanOption.filter((option) => !Number.isNaN(Number(option))); const filteredMessageIdList = filterMessageIdByUserId(userIdList, selectedMessageIds!); if (filteredMessageIdList && filteredMessageIdList.length) { reportMessages({ messageIds: filteredMessageIdList, reason: 'spam', description: '' }); } } if (chosenDeleteOption) { const userIdList = chosenDeleteOption.filter((option) => !Number.isNaN(Number(option))); const filteredMessageIdList = filterMessageIdByUserId(userIdList, messageIds!); handleDeleteMessages(filteredMessageIdList); } if (chosenBanOption && !havePermissionChanged) { const userIdList = chosenBanOption.filter((option) => !Number.isNaN(Number(option))); const filteredUserIdList = userIdList.filter((userId) => selectedMessageIds?.some((msgId) => { const sender = selectSenderFromMessage(getGlobal(), chat, msgId); return sender && sender.id === userId; })); handleDeleteMember(filteredUserIdList); const filteredMessageIdList = filterMessageIdByUserId(userIdList, selectedMessageIds!); handleDeleteMessages(filteredMessageIdList); } if (chosenBanOption && havePermissionChanged) { const userIdList = chosenBanOption.filter((option) => !Number.isNaN(Number(option))); const filteredUserIdList = userIdList.filter((userId) => selectedMessageIds?.some((msgId) => { const sender = selectSenderFromMessage(getGlobal(), chat, msgId); return sender && sender.id === userId; })); handleUpdateChatMemberBannedRights(filteredUserIdList); } } else { deleteMessages({ messageIds: selectedMessageIds!, shouldDeleteForAll: false }); } onClose(); }); const onCloseHandler = useLastCallback(() => { onClose(); }); const handleDeleteOptionChange = useLastCallback((options: string[]) => { setChosenDeleteOption(options); }); const handleBanOptionChange = useLastCallback((options: string[]) => { setChosenBanOptions(options); }); const handleSpanOptionChange = useLastCallback((options: string[]) => { setChosenSpanOptions(options); }); useEffect(() => { if (!isOpen && prevIsOpen) { exitMessageSelectMode(); setChosenSpanOptions(undefined); setChosenDeleteOption(undefined); setChosenBanOptions(undefined); setIsMediaDropdownOpen(false); setIsAdditionalOptionsVisible(false); resetPermissions(); } }, [exitMessageSelectMode, isOpen, prevIsOpen, resetPermissions]); function renderHeader() { return (
{(showAdditionalOptions && !canDeleteForAll && !isSchedule) && ( )}

{oldLang('Chat.DeleteMessagesConfirmation', selectedMessageIds?.length)}

); } function renderAdditionalActionOptions() { return (
= 2} /> = 2} /> {!isSenderOwner && canBanUsers && ( = 2} /> )}
); } function renderPartiallyRestrictedUser() { return (

{oldLang('UserRestrictionsCanDoUsers', userList.length)}

); } if (!selectedMessageIds) { return undefined; } return (
{renderHeader()} {!showAdditionalOptions &&

{lang('AreYouSureDeleteFewMessages')}

} {(showAdditionalOptions && !canDeleteForAll && !isSchedule && (isGroup || isSuperGroup)) && ( <>

{oldLang('DeleteAdditionalActions')}

{renderAdditionalActionOptions()} {renderPartiallyRestrictedUser()} { chosenBanOption && canBanUsers && chosenBanOption?.length ? ( {oldLang(isAdditionalOptionsVisible ? 'DeleteToggleBanUsers' : 'DeleteToggleRestrictUsers')} ) : setIsAdditionalOptionsVisible(false) } )} {willDeleteForCurrentUserOnly && lang('DeleteForMeDescription')} {(willDeleteForAll && !showAdditionalOptions) && lang('DeleteForEveryoneDescription')}
{canDeleteForAll && ( )}
); }; export default memo(withGlobal( (global, { isSchedule }): StateProps => { const { messageIds: selectedMessageIds } = selectTabState(global).selectedMessages || {}; const { canDeleteForAll } = selectCanDeleteSelectedMessages(global); const chat = selectCurrentChat(global); const chatFullInfo = chat && selectChatFullInfo(global, chat.id); const { threadId, type } = selectCurrentMessageList(global) || {}; const messageIds = chat && selectCurrentMessageIds(global, chat.id, threadId!, type!); const isChannel = Boolean(chat) && isChatChannel(chat); const isGroup = Boolean(chat) && isChatBasicGroup(chat); const isSuperGroup = Boolean(chat) && isChatSuperGroup(chat); const contactName = chat && isUserId(chat.id) ? getUserFirstOrLastName(selectUser(global, getPrivateChatUserId(chat)!)) : undefined; const adminMembersById = chatFullInfo?.adminMembersById; const canBanUsers = chat && (chat.isCreator || getHasAdminRight(chat, 'banUsers')); const willDeleteForCurrentUserOnly = chat && isChatBasicGroup(chat) && !canDeleteForAll; const willDeleteForAll = chat && isChatSuperGroup(chat); return { chat, isGroup, isChannel, isSuperGroup, selectedMessageIds, currentUserId: global.currentUserId, canDeleteForAll: !isSchedule && canDeleteForAll, contactName, willDeleteForCurrentUserOnly, willDeleteForAll, messageIds, adminMembersById, canBanUsers, }; }, )(DeleteSelectedMessageModal));