import type { FC } from '../../lib/teact/teact'; import React, { memo, useRef, useState } from '../../lib/teact/teact'; import { getGlobal } from '../../global'; import type { ObserveFn } from '../../hooks/useIntersectionObserver'; import { ApiMessageEntityTypes } from '../../api/types'; import { selectIsAlwaysHighPriorityEmoji } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import safePlay from '../../util/safePlay'; import useDynamicColorListener from '../../hooks/stickers/useDynamicColorListener'; import useLastCallback from '../../hooks/useLastCallback'; import useCustomEmoji from './hooks/useCustomEmoji'; import Sparkles from './Sparkles'; import StickerView from './StickerView'; import styles from './CustomEmoji.module.scss'; import blankImg from '../../assets/blank.png'; type OwnProps = { ref?: React.RefObject; documentId: string; className?: string; style?: string; size?: number; isBig?: boolean; noPlay?: boolean; noVideoOnMobile?: boolean; loopLimit?: number; isSelectable?: boolean; withSharedAnimation?: boolean; sharedCanvasRef?: React.RefObject; sharedCanvasHqRef?: React.RefObject; withTranslucentThumb?: boolean; shouldPreloadPreview?: boolean; forceOnHeavyAnimation?: boolean; forceAlways?: boolean; observeIntersectionForLoading?: ObserveFn; observeIntersectionForPlaying?: ObserveFn; onClick?: NoneToVoidFunction; onAnimationEnd?: NoneToVoidFunction; withSparkles?: boolean; sparklesClassName?: string; sparklesStyle?: string; }; const STICKER_SIZE = 20; const CustomEmoji: FC = ({ ref, documentId, className, style, size = STICKER_SIZE, isBig, noPlay, noVideoOnMobile, loopLimit, isSelectable, withSharedAnimation, sharedCanvasRef, sharedCanvasHqRef, withTranslucentThumb, shouldPreloadPreview, forceAlways, forceOnHeavyAnimation, observeIntersectionForLoading, observeIntersectionForPlaying, onClick, onAnimationEnd, withSparkles, sparklesStyle, sparklesClassName, }) => { // eslint-disable-next-line no-null/no-null let containerRef = useRef(null); if (ref) { containerRef = ref; } // An alternative to `withGlobal` to avoid adding numerous global containers const { customEmoji, canPlay } = useCustomEmoji(documentId); const loopCountRef = useRef(0); const [shouldPlay, setShouldPlay] = useState(true); const hasCustomColor = customEmoji?.shouldUseTextColor; const customColor = useDynamicColorListener(containerRef, undefined, !hasCustomColor); const handleVideoEnded = useLastCallback((e) => { if (!loopLimit) return; loopCountRef.current += 1; if (loopCountRef.current >= loopLimit) { setShouldPlay(false); e.currentTarget.currentTime = 0; } else { // Loop manually safePlay(e.currentTarget); } }); const handleStickerLoop = useLastCallback(() => { if (!loopLimit) return; loopCountRef.current += 1; if (loopCountRef.current >= loopLimit) { setShouldPlay(false); } }); const isHq = customEmoji?.stickerSetInfo && selectIsAlwaysHighPriorityEmoji(getGlobal(), customEmoji.stickerSetInfo); return (
{withSparkles && ( )} {isSelectable && ( {customEmoji?.emoji} )} {!customEmoji ? (
) : ( )}
); }; export default memo(CustomEmoji);