138 lines
4.5 KiB
TypeScript
138 lines
4.5 KiB
TypeScript
import type { FC } from '../../../lib/teact/teact';
|
|
import React, { useEffect, useRef } from '../../../lib/teact/teact';
|
|
|
|
import type { ApiMessage } from '../../../api/types';
|
|
|
|
import { NO_STICKER_SET_ID } from '../../../config';
|
|
import { getStickerDimensions } from '../../common/helpers/mediaDimensions';
|
|
import { getMessageMediaFormat, getMessageMediaHash } from '../../../global/helpers';
|
|
import buildClassName from '../../../util/buildClassName';
|
|
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
|
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
|
|
import useMedia from '../../../hooks/useMedia';
|
|
import useMediaTransition from '../../../hooks/useMediaTransition';
|
|
import useFlag from '../../../hooks/useFlag';
|
|
import useWebpThumbnail from '../../../hooks/useWebpThumbnail';
|
|
import safePlay from '../../../util/safePlay';
|
|
import { IS_WEBM_SUPPORTED } from '../../../util/environment';
|
|
|
|
import AnimatedSticker from '../../common/AnimatedSticker';
|
|
import StickerSetModal from '../../common/StickerSetModal.async';
|
|
|
|
import './Sticker.scss';
|
|
|
|
type OwnProps = {
|
|
message: ApiMessage;
|
|
observeIntersection: ObserveFn;
|
|
observeIntersectionForPlaying: ObserveFn;
|
|
shouldLoop?: boolean;
|
|
lastSyncTime?: number;
|
|
};
|
|
|
|
const Sticker: FC<OwnProps> = ({
|
|
message, observeIntersection, observeIntersectionForPlaying, shouldLoop, lastSyncTime,
|
|
}) => {
|
|
// eslint-disable-next-line no-null/no-null
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
|
|
const [isModalOpen, openModal, closeModal] = useFlag();
|
|
|
|
const sticker = message.content.sticker!;
|
|
const { isLottie, stickerSetId, isVideo } = sticker;
|
|
const canDisplayVideo = IS_WEBM_SUPPORTED;
|
|
const isMemojiSticker = stickerSetId === NO_STICKER_SET_ID;
|
|
|
|
const shouldLoad = useIsIntersecting(ref, observeIntersection);
|
|
const shouldPlay = useIsIntersecting(ref, observeIntersectionForPlaying);
|
|
|
|
const mediaHash = sticker.isPreloadedGlobally ? `sticker${sticker.id}` : getMessageMediaHash(message, 'inline')!;
|
|
const previewMediaHash = isVideo && !canDisplayVideo && (
|
|
sticker.isPreloadedGlobally ? `sticker${sticker.id}?size=m` : getMessageMediaHash(message, 'pictogram'));
|
|
const previewBlobUrl = useMedia(previewMediaHash);
|
|
const thumbDataUri = useWebpThumbnail(message);
|
|
const previewUrl = previewBlobUrl || thumbDataUri;
|
|
|
|
const mediaData = useMedia(
|
|
mediaHash,
|
|
!shouldLoad,
|
|
getMessageMediaFormat(message, 'inline'),
|
|
lastSyncTime,
|
|
);
|
|
|
|
const isMediaLoaded = Boolean(mediaData);
|
|
const [isLottieLoaded, markLottieLoaded] = useFlag(isMediaLoaded);
|
|
const isMediaReady = isLottie ? isLottieLoaded : isMediaLoaded;
|
|
const transitionClassNames = useMediaTransition(isMediaReady);
|
|
|
|
const { width, height } = getStickerDimensions(sticker);
|
|
const thumbClassName = buildClassName('thumbnail', !thumbDataUri && 'empty');
|
|
|
|
const stickerClassName = buildClassName(
|
|
'Sticker media-inner',
|
|
isMemojiSticker && 'inactive',
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!isVideo || !ref.current) return;
|
|
const video = ref.current.querySelector('video');
|
|
if (!video) return;
|
|
if (shouldPlay) {
|
|
safePlay(video);
|
|
} else {
|
|
video.pause();
|
|
}
|
|
}, [isVideo, shouldPlay]);
|
|
|
|
return (
|
|
<div ref={ref} className={stickerClassName} onClick={!isMemojiSticker ? openModal : undefined}>
|
|
{(!isMediaReady || (isVideo && !canDisplayVideo)) && (
|
|
<img
|
|
src={previewUrl}
|
|
width={width}
|
|
height={height}
|
|
alt=""
|
|
className={thumbClassName}
|
|
/>
|
|
)}
|
|
{!isLottie && !isVideo && (
|
|
<img
|
|
src={mediaData as string}
|
|
width={width}
|
|
height={height}
|
|
alt=""
|
|
className={buildClassName('full-media', transitionClassNames)}
|
|
/>
|
|
)}
|
|
{isVideo && canDisplayVideo && isMediaReady && (
|
|
<video
|
|
src={mediaData as string}
|
|
width={width}
|
|
height={height}
|
|
autoPlay={shouldPlay}
|
|
playsInline
|
|
loop={shouldLoop}
|
|
muted
|
|
/>
|
|
)}
|
|
{isLottie && isMediaLoaded && (
|
|
<AnimatedSticker
|
|
key={mediaHash}
|
|
className={buildClassName('full-media', transitionClassNames)}
|
|
tgsUrl={mediaData}
|
|
size={width}
|
|
play={shouldPlay}
|
|
noLoop={!shouldLoop}
|
|
onLoad={markLottieLoaded}
|
|
/>
|
|
)}
|
|
<StickerSetModal
|
|
isOpen={isModalOpen}
|
|
fromSticker={sticker}
|
|
onClose={closeModal}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Sticker;
|