From e77c6c40f83b3cccb532ae2a00ed2ec43490814a Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 15 Aug 2025 18:25:35 +0200 Subject: [PATCH] Animate gifts on hover --- src/components/common/AnimatedSticker.tsx | 6 ++++++ src/components/common/gift/SavedGift.tsx | 13 +++++++++---- .../middle/message/ActionMessage.module.scss | 1 + .../middle/message/WebPageUniqueGift.tsx | 9 ++++++++- .../middle/message/actions/StarGift.tsx | 9 ++++++++- .../middle/message/actions/StarGiftUnique.tsx | 9 ++++++++- src/components/modals/gift/GiftItemStar.tsx | 17 ++++++++++------- src/components/modals/gift/GiftModal.tsx | 6 +++--- src/components/modals/gift/UniqueGiftHeader.tsx | 10 ++++++++-- 9 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/components/common/AnimatedSticker.tsx b/src/components/common/AnimatedSticker.tsx index 3b265aae8..6347dd3b0 100644 --- a/src/components/common/AnimatedSticker.tsx +++ b/src/components/common/AnimatedSticker.tsx @@ -50,6 +50,8 @@ export type OwnProps = { sharedCanvas?: HTMLCanvasElement; sharedCanvasCoords?: { x: number; y: number }; onClick?: NoneToVoidFunction; + onMouseEnter?: NoneToVoidFunction; + onMouseLeave?: NoneToVoidFunction; onLoad?: NoneToVoidFunction; onEnded?: NoneToVoidFunction; onLoop?: NoneToVoidFunction; @@ -76,6 +78,8 @@ const AnimatedSticker: FC = ({ sharedCanvas, sharedCanvasCoords, onClick, + onMouseEnter, + onMouseLeave, onLoad, onEnded, onLoop, @@ -277,6 +281,8 @@ const AnimatedSticker: FC = ({ style, )} onClick={onClick} + onMouseEnter={onMouseEnter} + onMouseLeave={onMouseLeave} /> ); }; diff --git a/src/components/common/gift/SavedGift.tsx b/src/components/common/gift/SavedGift.tsx index 9458ee9a3..404b71687 100644 --- a/src/components/common/gift/SavedGift.tsx +++ b/src/components/common/gift/SavedGift.tsx @@ -6,6 +6,7 @@ import type { ApiEmojiStatusType, ApiPeer, ApiSavedStarGift } from '../../../api import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../../config'; import { getHasAdminRight } from '../../../global/helpers'; import { selectChat, selectPeer, selectUser } from '../../../global/selectors'; +import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment.ts'; import buildClassName from '../../../util/buildClassName'; import { formatStarsAsIcon, formatTonAsIcon } from '../../../util/localization/format'; import { CUSTOM_PEER_HIDDEN } from '../../../util/objects/customPeer'; @@ -13,6 +14,7 @@ import { formatIntegerCompact } from '../../../util/textFormat'; import { getGiftAttributes, getStickerFromGift, getTotalGiftAvailability } from '../helpers/gifts'; import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers'; +import useFlag from '../../../hooks/useFlag.ts'; import { type ObserveFn } from '../../../hooks/useIntersectionObserver'; import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; @@ -59,13 +61,13 @@ const SavedGift = ({ const { openGiftInfoModal } = getActions(); const ref = useRef(); - const stickerRef = useRef(); const lang = useLang(); - const canManage = peerId === currentUserId || hasAdminRights; + const [isHover, markHover, unmarkHover] = useFlag(); + const canManage = peerId === currentUserId || hasAdminRights; const totalIssued = getTotalGiftAvailability(gift.gift); const starGift = gift.gift; const starGiftUnique = starGift.type === 'starGiftUnique' ? starGift : undefined; @@ -150,6 +152,8 @@ const SavedGift = ({ onClick={handleClick} onContextMenu={handleContextMenu} onMouseDown={handleBeforeContextMenu} + onMouseEnter={!IS_TOUCH_ENV ? markHover : undefined} + onMouseLeave={!IS_TOUCH_ENV ? unmarkHover : undefined} > {radialPatternBackdrop} {!radialPatternBackdrop && } @@ -161,12 +165,13 @@ const SavedGift = ({ > {sticker && ( )} diff --git a/src/components/middle/message/ActionMessage.module.scss b/src/components/middle/message/ActionMessage.module.scss index 09268183b..ea7b53d07 100644 --- a/src/components/middle/message/ActionMessage.module.scss +++ b/src/components/middle/message/ActionMessage.module.scss @@ -167,6 +167,7 @@ } .starGift { + cursor: var(--custom-cursor, pointer); width: 13.75rem; } diff --git a/src/components/middle/message/WebPageUniqueGift.tsx b/src/components/middle/message/WebPageUniqueGift.tsx index 4a7232ba6..6ba42b7cf 100644 --- a/src/components/middle/message/WebPageUniqueGift.tsx +++ b/src/components/middle/message/WebPageUniqueGift.tsx @@ -1,9 +1,11 @@ -import { memo, useRef } from '../../../lib/teact/teact'; +import { memo, useRef } from '@teact'; import type { ApiStarGiftUnique } from '../../../api/types'; +import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment.ts'; import { getGiftAttributes } from '../../common/helpers/gifts'; +import useFlag from '../../../hooks/useFlag.ts'; import { type ObserveFn } from '../../../hooks/useIntersectionObserver'; import RadialPatternBackground from '../../common/profile/RadialPatternBackground'; @@ -31,12 +33,16 @@ const WebPageUniqueGift = ({ backdrop, model, pattern, } = getGiftAttributes(gift)!; + const [isHover, markHover, unmarkHover] = useFlag(); + const backgroundColors = [backdrop!.centerColor, backdrop!.edgeColor]; return (
diff --git a/src/components/middle/message/actions/StarGift.tsx b/src/components/middle/message/actions/StarGift.tsx index cab33405f..5703fe565 100644 --- a/src/components/middle/message/actions/StarGift.tsx +++ b/src/components/middle/message/actions/StarGift.tsx @@ -1,4 +1,4 @@ -import { memo, useMemo, useRef } from '../../../../lib/teact/teact'; +import { memo, useMemo, useRef } from '@teact'; import { withGlobal } from '../../../../global'; import type { ApiMessage, ApiPeer } from '../../../../api/types'; @@ -12,6 +12,7 @@ import { selectSender, selectUser, } from '../../../../global/selectors'; +import { IS_TOUCH_ENV } from '../../../../util/browser/windowEnvironment.ts'; import buildClassName from '../../../../util/buildClassName'; import { formatStarsAsText } from '../../../../util/localization/format'; import { getServerTime } from '../../../../util/serverTime'; @@ -21,6 +22,7 @@ import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEn import { renderPeerLink, translateWithYou } from '../helpers/messageActions'; import useDynamicColorListener from '../../../../hooks/stickers/useDynamicColorListener'; +import useFlag from '../../../../hooks/useFlag.ts'; import { type ObserveFn } from '../../../../hooks/useIntersectionObserver'; import useLang from '../../../../hooks/useLang'; @@ -62,6 +64,8 @@ const StarGiftAction = ({ const stickerRef = useRef(); const lang = useLang(); + const [isHover, markHover, unmarkHover] = useFlag(); + const { isOutgoing } = message; const sticker = getStickerFromGift(action.gift)!; @@ -123,6 +127,8 @@ const StarGiftAction = ({ tabIndex={0} role="button" onClick={onClick} + onMouseEnter={!IS_TOUCH_ENV ? markHover : undefined} + onMouseLeave={!IS_TOUCH_ENV ? unmarkHover : undefined} >
(); const lang = useLang(); + const [isHover, markHover, unmarkHover] = useFlag(); + const { isOutgoing } = message; const sticker = getStickerFromGift(action.gift)!; @@ -85,6 +89,8 @@ const StarGiftAction = ({ tabIndex={0} role="button" onClick={onClick} + onMouseEnter={!IS_TOUCH_ENV ? markHover : undefined} + onMouseLeave={!IS_TOUCH_ENV ? unmarkHover : undefined} >
{radialPatternBackdrop} @@ -190,6 +192,7 @@ function GiftItemStar({ containerRef={stickerRef} sticker={sticker} size={GIFT_STICKER_SIZE} + shouldLoop={isHover} shouldPreloadPreview /> )} diff --git a/src/components/modals/gift/GiftModal.tsx b/src/components/modals/gift/GiftModal.tsx index 2275a3fd4..97d1d1bb3 100644 --- a/src/components/modals/gift/GiftModal.tsx +++ b/src/components/modals/gift/GiftModal.tsx @@ -1,8 +1,8 @@ -import type { FC } from '../../../lib/teact/teact'; -import type React from '../../../lib/teact/teact'; +import type { FC } from '@teact'; import { memo, useEffect, useMemo, useRef, useState, -} from '../../../lib/teact/teact'; +} from '@teact'; +import type React from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { diff --git a/src/components/modals/gift/UniqueGiftHeader.tsx b/src/components/modals/gift/UniqueGiftHeader.tsx index 942fdd583..fb124c623 100644 --- a/src/components/modals/gift/UniqueGiftHeader.tsx +++ b/src/components/modals/gift/UniqueGiftHeader.tsx @@ -1,5 +1,5 @@ -import type { TeactNode } from '../../../lib/teact/teact'; -import { memo, useMemo } from '../../../lib/teact/teact'; +import type { TeactNode } from '@teact'; +import { memo, useMemo } from '@teact'; import { getActions } from '../../../global'; import type { @@ -10,10 +10,12 @@ import type { import { formatStarsTransactionAmount, } from '../../../global/helpers/payments'; +import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment.ts'; import buildClassName from '../../../util/buildClassName'; import buildStyle from '../../../util/buildStyle'; import { useTransitionActiveKey } from '../../../hooks/animations/useTransitionActiveKey'; +import useFlag from '../../../hooks/useFlag.ts'; import useLang from '../../../hooks/useLang'; import AnimatedIconFromSticker from '../../common/AnimatedIconFromSticker'; @@ -52,6 +54,7 @@ const UniqueGiftHeader = ({ } = getActions(); const lang = useLang(); + const [isHover, markHover, unmarkHover] = useFlag(); const activeKey = useTransitionActiveKey([modelAttribute, backdropAttribute, patternAttribute]); const subtitleColor = backdropAttribute?.textColor; @@ -83,6 +86,9 @@ const UniqueGiftHeader = ({ className={styles.sticker} sticker={modelAttribute.sticker} size={STICKER_SIZE} + noLoop={!isHover} + onMouseEnter={!IS_TOUCH_ENV ? markHover : undefined} + onMouseLeave={!IS_TOUCH_ENV ? unmarkHover : undefined} /> {title &&

{title}

}