import type { FC } from '../../../lib/teact/teact'; import React, { memo, useCallback, useMemo, useRef, useState, } from '../../../lib/teact/teact'; import { getActions, getGlobal } from '../../../global'; import type { ApiTopic } from '../../../api/types'; import type { GlobalState } from '../../../global/types'; import type { ThreadId } from '../../../types'; import { PEER_PICKER_ITEM_HEIGHT_PX } from '../../../config'; import { getCanPostInChat, getGroupStatus, getUserStatus, isUserOnline, } from '../../../global/helpers'; import { isApiPeerChat } from '../../../global/helpers/peers'; import { selectPeer, selectTopics, selectUserStatus } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import { REM } from '../helpers/mediaDimensions'; import renderText from '../helpers/renderText'; import useSelector from '../../../hooks/data/useSelector'; import useInfiniteScroll from '../../../hooks/useInfiniteScroll'; import useInputFocusOnOpen from '../../../hooks/useInputFocusOnOpen'; import useKeyboardListNavigation from '../../../hooks/useKeyboardListNavigation'; import useLastCallback from '../../../hooks/useLastCallback'; import useOldLang from '../../../hooks/useOldLang'; import Button from '../../ui/Button'; import InfiniteScroll from '../../ui/InfiniteScroll'; import InputText from '../../ui/InputText'; import Loading from '../../ui/Loading'; import Modal from '../../ui/Modal'; import Transition from '../../ui/Transition'; import Avatar from '../Avatar'; import FullNameTitle from '../FullNameTitle'; import Icon from '../icons/Icon'; import TopicIcon from '../TopicIcon'; import PickerItem from './PickerItem'; import './ChatOrUserPicker.scss'; export type OwnProps = { currentUserId?: string; chatOrUserIds: string[]; isOpen: boolean; searchPlaceholder: string; search: string; className?: string; loadMore?: NoneToVoidFunction; onSearchChange: (search: string) => void; onSelectChatOrUser: (chatOrUserId: string, threadId?: ThreadId) => void; onClose: NoneToVoidFunction; onCloseAnimationEnd?: NoneToVoidFunction; isLowStackPriority?: boolean; }; const CHAT_LIST_SLIDE = 0; const TOPIC_LIST_SLIDE = 1; const TOPIC_ICON_SIZE = 2.75 * REM; const ITEM_CLASS_NAME = 'ChatOrUserPicker-item'; const TOPIC_ITEM_HEIGHT_PX = 56; const ChatOrUserPicker: FC = ({ isOpen, currentUserId, chatOrUserIds, search, searchPlaceholder, className, loadMore, onSearchChange, onSelectChatOrUser, onClose, onCloseAnimationEnd, isLowStackPriority, }) => { const { loadTopics } = getActions(); const lang = useOldLang(); const containerRef = useRef(); const topicContainerRef = useRef(); const searchRef = useRef(); const topicSearchRef = useRef(); const [viewportIds, getMore] = useInfiniteScroll(loadMore, chatOrUserIds, Boolean(search)); const [forumId, setForumId] = useState(undefined); const [topicSearch, setTopicSearch] = useState(''); const activeKey = forumId ? TOPIC_LIST_SLIDE : CHAT_LIST_SLIDE; const viewportOffset = chatOrUserIds.indexOf(viewportIds![0]); const resetSearch = useLastCallback(() => { onSearchChange(''); }); useInputFocusOnOpen(searchRef, isOpen && activeKey === CHAT_LIST_SLIDE, resetSearch); useInputFocusOnOpen(topicSearchRef, isOpen && activeKey === TOPIC_LIST_SLIDE); const selectTopicsById = useLastCallback((global: GlobalState) => { if (!forumId) { return undefined; } return selectTopics(global, forumId); }); const forumTopicsById = useSelector(selectTopicsById); const [topicIds, topics] = useMemo(() => { const global = getGlobal(); const chatsById = global.chats.byId; const chatFullInfoById = global.chats.fullInfoById; const chat = chatsById[forumId!]; if (!chat || !forumTopicsById) { return [undefined, undefined]; } const searchTitle = topicSearch.toLowerCase(); const result = forumTopicsById ? Object.values(forumTopicsById).reduce((acc, topic) => { if ( getCanPostInChat(chat, topic, undefined, chatFullInfoById[forumId!]) && (!searchTitle || topic.title.toLowerCase().includes(searchTitle)) ) { acc[topic.id] = topic; } return acc; }, {} as Record) : forumTopicsById; return [Object.keys(result).map(Number), result]; }, [forumId, topicSearch, forumTopicsById]); const handleHeaderBackClick = useLastCallback(() => { setForumId(undefined); setTopicSearch(''); }); const handleSearchChange = useLastCallback((e: React.ChangeEvent) => { onSearchChange(e.currentTarget.value); }); const handleTopicSearchChange = useLastCallback((e: React.ChangeEvent) => { setTopicSearch(e.currentTarget.value); }); const handleKeyDown = useKeyboardListNavigation(containerRef, isOpen, (index) => { if (viewportIds && viewportIds.length > 0) { const chatsById = getGlobal().chats.byId; const chatId = viewportIds[index === -1 ? 0 : index]; const chat = chatsById[chatId]; if (chat?.isForum) { if (!forumTopicsById) loadTopics({ chatId }); setForumId(chatId); } else { onSelectChatOrUser(chatId); } } }, `.${ITEM_CLASS_NAME}`, true); const handleTopicKeyDown = useKeyboardListNavigation(topicContainerRef, isOpen, (index) => { if (topicIds?.length) { onSelectChatOrUser(forumId!, topicIds[index === -1 ? 0 : index]); } }, `.${ITEM_CLASS_NAME}`, true); const handleClick = useLastCallback((chatId: string) => { const chatsById = getGlobal().chats.byId; const chat = chatsById?.[chatId]; if (chat?.isForum) { if (!forumTopicsById) loadTopics({ chatId }); setForumId(chatId); resetSearch(); } else { onSelectChatOrUser(chatId); } }); const renderChatItem = useCallback((id: string, index: number) => { const global = getGlobal(); const peer = selectPeer(global, id); if (!peer) { return undefined; } const isSelf = peer && !isApiPeerChat(peer) ? peer.isSelf : undefined; function getSubtitle() { if (!peer) return undefined; if (peer.id === currentUserId) return [lang('SavedMessagesInfo')]; if (isApiPeerChat(peer)) { return [getGroupStatus(lang, peer)]; } const userStatus = selectUserStatus(global, peer.id); return [ getUserStatus(lang, peer, userStatus), buildClassName(isUserOnline(peer, userStatus, true) && 'online'), ]; } const [subtitle, subtitleClassName] = getSubtitle() || []; return ( } avatarElement={( )} subtitle={subtitle} subtitleClassName={subtitleClassName} ripple style={`top: ${(viewportOffset + index) * PEER_PICKER_ITEM_HEIGHT_PX}px;`} onClick={() => handleClick(id)} /> ); }, [currentUserId, lang, viewportOffset]); function renderTopicList() { return ( <>
{!topicIds && } {topicIds?.map((topicId, i) => ( onSelectChatOrUser(forumId!, topicId)} style={`top: ${(viewportOffset + i) * TOPIC_ITEM_HEIGHT_PX}px;`} avatarElement={( )} title={renderText(topics[topicId].title)} /> ))} ); } function renderChatList() { return ( <>
{viewportIds?.length ? ( {viewportIds.map(renderChatItem)} ) : viewportIds && !viewportIds.length ? (

{lang('lng_blocked_list_not_found')}

) : ( )} ); } return ( {() => { return activeKey === TOPIC_LIST_SLIDE ? renderTopicList() : renderChatList(); }} ); }; export default memo(ChatOrUserPicker);