More improvements for shared canvas emoji
This commit is contained in:
parent
a2a0161cc9
commit
7cc64ed6bb
@ -7,7 +7,6 @@ import type { FC, TeactNode } from '../../lib/teact/teact';
|
|||||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||||
import { ApiMessageEntityTypes } from '../../api/types';
|
import { ApiMessageEntityTypes } from '../../api/types';
|
||||||
|
|
||||||
import { REM } from './helpers/mediaDimensions';
|
|
||||||
import { getPropertyHexColor } from '../../util/themeStyle';
|
import { getPropertyHexColor } from '../../util/themeStyle';
|
||||||
import { hexToRgb } from '../../util/switchTheme';
|
import { hexToRgb } from '../../util/switchTheme';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
@ -79,8 +78,6 @@ const CustomEmoji: FC<OwnProps> = ({
|
|||||||
const [customColor, setCustomColor] = useState<[number, number, number] | undefined>();
|
const [customColor, setCustomColor] = useState<[number, number, number] | undefined>();
|
||||||
const hasCustomColor = customEmoji && selectIsDefaultEmojiStatusPack(getGlobal(), customEmoji.stickerSetInfo);
|
const hasCustomColor = customEmoji && selectIsDefaultEmojiStatusPack(getGlobal(), customEmoji.stickerSetInfo);
|
||||||
|
|
||||||
const [realSize, setRealSize] = useState<number>(size);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!hasCustomColor) {
|
if (!hasCustomColor) {
|
||||||
setCustomColor(undefined);
|
setCustomColor(undefined);
|
||||||
@ -95,13 +92,6 @@ const CustomEmoji: FC<OwnProps> = ({
|
|||||||
setCustomColor([customColorRgb.r, customColorRgb.g, customColorRgb.b]);
|
setCustomColor([customColorRgb.r, customColorRgb.g, customColorRgb.b]);
|
||||||
}, [hasCustomColor]);
|
}, [hasCustomColor]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const computedSize = getComputedStyle(containerRef.current!).getPropertyValue('--custom-emoji-size');
|
|
||||||
if (computedSize) {
|
|
||||||
setRealSize(Math.round(Number(computedSize.replace(/[^\d.]/g, '')) * REM));
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleVideoEnded = useCallback((e) => {
|
const handleVideoEnded = useCallback((e) => {
|
||||||
if (!loopLimit) return;
|
if (!loopLimit) return;
|
||||||
|
|
||||||
@ -154,7 +144,7 @@ const CustomEmoji: FC<OwnProps> = ({
|
|||||||
containerRef={containerRef}
|
containerRef={containerRef}
|
||||||
sticker={customEmoji}
|
sticker={customEmoji}
|
||||||
isSmall
|
isSmall
|
||||||
size={realSize}
|
size={size}
|
||||||
customColor={customColor}
|
customColor={customColor}
|
||||||
thumbClassName={styles.thumb}
|
thumbClassName={styles.thumb}
|
||||||
fullMediaClassName={styles.media}
|
fullMediaClassName={styles.media}
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
bottom: 0.625rem;
|
bottom: 0.625rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
img:not(.emoji) {
|
.pictogram {
|
||||||
margin-inline-start: 0.5rem;
|
margin-inline-start: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +120,7 @@
|
|||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
img:not(.emoji) {
|
.pictogram {
|
||||||
width: 2rem;
|
width: 2rem;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
@ -144,7 +144,7 @@
|
|||||||
bottom: 0.3125rem;
|
bottom: 0.3125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
img:not(.emoji) {
|
.pictogram {
|
||||||
margin-left: 0.125rem;
|
margin-left: 0.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -120,7 +120,7 @@ function renderPictogram(
|
|||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
alt=""
|
alt=""
|
||||||
className={isRoundVideo ? 'round' : ''}
|
className={buildClassName('pictogram', isRoundVideo && 'round')}
|
||||||
draggable={!isProtected}
|
draggable={!isProtected}
|
||||||
/>
|
/>
|
||||||
{isProtected && <span className="protector" />}
|
{isProtected && <span className="protector" />}
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
--custom-emoji-size: 1.25rem;
|
|
||||||
|
|
||||||
> h3 {
|
> h3 {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import { useIsIntersecting } from '../../hooks/useIntersectionObserver';
|
|||||||
import useThumbnail from '../../hooks/useThumbnail';
|
import useThumbnail from '../../hooks/useThumbnail';
|
||||||
import useMediaTransition from '../../hooks/useMediaTransition';
|
import useMediaTransition from '../../hooks/useMediaTransition';
|
||||||
import useFlag from '../../hooks/useFlag';
|
import useFlag from '../../hooks/useFlag';
|
||||||
import useSharedCanvasCoords from '../../hooks/useSharedCanvasCoords';
|
import useBoundsInSharedCanvas from '../../hooks/useBoundsInSharedCanvas';
|
||||||
|
|
||||||
import AnimatedSticker from './AnimatedSticker';
|
import AnimatedSticker from './AnimatedSticker';
|
||||||
import OptimizedVideo from '../ui/OptimizedVideo';
|
import OptimizedVideo from '../ui/OptimizedVideo';
|
||||||
@ -114,14 +114,15 @@ const StickerView: FC<OwnProps> = ({
|
|||||||
const fullMediaClassNames = useMediaTransition(isFullMediaReady);
|
const fullMediaClassNames = useMediaTransition(isFullMediaReady);
|
||||||
const noTransition = isLottie && preloadedPreviewData;
|
const noTransition = isLottie && preloadedPreviewData;
|
||||||
|
|
||||||
const sharedCanvasCoords = useSharedCanvasCoords(containerRef, sharedCanvasRef);
|
const bounds = useBoundsInSharedCanvas(containerRef, sharedCanvasRef);
|
||||||
|
const realSize = bounds.size || size;
|
||||||
|
|
||||||
// Preload preview for Message Input and local message
|
// Preload preview for Message Input and local message
|
||||||
useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview, undefined, cacheBuster);
|
useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview, undefined, cacheBuster);
|
||||||
|
|
||||||
const randomIdPrefix = useMemo(() => generateIdFor(ID_STORE, true), []);
|
const randomIdPrefix = useMemo(() => generateIdFor(ID_STORE, true), []);
|
||||||
const idKey = [
|
const idKey = [
|
||||||
(withSharedAnimation ? SHARED_PREFIX : randomIdPrefix), id, size, customColor?.join(','),
|
(withSharedAnimation ? SHARED_PREFIX : randomIdPrefix), id, realSize, customColor?.join(','),
|
||||||
].filter(Boolean).join('_');
|
].filter(Boolean).join('_');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -141,7 +142,7 @@ const StickerView: FC<OwnProps> = ({
|
|||||||
<AnimatedSticker
|
<AnimatedSticker
|
||||||
key={idKey}
|
key={idKey}
|
||||||
animationId={idKey}
|
animationId={idKey}
|
||||||
size={size}
|
size={realSize}
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
styles.media,
|
styles.media,
|
||||||
(noTransition || isThumbOpaque) && styles.noTransition,
|
(noTransition || isThumbOpaque) && styles.noTransition,
|
||||||
@ -155,7 +156,7 @@ const StickerView: FC<OwnProps> = ({
|
|||||||
forceOnHeavyAnimation={forceOnHeavyAnimation}
|
forceOnHeavyAnimation={forceOnHeavyAnimation}
|
||||||
isLowPriority={isSmall && !selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSetInfo)}
|
isLowPriority={isSmall && !selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSetInfo)}
|
||||||
sharedCanvas={sharedCanvasRef?.current || undefined}
|
sharedCanvas={sharedCanvasRef?.current || undefined}
|
||||||
sharedCanvasCoords={sharedCanvasCoords}
|
sharedCanvasCoords={bounds.coords}
|
||||||
onLoad={markPlayerReady}
|
onLoad={markPlayerReady}
|
||||||
onLoop={onAnimatedStickerLoop}
|
onLoop={onAnimatedStickerLoop}
|
||||||
onEnded={onAnimatedStickerLoop}
|
onEnded={onAnimatedStickerLoop}
|
||||||
|
|||||||
@ -175,10 +175,6 @@
|
|||||||
vertical-align: -0.125rem;
|
vertical-align: -0.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-emoji {
|
|
||||||
--custom-emoji-size: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-play {
|
.icon-play {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
@ -263,7 +263,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
|||||||
const isChat = chat && (isChatChannel(chat) || lastMessage.senderId === lastMessage.chatId);
|
const isChat = chat && (isChatChannel(chat) || lastMessage.senderId === lastMessage.chatId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p className="last-message" dir={lang.isRtl ? 'auto' : 'ltr'}>
|
<p className="last-message shared-canvas-container" dir={lang.isRtl ? 'auto' : 'ltr'}>
|
||||||
{renderActionMessageText(
|
{renderActionMessageText(
|
||||||
lang,
|
lang,
|
||||||
lastMessage,
|
lastMessage,
|
||||||
@ -281,7 +281,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
|||||||
const senderName = getMessageSenderName(lang, chatId, lastMessageSender);
|
const senderName = getMessageSenderName(lang, chatId, lastMessageSender);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p className="last-message" dir={lang.isRtl ? 'auto' : 'ltr'}>
|
<p className="last-message shared-canvas-container" dir={lang.isRtl ? 'auto' : 'ltr'}>
|
||||||
{senderName && (
|
{senderName && (
|
||||||
<>
|
<>
|
||||||
<span className="sender-name">{renderText(senderName)}</span>
|
<span className="sender-name">{renderText(senderName)}</span>
|
||||||
|
|||||||
@ -73,10 +73,6 @@
|
|||||||
vertical-align: -2px;
|
vertical-align: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-emoji {
|
|
||||||
--custom-emoji-size: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.multiline {
|
&.multiline {
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
|
|||||||
@ -519,10 +519,6 @@
|
|||||||
body.is-ios & {
|
body.is-ios & {
|
||||||
font-size: 0.9375rem;
|
font-size: 0.9375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-emoji {
|
|
||||||
--custom-emoji-size: 1.25rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -572,7 +572,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.custom-emoji {
|
.custom-emoji {
|
||||||
--custom-emoji-size: 1.25rem;
|
|
||||||
vertical-align: text-top;
|
vertical-align: text-top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import buildClassName from '../../../util/buildClassName';
|
|||||||
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
|
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
|
||||||
import useMedia from '../../../hooks/useMedia';
|
import useMedia from '../../../hooks/useMedia';
|
||||||
import useMediaTransition from '../../../hooks/useMediaTransition';
|
import useMediaTransition from '../../../hooks/useMediaTransition';
|
||||||
import useSharedCanvasCoords from '../../../hooks/useSharedCanvasCoords';
|
import useBoundsInSharedCanvas from '../../../hooks/useBoundsInSharedCanvas';
|
||||||
|
|
||||||
import AnimatedSticker from '../../common/AnimatedSticker';
|
import AnimatedSticker from '../../common/AnimatedSticker';
|
||||||
import OptimizedVideo from '../../ui/OptimizedVideo';
|
import OptimizedVideo from '../../ui/OptimizedVideo';
|
||||||
@ -46,7 +46,7 @@ const StickerSetCover: FC<OwnProps> = ({
|
|||||||
const isReady = mediaData && (!isVideo || IS_WEBM_SUPPORTED);
|
const isReady = mediaData && (!isVideo || IS_WEBM_SUPPORTED);
|
||||||
const transitionClassNames = useMediaTransition(isReady);
|
const transitionClassNames = useMediaTransition(isReady);
|
||||||
|
|
||||||
const sharedCanvasCoords = useSharedCanvasCoords(containerRef, sharedCanvasRef);
|
const bounds = useBoundsInSharedCanvas(containerRef, sharedCanvasRef);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className="sticker-set-cover">
|
<div ref={containerRef} className="sticker-set-cover">
|
||||||
@ -55,11 +55,11 @@ const StickerSetCover: FC<OwnProps> = ({
|
|||||||
<AnimatedSticker
|
<AnimatedSticker
|
||||||
className={transitionClassNames}
|
className={transitionClassNames}
|
||||||
tgsUrl={mediaData}
|
tgsUrl={mediaData}
|
||||||
size={size}
|
size={size || bounds.size}
|
||||||
play={isIntersecting && !noAnimate}
|
play={isIntersecting && !noAnimate}
|
||||||
isLowPriority={!selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSet)}
|
isLowPriority={!selectIsAlwaysHighPriorityEmoji(getGlobal(), stickerSet)}
|
||||||
sharedCanvas={sharedCanvasRef?.current || undefined}
|
sharedCanvas={sharedCanvasRef?.current || undefined}
|
||||||
sharedCanvasCoords={sharedCanvasCoords}
|
sharedCanvasCoords={bounds.coords}
|
||||||
/>
|
/>
|
||||||
) : isVideo ? (
|
) : isVideo ? (
|
||||||
<OptimizedVideo
|
<OptimizedVideo
|
||||||
|
|||||||
@ -95,6 +95,7 @@ import useOuterHandlers from './hooks/useOuterHandlers';
|
|||||||
import useInnerHandlers from './hooks/useInnerHandlers';
|
import useInnerHandlers from './hooks/useInnerHandlers';
|
||||||
import { getServerTime } from '../../../util/serverTime';
|
import { getServerTime } from '../../../util/serverTime';
|
||||||
import { isElementInViewport } from '../../../util/isElementInViewport';
|
import { isElementInViewport } from '../../../util/isElementInViewport';
|
||||||
|
import { getCustomEmojiSize } from '../composer/helpers/customEmoji';
|
||||||
|
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
@ -126,10 +127,9 @@ import DotAnimation from '../../common/DotAnimation';
|
|||||||
import CustomEmoji from '../../common/CustomEmoji';
|
import CustomEmoji from '../../common/CustomEmoji';
|
||||||
import PremiumIcon from '../../common/PremiumIcon';
|
import PremiumIcon from '../../common/PremiumIcon';
|
||||||
import FakeIcon from '../../common/FakeIcon';
|
import FakeIcon from '../../common/FakeIcon';
|
||||||
|
import MessageText from '../../common/MessageText';
|
||||||
|
|
||||||
import './Message.scss';
|
import './Message.scss';
|
||||||
import MessageText from '../../common/MessageText';
|
|
||||||
import { getCustomEmojiSize } from '../composer/helpers/customEmoji';
|
|
||||||
|
|
||||||
type MessagePositionProperties = {
|
type MessagePositionProperties = {
|
||||||
isFirstInGroup: boolean;
|
isFirstInGroup: boolean;
|
||||||
|
|||||||
@ -272,7 +272,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.custom-emoji {
|
.custom-emoji {
|
||||||
--custom-emoji-size: 1.25rem;
|
|
||||||
margin-left: 0.25rem;
|
margin-left: 0.25rem;
|
||||||
|
|
||||||
&.custom-color {
|
&.custom-color {
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
useCallback, useEffect, useMemo, useState,
|
useCallback, useEffect, useLayoutEffect, useMemo, useState,
|
||||||
} from '../lib/teact/teact';
|
} from '../lib/teact/teact';
|
||||||
|
|
||||||
import { throttle } from '../util/schedulers';
|
import { throttle } from '../util/schedulers';
|
||||||
import { round } from '../util/math';
|
import { round } from '../util/math';
|
||||||
|
|
||||||
export default function useSharedCanvasCoords(
|
export default function useBoundsInSharedCanvas(
|
||||||
containerRef: React.RefObject<HTMLDivElement>,
|
containerRef: React.RefObject<HTMLDivElement>,
|
||||||
sharedCanvasRef?: React.RefObject<HTMLCanvasElement>,
|
sharedCanvasRef?: React.RefObject<HTMLCanvasElement>,
|
||||||
) {
|
) {
|
||||||
const [x, setX] = useState<number>();
|
const [x, setX] = useState<number>();
|
||||||
const [y, setY] = useState<number>();
|
const [y, setY] = useState<number>();
|
||||||
|
const [size, setSize] = useState<number>();
|
||||||
|
|
||||||
const recalculate = useCallback(() => {
|
const recalculate = useCallback(() => {
|
||||||
const container = containerRef.current;
|
const container = containerRef.current;
|
||||||
@ -25,8 +27,11 @@ export default function useSharedCanvasCoords(
|
|||||||
// Factor coords are used to support rendering while being rescaled (e.g. message appearance animation)
|
// Factor coords are used to support rendering while being rescaled (e.g. message appearance animation)
|
||||||
setX(round((targetBounds.left - canvasBounds.left) / canvasBounds.width, 4));
|
setX(round((targetBounds.left - canvasBounds.left) / canvasBounds.width, 4));
|
||||||
setY(round((targetBounds.top - canvasBounds.top) / canvasBounds.height, 4));
|
setY(round((targetBounds.top - canvasBounds.top) / canvasBounds.height, 4));
|
||||||
|
setSize(Math.round(targetBounds.width));
|
||||||
}, [containerRef, sharedCanvasRef]);
|
}, [containerRef, sharedCanvasRef]);
|
||||||
|
|
||||||
|
useLayoutEffect(recalculate, [recalculate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!('ResizeObserver' in window) || !sharedCanvasRef?.current) {
|
if (!('ResizeObserver' in window) || !sharedCanvasRef?.current) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -48,5 +53,7 @@ export default function useSharedCanvasCoords(
|
|||||||
};
|
};
|
||||||
}, [recalculate, sharedCanvasRef]);
|
}, [recalculate, sharedCanvasRef]);
|
||||||
|
|
||||||
return useMemo(() => (x !== undefined && y !== undefined ? { x, y } : undefined), [x, y]);
|
const coords = useMemo(() => (x !== undefined && y !== undefined ? { x, y } : undefined), [x, y]);
|
||||||
|
|
||||||
|
return { coords, size };
|
||||||
}
|
}
|
||||||
@ -28,6 +28,7 @@ const rLottieApiPromise = new Promise<void>((resolve) => {
|
|||||||
|
|
||||||
const HIGH_PRIORITY_MAX_FPS = 60;
|
const HIGH_PRIORITY_MAX_FPS = 60;
|
||||||
const LOW_PRIORITY_MAX_FPS = 30;
|
const LOW_PRIORITY_MAX_FPS = 30;
|
||||||
|
const DESTROY_REPEAT_DELAY = 1000;
|
||||||
|
|
||||||
const renderers = new Map<string, {
|
const renderers = new Map<string, {
|
||||||
imgSize: number;
|
imgSize: number;
|
||||||
@ -150,12 +151,17 @@ function applyColor(arr: Uint8ClampedArray, color: [number, number, number]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroy(key: string) {
|
function destroy(key: string, isRepeated = false) {
|
||||||
const renderer = renderers.get(key)!;
|
try {
|
||||||
|
const renderer = renderers.get(key)!;
|
||||||
rLottieApi.destroy(renderer.handle);
|
rLottieApi.destroy(renderer.handle);
|
||||||
|
renderers.delete(key);
|
||||||
renderers.delete(key);
|
} catch (err) {
|
||||||
|
// `destroy` sometimes can be called before the initialization is finished
|
||||||
|
if (!isRepeated) {
|
||||||
|
setTimeout(() => destroy(key, true), DESTROY_REPEAT_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createWorkerInterface({
|
createWorkerInterface({
|
||||||
|
|||||||
@ -184,7 +184,7 @@ $color-message-reaction-own-hover: #b5e0a4;
|
|||||||
--messages-container-width: 45.5rem;
|
--messages-container-width: 45.5rem;
|
||||||
--right-column-width: 26.5rem;
|
--right-column-width: 26.5rem;
|
||||||
--header-height: 3.5rem;
|
--header-height: 3.5rem;
|
||||||
--custom-emoji-size: 1.5rem;
|
--custom-emoji-size: 1.25rem;
|
||||||
|
|
||||||
--symbol-menu-width: 26.25rem;
|
--symbol-menu-width: 26.25rem;
|
||||||
--symbol-menu-height: 23.25rem;
|
--symbol-menu-height: 23.25rem;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user