Premium modal: Various fixes (#4045)

This commit is contained in:
Alexander Zinchuk 2023-12-04 14:39:22 +01:00
parent 3ef2fa7680
commit 76629db59f
5 changed files with 50 additions and 33 deletions

View File

@ -1,6 +1,6 @@
import type { FC } from '../../../lib/teact/teact'; import type { FC } from '../../../lib/teact/teact';
import React, { import React, {
memo, useCallback, useEffect, useRef, useState, memo, useCallback, useEffect, useMemo, useRef, useState,
} from '../../../lib/teact/teact'; } from '../../../lib/teact/teact';
import type { ApiPremiumPromo } from '../../../api/types'; import type { ApiPremiumPromo } from '../../../api/types';
@ -127,21 +127,23 @@ const LIMITS_DESCRIPTIONS: Record<ApiLimitTypeWithoutUpload, string> = {
const BORDER_THRESHOLD = 20; const BORDER_THRESHOLD = 20;
type OwnProps = { type OwnProps = {
onBack: VoidFunction;
initialSection: string; initialSection: string;
promo: ApiPremiumPromo; promo: ApiPremiumPromo;
onClickSubscribe: (startParam?: string) => void;
isPremium?: boolean; isPremium?: boolean;
limits?: NonNullable<GlobalState['appConfig']>['limits']; limits?: NonNullable<GlobalState['appConfig']>['limits'];
premiumPromoOrder?: string[];
onBack: VoidFunction;
onClickSubscribe: (startParam?: string) => void;
}; };
const PremiumFeatureModal: FC<OwnProps> = ({ const PremiumFeatureModal: FC<OwnProps> = ({
promo, promo,
initialSection, initialSection,
onBack,
onClickSubscribe,
isPremium, isPremium,
limits, limits,
premiumPromoOrder,
onBack,
onClickSubscribe,
}) => { }) => {
const lang = useLang(); const lang = useLang();
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
@ -155,6 +157,11 @@ const PremiumFeatureModal: FC<OwnProps> = ({
const prevInitialSection = usePrevious(initialSection); const prevInitialSection = usePrevious(initialSection);
const filteredSections = useMemo(() => {
if (!premiumPromoOrder) return PREMIUM_FEATURE_SECTIONS;
return premiumPromoOrder.filter((section) => PREMIUM_FEATURE_SECTIONS.includes(section));
}, [premiumPromoOrder]);
function handleClick() { function handleClick() {
onClickSubscribe(initialSection); onClickSubscribe(initialSection);
} }
@ -192,12 +199,12 @@ const PremiumFeatureModal: FC<OwnProps> = ({
const scrollContainer = scrollContainerRef.current; const scrollContainer = scrollContainerRef.current;
if (!scrollContainer || (prevInitialSection === initialSection)) return; if (!scrollContainer || (prevInitialSection === initialSection)) return;
const index = PREMIUM_FEATURE_SECTIONS.indexOf(initialSection); const index = filteredSections.indexOf(initialSection);
setCurrentSlideIndex(index); setCurrentSlideIndex(index);
startScrolling(); startScrolling();
animateHorizontalScroll(scrollContainer, scrollContainer.clientWidth * index, 0) animateHorizontalScroll(scrollContainer, scrollContainer.clientWidth * index, 0)
.then(stopScrolling); .then(stopScrolling);
}, [currentSlideIndex, initialSection, prevInitialSection, startScrolling, stopScrolling]); }, [currentSlideIndex, filteredSections, initialSection, prevInitialSection]);
const handleSelectSlide = useCallback(async (index: number) => { const handleSelectSlide = useCallback(async (index: number) => {
const scrollContainer = scrollContainerRef.current; const scrollContainer = scrollContainerRef.current;
@ -208,7 +215,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
startScrolling(); startScrolling();
await animateHorizontalScroll(scrollContainer, scrollContainer.clientWidth * index, 800); await animateHorizontalScroll(scrollContainer, scrollContainer.clientWidth * index, 800);
stopScrolling(); stopScrolling();
}, [startScrolling, stopScrolling]); }, []);
// TODO Support all subscription options // TODO Support all subscription options
const month = promo.options.find((option) => option.months === 1)!; const month = promo.options.find((option) => option.months === 1)!;
@ -230,7 +237,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
<div className={buildClassName(styles.content, 'no-scrollbar')} onScroll={handleScroll} ref={scrollContainerRef}> <div className={buildClassName(styles.content, 'no-scrollbar')} onScroll={handleScroll} ref={scrollContainerRef}>
{PREMIUM_FEATURE_SECTIONS.map((section, index) => { {filteredSections.map((section, index) => {
if (section === 'double_limits') { if (section === 'double_limits') {
return ( return (
<div className={buildClassName(styles.slide, styles.limits)}> <div className={buildClassName(styles.slide, styles.limits)}>

View File

@ -372,6 +372,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
onClickSubscribe={handleClickWithStartParam} onClickSubscribe={handleClickWithStartParam}
isPremium={isPremium} isPremium={isPremium}
limits={limits} limits={limits}
premiumPromoOrder={premiumPromoOrder}
/> />
)} )}
</Transition> </Transition>

View File

@ -1,6 +1,6 @@
import type { FC } from '../../../../lib/teact/teact'; import type { FC } from '../../../../lib/teact/teact';
import React, { import React, {
memo, useCallback, useEffect, useRef, useState, memo, useEffect, useRef, useState,
} from '../../../../lib/teact/teact'; } from '../../../../lib/teact/teact';
import { withGlobal } from '../../../../global'; import { withGlobal } from '../../../../global';
@ -10,6 +10,7 @@ import type { GlobalState } from '../../../../global/types';
import cycleRestrict from '../../../../util/cycleRestrict'; import cycleRestrict from '../../../../util/cycleRestrict';
import useFlag from '../../../../hooks/useFlag'; import useFlag from '../../../../hooks/useFlag';
import useLastCallback from '../../../../hooks/useLastCallback';
import useMedia from '../../../../hooks/useMedia'; import useMedia from '../../../../hooks/useMedia';
import AnimatedSticker from '../../../common/AnimatedSticker'; import AnimatedSticker from '../../../common/AnimatedSticker';
@ -36,7 +37,7 @@ const AnimatedCircleSticker: FC<{
index: number; index: number;
maxLength: number; maxLength: number;
onClick: (index: number) => void; onClick: (index: number) => void;
onEnded: NoneToVoidFunction; onEnded: (index: number) => void;
canPlay: boolean; canPlay: boolean;
}> = ({ }> = ({
size, realIndex, canPlay, size, realIndex, canPlay,
@ -60,14 +61,14 @@ const AnimatedCircleSticker: FC<{
const x = Math.cos(angle) * width - circleSize * 2.8; const x = Math.cos(angle) * width - circleSize * 2.8;
const y = Math.sin(angle) * height; const y = Math.sin(angle) * height;
const handleClick = useCallback(() => { const handleClick = useLastCallback(() => {
onClick(realIndex); onClick(realIndex);
}, [onClick, realIndex]); });
const handleEnded = useCallback(() => { const handleEnded = useLastCallback(() => {
inanimate(); inanimate();
onEnded(); onEnded(realIndex);
}, [inanimate, onEnded]); });
useEffect(() => { useEffect(() => {
if (isActivated) { if (isActivated) {
@ -112,15 +113,20 @@ const PremiumFeaturePreviewStickers: FC<OwnProps & StateProps> = ({
const renderedStickers = stickers?.slice(0, MAX_EMOJIS); const renderedStickers = stickers?.slice(0, MAX_EMOJIS);
const handleClick = useCallback((i: number) => { const handleClick = useLastCallback((i: number) => {
setOffset(-i); setOffset(-i);
}, []); });
const handleEnded = useLastCallback((i: number) => {
const displayIndex = cycleRestrict(renderedStickers.length, i + offset);
if (displayIndex !== 0) return;
const handleEnded = useCallback(() => {
setTimeout(() => { setTimeout(() => {
setOffset((current) => cycleRestrict(renderedStickers.length, current + 1)); setOffset((current) => {
return cycleRestrict(renderedStickers.length, current + 1);
});
}, ENDED_DELAY); }, ENDED_DELAY);
}, [renderedStickers.length]); });
useEffect(() => { useEffect(() => {
const container = containerRef.current; const container = containerRef.current;
@ -134,11 +140,11 @@ const PremiumFeaturePreviewStickers: FC<OwnProps & StateProps> = ({
className={styles.root} className={styles.root}
ref={containerRef} ref={containerRef}
> >
{Boolean(size) && renderedStickers?.map((l, i) => { {Boolean(size) && renderedStickers?.map((sticker, i) => {
return ( return (
<AnimatedCircleSticker <AnimatedCircleSticker
size={size} size={size}
sticker={l} sticker={sticker}
realIndex={i} realIndex={i}
index={(i + offset + renderedStickers.length) % renderedStickers.length} index={(i + offset + renderedStickers.length) % renderedStickers.length}
maxLength={renderedStickers.length} maxLength={renderedStickers.length}

View File

@ -30,10 +30,11 @@
overflow-y: scroll; overflow-y: scroll;
border-top: 0.0625rem solid transparent; border-top: 0.0625rem solid transparent;
transition: 0.2s ease-in-out border-color; transition: 0.2s ease-in-out border-color;
margin-inline: 1rem;
} }
.mobile { .mobile {
padding: 1rem 1rem 1rem 2rem; padding: 1rem 0rem 1rem 1rem;
color: var(--color-text-secondary); color: var(--color-text-secondary);
font-size: 0.875rem; font-size: 0.875rem;
} }

View File

@ -88,15 +88,17 @@ const MediaAreaSuggestedReaction = ({
<div <div
className={buildClassName(styles.background, isFlipped && styles.flipped)} className={buildClassName(styles.background, isFlipped && styles.flipped)}
/> />
<ReactionAnimatedEmoji {Boolean(customEmojiSize) && (
className={buildClassName(styles.reaction, shouldDisplayCount && styles.withCount)} <ReactionAnimatedEmoji
reaction={reaction} className={buildClassName(styles.reaction, shouldDisplayCount && styles.withCount)}
containerId={containerId} reaction={reaction}
size={customEmojiSize} containerId={containerId}
effectSize={customEmojiSize * EFFECT_SIZE_MULTIPLIER} size={customEmojiSize}
shouldPause={isPreview} effectSize={customEmojiSize * EFFECT_SIZE_MULTIPLIER}
shouldLoop={!isPreview} shouldPause={isPreview}
/> shouldLoop={!isPreview}
/>
)}
{shouldDisplayCount && ( {shouldDisplayCount && (
<span className={styles.reactionCount}>{reactionCount}</span> <span className={styles.reactionCount}>{reactionCount}</span>
)} )}