import type { FC } from '../../../lib/teact/teact'; import React, { memo, useCallback, useMemo, useRef, useState, } from '../../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../../global'; import type { ApiChatMember, ApiUserStatus } from '../../../api/types'; import { ManagementScreens } from '../../../types'; import { unique } from '../../../util/iteratees'; import { selectChat } from '../../../global/selectors'; import { sortUserIds, isChatChannel, filterUsersByName, sortChatIds, isUserBot, getHasAdminRight, isChatBasicGroup, } from '../../../global/helpers'; import useLang from '../../../hooks/useLang'; import useHistoryBack from '../../../hooks/useHistoryBack'; import useInfiniteScroll from '../../../hooks/useInfiniteScroll'; import useKeyboardListNavigation from '../../../hooks/useKeyboardListNavigation'; import PrivateChatInfo from '../../common/PrivateChatInfo'; import NothingFound from '../../common/NothingFound'; import ListItem from '../../ui/ListItem'; import InputText from '../../ui/InputText'; import InfiniteScroll from '../../ui/InfiniteScroll'; import Loading from '../../ui/Loading'; import DeleteMemberModal from '../DeleteMemberModal'; import Switcher from '../../ui/Switcher'; type OwnProps = { chatId: string; isActive: boolean; noAdmins?: boolean; onClose: NoneToVoidFunction; onScreenSelect?: (screen: ManagementScreens) => void; onChatMemberSelect?: (memberId: string, isPromotedByCurrentUser?: boolean) => void; }; type StateProps = { userStatusesById: Record; members?: ApiChatMember[]; adminMembersById?: Record; isChannel?: boolean; localContactIds?: string[]; searchQuery?: string; isSearching?: boolean; localUserIds?: string[]; globalUserIds?: string[]; currentUserId?: string; canDeleteMembers?: boolean; isBasicGroup?: boolean; areParticipantsHidden?: boolean; }; const ManageGroupMembers: FC = ({ chatId, noAdmins, members, adminMembersById, userStatusesById, isChannel, isActive, globalUserIds, localContactIds, localUserIds, isSearching, searchQuery, currentUserId, canDeleteMembers, isBasicGroup, areParticipantsHidden, onClose, onScreenSelect, onChatMemberSelect, }) => { const { openChat, setUserSearchQuery, closeManagement, toggleParticipantsHidden, } = getActions(); const lang = useLang(); // eslint-disable-next-line no-null/no-null const inputRef = useRef(null); // eslint-disable-next-line no-null/no-null const containerRef = useRef(null); const [deletingUserId, setDeletingUserId] = useState(); const adminIds = useMemo(() => { return noAdmins && adminMembersById ? Object.keys(adminMembersById) : []; }, [adminMembersById, noAdmins]); const memberIds = useMemo(() => { // No need for expensive global updates on users, so we avoid them const usersById = getGlobal().users.byId; if (!members || !usersById) { return []; } const userIds = sortUserIds( members.map(({ userId }) => userId), usersById, userStatusesById, ); return noAdmins ? userIds.filter((userId) => !adminIds.includes(userId)) : userIds; }, [members, userStatusesById, noAdmins, adminIds]); const displayedIds = useMemo(() => { // No need for expensive global updates on users, so we avoid them const usersById = getGlobal().users.byId; const chatsById = getGlobal().chats.byId; const shouldUseSearchResults = Boolean(searchQuery); const listedIds = !shouldUseSearchResults ? memberIds : (localContactIds ? filterUsersByName(localContactIds, usersById, searchQuery) : []); return sortChatIds( unique([ ...listedIds, ...(shouldUseSearchResults ? localUserIds || [] : []), ...(shouldUseSearchResults ? globalUserIds || [] : []), ]).filter((contactId) => { const user = usersById[contactId]; if (!user) { return true; } return (isChannel || user.canBeInvitedToGroup || !isUserBot(user)) && (!noAdmins || !adminIds.includes(contactId)); }), chatsById, true, ); }, [memberIds, localContactIds, searchQuery, localUserIds, globalUserIds, isChannel, noAdmins, adminIds]); const [viewportIds, getMore] = useInfiniteScroll(undefined, displayedIds, Boolean(searchQuery)); const handleMemberClick = useCallback((id: string) => { if (noAdmins) { onChatMemberSelect!(id, false); onScreenSelect!(ManagementScreens.ChatNewAdminRights); } else { closeManagement(); openChat({ id }); } }, [closeManagement, noAdmins, onChatMemberSelect, onScreenSelect, openChat]); const handleFilterChange = useCallback((e: React.ChangeEvent) => { setUserSearchQuery({ query: e.target.value }); }, [setUserSearchQuery]); const handleKeyDown = useKeyboardListNavigation(containerRef, isActive, (index) => { if (viewportIds && viewportIds.length > 0) { handleMemberClick(viewportIds[index === -1 ? 0 : index]); } }, '.ListItem-button', true); const handleDeleteMembersModalClose = useCallback(() => { setDeletingUserId(undefined); }, []); const handleToggleParticipantsHidden = useCallback(() => { toggleParticipantsHidden({ chatId, isEnabled: !areParticipantsHidden }); }, [areParticipantsHidden, chatId, toggleParticipantsHidden]); useHistoryBack({ isActive, onBack: onClose, }); function getMemberContextAction(memberId: string) { return memberId === currentUserId || !canDeleteMembers ? undefined : [{ title: lang('lng_context_remove_from_group'), icon: 'stop', handler: () => { setDeletingUserId(memberId); }, }]; } function renderSearchField() { return (
); } return (
{noAdmins && renderSearchField()}
{!isBasicGroup && (
{lang('ChannelHideMembers')}

{lang(areParticipantsHidden ? 'GroupMembers.MembersHiddenOn' : 'GroupMembers.MembersHiddenOff')}

)}
{viewportIds?.length ? ( {viewportIds.map((id) => ( handleMemberClick(id)} contextActions={getMemberContextAction(id)} > ))} ) : !isSearching && viewportIds && !viewportIds.length ? ( ) : ( )}
{canDeleteMembers && ( )}
); }; export default memo(withGlobal( (global, { chatId }): StateProps => { const chat = selectChat(global, chatId); const { statusesById: userStatusesById } = global.users; const members = chat?.fullInfo?.members; const adminMembersById = chat?.fullInfo?.adminMembersById; const isChannel = chat && isChatChannel(chat); const { userIds: localContactIds } = global.contactList || {}; const { query: searchQuery, fetchingStatus, globalUserIds, localUserIds, } = global.userSearch; const canDeleteMembers = chat && (chat.isCreator || getHasAdminRight(chat, 'banUsers')); return { isBasicGroup: Boolean(chat && isChatBasicGroup(chat)), areParticipantsHidden: Boolean(chat && chat.fullInfo?.areParticipantsHidden), members, adminMembersById, userStatusesById, isChannel, localContactIds, searchQuery, isSearching: fetchingStatus, globalUserIds, localUserIds, canDeleteMembers, currentUserId: global.currentUserId, }; }, )(ManageGroupMembers));