Sticker: Fix looping behavior (#4279)
This commit is contained in:
parent
ae9d09c762
commit
95f98442fc
@ -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}
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user