import React, { FC, memo, useCallback, useMemo, useRef, } from '../../../lib/teact/teact'; import { getDispatch, getGlobal, withGlobal } from '../../../lib/teact/teactn'; import { ApiChatMember, ApiUserStatus } from '../../../api/types'; import { ManagementScreens } from '../../../types'; import { unique } from '../../../util/iteratees'; import { selectChat } from '../../../modules/selectors'; import { sortUserIds, isChatChannel, filterUsersByName, sortChatIds, isUserBot, } from '../../../modules/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'; 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[]; adminMembers?: ApiChatMember[]; isChannel?: boolean; localContactIds?: string[]; searchQuery?: string; isSearching?: boolean; localUserIds?: string[]; globalUserIds?: string[]; serverTimeOffset: number; }; const ManageGroupMembers: FC = ({ noAdmins, members, adminMembers, userStatusesById, isChannel, isActive, globalUserIds, localContactIds, localUserIds, isSearching, searchQuery, serverTimeOffset, onClose, onScreenSelect, onChatMemberSelect, }) => { const { openUserInfo, setUserSearchQuery, loadContactList } = getDispatch(); 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 adminIds = useMemo(() => { return noAdmins ? adminMembers?.map(({ userId }) => userId) || [] : []; }, [adminMembers, 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, undefined, serverTimeOffset, ); return noAdmins ? userIds.filter((userId) => !adminIds.includes(userId)) : userIds; }, [members, userStatusesById, serverTimeOffset, 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(loadContactList, displayedIds, Boolean(searchQuery)); const handleMemberClick = useCallback((id: string) => { if (noAdmins) { onChatMemberSelect!(id, false); onScreenSelect!(ManagementScreens.ChatNewAdminRights); } else { openUserInfo({ id }); } }, [noAdmins, onChatMemberSelect, onScreenSelect, openUserInfo]); 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); useHistoryBack(isActive, onClose); function renderSearchField() { return (
); } return (
{noAdmins && renderSearchField()}
{viewportIds?.length ? ( {viewportIds.map((id) => ( handleMemberClick(id)} > ))} ) : !isSearching && viewportIds && !viewportIds.length ? ( ) : ( )}
); }; export default memo(withGlobal( (global, { chatId }): StateProps => { const chat = selectChat(global, chatId); const { statusesById: userStatusesById } = global.users; const members = chat?.fullInfo?.members; const adminMembers = chat?.fullInfo?.adminMembers; const isChannel = chat && isChatChannel(chat); const { userIds: localContactIds } = global.contactList || {}; const { query: searchQuery, fetchingStatus, globalUserIds, localUserIds, } = global.userSearch; return { members, adminMembers, userStatusesById, isChannel, localContactIds, searchQuery, isSearching: fetchingStatus, globalUserIds, localUserIds, serverTimeOffset: global.serverTimeOffset, }; }, )(ManageGroupMembers));