TelegramPWA/src/components/common/CustomEmoji.tsx

139 lines
3.9 KiB
TypeScript

import React, {
memo, useCallback, useEffect, useRef, useState,
} from '../../lib/teact/teact';
import { getGlobal } from '../../global';
import type { FC, TeactNode } from '../../lib/teact/teact';
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
import { getPropertyHexColor } from '../../util/themeStyle';
import { hexToRgb } from '../../util/switchTheme';
import buildClassName from '../../util/buildClassName';
import safePlay from '../../util/safePlay';
import { selectIsDefaultEmojiStatusPack } from '../../global/selectors';
import useEnsureCustomEmoji from '../../hooks/useEnsureCustomEmoji';
import useCustomEmoji from './hooks/useCustomEmoji';
import StickerView from './StickerView';
import styles from './CustomEmoji.module.scss';
import svgPlaceholder from '../../assets/square.svg';
type OwnProps = {
documentId: string;
children?: TeactNode;
size?: number;
className?: string;
loopLimit?: number;
withGridFix?: boolean;
shouldPreloadPreview?: boolean;
observeIntersection?: ObserveFn;
observeIntersectionForPlaying?: ObserveFn;
onClick?: NoneToVoidFunction;
};
const STICKER_SIZE = 24;
const CustomEmoji: FC<OwnProps> = ({
documentId,
size = STICKER_SIZE,
className,
loopLimit,
withGridFix,
shouldPreloadPreview,
observeIntersection,
observeIntersectionForPlaying,
onClick,
}) => {
// eslint-disable-next-line no-null/no-null
const ref = useRef<HTMLDivElement>(null);
// An alternative to `withGlobal` to avoid adding numerous global containers
const customEmoji = useCustomEmoji(documentId);
useEnsureCustomEmoji(documentId);
const loopCountRef = useRef(0);
const [shouldLoop, setShouldLoop] = useState(true);
const [customColor, setCustomColor] = useState<[number, number, number] | undefined>();
const hasCustomColor = customEmoji && selectIsDefaultEmojiStatusPack(getGlobal(), customEmoji.stickerSetInfo);
useEffect(() => {
if (!hasCustomColor || !ref.current) {
setCustomColor(undefined);
return;
}
const hexColor = getPropertyHexColor(getComputedStyle(ref.current), '--emoji-status-color');
if (!hexColor) {
setCustomColor(undefined);
return;
}
const customColorRgb = hexToRgb(hexColor);
setCustomColor([customColorRgb.r, customColorRgb.g, customColorRgb.b]);
}, [hasCustomColor]);
const handleVideoEnded = useCallback((e) => {
if (!loopLimit) return;
loopCountRef.current += 1;
if (loopCountRef.current >= loopLimit) {
setShouldLoop(false);
e.currentTarget.currentTime = 0;
} else {
// Loop manually
safePlay(e.currentTarget);
}
}, [loopLimit]);
const handleStickerLoop = useCallback(() => {
if (!loopLimit) return;
loopCountRef.current += 1;
// Sticker plays 1 more time after disabling loop
if (loopCountRef.current >= loopLimit - 1) {
setShouldLoop(false);
}
}, [loopLimit]);
return (
<div
ref={ref}
className={buildClassName(
styles.root,
className,
'custom-emoji',
'emoji',
hasCustomColor && 'custom-color',
withGridFix && styles.withGridFix,
)}
onClick={onClick}
>
{!customEmoji ? (
<img className={styles.thumb} src={svgPlaceholder} alt="Emoji" />
) : (
<StickerView
containerRef={ref}
sticker={customEmoji}
isSmall
size={size}
customColor={customColor}
thumbClassName={styles.thumb}
fullMediaClassName={styles.media}
shouldLoop={shouldLoop}
loopLimit={loopLimit}
shouldPreloadPreview={shouldPreloadPreview}
observeIntersection={observeIntersection}
observeIntersectionForPlaying={observeIntersectionForPlaying}
onVideoEnded={handleVideoEnded}
onAnimatedStickerLoop={handleStickerLoop}
/>
)}
</div>
);
};
export default memo(CustomEmoji);