import React, { memo, useState } from '../../lib/teact/teact'; import { getGlobal } from '../../global'; import type { FC } from '../../lib/teact/teact'; import type { ObserveFn } from '../../hooks/useIntersectionObserver'; import type { ApiSticker } from '../../api/types'; import { IS_WEBM_SUPPORTED } from '../../util/environment'; import * as mediaLoader from '../../util/mediaLoader'; import buildClassName from '../../util/buildClassName'; import generateIdFor from '../../util/generateIdFor'; import { getStickerPreviewHash } from '../../global/helpers'; import { selectIsAlwaysHighPriorityEmoji } from '../../global/selectors'; import useMedia from '../../hooks/useMedia'; import { useIsIntersecting } from '../../hooks/useIntersectionObserver'; import useThumbnail from '../../hooks/useThumbnail'; import useMediaTransition from '../../hooks/useMediaTransition'; import useFlag from '../../hooks/useFlag'; import AnimatedSticker from './AnimatedSticker'; import OptimizedVideo from '../ui/OptimizedVideo'; import styles from './StickerView.module.scss'; type OwnProps = { containerRef: React.RefObject; sticker: ApiSticker; thumbClassName?: string; fullMediaHash?: string; fullMediaClassName?: string; isSmall?: boolean; size?: number; customColor?: [number, number, number]; loopLimit?: number; shouldLoop?: boolean; shouldPreloadPreview?: boolean; forceOnHeavyAnimation?: boolean; observeIntersectionForLoading?: ObserveFn; observeIntersectionForPlaying?: ObserveFn; noLoad?: boolean; noPlay?: boolean; withSharedAnimation?: boolean; cacheBuster?: number; onVideoEnded?: AnyToVoidFunction; onAnimatedStickerLoop?: AnyToVoidFunction; }; const SHARED_PREFIX = 'shared'; const STICKER_SIZE = 24; const ID_STORE = {}; const StickerView: FC = ({ containerRef, sticker, thumbClassName, fullMediaHash, fullMediaClassName, isSmall, size = STICKER_SIZE, customColor, loopLimit, shouldLoop = false, shouldPreloadPreview, forceOnHeavyAnimation, observeIntersectionForLoading, observeIntersectionForPlaying, noLoad, noPlay, withSharedAnimation, cacheBuster, onVideoEnded, onAnimatedStickerLoop, }) => { const { id, isLottie, stickerSetInfo, emoji, } = sticker; const isUnsupportedVideo = sticker.isVideo && !IS_WEBM_SUPPORTED; const isVideo = sticker.isVideo && !isUnsupportedVideo; const isStatic = !isLottie && !isVideo; const previewMediaHash = getStickerPreviewHash(sticker.id); const isIntersectingForLoading = useIsIntersecting(containerRef, observeIntersectionForLoading); const shouldLoad = isIntersectingForLoading && !noLoad; const isIntersectingForPlaying = ( useIsIntersecting(containerRef, observeIntersectionForPlaying) && isIntersectingForLoading ); const shouldPlay = isIntersectingForPlaying && !noPlay; const thumbDataUri = useThumbnail(sticker); // Use preview instead of thumb but only if it's already loaded const [preloadedPreviewData] = useState(mediaLoader.getFromMemory(previewMediaHash)); const thumbData = preloadedPreviewData || thumbDataUri; const shouldForcePreview = isUnsupportedVideo || (isStatic && isSmall); fullMediaHash ||= shouldForcePreview ? previewMediaHash : `sticker${id}`; // If preloaded preview is forced, it will render as thumb, so no need to load it again const shouldSkipFullMedia = Boolean(fullMediaHash === previewMediaHash && preloadedPreviewData); const fullMediaData = useMedia(fullMediaHash, !shouldLoad || shouldSkipFullMedia, undefined, cacheBuster); // If Lottie data is loaded we will only render thumb if it's good enough (from preview) const [isPlayerReady, markPlayerReady] = useFlag(Boolean(isLottie && fullMediaData && !preloadedPreviewData)); const isFullMediaReady = fullMediaData && (isStatic || isPlayerReady); const thumbClassNames = useMediaTransition(thumbData && !isFullMediaReady); const fullMediaClassNames = useMediaTransition(isFullMediaReady); const noTransition = isLottie && preloadedPreviewData; // Preload preview for Message Input and local message useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview, undefined, cacheBuster); const [randomIdPrefix] = useState(generateIdFor(ID_STORE, true)); const idKey = [ (withSharedAnimation ? SHARED_PREFIX : randomIdPrefix), id, size, customColor?.join(','), ].filter(Boolean).join('_'); return ( <> {isLottie ? ( ) : isVideo ? ( ) : ( {emoji} )} ); }; export default memo(StickerView);