[Perf] Premium Modal: Optimizations (#1957)
This commit is contained in:
parent
fcf3aa857d
commit
a5219b69f7
@ -65,7 +65,6 @@ export const PREMIUM_FEATURE_SECTIONS = [
|
|||||||
const PREMIUM_BOTTOM_VIDEOS: string[] = [
|
const PREMIUM_BOTTOM_VIDEOS: string[] = [
|
||||||
'faster_download',
|
'faster_download',
|
||||||
'voice_to_text',
|
'voice_to_text',
|
||||||
'no_ads',
|
|
||||||
'advanced_chat_management',
|
'advanced_chat_management',
|
||||||
'profile_badge',
|
'profile_badge',
|
||||||
'animated_userpics',
|
'animated_userpics',
|
||||||
@ -241,7 +240,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.slide}>
|
<div className={styles.slide}>
|
||||||
<div className={styles.frame}>
|
<div className={styles.frame}>
|
||||||
<PremiumFeaturePreviewReactions />
|
<PremiumFeaturePreviewReactions isActive={currentSlideIndex === index} />
|
||||||
</div>
|
</div>
|
||||||
<h1 className={styles.title}>
|
<h1 className={styles.title}>
|
||||||
{lang(PREMIUM_FEATURE_TITLES.reactions)}
|
{lang(PREMIUM_FEATURE_TITLES.reactions)}
|
||||||
@ -257,7 +256,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.slide}>
|
<div className={styles.slide}>
|
||||||
<div className={styles.frame}>
|
<div className={styles.frame}>
|
||||||
<PremiumFeaturePreviewStickers />
|
<PremiumFeaturePreviewStickers isActive={currentSlideIndex === index} />
|
||||||
</div>
|
</div>
|
||||||
<h1 className={styles.title}>
|
<h1 className={styles.title}>
|
||||||
{lang(PREMIUM_FEATURE_TITLES.stickers)}
|
{lang(PREMIUM_FEATURE_TITLES.stickers)}
|
||||||
@ -275,6 +274,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
|
|||||||
<div className={styles.slide}>
|
<div className={styles.slide}>
|
||||||
<div className={styles.frame}>
|
<div className={styles.frame}>
|
||||||
<PremiumFeaturePreviewVideo
|
<PremiumFeaturePreviewVideo
|
||||||
|
isActive={currentSlideIndex === index}
|
||||||
videoId={promo.videos[i].id!}
|
videoId={promo.videos[i].id!}
|
||||||
videoThumbnail={promo.videos[i].thumbnail!}
|
videoThumbnail={promo.videos[i].thumbnail!}
|
||||||
isDown={PREMIUM_BOTTOM_VIDEOS.includes(section)}
|
isDown={PREMIUM_BOTTOM_VIDEOS.includes(section)}
|
||||||
|
|||||||
@ -16,6 +16,10 @@ import AnimatedSticker from '../../../common/AnimatedSticker';
|
|||||||
|
|
||||||
import styles from './PremiumFeaturePreviewReactions.module.scss';
|
import styles from './PremiumFeaturePreviewReactions.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
isActive: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
availableReactions: GlobalState['availableReactions'];
|
availableReactions: GlobalState['availableReactions'];
|
||||||
};
|
};
|
||||||
@ -34,8 +38,9 @@ const AnimatedCircleReaction: FC<{
|
|||||||
maxLength: number;
|
maxLength: number;
|
||||||
handleClick: (index: number) => void;
|
handleClick: (index: number) => void;
|
||||||
isActivated: boolean;
|
isActivated: boolean;
|
||||||
|
canPlay: boolean;
|
||||||
}> = ({
|
}> = ({
|
||||||
size, realIndex, isActivated,
|
size, realIndex, isActivated, canPlay,
|
||||||
reaction, index, maxLength, handleClick,
|
reaction, index, maxLength, handleClick,
|
||||||
}) => {
|
}) => {
|
||||||
const mediaData = useMedia(`document${reaction.activateAnimation?.id}`);
|
const mediaData = useMedia(`document${reaction.activateAnimation?.id}`);
|
||||||
@ -71,7 +76,7 @@ const AnimatedCircleReaction: FC<{
|
|||||||
<AnimatedSticker
|
<AnimatedSticker
|
||||||
className={styles.effectSticker}
|
className={styles.effectSticker}
|
||||||
tgsUrl={mediaDataAround}
|
tgsUrl={mediaDataAround}
|
||||||
play
|
play={canPlay}
|
||||||
isLowPriority
|
isLowPriority
|
||||||
noLoop
|
noLoop
|
||||||
size={EFFECT_SIZE_MULTIPLIER * size}
|
size={EFFECT_SIZE_MULTIPLIER * size}
|
||||||
@ -83,7 +88,7 @@ const AnimatedCircleReaction: FC<{
|
|||||||
className={styles.sticker}
|
className={styles.sticker}
|
||||||
tgsUrl={mediaData}
|
tgsUrl={mediaData}
|
||||||
onClick={handleClickEmoji}
|
onClick={handleClickEmoji}
|
||||||
play={isAnimated}
|
play={isAnimated && canPlay}
|
||||||
noLoop
|
noLoop
|
||||||
size={EMOJI_SIZE_MULTIPLIER * size}
|
size={EMOJI_SIZE_MULTIPLIER * size}
|
||||||
style={`--x: ${x}px; --y: ${y}px; --scale: ${scale};`}
|
style={`--x: ${x}px; --y: ${y}px; --scale: ${scale};`}
|
||||||
@ -92,8 +97,8 @@ const AnimatedCircleReaction: FC<{
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const PremiumFeaturePreviewReactions: FC<StateProps> = ({
|
const PremiumFeaturePreviewReactions: FC<OwnProps & StateProps> = ({
|
||||||
availableReactions,
|
availableReactions, isActive,
|
||||||
}) => {
|
}) => {
|
||||||
// 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);
|
||||||
@ -106,7 +111,7 @@ const PremiumFeaturePreviewReactions: FC<StateProps> = ({
|
|||||||
|
|
||||||
useInterval(() => {
|
useInterval(() => {
|
||||||
setOffset((current) => cycleRestrict(renderedReactions.length, current + 1));
|
setOffset((current) => cycleRestrict(renderedReactions.length, current + 1));
|
||||||
}, isIntervalPaused ? undefined : ROTATE_INTERVAL);
|
}, isIntervalPaused || !isActive ? undefined : ROTATE_INTERVAL);
|
||||||
|
|
||||||
const handleClickEmoji = useCallback((i: number) => {
|
const handleClickEmoji = useCallback((i: number) => {
|
||||||
setOffset(i);
|
setOffset(i);
|
||||||
@ -139,6 +144,7 @@ const PremiumFeaturePreviewReactions: FC<StateProps> = ({
|
|||||||
maxLength={renderedReactions.length}
|
maxLength={renderedReactions.length}
|
||||||
handleClick={handleClickEmoji}
|
handleClick={handleClickEmoji}
|
||||||
isActivated={offset === i}
|
isActivated={offset === i}
|
||||||
|
canPlay={isActive}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -146,7 +152,7 @@ const PremiumFeaturePreviewReactions: FC<StateProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(withGlobal(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
return {
|
return {
|
||||||
availableReactions: global.availableReactions,
|
availableReactions: global.availableReactions,
|
||||||
|
|||||||
@ -15,6 +15,10 @@ import AnimatedSticker from '../../../common/AnimatedSticker';
|
|||||||
|
|
||||||
import styles from './PremiumFeaturePreviewStickers.module.scss';
|
import styles from './PremiumFeaturePreviewStickers.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
isActive: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
stickers: GlobalState['stickers']['premium']['stickers'];
|
stickers: GlobalState['stickers']['premium']['stickers'];
|
||||||
};
|
};
|
||||||
@ -32,8 +36,9 @@ const AnimatedCircleSticker: FC<{
|
|||||||
maxLength: number;
|
maxLength: number;
|
||||||
onClick: (index: number) => void;
|
onClick: (index: number) => void;
|
||||||
onEnded: NoneToVoidFunction;
|
onEnded: NoneToVoidFunction;
|
||||||
|
canPlay: boolean;
|
||||||
}> = ({
|
}> = ({
|
||||||
size, realIndex,
|
size, realIndex, canPlay,
|
||||||
sticker, index, maxLength, onClick, onEnded,
|
sticker, index, maxLength, onClick, onEnded,
|
||||||
}) => {
|
}) => {
|
||||||
const mediaData = useMedia(`sticker${sticker.id}`);
|
const mediaData = useMedia(`sticker${sticker.id}`);
|
||||||
@ -75,7 +80,7 @@ const AnimatedCircleSticker: FC<{
|
|||||||
<AnimatedSticker
|
<AnimatedSticker
|
||||||
className={styles.effectSticker}
|
className={styles.effectSticker}
|
||||||
tgsUrl={mediaDataAround}
|
tgsUrl={mediaDataAround}
|
||||||
play
|
play={canPlay}
|
||||||
isLowPriority
|
isLowPriority
|
||||||
noLoop
|
noLoop
|
||||||
size={EFFECT_SIZE_MULTIPLIER * size}
|
size={EFFECT_SIZE_MULTIPLIER * size}
|
||||||
@ -85,7 +90,7 @@ const AnimatedCircleSticker: FC<{
|
|||||||
<AnimatedSticker
|
<AnimatedSticker
|
||||||
className={styles.sticker}
|
className={styles.sticker}
|
||||||
tgsUrl={mediaData}
|
tgsUrl={mediaData}
|
||||||
play={isAnimated}
|
play={canPlay && isAnimated}
|
||||||
noLoop
|
noLoop
|
||||||
size={EMOJI_SIZE_MULTIPLIER * size}
|
size={EMOJI_SIZE_MULTIPLIER * size}
|
||||||
style={`--x: ${x}px; --y: ${y}px; --opacity: ${scale}`}
|
style={`--x: ${x}px; --y: ${y}px; --opacity: ${scale}`}
|
||||||
@ -95,8 +100,8 @@ const AnimatedCircleSticker: FC<{
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const PremiumFeaturePreviewStickers: FC<StateProps> = ({
|
const PremiumFeaturePreviewStickers: FC<OwnProps & StateProps> = ({
|
||||||
stickers,
|
stickers, isActive,
|
||||||
}) => {
|
}) => {
|
||||||
// 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);
|
||||||
@ -137,6 +142,7 @@ const PremiumFeaturePreviewStickers: FC<StateProps> = ({
|
|||||||
maxLength={renderedStickers.length}
|
maxLength={renderedStickers.length}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
onEnded={handleEnded}
|
onEnded={handleEnded}
|
||||||
|
canPlay={isActive}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -144,7 +150,7 @@ const PremiumFeaturePreviewStickers: FC<StateProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(withGlobal(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
return {
|
return {
|
||||||
stickers: global.stickers.premium.stickers,
|
stickers: global.stickers.premium.stickers,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { FC } from '../../../../lib/teact/teact';
|
import type { FC } from '../../../../lib/teact/teact';
|
||||||
import React, { memo } from '../../../../lib/teact/teact';
|
import React, { memo, useEffect, useRef } from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiThumbnail } from '../../../../api/types';
|
import type { ApiThumbnail } from '../../../../api/types';
|
||||||
|
|
||||||
@ -7,6 +7,7 @@ import useMedia from '../../../../hooks/useMedia';
|
|||||||
import buildClassName from '../../../../util/buildClassName';
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
import useCanvasBlur from '../../../../hooks/useCanvasBlur';
|
import useCanvasBlur from '../../../../hooks/useCanvasBlur';
|
||||||
import useMediaTransition from '../../../../hooks/useMediaTransition';
|
import useMediaTransition from '../../../../hooks/useMediaTransition';
|
||||||
|
import safePlay from '../../../../util/safePlay';
|
||||||
|
|
||||||
import DeviceFrame from '../../../../assets/premium/DeviceFrame.svg';
|
import DeviceFrame from '../../../../assets/premium/DeviceFrame.svg';
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ type OwnProps = {
|
|||||||
isDown: boolean;
|
isDown: boolean;
|
||||||
videoThumbnail: ApiThumbnail;
|
videoThumbnail: ApiThumbnail;
|
||||||
index: number;
|
index: number;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PremiumFeaturePreviewVideo: FC<OwnProps> = ({
|
const PremiumFeaturePreviewVideo: FC<OwnProps> = ({
|
||||||
@ -26,10 +28,24 @@ const PremiumFeaturePreviewVideo: FC<OwnProps> = ({
|
|||||||
isDown,
|
isDown,
|
||||||
videoThumbnail,
|
videoThumbnail,
|
||||||
index,
|
index,
|
||||||
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
const mediaData = useMedia(`document${videoId}`);
|
const mediaData = useMedia(`document${videoId}`);
|
||||||
const thumbnailRef = useCanvasBlur(videoThumbnail.dataUri);
|
const thumbnailRef = useCanvasBlur(videoThumbnail.dataUri);
|
||||||
const transitionClassNames = useMediaTransition(mediaData);
|
const transitionClassNames = useMediaTransition(mediaData);
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (!video) return;
|
||||||
|
|
||||||
|
if (isActive) {
|
||||||
|
safePlay(video);
|
||||||
|
} else {
|
||||||
|
video.pause();
|
||||||
|
}
|
||||||
|
}, [isActive]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.root}>
|
<div className={styles.root}>
|
||||||
@ -44,12 +60,13 @@ const PremiumFeaturePreviewVideo: FC<OwnProps> = ({
|
|||||||
<img src={DeviceFrame} alt="" className={styles.frame} />
|
<img src={DeviceFrame} alt="" className={styles.frame} />
|
||||||
<canvas ref={thumbnailRef} className={styles.video} />
|
<canvas ref={thumbnailRef} className={styles.video} />
|
||||||
<video
|
<video
|
||||||
|
ref={videoRef}
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
styles.video,
|
styles.video,
|
||||||
transitionClassNames,
|
transitionClassNames,
|
||||||
)}
|
)}
|
||||||
src={mediaData}
|
src={mediaData}
|
||||||
autoPlay
|
autoPlay={isActive}
|
||||||
disablePictureInPicture
|
disablePictureInPicture
|
||||||
playsInline
|
playsInline
|
||||||
muted
|
muted
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user