Sticker Set Cover: Fix pausing when hidden
This commit is contained in:
parent
b8573ebf35
commit
3bffde60cb
@ -29,13 +29,13 @@ type OwnProps<T> = {
|
||||
noAnimate?: boolean;
|
||||
title?: string;
|
||||
className?: string;
|
||||
clickArg: T;
|
||||
noContextMenu?: boolean;
|
||||
isSavedMessages?: boolean;
|
||||
canViewSet?: boolean;
|
||||
isCurrentUserPremium?: boolean;
|
||||
observeIntersection: ObserveFn;
|
||||
onClick?: (arg: OwnProps<T>['clickArg'], isSilent?: boolean, shouldSchedule?: boolean) => void;
|
||||
clickArg: T;
|
||||
onFaveClick?: (sticker: ApiSticker) => void;
|
||||
onUnfaveClick?: (sticker: ApiSticker) => void;
|
||||
onRemoveRecentClick?: (sticker: ApiSticker) => void;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { memo, useCallback, useMemo } from '../../lib/teact/teact';
|
||||
import React, { memo, useCallback } from '../../lib/teact/teact';
|
||||
|
||||
import type { ApiSticker, ApiStickerSet } from '../../api/types';
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
@ -11,7 +11,6 @@ import useLang from '../../hooks/useLang';
|
||||
|
||||
import ListItem from '../ui/ListItem';
|
||||
import Button from '../ui/Button';
|
||||
import StickerSetCoverAnimated from '../middle/composer/StickerSetCoverAnimated';
|
||||
import StickerSetCover from '../middle/composer/StickerSetCover';
|
||||
import StickerButton from './StickerButton';
|
||||
|
||||
@ -19,6 +18,7 @@ import './StickerSetCard.scss';
|
||||
|
||||
type OwnProps = {
|
||||
stickerSet?: ApiStickerSet;
|
||||
noAnimate?: boolean;
|
||||
className?: string;
|
||||
observeIntersection: ObserveFn;
|
||||
onClick: (value: ApiSticker) => void;
|
||||
@ -26,6 +26,7 @@ type OwnProps = {
|
||||
|
||||
const StickerSetCard: FC<OwnProps> = ({
|
||||
stickerSet,
|
||||
noAnimate,
|
||||
className,
|
||||
observeIntersection,
|
||||
onClick,
|
||||
@ -38,7 +39,11 @@ const StickerSetCard: FC<OwnProps> = ({
|
||||
if (firstSticker) onClick(firstSticker);
|
||||
}, [firstSticker, onClick]);
|
||||
|
||||
const preview = useMemo(() => {
|
||||
if (!stickerSet || !stickerSet.stickers) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function renderPreview() {
|
||||
if (!stickerSet) return undefined;
|
||||
if (stickerSet.hasThumbnail || !firstSticker) {
|
||||
return (
|
||||
@ -47,18 +52,12 @@ const StickerSetCard: FC<OwnProps> = ({
|
||||
color="translucent"
|
||||
isRtl={lang.isRtl}
|
||||
>
|
||||
{stickerSet.isLottie ? (
|
||||
<StickerSetCoverAnimated
|
||||
size={STICKER_SIZE_GENERAL_SETTINGS}
|
||||
stickerSet={stickerSet}
|
||||
observeIntersection={observeIntersection}
|
||||
/>
|
||||
) : (
|
||||
<StickerSetCover
|
||||
stickerSet={stickerSet}
|
||||
observeIntersection={observeIntersection}
|
||||
/>
|
||||
)}
|
||||
<StickerSetCover
|
||||
stickerSet={stickerSet}
|
||||
size={STICKER_SIZE_GENERAL_SETTINGS}
|
||||
noAnimate={noAnimate}
|
||||
observeIntersection={observeIntersection}
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
@ -67,17 +66,14 @@ const StickerSetCard: FC<OwnProps> = ({
|
||||
sticker={firstSticker}
|
||||
size={STICKER_SIZE_GENERAL_SETTINGS}
|
||||
title={stickerSet.title}
|
||||
noAnimate={noAnimate}
|
||||
observeIntersection={observeIntersection}
|
||||
clickArg={undefined}
|
||||
noContextMenu
|
||||
isCurrentUserPremium
|
||||
clickArg={undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}, [firstSticker, lang.isRtl, observeIntersection, stickerSet]);
|
||||
|
||||
if (!stickerSet || !stickerSet.stickers) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -87,7 +83,7 @@ const StickerSetCard: FC<OwnProps> = ({
|
||||
inactive={!firstSticker}
|
||||
onClick={handleCardClick}
|
||||
>
|
||||
{preview}
|
||||
{renderPreview()}
|
||||
<div className="multiline-menu-item">
|
||||
<div className="title">{stickerSet.title}</div>
|
||||
<div className="subtitle">{lang('StickerPack.StickerCount', stickerSet.count, 'i')}</div>
|
||||
|
||||
@ -33,7 +33,6 @@ import Button from '../../ui/Button';
|
||||
import StickerButton from '../../common/StickerButton';
|
||||
import StickerSet from './StickerSet';
|
||||
import StickerSetCover from './StickerSetCover';
|
||||
import StickerSetCoverAnimated from './StickerSetCoverAnimated';
|
||||
|
||||
import './StickerPicker.scss';
|
||||
|
||||
@ -50,7 +49,7 @@ type StateProps = {
|
||||
addedCustomEmojiIds?: string[];
|
||||
recentCustomEmoji: ApiSticker[];
|
||||
featuredCustomEmojiIds?: string[];
|
||||
shouldPlay?: boolean;
|
||||
canAnimate?: boolean;
|
||||
isSavedMessages?: boolean;
|
||||
isCurrentUserPremium?: boolean;
|
||||
};
|
||||
@ -68,7 +67,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
recentCustomEmoji,
|
||||
stickerSetsById,
|
||||
featuredCustomEmojiIds,
|
||||
shouldPlay,
|
||||
canAnimate,
|
||||
isSavedMessages,
|
||||
isCurrentUserPremium,
|
||||
onCustomEmojiSelect,
|
||||
@ -199,14 +198,10 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
{stickerSet.id === RECENT_SYMBOL_SET_ID ? (
|
||||
<i className="icon-recent" />
|
||||
) : stickerSet.isLottie ? (
|
||||
<StickerSetCoverAnimated
|
||||
stickerSet={stickerSet as ApiStickerSet}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
/>
|
||||
) : (
|
||||
<StickerSetCover
|
||||
stickerSet={stickerSet as ApiStickerSet}
|
||||
noAnimate={!canAnimate || !loadAndPlay}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
/>
|
||||
)}
|
||||
@ -220,11 +215,12 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
size={STICKER_SIZE_PICKER_HEADER}
|
||||
title={stickerSet.title}
|
||||
className={buttonClassName}
|
||||
noAnimate={!canAnimate || !loadAndPlay}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
onClick={selectStickerSet}
|
||||
clickArg={index}
|
||||
noContextMenu
|
||||
isCurrentUserPremium
|
||||
onClick={selectStickerSet}
|
||||
clickArg={index}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -260,14 +256,14 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
<StickerSet
|
||||
key={stickerSet.id}
|
||||
stickerSet={stickerSet}
|
||||
loadAndPlay={Boolean(shouldPlay && loadAndPlay)}
|
||||
loadAndPlay={Boolean(canAnimate && loadAndPlay)}
|
||||
index={i}
|
||||
observeIntersection={observeIntersection}
|
||||
shouldRender={activeSetIndex >= i - 1 && activeSetIndex <= i + 1}
|
||||
onStickerSelect={handleEmojiSelect}
|
||||
isSavedMessages={isSavedMessages}
|
||||
isCustomEmojiPicker
|
||||
isCurrentUserPremium={isCurrentUserPremium}
|
||||
onStickerSelect={handleEmojiSelect}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -288,7 +284,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
return {
|
||||
stickerSetsById: setsById,
|
||||
addedCustomEmojiIds: global.customEmojis.added.setIds,
|
||||
shouldPlay: global.settings.byKey.shouldLoopStickers,
|
||||
canAnimate: global.settings.byKey.shouldLoopStickers,
|
||||
isSavedMessages,
|
||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||
recentCustomEmoji,
|
||||
|
||||
@ -35,7 +35,6 @@ import Button from '../../ui/Button';
|
||||
import StickerButton from '../../common/StickerButton';
|
||||
import StickerSet from './StickerSet';
|
||||
import StickerSetCover from './StickerSetCover';
|
||||
import StickerSetCoverAnimated from './StickerSetCoverAnimated';
|
||||
import PremiumIcon from '../../common/PremiumIcon';
|
||||
|
||||
import './StickerPicker.scss';
|
||||
@ -56,7 +55,7 @@ type StateProps = {
|
||||
premiumStickers: ApiSticker[];
|
||||
stickerSetsById: Record<string, ApiStickerSet>;
|
||||
addedSetIds?: string[];
|
||||
shouldPlay?: boolean;
|
||||
canAnimate?: boolean;
|
||||
isSavedMessages?: boolean;
|
||||
isCurrentUserPremium?: boolean;
|
||||
};
|
||||
@ -78,7 +77,7 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
premiumStickers,
|
||||
addedSetIds,
|
||||
stickerSetsById,
|
||||
shouldPlay,
|
||||
canAnimate,
|
||||
isSavedMessages,
|
||||
isCurrentUserPremium,
|
||||
onStickerSelect,
|
||||
@ -286,14 +285,10 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
<i className="icon-favorite" />
|
||||
) : stickerSet.id === CHAT_STICKER_SET_ID ? (
|
||||
<Avatar chat={chat} size="small" />
|
||||
) : stickerSet.isLottie ? (
|
||||
<StickerSetCoverAnimated
|
||||
stickerSet={stickerSet as ApiStickerSet}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
/>
|
||||
) : (
|
||||
<StickerSetCover
|
||||
stickerSet={stickerSet as ApiStickerSet}
|
||||
noAnimate={!canAnimate || !loadAndPlay}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
/>
|
||||
)}
|
||||
@ -307,11 +302,12 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
size={STICKER_SIZE_PICKER_HEADER}
|
||||
title={stickerSet.title}
|
||||
className={buttonClassName}
|
||||
noAnimate={!canAnimate || !loadAndPlay}
|
||||
observeIntersection={observeIntersectionForCovers}
|
||||
onClick={selectStickerSet}
|
||||
clickArg={index}
|
||||
noContextMenu
|
||||
isCurrentUserPremium
|
||||
onClick={selectStickerSet}
|
||||
clickArg={index}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -350,17 +346,17 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
<StickerSet
|
||||
key={stickerSet.id}
|
||||
stickerSet={stickerSet}
|
||||
loadAndPlay={Boolean(shouldPlay && loadAndPlay)}
|
||||
loadAndPlay={Boolean(canAnimate && loadAndPlay)}
|
||||
index={i}
|
||||
observeIntersection={observeIntersection}
|
||||
shouldRender={activeSetIndex >= i - 1 && activeSetIndex <= i + 1}
|
||||
favoriteStickers={favoriteStickers}
|
||||
isSavedMessages={isSavedMessages}
|
||||
isCurrentUserPremium={isCurrentUserPremium}
|
||||
onStickerSelect={handleStickerSelect}
|
||||
onStickerUnfave={handleStickerUnfave}
|
||||
onStickerFave={handleStickerFave}
|
||||
onStickerRemoveRecent={handleRemoveRecentSticker}
|
||||
favoriteStickers={favoriteStickers}
|
||||
isSavedMessages={isSavedMessages}
|
||||
isCurrentUserPremium={isCurrentUserPremium}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -388,7 +384,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
premiumStickers: premiumSet.stickers,
|
||||
stickerSetsById: setsById,
|
||||
addedSetIds: added.setIds,
|
||||
shouldPlay: global.settings.byKey.shouldLoopStickers,
|
||||
canAnimate: global.settings.byKey.shouldLoopStickers,
|
||||
isSavedMessages,
|
||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||
};
|
||||
|
||||
@ -166,15 +166,15 @@ const StickerSet: FC<OwnProps> = ({
|
||||
size={itemSize}
|
||||
observeIntersection={observeIntersection}
|
||||
noAnimate={!loadAndPlay}
|
||||
isSavedMessages={isSavedMessages}
|
||||
canViewSet
|
||||
isCurrentUserPremium={isCurrentUserPremium}
|
||||
onClick={onStickerSelect}
|
||||
clickArg={sticker}
|
||||
onUnfaveClick={stickerSet.id === FAVORITE_SYMBOL_SET_ID && favoriteStickerIdsSet?.has(sticker.id)
|
||||
? onStickerUnfave : undefined}
|
||||
onFaveClick={!favoriteStickerIdsSet?.has(sticker.id) ? onStickerFave : undefined}
|
||||
onRemoveRecentClick={isRecent ? onStickerRemoveRecent : undefined}
|
||||
isSavedMessages={isSavedMessages}
|
||||
canViewSet
|
||||
isCurrentUserPremium={isCurrentUserPremium}
|
||||
/>
|
||||
))}
|
||||
{!isExpanded && stickerSet.count > itemsBeforeCutout && (
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
.video {
|
||||
width: 100%;
|
||||
}
|
||||
@ -1,44 +1,73 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useMemo, useRef } from '../../../lib/teact/teact';
|
||||
import React, { memo, useRef } from '../../../lib/teact/teact';
|
||||
|
||||
import type { ApiStickerSet } from '../../../api/types';
|
||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||
|
||||
import { STICKER_SIZE_PICKER_HEADER } from '../../../config';
|
||||
import { IS_WEBM_SUPPORTED } from '../../../util/environment';
|
||||
import { getFirstLetters } from '../../../util/textFormat';
|
||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
|
||||
import useMedia from '../../../hooks/useMedia';
|
||||
import useMediaTransition from '../../../hooks/useMediaTransition';
|
||||
|
||||
import AnimatedSticker from '../../common/AnimatedSticker';
|
||||
import OptimizedVideo from '../../ui/OptimizedVideo';
|
||||
|
||||
import styles from './StickerSetCover.module.scss';
|
||||
|
||||
type OwnProps = {
|
||||
stickerSet: ApiStickerSet;
|
||||
size?: number;
|
||||
noAnimate?: boolean;
|
||||
observeIntersection: ObserveFn;
|
||||
};
|
||||
|
||||
const StickerSetCover: FC<OwnProps> = ({ stickerSet, observeIntersection }) => {
|
||||
const StickerSetCover: FC<OwnProps> = ({
|
||||
stickerSet,
|
||||
size = STICKER_SIZE_PICKER_HEADER,
|
||||
noAnimate,
|
||||
observeIntersection,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { hasThumbnail, isLottie, isVideos: isVideo } = stickerSet;
|
||||
|
||||
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
||||
|
||||
const mediaData = useMedia(stickerSet.hasThumbnail && `stickerSet${stickerSet.id}`, !isIntersecting);
|
||||
const transitionClassNames = useMediaTransition(mediaData);
|
||||
const isVideo = stickerSet.isVideos;
|
||||
|
||||
const firstLetters = useMemo(() => {
|
||||
if ((isVideo && !IS_WEBM_SUPPORTED) || !mediaData) return getFirstLetters(stickerSet.title, 2);
|
||||
return undefined;
|
||||
}, [isVideo, mediaData, stickerSet.title]);
|
||||
const mediaData = useMedia((hasThumbnail || isLottie) && `stickerSet${stickerSet.id}`, !isIntersecting);
|
||||
const isReady = mediaData && (!isVideo || IS_WEBM_SUPPORTED);
|
||||
const transitionClassNames = useMediaTransition(isReady);
|
||||
|
||||
return (
|
||||
<div ref={ref} className="sticker-set-cover">
|
||||
{firstLetters}
|
||||
{isVideo ? (
|
||||
<OptimizedVideo canPlay src={mediaData} className={transitionClassNames} loop disablePictureInPicture />
|
||||
{isReady ? (
|
||||
isLottie ? (
|
||||
<AnimatedSticker
|
||||
className={transitionClassNames}
|
||||
tgsUrl={mediaData}
|
||||
size={size}
|
||||
play={isIntersecting && !noAnimate}
|
||||
/>
|
||||
) : isVideo ? (
|
||||
<OptimizedVideo
|
||||
className={buildClassName(styles.video, transitionClassNames)}
|
||||
src={mediaData}
|
||||
canPlay={isIntersecting && !noAnimate}
|
||||
loop
|
||||
disablePictureInPicture
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
src={mediaData}
|
||||
className={transitionClassNames}
|
||||
alt=""
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<img src={mediaData} className={transitionClassNames} alt="" />
|
||||
getFirstLetters(stickerSet.title, 2)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useMemo, useRef } from '../../../lib/teact/teact';
|
||||
|
||||
import type { ApiStickerSet } from '../../../api/types';
|
||||
|
||||
import { STICKER_SIZE_PICKER_HEADER } from '../../../config';
|
||||
import { getFirstLetters } from '../../../util/textFormat';
|
||||
|
||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
|
||||
import useMedia from '../../../hooks/useMedia';
|
||||
import useMediaTransition from '../../../hooks/useMediaTransition';
|
||||
|
||||
import AnimatedSticker from '../../common/AnimatedSticker';
|
||||
|
||||
type OwnProps = {
|
||||
size?: number;
|
||||
stickerSet: ApiStickerSet;
|
||||
observeIntersection: ObserveFn;
|
||||
};
|
||||
|
||||
const StickerSetCoverAnimated: FC<OwnProps> = ({
|
||||
size = STICKER_SIZE_PICKER_HEADER,
|
||||
stickerSet,
|
||||
observeIntersection,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
||||
|
||||
const mediaHash = `stickerSet${stickerSet.id}`;
|
||||
const lottieData = useMedia(mediaHash, !isIntersecting);
|
||||
const transitionClassNames = useMediaTransition(lottieData);
|
||||
|
||||
const firstLetters = useMemo(() => {
|
||||
if (lottieData) return undefined;
|
||||
|
||||
return getFirstLetters(stickerSet.title, 2);
|
||||
}, [lottieData, stickerSet.title]);
|
||||
|
||||
return (
|
||||
<div ref={ref} className="sticker-set-cover">
|
||||
{firstLetters}
|
||||
{lottieData && (
|
||||
<AnimatedSticker
|
||||
size={size}
|
||||
tgsUrl={lottieData}
|
||||
className={transitionClassNames}
|
||||
play={isIntersecting}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(StickerSetCoverAnimated);
|
||||
Loading…
x
Reference in New Issue
Block a user