import type { FC } from '../../../lib/teact/teact'; import React, { memo, useCallback, useEffect, useRef, useState, } from '../../../lib/teact/teact'; import type { ApiPremiumPromo } from '../../../api/types'; import type { ApiLimitType, GlobalState } from '../../../global/types'; import buildClassName from '../../../util/buildClassName'; import useLang from '../../../hooks/useLang'; import fastSmoothScrollHorizontal from '../../../util/fastSmoothScrollHorizontal'; import useFlag from '../../../hooks/useFlag'; import renderText from '../../common/helpers/renderText'; import usePrevious from '../../../hooks/usePrevious'; import { formatCurrency } from '../../../util/formatCurrency'; import Button from '../../ui/Button'; import PremiumLimitPreview from './common/PremiumLimitPreview'; import PremiumFeaturePreviewVideo from './previews/PremiumFeaturePreviewVideo'; import PremiumFeaturePreviewReactions from './previews/PremiumFeaturePreviewReactions'; import SliderDots from '../../common/SliderDots'; import PremiumFeaturePreviewStickers from './previews/PremiumFeaturePreviewStickers'; import styles from './PremiumFeatureModal.module.scss'; export const PREMIUM_FEATURE_TITLES: Record = { double_limits: 'PremiumPreviewLimits', unique_reactions: 'PremiumPreviewReactions', premium_stickers: 'PremiumPreviewStickers', animated_emoji: 'PremiumPreviewEmoji', no_ads: 'PremiumPreviewNoAds', voice_to_text: 'PremiumPreviewVoiceToText', profile_badge: 'PremiumPreviewProfileBadge', faster_download: 'PremiumPreviewDownloadSpeed', more_upload: 'PremiumPreviewUploads', advanced_chat_management: 'PremiumPreviewAdvancedChatManagement', animated_userpics: 'PremiumPreviewAnimatedProfiles', }; export const PREMIUM_FEATURE_DESCRIPTIONS: Record = { double_limits: 'PremiumPreviewLimitsDescription', unique_reactions: 'PremiumPreviewReactionsDescription', premium_stickers: 'PremiumPreviewStickersDescription', no_ads: 'PremiumPreviewNoAdsDescription', animated_emoji: 'PremiumPreviewEmojiDescription', voice_to_text: 'PremiumPreviewVoiceToTextDescription', profile_badge: 'PremiumPreviewProfileBadgeDescription', faster_download: 'PremiumPreviewDownloadSpeedDescription', more_upload: 'PremiumPreviewUploadsDescription', advanced_chat_management: 'PremiumPreviewAdvancedChatManagementDescription', animated_userpics: 'PremiumPreviewAnimatedProfilesDescription', }; export const PREMIUM_FEATURE_SECTIONS = [ 'double_limits', 'more_upload', 'faster_download', 'voice_to_text', 'no_ads', 'unique_reactions', 'premium_stickers', 'animated_emoji', 'advanced_chat_management', 'profile_badge', 'animated_userpics', ]; const PREMIUM_BOTTOM_VIDEOS: string[] = [ 'faster_download', 'voice_to_text', 'advanced_chat_management', 'profile_badge', 'animated_userpics', ]; type ApiLimitTypeWithoutUpload = Exclude; const LIMITS_ORDER: ApiLimitTypeWithoutUpload[] = [ 'channels', 'dialogFolderPinned', 'channelsPublic', 'savedGifs', 'stickersFaved', 'aboutLength', 'captionLength', 'dialogFilters', 'dialogFiltersChats', ]; const LIMITS_TITLES: Record = { channels: 'GroupsAndChannelsLimitTitle', dialogFolderPinned: 'PinChatsLimitTitle', channelsPublic: 'PublicLinksLimitTitle', savedGifs: 'SavedGifsLimitTitle', stickersFaved: 'FavoriteStickersLimitTitle', aboutLength: 'BioLimitTitle', captionLength: 'CaptionsLimitTitle', dialogFilters: 'FoldersLimitTitle', dialogFiltersChats: 'ChatPerFolderLimitTitle', }; const LIMITS_DESCRIPTIONS: Record = { channels: 'GroupsAndChannelsLimitSubtitle', dialogFolderPinned: 'PinChatsLimitSubtitle', channelsPublic: 'PublicLinksLimitSubtitle', savedGifs: 'SavedGifsLimitSubtitle', stickersFaved: 'FavoriteStickersLimitSubtitle', aboutLength: 'BioLimitSubtitle', captionLength: 'CaptionsLimitSubtitle', dialogFilters: 'FoldersLimitSubtitle', dialogFiltersChats: 'ChatPerFolderLimitSubtitle', }; const BORDER_THRESHOLD = 20; type OwnProps = { onBack: VoidFunction; initialSection: string; promo: ApiPremiumPromo; onClickSubscribe: (startParam?: string) => void; isPremium?: boolean; limits?: NonNullable['limits']; }; const PremiumFeatureModal: FC = ({ promo, initialSection, onBack, onClickSubscribe, isPremium, limits, }) => { const lang = useLang(); // eslint-disable-next-line no-null/no-null const scrollContainerRef = useRef(null); const [currentSlideIndex, setCurrentSlideIndex] = useState(PREMIUM_FEATURE_SECTIONS.indexOf(initialSection)); const [reverseAnimationSlideIndex, setReverseAnimationSlideIndex] = useState(0); const [isScrolling, startScrolling, stopScrolling] = useFlag(); const [isScrolledToTop, setIsScrolledToTop] = useState(true); const [isScrolledToBottom, setIsScrolledToBottom] = useState(false); const prevInitialSection = usePrevious(initialSection); function handleClick() { onClickSubscribe(initialSection); } function handleScroll(e: React.UIEvent) { const { clientWidth, scrollLeft: scrollLeftOriginal } = e.currentTarget; const scrollLeft = Math.round(scrollLeftOriginal); const left = scrollLeft % (clientWidth); const progress = left / (clientWidth); e.currentTarget.style.setProperty('--scroll-progress', progress.toString()); e.currentTarget.style.setProperty('--abs-scroll-progress', Math.abs(progress).toString()); const reverseIndex = Math.ceil((scrollLeft + 1) / clientWidth); setReverseAnimationSlideIndex(reverseIndex); const prevElement = e.currentTarget.querySelector(`#premium_feature_preview_video_${reverseIndex - 1}`); const reverseElement = e.currentTarget.querySelector(`#premium_feature_preview_video_${reverseIndex}`); prevElement?.classList.toggle('reverse', false); reverseElement?.classList.toggle('reverse', true); if (isScrolling) return; const slide = Math.round(scrollLeft / clientWidth); setCurrentSlideIndex(slide); } function handleLimitsScroll(e: React.UIEvent) { const { scrollTop, clientHeight, scrollHeight } = e.currentTarget; setIsScrolledToTop(scrollTop <= BORDER_THRESHOLD); setIsScrolledToBottom(scrollTop >= scrollHeight - clientHeight - BORDER_THRESHOLD); } useEffect(() => { const scrollContainer = scrollContainerRef.current; if (!scrollContainer || (prevInitialSection === initialSection)) return; const index = PREMIUM_FEATURE_SECTIONS.indexOf(initialSection); setCurrentSlideIndex(index); startScrolling(); fastSmoothScrollHorizontal(scrollContainer, scrollContainer.clientWidth * index, 0) .then(stopScrolling); }, [currentSlideIndex, initialSection, prevInitialSection, startScrolling, stopScrolling]); const handleSelectSlide = useCallback(async (index: number) => { const scrollContainer = scrollContainerRef.current; if (!scrollContainer) return; setCurrentSlideIndex(index); startScrolling(); await fastSmoothScrollHorizontal(scrollContainer, scrollContainer.clientWidth * index, 800); stopScrolling(); }, [startScrolling, stopScrolling]); return (
{PREMIUM_FEATURE_SECTIONS.map((section, index) => { if (section === 'double_limits') { return (

{lang(PREMIUM_FEATURE_TITLES.double_limits)}

{LIMITS_ORDER.map((limit, i) => { const defaultLimit = limits?.[limit][0].toString(); const premiumLimit = limits?.[limit][1].toString(); return ( ); })}
); } if (section === 'unique_reactions') { return (

{lang(PREMIUM_FEATURE_TITLES.unique_reactions)}

{renderText(lang(PREMIUM_FEATURE_DESCRIPTIONS.unique_reactions), ['br'])}
); } if (section === 'premium_stickers') { return (

{lang(PREMIUM_FEATURE_TITLES.premium_stickers)}

{renderText(lang(PREMIUM_FEATURE_DESCRIPTIONS.premium_stickers), ['br'])}
); } const i = promo.videoSections.indexOf(section); if (i === -1) return undefined; return (

{lang(PREMIUM_FEATURE_TITLES[promo.videoSections[i]!])}

{renderText(lang(PREMIUM_FEATURE_DESCRIPTIONS[promo.videoSections[i]!]), ['br'])}
); })}
); }; export default memo(PremiumFeatureModal);