[Perf] Premium Modal: Optimizations (#1957)

This commit is contained in:
Alexander Zinchuk 2022-08-28 22:14:06 +02:00
parent fcf3aa857d
commit a5219b69f7
4 changed files with 47 additions and 18 deletions

View File

@ -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)}

View File

@ -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,

View File

@ -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,

View File

@ -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