From ca7d6eef8af8d55dc4dc48ae371ed34e4c52bd02 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 27 Feb 2026 19:51:25 +0100 Subject: [PATCH] Gift Preview Modal: Fix animation (#6734) --- src/components/modals/gift/UniqueGiftHeader.tsx | 11 ++++++++++- .../modals/gift/preview/GiftPreviewModal.tsx | 10 ++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/components/modals/gift/UniqueGiftHeader.tsx b/src/components/modals/gift/UniqueGiftHeader.tsx index 20cbb5e75..fcd9b55ca 100644 --- a/src/components/modals/gift/UniqueGiftHeader.tsx +++ b/src/components/modals/gift/UniqueGiftHeader.tsx @@ -44,6 +44,8 @@ type OwnProps = { resellPrice?: ApiTypeCurrencyAmount; showManageButtons?: boolean; savedGift?: ApiSavedStarGift; + noLoop?: boolean; + onStickerAnimationEnded?: (modelName: string) => void; children?: React.ReactNode; }; @@ -61,6 +63,8 @@ const UniqueGiftHeader = ({ resellPrice, showManageButtons, savedGift, + noLoop, + onStickerAnimationEnded, children, }: OwnProps) => { const { @@ -74,6 +78,10 @@ const UniqueGiftHeader = ({ const activeKey = useTransitionActiveKey([modelAttribute, backdropAttribute, patternAttribute]); const subtitleColor = backdropAttribute?.textColor; + const handleStickerEnded = () => { + onStickerAnimationEnded?.(modelAttribute.name); + }; + const radialPatternBackdrop = useMemo(() => { const backdropColors = [backdropAttribute.centerColor, backdropAttribute.edgeColor]; @@ -112,7 +120,8 @@ const UniqueGiftHeader = ({ className={styles.sticker} sticker={modelAttribute.sticker} size={STICKER_SIZE} - noLoop={!isGiftHover} + noLoop={noLoop ?? !isGiftHover} + onEnded={handleStickerEnded} onMouseEnter={!IS_TOUCH_ENV ? markGiftHover : undefined} onMouseLeave={!IS_TOUCH_ENV ? unmarkGiftHover : undefined} /> diff --git a/src/components/modals/gift/preview/GiftPreviewModal.tsx b/src/components/modals/gift/preview/GiftPreviewModal.tsx index 3ad2209d8..60720aabb 100644 --- a/src/components/modals/gift/preview/GiftPreviewModal.tsx +++ b/src/components/modals/gift/preview/GiftPreviewModal.tsx @@ -15,7 +15,6 @@ import { getNextArrowReplacement } from '../../../../util/localization/format'; import { resolveTransitionName } from '../../../../util/resolveTransitionName'; import { getGiftAttributes, getRandomGiftPreviewAttributes } from '../../../common/helpers/gifts'; -import useInterval from '../../../../hooks/schedulers/useInterval'; import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev'; import useFlag from '../../../../hooks/useFlag'; import { useIntersectionObserver } from '../../../../hooks/useIntersectionObserver'; @@ -45,7 +44,6 @@ type StateProps = { const MODEL_STICKER_SIZE = 80; const PATTERN_STICKER_SIZE = 60; const INTERSECTION_THROTTLE = 200; -const PLAYBACK_INTERVAL = 5000; enum AttributeTab { Model, @@ -125,7 +123,9 @@ const GiftPreviewModal = ({ modal, animationLevel }: OwnProps & StateProps) => { if (newModel && newModel.rarity.type !== 'regular') showCraftableModels(); }, [initialAttributes, firstModel, firstPattern, firstBackdrop]); - useInterval(() => { + const handleStickerAnimationEnded = useLastCallback((modelName: string) => { + if (modelName !== selectedModel?.name || !isPlayingRandomPreviews) return; + if (!originGift || !selectedModel || !selectedPattern || !selectedBackdrop) return; const newAttributes = getRandomGiftPreviewAttributes(renderingModal?.attributes, { model: selectedModel, @@ -135,7 +135,7 @@ const GiftPreviewModal = ({ modal, animationLevel }: OwnProps & StateProps) => { setSelectedModel(newAttributes.model); setSelectedPattern(newAttributes.pattern); setSelectedBackdrop(newAttributes.backdrop); - }, isPlayingRandomPreviews ? PLAYBACK_INTERVAL : undefined, true); + }); const { observe: observeModelsIntersection, @@ -273,6 +273,8 @@ const GiftPreviewModal = ({ modal, animationLevel }: OwnProps & StateProps) => { patternAttribute={selectedPattern} title={originGift?.title} subtitle={lang('GiftPreviewSelectedTraits')} + noLoop={isPlayingRandomPreviews} + onStickerAnimationEnded={handleStickerAnimationEnded} >