import type { FC } from '../../lib/teact/teact'; import React, { useCallback, memo, useMemo, useEffect, useState, useRef, } from '../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../global'; import type { ApiAvailableReaction, ApiMessage, ApiReaction } from '../../api/types'; import type { AnimationLevel } from '../../types'; import { LoadMoreDirection } from '../../types'; import { selectChatMessage, selectTabState } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import { formatIntegerCompact } from '../../util/textFormat'; import { unique } from '../../util/iteratees'; import { isSameReaction, getReactionUniqueKey } from '../../global/helpers'; import useLang from '../../hooks/useLang'; import useInfiniteScroll from '../../hooks/useInfiniteScroll'; import useFlag from '../../hooks/useFlag'; import InfiniteScroll from '../ui/InfiniteScroll'; import Modal from '../ui/Modal'; import Button from '../ui/Button'; import Avatar from '../common/Avatar'; import ListItem from '../ui/ListItem'; import ReactionStaticEmoji from '../common/ReactionStaticEmoji'; import Loading from '../ui/Loading'; import FullNameTitle from '../common/FullNameTitle'; import './ReactorListModal.scss'; const MIN_REACTIONS_COUNT_FOR_FILTERS = 10; export type OwnProps = { isOpen: boolean; }; export type StateProps = Pick & { chatId?: string; messageId?: number; animationLevel: AnimationLevel; availableReactions?: ApiAvailableReaction[]; }; const ReactorListModal: FC = ({ isOpen, reactors, reactions, chatId, messageId, seenByUserIds, animationLevel, availableReactions, }) => { const { loadReactors, closeReactorListModal, openChat, } = getActions(); // No need for expensive global updates on users, so we avoid them const usersById = getGlobal().users.byId; const lang = useLang(); const [isClosing, startClosing, stopClosing] = useFlag(false); const [chosenTab, setChosenTab] = useState(undefined); const canShowFilters = reactors && reactions && reactors.count >= MIN_REACTIONS_COUNT_FOR_FILTERS && reactions.results.length > 1; const chatIdRef = useRef(); useEffect(() => { if (isOpen && !isClosing) { chatIdRef.current = undefined; } if (isClosing && !isOpen) { stopClosing(); setChosenTab(undefined); } }, [isClosing, isOpen, stopClosing]); const handleCloseAnimationEnd = useCallback(() => { if (chatIdRef.current) { openChat({ id: chatIdRef.current }); } closeReactorListModal(); }, [closeReactorListModal, openChat]); const handleClose = useCallback(() => { startClosing(); }, [startClosing]); const handleClick = useCallback((userId: string) => { chatIdRef.current = userId; handleClose(); }, [handleClose]); const handleLoadMore = useCallback(() => { loadReactors({ chatId: chatId!, messageId: messageId!, }); }, [chatId, loadReactors, messageId]); const allReactions = useMemo(() => { const uniqueReactions: ApiReaction[] = []; reactors?.reactions?.forEach(({ reaction }) => { if (!uniqueReactions.some((r) => isSameReaction(r, reaction))) { uniqueReactions.push(reaction); } }); return uniqueReactions; }, [reactors]); const userIds = useMemo(() => { if (chosenTab) { return reactors?.reactions .filter(({ reaction }) => isSameReaction(reaction, chosenTab)) .map(({ userId }) => userId); } return unique(reactors?.reactions.map(({ userId }) => userId).concat(seenByUserIds || []) || []); }, [chosenTab, reactors, seenByUserIds]); const [viewportIds, getMore] = useInfiniteScroll( handleLoadMore, userIds, reactors && reactors.nextOffset === undefined, ); useEffect(() => { getMore?.({ direction: LoadMoreDirection.Backwards }); }, [getMore]); return ( {canShowFilters && (
{allReactions.map((reaction) => { const count = reactions?.results .find((reactionsCount) => isSameReaction(reactionsCount.reaction, reaction))?.count; return ( ); })}
)}
{viewportIds?.length ? ( {viewportIds?.flatMap( (userId) => { const user = usersById[userId]; const userReactions = reactors?.reactions.filter((reactor) => reactor.userId === userId); const items: React.ReactNode[] = []; userReactions?.forEach((r) => { if (chosenTab && !isSameReaction(r.reaction, chosenTab)) return; items.push( handleClick(userId)} > {r.reaction && ( )} , ); }); return items; }, )} ) : }
); }; export default memo(withGlobal( (global): StateProps => { const { chatId, messageId } = selectTabState(global).reactorModal || {}; const message = chatId && messageId ? selectChatMessage(global, chatId, messageId) : undefined; return { chatId, messageId, reactions: message?.reactions, reactors: message?.reactors, seenByUserIds: message?.seenByUserIds, animationLevel: global.settings.byKey.animationLevel, availableReactions: global.availableReactions, }; }, )(ReactorListModal));