import React, { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from '../../../lib/teact/teact'; import { getActions, getGlobal } from '../../../global'; import type { FC } from '../../../lib/teact/teact'; import type { ApiSticker } from '../../../api/types'; import type { StickerSetOrRecent } from '../../../types'; import type { ObserveFn } from '../../../hooks/useIntersectionObserver'; import { DEFAULT_STATUS_ICON_ID, DEFAULT_TOPIC_ICON_STICKER_ID, EMOJI_SIZE_PICKER, FAVORITE_SYMBOL_SET_ID, RECENT_SYMBOL_SET_ID, STICKER_SIZE_PICKER, } from '../../../config'; import buildClassName from '../../../util/buildClassName'; import { selectIsAlwaysHighPriorityEmoji, selectIsSetPremium } from '../../../global/selectors'; import useLang from '../../../hooks/useLang'; import useFlag from '../../../hooks/useFlag'; import useMediaTransition from '../../../hooks/useMediaTransition'; import useResizeObserver from '../../../hooks/useResizeObserver'; import { useIsIntersecting } from '../../../hooks/useIntersectionObserver'; import useAppLayout from '../../../hooks/useAppLayout'; import StickerButton from '../../common/StickerButton'; import ConfirmDialog from '../../ui/ConfirmDialog'; import Button from '../../ui/Button'; import grey from '../../../assets/icons/forumTopic/grey.svg'; type OwnProps = { stickerSet: StickerSetOrRecent; loadAndPlay: boolean; index: number; idPrefix?: string; shouldRender: boolean; favoriteStickers?: ApiSticker[]; isSavedMessages?: boolean; isStatusPicker?: boolean; isCurrentUserPremium?: boolean; shouldHideRecentHeader?: boolean; withDefaultTopicIcon?: boolean; withDefaultStatusIcon?: boolean; observeIntersection: ObserveFn; onStickerSelect?: (sticker: ApiSticker, isSilent?: boolean, shouldSchedule?: boolean) => void; onStickerUnfave?: (sticker: ApiSticker) => void; onStickerFave?: (sticker: ApiSticker) => void; onStickerRemoveRecent?: (sticker: ApiSticker) => void; onContextMenuOpen?: NoneToVoidFunction; onContextMenuClose?: NoneToVoidFunction; onContextMenuClick?: NoneToVoidFunction; }; const ITEMS_PER_ROW_FALLBACK = 8; const StickerSet: FC = ({ stickerSet, loadAndPlay, index, idPrefix, shouldRender, favoriteStickers, isSavedMessages, isStatusPicker, isCurrentUserPremium, shouldHideRecentHeader, withDefaultTopicIcon, withDefaultStatusIcon, observeIntersection, onStickerSelect, onStickerUnfave, onStickerFave, onStickerRemoveRecent, onContextMenuOpen, onContextMenuClose, onContextMenuClick, }) => { const { clearRecentStickers, clearRecentCustomEmoji, openPremiumModal, toggleStickerSet, loadStickers, } = getActions(); // eslint-disable-next-line no-null/no-null const ref = useRef(null); // eslint-disable-next-line no-null/no-null const sharedCanvasRef = useRef(null); // eslint-disable-next-line no-null/no-null const sharedCanvasHqRef = useRef(null); const lang = useLang(); const [isConfirmModalOpen, openConfirmModal, closeConfirmModal] = useFlag(); const { isMobile } = useAppLayout(); const [itemsPerRow, setItemsPerRow] = useState(ITEMS_PER_ROW_FALLBACK); const isIntersecting = useIsIntersecting(ref, observeIntersection); const transitionClassNames = useMediaTransition(shouldRender); const stickerMarginPx = isMobile ? 8 : 16; const emojiMarginPx = isMobile ? 8 : 10; const containerPaddingPx = isMobile && !isStatusPicker ? 8 : 0; const isRecent = stickerSet.id === RECENT_SYMBOL_SET_ID; const isFavorite = stickerSet.id === FAVORITE_SYMBOL_SET_ID; const isEmoji = stickerSet.isEmoji; const isPremiumSet = !isRecent && selectIsSetPremium(stickerSet); const handleClearRecent = useCallback(() => { if (isEmoji) { clearRecentCustomEmoji(); } else { clearRecentStickers(); } closeConfirmModal(); }, [clearRecentCustomEmoji, clearRecentStickers, closeConfirmModal, isEmoji]); const handleAddClick = useCallback(() => { if (isPremiumSet && !isCurrentUserPremium) { openPremiumModal({ initialSection: 'animated_emoji', }); } else { toggleStickerSet({ stickerSetId: stickerSet.id, }); } }, [isCurrentUserPremium, isPremiumSet, openPremiumModal, stickerSet, toggleStickerSet]); const handleDefaultTopicIconClick = useCallback(() => { onStickerSelect?.({ id: DEFAULT_TOPIC_ICON_STICKER_ID, isLottie: false, isVideo: false, stickerSetInfo: { shortName: 'dummy', }, } satisfies ApiSticker); }, [onStickerSelect]); const handleDefaultStatusIconClick = useCallback(() => { onStickerSelect?.({ id: DEFAULT_STATUS_ICON_ID, isLottie: false, isVideo: false, stickerSetInfo: { shortName: 'dummy', }, } satisfies ApiSticker); }, [onStickerSelect]); const itemSize = isEmoji ? EMOJI_SIZE_PICKER : STICKER_SIZE_PICKER; const margin = isEmoji ? emojiMarginPx : stickerMarginPx; const calculateItemsPerRow = useCallback((width: number) => { if (!width) return ITEMS_PER_ROW_FALLBACK; return Math.floor((width - containerPaddingPx) / (itemSize + margin)); }, [containerPaddingPx, itemSize, margin]); const handleResize = useCallback((entry: ResizeObserverEntry) => { setItemsPerRow(calculateItemsPerRow(entry.contentRect.width)); }, [calculateItemsPerRow]); useResizeObserver(ref, handleResize); useLayoutEffect(() => { if (!ref.current) return; setItemsPerRow(calculateItemsPerRow(ref.current.clientWidth)); }, [calculateItemsPerRow]); useEffect(() => { if (isIntersecting && !stickerSet.stickers?.length && stickerSet.accessHash) { loadStickers({ stickerSetInfo: { id: stickerSet.id, accessHash: stickerSet.accessHash, }, }); } }, [isIntersecting, loadStickers, stickerSet]); const isLocked = !isSavedMessages && !isRecent && isEmoji && !isCurrentUserPremium && stickerSet.stickers?.some(({ isFree }) => !isFree); const isInstalled = stickerSet.installedDate && !stickerSet.isArchived; const canCut = !isInstalled && stickerSet.id !== RECENT_SYMBOL_SET_ID; const [isCut, , expand] = useFlag(canCut); const itemsBeforeCutout = itemsPerRow * 3 - 1; const totalItemsCount = withDefaultTopicIcon ? stickerSet.count + 1 : stickerSet.count; const heightWhenCut = Math.ceil(Math.min(itemsBeforeCutout, totalItemsCount) / itemsPerRow) * (itemSize + margin); const height = isCut ? heightWhenCut : Math.ceil(totalItemsCount / itemsPerRow) * (itemSize + margin); const shouldHideHeader = isRecent && shouldHideRecentHeader; const favoriteStickerIdsSet = useMemo(() => ( favoriteStickers ? new Set(favoriteStickers.map(({ id }) => id)) : undefined ), [favoriteStickers]); return (
{!shouldHideHeader && (

{isLocked && } {stickerSet.title}

{isRecent && ( )} {!isRecent && isEmoji && !isInstalled && ( )}
)}
{(isRecent || isFavorite || canCut) && } {withDefaultTopicIcon && ( )} {withDefaultStatusIcon && ( )} {shouldRender && stickerSet.stickers && stickerSet.stickers .slice(0, isCut ? itemsBeforeCutout : stickerSet.stickers.length) .map((sticker, i) => { const isHqEmoji = (isRecent || isFavorite) && selectIsAlwaysHighPriorityEmoji(getGlobal(), sticker.stickerSetInfo); const canvasRef = (canCut && i >= itemsBeforeCutout) || isHqEmoji ? sharedCanvasHqRef : sharedCanvasRef; return ( ); })} {isCut && totalItemsCount > itemsBeforeCutout && ( )}
{isRecent && ( )}
); }; export default memo(StickerSet);