[Refactoring] Custom Emoji Manager: Do not run during chat opening, fix fasterdom phasing
This commit is contained in:
parent
2f3671899c
commit
577d055d68
@ -62,7 +62,7 @@ export type OwnProps = {
|
|||||||
attachments: ApiAttachment[];
|
attachments: ApiAttachment[];
|
||||||
getHtml: Signal<string>;
|
getHtml: Signal<string>;
|
||||||
canShowCustomSendMenu?: boolean;
|
canShowCustomSendMenu?: boolean;
|
||||||
isReady?: boolean;
|
isReady: boolean;
|
||||||
shouldSchedule?: boolean;
|
shouldSchedule?: boolean;
|
||||||
shouldSuggestCompression?: boolean;
|
shouldSuggestCompression?: boolean;
|
||||||
shouldForceCompression?: boolean;
|
shouldForceCompression?: boolean;
|
||||||
@ -595,6 +595,7 @@ const AttachmentModal: FC<OwnProps & StateProps> = ({
|
|||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
threadId={threadId}
|
threadId={threadId}
|
||||||
isAttachmentModalInput
|
isAttachmentModalInput
|
||||||
|
isReady={isReady}
|
||||||
isActive={isOpen}
|
isActive={isOpen}
|
||||||
getHtml={getHtml}
|
getHtml={getHtml}
|
||||||
editableInputId={EDITABLE_INPUT_MODAL_ID}
|
editableInputId={EDITABLE_INPUT_MODAL_ID}
|
||||||
|
|||||||
@ -1421,6 +1421,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
canSendPlainText={!isComposerBlocked}
|
canSendPlainText={!isComposerBlocked}
|
||||||
threadId={threadId}
|
threadId={threadId}
|
||||||
|
isReady={isReady}
|
||||||
isActive={!hasAttachments}
|
isActive={!hasAttachments}
|
||||||
getHtml={getHtml}
|
getHtml={getHtml}
|
||||||
placeholder={
|
placeholder={
|
||||||
|
|||||||
@ -47,6 +47,7 @@ type OwnProps = {
|
|||||||
threadId: number;
|
threadId: number;
|
||||||
isAttachmentModalInput?: boolean;
|
isAttachmentModalInput?: boolean;
|
||||||
editableInputId?: string;
|
editableInputId?: string;
|
||||||
|
isReady: boolean;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
getHtml: Signal<string>;
|
getHtml: Signal<string>;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
@ -101,6 +102,7 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
|||||||
captionLimit,
|
captionLimit,
|
||||||
isAttachmentModalInput,
|
isAttachmentModalInput,
|
||||||
editableInputId,
|
editableInputId,
|
||||||
|
isReady,
|
||||||
isActive,
|
isActive,
|
||||||
getHtml,
|
getHtml,
|
||||||
placeholder,
|
placeholder,
|
||||||
@ -160,6 +162,7 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
|||||||
absoluteContainerRef,
|
absoluteContainerRef,
|
||||||
isAttachmentModalInput ? 'attachment' : 'composer',
|
isAttachmentModalInput ? 'attachment' : 'composer',
|
||||||
canPlayAnimatedEmojis,
|
canPlayAnimatedEmojis,
|
||||||
|
isReady,
|
||||||
isActive,
|
isActive,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
useCallback, useEffect, useRef,
|
useCallback, useEffect, useLayoutEffect, useRef,
|
||||||
} from '../../../../lib/teact/teact';
|
} from '../../../../lib/teact/teact';
|
||||||
import { requestMeasure, requestMutation } from '../../../../lib/fasterdom/fasterdom';
|
import { requestMeasure } from '../../../../lib/fasterdom/fasterdom';
|
||||||
import { ensureRLottie } from '../../../../lib/rlottie/RLottie.async';
|
import { ensureRLottie } from '../../../../lib/rlottie/RLottie.async';
|
||||||
|
|
||||||
import type { ApiSticker } from '../../../../api/types';
|
import type { ApiSticker } from '../../../../api/types';
|
||||||
@ -12,7 +12,6 @@ import { selectIsAlwaysHighPriorityEmoji } from '../../../../global/selectors';
|
|||||||
import {
|
import {
|
||||||
addCustomEmojiInputRenderCallback,
|
addCustomEmojiInputRenderCallback,
|
||||||
getCustomEmojiMediaDataForInput,
|
getCustomEmojiMediaDataForInput,
|
||||||
removeCustomEmojiInputRenderCallback,
|
|
||||||
} from '../../../../util/customEmojiManager';
|
} from '../../../../util/customEmojiManager';
|
||||||
import { round } from '../../../../util/math';
|
import { round } from '../../../../util/math';
|
||||||
import AbsoluteVideo from '../../../../util/AbsoluteVideo';
|
import AbsoluteVideo from '../../../../util/AbsoluteVideo';
|
||||||
@ -25,6 +24,7 @@ import useBackgroundMode from '../../../../hooks/useBackgroundMode';
|
|||||||
import useThrottledCallback from '../../../../hooks/useThrottledCallback';
|
import useThrottledCallback from '../../../../hooks/useThrottledCallback';
|
||||||
import useDynamicColorListener from '../../../../hooks/stickers/useDynamicColorListener';
|
import useDynamicColorListener from '../../../../hooks/stickers/useDynamicColorListener';
|
||||||
import useEffectWithPrevDeps from '../../../../hooks/useEffectWithPrevDeps';
|
import useEffectWithPrevDeps from '../../../../hooks/useEffectWithPrevDeps';
|
||||||
|
import { useLastCallback } from '../../../../hooks/useLastCallback';
|
||||||
|
|
||||||
const SIZE = 1.25 * REM;
|
const SIZE = 1.25 * REM;
|
||||||
const THROTTLE_MS = 300;
|
const THROTTLE_MS = 300;
|
||||||
@ -44,9 +44,10 @@ export default function useInputCustomEmojis(
|
|||||||
absoluteContainerRef: React.RefObject<HTMLElement>,
|
absoluteContainerRef: React.RefObject<HTMLElement>,
|
||||||
prefixId: string,
|
prefixId: string,
|
||||||
canPlayAnimatedEmojis: boolean,
|
canPlayAnimatedEmojis: boolean,
|
||||||
|
isReady?: boolean,
|
||||||
isActive?: boolean,
|
isActive?: boolean,
|
||||||
) {
|
) {
|
||||||
const customColor = useDynamicColorListener(inputRef);
|
const customColor = useDynamicColorListener(inputRef, !isReady);
|
||||||
const colorFilter = useColorFilter(customColor, true);
|
const colorFilter = useColorFilter(customColor, true);
|
||||||
const playersById = useRef<Map<string, CustomEmojiPlayer>>(new Map());
|
const playersById = useRef<Map<string, CustomEmojiPlayer>>(new Map());
|
||||||
|
|
||||||
@ -60,12 +61,8 @@ export default function useInputCustomEmojis(
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const synchronizeElements = useCallback(() => {
|
const synchronizeElements = useLastCallback(() => {
|
||||||
if (!inputRef.current || !sharedCanvasRef.current || !sharedCanvasHqRef.current) return;
|
if (!isReady || !inputRef.current || !sharedCanvasRef.current || !sharedCanvasHqRef.current) return;
|
||||||
|
|
||||||
requestMutation(() => {
|
|
||||||
document.documentElement.style.setProperty('--input-custom-emoji-filter', colorFilter || 'none');
|
|
||||||
});
|
|
||||||
|
|
||||||
const global = getGlobal();
|
const global = getGlobal();
|
||||||
const playerIdsToClear = new Set(playersById.current.keys());
|
const playerIdsToClear = new Set(playersById.current.keys());
|
||||||
@ -85,65 +82,56 @@ export default function useInputCustomEmojis(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestMeasure(() => {
|
const canvasBounds = sharedCanvasRef.current!.getBoundingClientRect();
|
||||||
const canvasBounds = sharedCanvasRef.current!.getBoundingClientRect();
|
const elementBounds = element.getBoundingClientRect();
|
||||||
const elementBounds = element.getBoundingClientRect();
|
const x = round((elementBounds.left - canvasBounds.left) / canvasBounds.width, 4);
|
||||||
const x = round((elementBounds.left - canvasBounds.left) / canvasBounds.width, 4);
|
const y = round((elementBounds.top - canvasBounds.top) / canvasBounds.height, 4);
|
||||||
const y = round((elementBounds.top - canvasBounds.top) / canvasBounds.height, 4);
|
|
||||||
|
|
||||||
if (playersById.current.has(playerId)) {
|
if (playersById.current.has(playerId)) {
|
||||||
const player = playersById.current.get(playerId)!;
|
const player = playersById.current.get(playerId)!;
|
||||||
player.updatePosition(x, y);
|
player.updatePosition(x, y);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const customEmoji = global.customEmojis.byId[documentId];
|
||||||
|
if (!customEmoji) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const isHq = customEmoji?.stickerSetInfo && selectIsAlwaysHighPriorityEmoji(global, customEmoji.stickerSetInfo);
|
||||||
|
const renderId = [
|
||||||
|
prefixId, documentId, customColor,
|
||||||
|
].filter(Boolean).join('_');
|
||||||
|
|
||||||
|
createPlayer({
|
||||||
|
customEmoji,
|
||||||
|
sharedCanvasRef,
|
||||||
|
sharedCanvasHqRef,
|
||||||
|
absoluteContainerRef,
|
||||||
|
renderId,
|
||||||
|
viewId: playerId,
|
||||||
|
mediaUrl,
|
||||||
|
isHq,
|
||||||
|
position: { x, y },
|
||||||
|
textColor: customColor,
|
||||||
|
colorFilter,
|
||||||
|
}).then((animation) => {
|
||||||
|
if (canPlayAnimatedEmojis) {
|
||||||
|
animation.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
const customEmoji = global.customEmojis.byId[documentId];
|
playersById.current.set(playerId, animation);
|
||||||
if (!customEmoji) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const isHq = customEmoji?.stickerSetInfo && selectIsAlwaysHighPriorityEmoji(global, customEmoji.stickerSetInfo);
|
|
||||||
const renderId = [
|
|
||||||
prefixId, documentId, customColor,
|
|
||||||
].filter(Boolean).join('_');
|
|
||||||
|
|
||||||
createPlayer({
|
|
||||||
customEmoji,
|
|
||||||
sharedCanvasRef,
|
|
||||||
sharedCanvasHqRef,
|
|
||||||
absoluteContainerRef,
|
|
||||||
renderId,
|
|
||||||
viewId: playerId,
|
|
||||||
mediaUrl,
|
|
||||||
isHq,
|
|
||||||
position: { x, y },
|
|
||||||
textColor: customColor,
|
|
||||||
colorFilter,
|
|
||||||
}).then((animation) => {
|
|
||||||
if (canPlayAnimatedEmojis) {
|
|
||||||
animation.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
playersById.current.set(playerId, animation);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
clearPlayers(Array.from(playerIdsToClear));
|
clearPlayers(Array.from(playerIdsToClear));
|
||||||
}, [
|
});
|
||||||
inputRef, sharedCanvasRef, sharedCanvasHqRef, clearPlayers, prefixId, customColor, absoluteContainerRef,
|
|
||||||
canPlayAnimatedEmojis, colorFilter,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
addCustomEmojiInputRenderCallback(synchronizeElements);
|
return addCustomEmojiInputRenderCallback(synchronizeElements);
|
||||||
|
|
||||||
return () => {
|
|
||||||
removeCustomEmojiInputRenderCallback(synchronizeElements);
|
|
||||||
};
|
|
||||||
}, [synchronizeElements]);
|
}, [synchronizeElements]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!getHtml() || !inputRef.current || !sharedCanvasRef.current || !isActive) {
|
if (!getHtml() || !inputRef.current || !sharedCanvasRef.current || !isActive || !isReady) {
|
||||||
clearPlayers(Array.from(playersById.current.keys()));
|
clearPlayers(Array.from(playersById.current.keys()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -152,10 +140,14 @@ export default function useInputCustomEmojis(
|
|||||||
requestMeasure(() => {
|
requestMeasure(() => {
|
||||||
synchronizeElements();
|
synchronizeElements();
|
||||||
});
|
});
|
||||||
}, [getHtml, synchronizeElements, inputRef, clearPlayers, sharedCanvasRef, isActive]);
|
}, [getHtml, synchronizeElements, inputRef, clearPlayers, sharedCanvasRef, isActive, isReady]);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
document.documentElement.style.setProperty('--input-custom-emoji-filter', colorFilter || 'none');
|
||||||
|
}, [colorFilter]);
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevCustomColor]) => {
|
useEffectWithPrevDeps(([prevCustomColor]) => {
|
||||||
if (customColor !== prevCustomColor) {
|
if (prevCustomColor !== undefined && customColor !== prevCustomColor) {
|
||||||
synchronizeElements();
|
synchronizeElements();
|
||||||
}
|
}
|
||||||
}, [customColor, synchronizeElements]);
|
}, [customColor, synchronizeElements]);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
export function createCallbackManager() {
|
export function createCallbackManager<T extends AnyToVoidFunction = AnyToVoidFunction>() {
|
||||||
const callbacks = new Set<AnyToVoidFunction>();
|
const callbacks = new Set<T>();
|
||||||
|
|
||||||
function addCallback(cb: AnyToVoidFunction) {
|
function addCallback(cb: T) {
|
||||||
callbacks.add(cb);
|
callbacks.add(cb);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -9,11 +9,11 @@ export function createCallbackManager() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeCallback(cb: AnyToVoidFunction) {
|
function removeCallback(cb: T) {
|
||||||
callbacks.delete(cb);
|
callbacks.delete(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
function runCallbacks(...args: any[]) {
|
function runCallbacks(...args: Parameters<T>) {
|
||||||
callbacks.forEach((callback) => {
|
callbacks.forEach((callback) => {
|
||||||
callback(...args);
|
callback(...args);
|
||||||
});
|
});
|
||||||
@ -31,4 +31,5 @@ export function createCallbackManager() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CallbackManager = ReturnType<typeof createCallbackManager>;
|
export type CallbackManager<T extends AnyToVoidFunction = AnyToVoidFunction>
|
||||||
|
= ReturnType<typeof createCallbackManager<T>>;
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import * as mediaLoader from './mediaLoader';
|
|||||||
import { throttle } from './schedulers';
|
import { throttle } from './schedulers';
|
||||||
import generateIdFor from './generateIdFor';
|
import generateIdFor from './generateIdFor';
|
||||||
import { IS_WEBM_SUPPORTED } from './windowEnvironment';
|
import { IS_WEBM_SUPPORTED } from './windowEnvironment';
|
||||||
|
import { createCallbackManager } from './callbacks';
|
||||||
|
|
||||||
import placeholderSrc from '../assets/square.svg';
|
import placeholderSrc from '../assets/square.svg';
|
||||||
import blankSrc from '../assets/blank.png';
|
import blankSrc from '../assets/blank.png';
|
||||||
@ -25,7 +26,7 @@ const DOM_PROCESS_THROTTLE = 500;
|
|||||||
const INPUT_WAITING_CUSTOM_EMOJI_IDS: Set<string> = new Set();
|
const INPUT_WAITING_CUSTOM_EMOJI_IDS: Set<string> = new Set();
|
||||||
|
|
||||||
const handlers = new Map<CustomEmojiLoadCallback, string>();
|
const handlers = new Map<CustomEmojiLoadCallback, string>();
|
||||||
const renderHandlers = new Set<CustomEmojiInputRenderCallback>();
|
const renderCallbacks = createCallbackManager<CustomEmojiInputRenderCallback>();
|
||||||
|
|
||||||
let prevGlobal: GlobalState | undefined;
|
let prevGlobal: GlobalState | undefined;
|
||||||
|
|
||||||
@ -55,17 +56,8 @@ export function removeCustomEmojiCallback(handler: CustomEmojiLoadCallback) {
|
|||||||
handlers.delete(handler);
|
handlers.delete(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addCustomEmojiInputRenderCallback(handler: AnyToVoidFunction) {
|
export const addCustomEmojiInputRenderCallback = renderCallbacks.addCallback;
|
||||||
renderHandlers.add(handler);
|
const callInputRenderHandlers = throttle(renderCallbacks.runCallbacks, DOM_PROCESS_THROTTLE);
|
||||||
}
|
|
||||||
|
|
||||||
export function removeCustomEmojiInputRenderCallback(handler: AnyToVoidFunction) {
|
|
||||||
renderHandlers.delete(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
const callInputRenderHandlers = throttle((emojiId: string) => {
|
|
||||||
renderHandlers.forEach((handler) => handler(emojiId));
|
|
||||||
}, DOM_PROCESS_THROTTLE);
|
|
||||||
|
|
||||||
function processDomForCustomEmoji() {
|
function processDomForCustomEmoji() {
|
||||||
const emojis = document.querySelectorAll<HTMLImageElement>('.custom-emoji.placeholder');
|
const emojis = document.querySelectorAll<HTMLImageElement>('.custom-emoji.placeholder');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user