[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[] = [
'faster_download',
'voice_to_text',
'no_ads',
'advanced_chat_management',
'profile_badge',
'animated_userpics',
@ -241,7 +240,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
return (
<div className={styles.slide}>
<div className={styles.frame}>
<PremiumFeaturePreviewReactions />
<PremiumFeaturePreviewReactions isActive={currentSlideIndex === index} />
</div>
<h1 className={styles.title}>
{lang(PREMIUM_FEATURE_TITLES.reactions)}
@ -257,7 +256,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
return (
<div className={styles.slide}>
<div className={styles.frame}>
<PremiumFeaturePreviewStickers />
<PremiumFeaturePreviewStickers isActive={currentSlideIndex === index} />
</div>
<h1 className={styles.title}>
{lang(PREMIUM_FEATURE_TITLES.stickers)}
@ -275,6 +274,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
<div className={styles.slide}>
<div className={styles.frame}>
<PremiumFeaturePreviewVideo
isActive={currentSlideIndex === index}
videoId={promo.videos[i].id!}
videoThumbnail={promo.videos[i].thumbnail!}
isDown={PREMIUM_BOTTOM_VIDEOS.includes(section)}

View File

@ -16,6 +16,10 @@ import AnimatedSticker from '../../../common/AnimatedSticker';
import styles from './PremiumFeaturePreviewReactions.module.scss';
type OwnProps = {
isActive: boolean;
};
type StateProps = {
availableReactions: GlobalState['availableReactions'];
};
@ -34,8 +38,9 @@ const AnimatedCircleReaction: FC<{
maxLength: number;
handleClick: (index: number) => void;
isActivated: boolean;
canPlay: boolean;
}> = ({
size, realIndex, isActivated,
size, realIndex, isActivated, canPlay,
reaction, index, maxLength, handleClick,
}) => {
const mediaData = useMedia(`document${reaction.activateAnimation?.id}`);
@ -71,7 +76,7 @@ const AnimatedCircleReaction: FC<{
<AnimatedSticker
className={styles.effectSticker}
tgsUrl={mediaDataAround}
play
play={canPlay}
isLowPriority
noLoop
size={EFFECT_SIZE_MULTIPLIER * size}
@ -83,7 +88,7 @@ const AnimatedCircleReaction: FC<{
className={styles.sticker}
tgsUrl={mediaData}
onClick={handleClickEmoji}
play={isAnimated}
play={isAnimated && canPlay}
noLoop
size={EMOJI_SIZE_MULTIPLIER * size}
style={`--x: ${x}px; --y: ${y}px; --scale: ${scale};`}
@ -92,8 +97,8 @@ const AnimatedCircleReaction: FC<{
</>
);
};
const PremiumFeaturePreviewReactions: FC<StateProps> = ({
availableReactions,
const PremiumFeaturePreviewReactions: FC<OwnProps & StateProps> = ({
availableReactions, isActive,
}) => {
// eslint-disable-next-line no-null/no-null
const containerRef = useRef<HTMLDivElement>(null);
@ -106,7 +111,7 @@ const PremiumFeaturePreviewReactions: FC<StateProps> = ({
useInterval(() => {
setOffset((current) => cycleRestrict(renderedReactions.length, current + 1));
}, isIntervalPaused ? undefined : ROTATE_INTERVAL);
}, isIntervalPaused || !isActive ? undefined : ROTATE_INTERVAL);
const handleClickEmoji = useCallback((i: number) => {
setOffset(i);
@ -139,6 +144,7 @@ const PremiumFeaturePreviewReactions: FC<StateProps> = ({
maxLength={renderedReactions.length}
handleClick={handleClickEmoji}
isActivated={offset === i}
canPlay={isActive}
/>
);
})}
@ -146,7 +152,7 @@ const PremiumFeaturePreviewReactions: FC<StateProps> = ({
);
};
export default memo(withGlobal(
export default memo(withGlobal<OwnProps>(
(global): StateProps => {
return {
availableReactions: global.availableReactions,

View File

@ -15,6 +15,10 @@ import AnimatedSticker from '../../../common/AnimatedSticker';
import styles from './PremiumFeaturePreviewStickers.module.scss';
type OwnProps = {
isActive: boolean;
};
type StateProps = {
stickers: GlobalState['stickers']['premium']['stickers'];
};
@ -32,8 +36,9 @@ const AnimatedCircleSticker: FC<{
maxLength: number;
onClick: (index: number) => void;
onEnded: NoneToVoidFunction;
canPlay: boolean;
}> = ({
size, realIndex,
size, realIndex, canPlay,
sticker, index, maxLength, onClick, onEnded,
}) => {
const mediaData = useMedia(`sticker${sticker.id}`);
@ -75,7 +80,7 @@ const AnimatedCircleSticker: FC<{
<AnimatedSticker
className={styles.effectSticker}
tgsUrl={mediaDataAround}
play
play={canPlay}
isLowPriority
noLoop
size={EFFECT_SIZE_MULTIPLIER * size}
@ -85,7 +90,7 @@ const AnimatedCircleSticker: FC<{
<AnimatedSticker
className={styles.sticker}
tgsUrl={mediaData}
play={isAnimated}
play={canPlay && isAnimated}
noLoop
size={EMOJI_SIZE_MULTIPLIER * size}
style={`--x: ${x}px; --y: ${y}px; --opacity: ${scale}`}
@ -95,8 +100,8 @@ const AnimatedCircleSticker: FC<{
</>
);
};
const PremiumFeaturePreviewStickers: FC<StateProps> = ({
stickers,
const PremiumFeaturePreviewStickers: FC<OwnProps & StateProps> = ({
stickers, isActive,
}) => {
// eslint-disable-next-line no-null/no-null
const containerRef = useRef<HTMLDivElement>(null);
@ -137,6 +142,7 @@ const PremiumFeaturePreviewStickers: FC<StateProps> = ({
maxLength={renderedStickers.length}
onClick={handleClick}
onEnded={handleEnded}
canPlay={isActive}
/>
);
})}
@ -144,7 +150,7 @@ const PremiumFeaturePreviewStickers: FC<StateProps> = ({
);
};
export default memo(withGlobal(
export default memo(withGlobal<OwnProps>(
(global): StateProps => {
return {
stickers: global.stickers.premium.stickers,

View File

@ -1,5 +1,5 @@
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';
@ -7,6 +7,7 @@ import useMedia from '../../../../hooks/useMedia';
import buildClassName from '../../../../util/buildClassName';
import useCanvasBlur from '../../../../hooks/useCanvasBlur';
import useMediaTransition from '../../../../hooks/useMediaTransition';
import safePlay from '../../../../util/safePlay';
import DeviceFrame from '../../../../assets/premium/DeviceFrame.svg';
@ -18,6 +19,7 @@ type OwnProps = {
isDown: boolean;
videoThumbnail: ApiThumbnail;
index: number;
isActive: boolean;
};
const PremiumFeaturePreviewVideo: FC<OwnProps> = ({
@ -26,10 +28,24 @@ const PremiumFeaturePreviewVideo: FC<OwnProps> = ({
isDown,
videoThumbnail,
index,
isActive,
}) => {
const mediaData = useMedia(`document${videoId}`);
const thumbnailRef = useCanvasBlur(videoThumbnail.dataUri);
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 (
<div className={styles.root}>
@ -44,12 +60,13 @@ const PremiumFeaturePreviewVideo: FC<OwnProps> = ({
<img src={DeviceFrame} alt="" className={styles.frame} />
<canvas ref={thumbnailRef} className={styles.video} />
<video
ref={videoRef}
className={buildClassName(
styles.video,
transitionClassNames,
)}
src={mediaData}
autoPlay
autoPlay={isActive}
disablePictureInPicture
playsInline
muted