import type { FC } from '../../../lib/teact/teact'; import React, { memo, useEffect, useMemo, useRef, useState, } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { ApiPremiumGiftCodeOption, ApiStarGiftRegular, ApiStarsAmount, ApiUser, } from '../../../api/types'; import type { TabState } from '../../../global/types'; import type { StarGiftCategory } from '../../../types'; import { getUserFullName } from '../../../global/helpers'; import { selectUser } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev'; import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver'; import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; import useOldLang from '../../../hooks/useOldLang'; import Avatar from '../../common/Avatar'; import SafeLink from '../../common/SafeLink'; import Button from '../../ui/Button'; import Modal from '../../ui/Modal'; import Transition from '../../ui/Transition'; import BalanceBlock from '../stars/BalanceBlock'; import GiftSendingOptions from './GiftComposer'; import GiftItemPremium from './GiftItemPremium'; import GiftItemStar from './GiftItemStar'; import StarGiftCategoryList from './StarGiftCategoryList'; import styles from './GiftModal.module.scss'; import StarsBackground from '../../../assets/stars-bg.png'; export type OwnProps = { modal: TabState['giftModal']; }; export type GiftOption = ApiPremiumGiftCodeOption | ApiStarGiftRegular; type StateProps = { boostPerSentGift?: number; starGiftsById?: Record; starGiftCategoriesByName: Record; starBalance?: ApiStarsAmount; user?: ApiUser; isSelf?: boolean; }; const AVATAR_SIZE = 100; const INTERSECTION_THROTTLE = 200; const PremiumGiftModal: FC = ({ modal, starGiftsById, starGiftCategoriesByName, starBalance, user, isSelf, }) => { const { closeGiftModal, requestConfetti, } = getActions(); // eslint-disable-next-line no-null/no-null const dialogRef = useRef(null); // eslint-disable-next-line no-null/no-null const transitionRef = useRef(null); // eslint-disable-next-line no-null/no-null const giftHeaderRef = useRef(null); // eslint-disable-next-line no-null/no-null const scrollerRef = useRef(null); const isOpen = Boolean(modal); const renderingModal = useCurrentOrPrev(modal); const [selectedGift, setSelectedGift] = useState(); const [isHeaderHidden, setIsHeaderHidden] = useState(true); const [isHeaderForStarGifts, setIsHeaderForStarGifts] = useState(false); const [selectedCategory, setSelectedCategory] = useState('all'); const oldLang = useOldLang(); const lang = useLang(); const filteredGifts = useMemo(() => { return renderingModal?.gifts?.sort((prevGift, gift) => prevGift.months - gift.months) .filter((gift) => gift.users === 1); }, [renderingModal]); const baseGift = useMemo(() => { return filteredGifts?.reduce((prev, gift) => (prev.amount < gift.amount ? prev : gift)); }, [filteredGifts]); const { observe: observeIntersection, } = useIntersectionObserver({ rootRef: scrollerRef, throttleMs: INTERSECTION_THROTTLE, isDisabled: !isOpen }); const showConfetti = useLastCallback(() => { const dialog = dialogRef.current; if (!dialog) return; if (isOpen) { const { top, left, width, height, } = dialog.querySelector('.modal-content')!.getBoundingClientRect(); requestConfetti({ top, left, width, height, withStars: true, }); } }); useEffect(() => { if (renderingModal?.isCompleted) { showConfetti(); } }, [renderingModal]); useEffect(() => { if (!isOpen) { setIsHeaderHidden(true); setSelectedGift(undefined); } }, [isOpen]); const handleScroll = useLastCallback((e: React.UIEvent) => { if (selectedGift) return; const { scrollTop } = e.currentTarget; setIsHeaderHidden(scrollTop <= 150); if (transitionRef.current && giftHeaderRef.current) { const { top: headerTop } = giftHeaderRef.current.getBoundingClientRect(); const { top: transitionTop } = transitionRef.current.getBoundingClientRect(); setIsHeaderForStarGifts(headerTop - transitionTop <= 0); } }); const giftPremiumDescription = lang('GiftPremiumDescription', { user: getUserFullName(user)!, link: ( ), }, { withNodes: true }); const starGiftDescription = isSelf ? lang('StarGiftDescriptionSelf', undefined, { withNodes: true, renderTextFilters: ['br'], }) : lang('StarGiftDescription', { user: getUserFullName(user)!, }, { withNodes: true, withMarkdown: true }); function renderGiftPremiumHeader() { return (

{lang('GiftPremiumHeader')}

); } function renderGiftPremiumDescription() { return (

{giftPremiumDescription}

); } function renderStarGiftsHeader() { return (

{lang(isSelf ? 'StarsGiftHeaderSelf' : 'StarsGiftHeader')}

); } function renderStarGiftsDescription() { return (

{starGiftDescription}

); } const handleGiftClick = useLastCallback((gift: GiftOption) => { setSelectedGift(gift); setIsHeaderForStarGifts('id' in gift); setIsHeaderHidden(false); }); function renderStarGifts() { return (
{starGiftsById && starGiftCategoriesByName[selectedCategory].map((giftId) => { const gift = starGiftsById[giftId]; return ( ); })}
); } function renderPremiumGifts() { return (
{filteredGifts?.map((gift) => { return ( ); })}
); } const onCategoryChanged = useLastCallback((category: StarGiftCategory) => { setSelectedCategory(category); }); const handleCloseButtonClick = useLastCallback(() => { if (selectedGift) { setSelectedGift(undefined); return; } closeGiftModal(); }); function renderMainScreen() { return (
{!isSelf && renderGiftPremiumHeader()} {!isSelf && renderGiftPremiumDescription()} {!isSelf && renderPremiumGifts()} {renderStarGiftsHeader()} {renderStarGiftsDescription()} {renderStarGifts()}
); } const isBackButton = Boolean(selectedGift); const buttonClassName = buildClassName( 'animated-close-icon', isBackButton && 'state-back', ); return (

{lang(isHeaderForStarGifts ? (isSelf ? 'StarsGiftHeaderSelf' : 'StarsGiftHeader') : 'GiftPremiumHeader')}

{!selectedGift && renderMainScreen()} {selectedGift && renderingModal?.forUserId && ( )}
); }; export default memo(withGlobal((global, { modal }): StateProps => { const { starGiftsById, starGiftCategoriesByName, stars, currentUserId, } = global; const user = modal?.forUserId ? selectUser(global, modal.forUserId) : undefined; const isSelf = Boolean(currentUserId && modal?.forUserId === currentUserId); return { boostPerSentGift: global.appConfig?.boostsPerSentGift, starGiftsById, starGiftCategoriesByName, starBalance: stars?.balance, user, isSelf, }; })(PremiumGiftModal)); function getCategoryKey(category: StarGiftCategory) { if (category === 'all') return -2; if (category === 'stock') return -1; if (category === 'limited') return 0; return category; }