import { memo, useEffect, useMemo, useState, } from '../../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../../global'; import type { ApiPeer, ApiStarGiftAttribute, ApiStarGiftAttributeBackdrop, ApiStarGiftAttributeModel, ApiStarGiftAttributePattern, } from '../../../../api/types'; import type { TabState } from '../../../../global/types'; import { ApiMediaFormat } from '../../../../api/types'; import { getStickerMediaHash } from '../../../../global/helpers'; import { getPeerTitle } from '../../../../global/helpers/peers'; import { selectPeer } from '../../../../global/selectors'; import { fetch } from '../../../../util/mediaLoader'; import useInterval from '../../../../hooks/schedulers/useInterval'; import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev'; import useLang from '../../../../hooks/useLang'; import useLastCallback from '../../../../hooks/useLastCallback'; import AnimatedCounter from '../../../common/AnimatedCounter'; import Icon from '../../../common/icons/Icon'; import Button from '../../../ui/Button'; import Checkbox from '../../../ui/Checkbox'; import Link from '../../../ui/Link'; import TextTimer from '../../../ui/TextTimer'; import TableAboutModal, { type TableAboutData } from '../../common/TableAboutModal'; import UniqueGiftHeader from '../UniqueGiftHeader'; import styles from './GiftUpgradeModal.module.scss'; export type OwnProps = { modal: TabState['giftUpgradeModal']; }; type StateProps = { recipient?: ApiPeer; }; type Attributes = { model: ApiStarGiftAttributeModel; pattern: ApiStarGiftAttributePattern; backdrop: ApiStarGiftAttributeBackdrop; }; const PREVIEW_UPDATE_INTERVAL = 3000; const GiftUpgradeModal = ({ modal, recipient }: OwnProps & StateProps) => { const { closeGiftUpgradeModal, closeGiftInfoModal, upgradeGift, upgradePrepaidGift, openStarGiftPriceDecreaseInfoModal, shiftGiftUpgradeNextPrice, } = getActions(); const isOpen = Boolean(modal); const renderingModal = useCurrentOrPrev(modal); const renderingRecipient = useCurrentOrPrev(recipient); const [shouldKeepOriginalDetails, setShouldKeepOriginalDetails] = useState(false); const isPrepaid = Boolean(renderingModal?.gift?.prepaidUpgradeHash); const [previewAttributes, setPreviewAttributes] = useState(); const lang = useLang(); const handleClose = useLastCallback(() => closeGiftUpgradeModal()); const handleTimerEnd = useLastCallback(() => { shiftGiftUpgradeNextPrice(); }); const nextPrice = renderingModal?.nextPrices?.[0]; const nextPriceDate = nextPrice?.date; const upgradeStars = renderingModal?.currentUpgradeStars; const handleUpgrade = useLastCallback(() => { const gift = renderingModal?.gift; if (!gift) return; if (isPrepaid && gift.prepaidUpgradeHash && renderingRecipient) { if (!upgradeStars) return; upgradePrepaidGift({ peerId: renderingRecipient.id, hash: gift.prepaidUpgradeHash, stars: upgradeStars, }); handleClose(); closeGiftInfoModal(); return; } if (!gift?.inputGift) return; upgradeGift({ gift: gift.inputGift, shouldKeepOriginalDetails, upgradeStars: !gift.alreadyPaidUpgradeStars ? upgradeStars : undefined, }); handleClose(); }); const updatePreviewAttributes = useLastCallback(() => { if (!renderingModal?.sampleAttributes) return; setPreviewAttributes(getRandomAttributes(renderingModal.sampleAttributes, previewAttributes)); }); const handleOpenPriceInfo = useLastCallback(() => { if (!renderingModal?.prices) return; openStarGiftPriceDecreaseInfoModal({ prices: renderingModal.prices, currentPrice: upgradeStars || 0, minPrice: renderingModal.minPrice || 0, maxPrice: renderingModal.maxPrice || 0, }); }); useInterval(updatePreviewAttributes, isOpen ? PREVIEW_UPDATE_INTERVAL : undefined, true); useEffect(() => { if (isOpen && renderingModal?.sampleAttributes) { updatePreviewAttributes(); } }, [isOpen, renderingModal?.sampleAttributes]); // Preload stickers and patterns useEffect(() => { const attributes = renderingModal?.sampleAttributes; if (!attributes) return; const patternStickers = attributes.filter((attr): attr is ApiStarGiftAttributeModel => attr.type === 'pattern') .map((attr) => attr.sticker); const modelStickers = attributes.filter((attr): attr is ApiStarGiftAttributeModel => attr.type === 'model') .map((attr) => attr.sticker); const mediaHashes = [...patternStickers, ...modelStickers].map((sticker) => getStickerMediaHash(sticker, 'full')); mediaHashes.forEach((hash) => { fetch(hash, ApiMediaFormat.BlobUrl); }); }, [renderingModal?.sampleAttributes]); const formattedPriceElement = useMemo(() => (upgradeStars ? ( ) : undefined), [lang, upgradeStars]); const modalData = useMemo(() => { if (!previewAttributes || !isOpen) { return undefined; } const gift = renderingModal?.gift; const userName = renderingRecipient ? getPeerTitle(lang, renderingRecipient) : lang('ActionFallbackUser'); const listItemData = (renderingRecipient ? [ ['diamond', lang('GiftUpgradeUniqueTitle'), lang('GiftPeerUpgradeUniqueDescription', { user: userName })], ['trade', lang('GiftUpgradeTransferableTitle'), lang('GiftPeerUpgradeTransferableDescription', { user: userName })], ['auction', lang('GiftUpgradeTradeableTitle'), lang('GiftPeerUpgradeTradeableDescription', { user: userName })], ] : [ ['diamond', lang('GiftUpgradeUniqueTitle'), lang('GiftUpgradeUniqueDescription')], ['trade', lang('GiftUpgradeTransferableTitle'), lang('GiftUpgradeTransferableDescription')], ['auction', lang('GiftUpgradeTradeableTitle'), lang('GiftUpgradeTradeableDescription')], ]) satisfies TableAboutData; const subtitle = renderingRecipient ? lang('GiftPeerUpgradeText', { peer: getPeerTitle(lang, renderingRecipient) }) : lang('GiftUpgradeTextOwn'); const hasPriceDecreaseInfo = Boolean(nextPriceDate) && Boolean(renderingModal?.prices?.length) && !gift?.alreadyPaidUpgradeStars; const header = ( ); const footer = (
{!gift && ( )} {gift && ( <> {!isPrepaid && ( )} {hasPriceDecreaseInfo && ( {lang('StarGiftPriceDecreaseInfoLink')} )} )}
); return { listItemData, header, footer, }; }, [previewAttributes, isOpen, lang, renderingRecipient, renderingModal?.gift, shouldKeepOriginalDetails, isPrepaid, renderingModal?.prices?.length, nextPriceDate, formattedPriceElement]); return ( ); }; export default memo(withGlobal( (global, { modal }): Complete => { const recipientId = modal?.recipientId; const recipient = recipientId ? selectPeer(global, recipientId) : undefined; return { recipient, }; }, )(GiftUpgradeModal)); function getRandomAttributes(list: ApiStarGiftAttribute[], previousSelection?: Attributes): Attributes { const models = list.filter((attr): attr is ApiStarGiftAttributeModel => ( attr.type === 'model' && attr.name !== previousSelection?.model.name )); const patterns = list.filter((attr): attr is ApiStarGiftAttributePattern => ( attr.type === 'pattern' && attr.name !== previousSelection?.pattern.name )); const backdrops = list.filter((attr): attr is ApiStarGiftAttributeBackdrop => ( attr.type === 'backdrop' && attr.name !== previousSelection?.backdrop.name )); const randomModel = models[Math.floor(Math.random() * models.length)]; const randomPattern = patterns[Math.floor(Math.random() * patterns.length)]; const randomBackdrop = backdrops[Math.floor(Math.random() * backdrops.length)]; return { model: randomModel, pattern: randomPattern, backdrop: randomBackdrop, }; }