import { AnimatePresence, motion, useReducedMotion } from 'motion/react' import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import broadcast from '@/assets/system/broadcast.webp' import { JackpotPoolTicker } from '@/components/jackpot-pool-ticker.tsx' import { SmartImage } from '@/components/smart-image.tsx' import { cn } from '@/lib/utils.ts' import { useGameSessionStore } from '@/store/game' import type { JackpotBroadcastItem } from '@/type' const winAmountFormatter = new Intl.NumberFormat('en-US', { maximumFractionDigits: 6, }) const MESSAGE_BROADCAST_ANIMATION_MS = 28_000 function formatWinAmount(value: string) { const amount = Number(value) return Number.isFinite(amount) ? winAmountFormatter.format(amount) : value } type MessageBroadcastProps = { className?: string showJackpotPool?: boolean } export function MessageBroadcast({ className, showJackpotPool = true, }: MessageBroadcastProps) { const { t } = useTranslation() const prefersReducedMotion = useReducedMotion() const jackpotBroadcasts = useGameSessionStore( (state) => state.jackpotBroadcasts, ) const shownBroadcastIdsRef = useRef(new Set()) const queuedBroadcastIdsRef = useRef(new Set()) const [queue, setQueue] = useState([]) const [activeBroadcast, setActiveBroadcast] = useState(null) const activeBroadcastLabel = useMemo(() => { if (!activeBroadcast) { return '' } return t('game.jackpotBroadcast.ariaMessage', { amount: formatWinAmount(activeBroadcast.totalWin), nickname: activeBroadcast.nickname, streak: activeBroadcast.currentStreak, }) }, [activeBroadcast, t]) useEffect(() => { const nextBroadcasts = jackpotBroadcasts.filter((item) => { if ( shownBroadcastIdsRef.current.has(item.id) || queuedBroadcastIdsRef.current.has(item.id) || activeBroadcast?.id === item.id ) { return false } return true }) if (nextBroadcasts.length === 0) { return } for (const item of nextBroadcasts) { queuedBroadcastIdsRef.current.add(item.id) } setQueue((currentQueue) => [...currentQueue, ...nextBroadcasts]) }, [activeBroadcast?.id, jackpotBroadcasts]) useEffect(() => { if (activeBroadcast || queue.length === 0) { return } const [nextBroadcast, ...remainingQueue] = queue queuedBroadcastIdsRef.current.delete(nextBroadcast.id) shownBroadcastIdsRef.current.add(nextBroadcast.id) setActiveBroadcast(nextBroadcast) setQueue(remainingQueue) }, [activeBroadcast, queue]) useEffect(() => { if (!activeBroadcast) { return } const timerId = window.setTimeout(() => { setActiveBroadcast((currentBroadcast) => currentBroadcast?.id === activeBroadcast.id ? null : currentBroadcast, ) }, MESSAGE_BROADCAST_ANIMATION_MS) return () => { window.clearTimeout(timerId) } }, [activeBroadcast]) return (
{activeBroadcast ? ( {t('game.jackpotBroadcast.prefix')} {activeBroadcast.nickname} {t('game.jackpotBroadcast.separator')} {t('game.jackpotBroadcast.streak', { count: activeBroadcast.currentStreak, })} {t('game.jackpotBroadcast.winAction')} {formatWinAmount(activeBroadcast.totalWin)} ) : null}
{showJackpotPool ? ( ) : null}
) }