Sticker: Fix looping behavior (#4279)

This commit is contained in:
Alexander Zinchuk 2024-02-23 14:06:16 +01:00
parent ae9d09c762
commit 95f98442fc
6 changed files with 41 additions and 29 deletions

View File

@ -78,7 +78,7 @@ const CustomEmoji: FC<OwnProps> = ({
const { customEmoji, canPlay } = useCustomEmoji(documentId); const { customEmoji, canPlay } = useCustomEmoji(documentId);
const loopCountRef = useRef(0); const loopCountRef = useRef(0);
const [shouldLoop, setShouldLoop] = useState(true); const [shouldPlay, setShouldPlay] = useState(true);
const hasCustomColor = customEmoji?.shouldUseTextColor; const hasCustomColor = customEmoji?.shouldUseTextColor;
const customColor = useDynamicColorListener(containerRef, !hasCustomColor); const customColor = useDynamicColorListener(containerRef, !hasCustomColor);
@ -89,7 +89,7 @@ const CustomEmoji: FC<OwnProps> = ({
loopCountRef.current += 1; loopCountRef.current += 1;
if (loopCountRef.current >= loopLimit) { if (loopCountRef.current >= loopLimit) {
setShouldLoop(false); setShouldPlay(false);
e.currentTarget.currentTime = 0; e.currentTarget.currentTime = 0;
} else { } else {
// Loop manually // Loop manually
@ -102,9 +102,8 @@ const CustomEmoji: FC<OwnProps> = ({
loopCountRef.current += 1; loopCountRef.current += 1;
// Sticker plays 1 more time after disabling loop if (loopCountRef.current >= loopLimit) {
if (loopCountRef.current >= loopLimit - 1) { setShouldPlay(false);
setShouldLoop(false);
} }
}); });
@ -143,10 +142,10 @@ const CustomEmoji: FC<OwnProps> = ({
sticker={customEmoji} sticker={customEmoji}
isSmall={!isBig} isSmall={!isBig}
size={size} size={size}
noPlay={noPlay || !canPlay} noPlay={noPlay || !(shouldPlay && canPlay)}
thumbClassName={styles.thumb} thumbClassName={styles.thumb}
fullMediaClassName={styles.media} fullMediaClassName={styles.media}
shouldLoop={shouldLoop} shouldLoop
loopLimit={loopLimit} loopLimit={loopLimit}
shouldPreloadPreview={shouldPreloadPreview || noPlay || !canPlay} shouldPreloadPreview={shouldPreloadPreview || noPlay || !canPlay}
forceOnHeavyAnimation={forceOnHeavyAnimation} forceOnHeavyAnimation={forceOnHeavyAnimation}

View File

@ -180,12 +180,12 @@ const StickerView: FC<OwnProps> = ({
/> />
) : isVideo ? ( ) : isVideo ? (
<OptimizedVideo <OptimizedVideo
canPlay={shouldPlay && shouldLoop} canPlay={shouldPlay}
className={buildClassName(styles.media, fullMediaClassName, fullMediaClassNames, 'sticker-media')} className={buildClassName(styles.media, fullMediaClassName, fullMediaClassNames, 'sticker-media')}
src={fullMediaData} src={fullMediaData}
playsInline playsInline
muted muted
loop={!loopLimit} loop={shouldLoop && !loopLimit}
isPriority={forceAlways} isPriority={forceAlways}
disablePictureInPicture disablePictureInPicture
onReady={markPlayerReady} onReady={markPlayerReady}

View File

@ -35,6 +35,7 @@ type OwnProps = {
withEffectOnly?: boolean; withEffectOnly?: boolean;
shouldPause?: boolean; shouldPause?: boolean;
shouldLoop?: boolean; shouldLoop?: boolean;
loopLimit?: number;
observeIntersection?: ObserveFn; observeIntersection?: ObserveFn;
}; };
@ -64,6 +65,7 @@ const ReactionAnimatedEmoji = ({
withEffectOnly, withEffectOnly,
shouldPause, shouldPause,
shouldLoop, shouldLoop,
loopLimit,
observeIntersection, observeIntersection,
}: OwnProps & StateProps) => { }: OwnProps & StateProps) => {
const { stopActiveReaction } = getActions(); const { stopActiveReaction } = getActions();
@ -164,6 +166,7 @@ const ReactionAnimatedEmoji = ({
className={styles.customEmoji} className={styles.customEmoji}
size={size} size={size}
noPlay={shouldPause} noPlay={shouldPause}
loopLimit={loopLimit}
forceAlways forceAlways
observeIntersectionForPlaying={observeIntersection} observeIntersectionForPlaying={observeIntersection}
/> />

View File

@ -1,11 +1,12 @@
import type { FC } from '../../lib/teact/teact'; import type { FC } from '../../lib/teact/teact';
import React, { memo, useEffect, useRef } from '../../lib/teact/teact'; import React, {
memo, useEffect, useMemo, useRef,
} from '../../lib/teact/teact';
import { getActions, withGlobal } from '../../global'; import { getActions, withGlobal } from '../../global';
import type { ApiSticker, ApiUpdateConnectionStateType } from '../../api/types'; import type { ApiSticker, ApiUpdateConnectionStateType } from '../../api/types';
import type { MessageList } from '../../global/types'; import type { MessageList } from '../../global/types';
import { getPeerIdDividend } from '../../global/helpers';
import { selectChat, selectChatLastMessage, selectCurrentMessageList } from '../../global/selectors'; import { selectChat, selectChatLastMessage, selectCurrentMessageList } from '../../global/selectors';
import useLang from '../../hooks/useLang'; import useLang from '../../hooks/useLang';
@ -20,14 +21,14 @@ type OwnProps = {
}; };
type StateProps = { type StateProps = {
sticker?: ApiSticker; stickers?: ApiSticker[];
lastUnreadMessageId?: number; lastUnreadMessageId?: number;
connectionState?: ApiUpdateConnectionStateType; connectionState?: ApiUpdateConnectionStateType;
currentMessageList?: MessageList; currentMessageList?: MessageList;
}; };
const ContactGreeting: FC<OwnProps & StateProps> = ({ const ContactGreeting: FC<OwnProps & StateProps> = ({
sticker, stickers,
connectionState, connectionState,
lastUnreadMessageId, lastUnreadMessageId,
currentMessageList, currentMessageList,
@ -43,13 +44,20 @@ const ContactGreeting: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const sticker = useMemo(() => {
if (!stickers?.length) return undefined;
const randomIndex = Math.floor(Math.random() * stickers.length);
return stickers[randomIndex];
}, [stickers]);
useEffect(() => { useEffect(() => {
if (sticker || connectionState !== 'connectionStateReady') { if (stickers?.length || connectionState !== 'connectionStateReady') {
return; return;
} }
loadGreetingStickers(); loadGreetingStickers();
}, [connectionState, loadGreetingStickers, sticker]); }, [connectionState, loadGreetingStickers, stickers]);
useEffect(() => { useEffect(() => {
if (connectionState === 'connectionStateReady' && lastUnreadMessageId) { if (connectionState === 'connectionStateReady' && lastUnreadMessageId) {
@ -94,8 +102,6 @@ const ContactGreeting: FC<OwnProps & StateProps> = ({
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, { userId }): StateProps => { (global, { userId }): StateProps => {
const { stickers } = global.stickers.greeting; const { stickers } = global.stickers.greeting;
const dividend = getPeerIdDividend(userId) + getPeerIdDividend(global.currentUserId!);
const sticker = stickers?.length ? stickers[dividend % stickers.length] : undefined;
const chat = selectChat(global, userId); const chat = selectChat(global, userId);
if (!chat) { if (!chat) {
return {}; return {};
@ -104,7 +110,7 @@ export default memo(withGlobal<OwnProps>(
const lastMessage = selectChatLastMessage(global, chat.id); const lastMessage = selectChatLastMessage(global, chat.id);
return { return {
sticker, stickers,
lastUnreadMessageId: lastMessage && lastMessage.id !== chat.lastReadInboxMessageId lastUnreadMessageId: lastMessage && lastMessage.id !== chat.lastReadInboxMessageId
? lastMessage.id ? lastMessage.id
: undefined, : undefined,

View File

@ -607,7 +607,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
) : isBot && !hasMessages ? ( ) : isBot && !hasMessages ? (
<MessageListBotInfo chatId={chatId} /> <MessageListBotInfo chatId={chatId} />
) : shouldRenderGreeting ? ( ) : shouldRenderGreeting ? (
<ContactGreeting userId={chatId} /> <ContactGreeting key={chatId} userId={chatId} />
) : messageIds && (!messageGroups || isGroupChatJustCreated || isEmptyTopic) ? ( ) : messageIds && (!messageGroups || isGroupChatJustCreated || isEmptyTopic) ? (
<NoMessages <NoMessages
chatId={chatId} chatId={chatId}

View File

@ -26,6 +26,7 @@ import styles from './ReactionButton.module.scss';
const REACTION_SIZE = 1.25 * REM; const REACTION_SIZE = 1.25 * REM;
const TITLE_MAX_LENGTH = 15; const TITLE_MAX_LENGTH = 15;
const LOOP_LIMIT = 1;
const SavedTagButton: FC<{ const SavedTagButton: FC<{
reaction: ApiReaction; reaction: ApiReaction;
@ -134,6 +135,7 @@ const SavedTagButton: FC<{
className={styles.animatedEmoji} className={styles.animatedEmoji}
containerId={containerId} containerId={containerId}
reaction={reaction} reaction={reaction}
loopLimit={LOOP_LIMIT}
size={REACTION_SIZE} size={REACTION_SIZE}
observeIntersection={observeIntersection} observeIntersection={observeIntersection}
/> />
@ -151,16 +153,18 @@ const SavedTagButton: FC<{
> >
<path className={styles.tailFill} d="m 0,30 c 3.1855,0 6.1803,-1.5176 8.0641,-4.0864 l 5.835,-7.9568 c 1.2906,-1.7599 1.2906,-4.1537 0,-5.9136 L 8.0641,4.08636 C 6.1803,1.51761 3.1855,0 0,0" /> <path className={styles.tailFill} d="m 0,30 c 3.1855,0 6.1803,-1.5176 8.0641,-4.0864 l 5.835,-7.9568 c 1.2906,-1.7599 1.2906,-4.1537 0,-5.9136 L 8.0641,4.08636 C 6.1803,1.51761 3.1855,0 0,0" />
</svg> </svg>
<PromptDialog {withContextMenu && (
isOpen={isRenamePromptOpen} <PromptDialog
maxLength={TITLE_MAX_LENGTH} isOpen={isRenamePromptOpen}
title={lang(tag?.title ? 'SavedTagRenameTag' : 'SavedTagLabelTag')} maxLength={TITLE_MAX_LENGTH}
subtitle={lang('SavedTagLabelTagText')} title={lang(tag?.title ? 'SavedTagRenameTag' : 'SavedTagLabelTag')}
placeholder={lang('SavedTagLabelPlaceholder')} subtitle={lang('SavedTagLabelTagText')}
initialValue={tag?.title} placeholder={lang('SavedTagLabelPlaceholder')}
onClose={closeRenamePrompt} initialValue={tag?.title}
onSubmit={handleRenameTag} onClose={closeRenamePrompt}
/> onSubmit={handleRenameTag}
/>
)}
{withContextMenu && contextMenuPosition && ( {withContextMenu && contextMenuPosition && (
<Menu <Menu
ref={menuRef} ref={menuRef}