import React, { useState, useEffect, useRef } from 'react'; import { Link } from '@tanstack/react-router'; import { useActiveLiveBlogs } from '@/queries/live-blogs'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Play, Pause, ChevronRight, Radio } from 'lucide-react'; interface LiveBlogTickerProps { className?: string; maxItems?: number; autoScroll?: boolean; scrollSpeed?: number; // pixels per second } export function LiveBlogTicker({ className = '', maxItems = 10, autoScroll = true, scrollSpeed = 30 }: LiveBlogTickerProps) { const { data: activeBlogs, isLoading } = useActiveLiveBlogs(); const [isPaused, setIsPaused] = useState(false); const [scrollPosition, setScrollPosition] = useState(0); const tickerRef = useRef(null); const contentRef = useRef(null); const animationRef = useRef(undefined); // Calculate total width needed for scrolling const [totalWidth, setTotalWidth] = useState(0); useEffect(() => { if (contentRef.current) { const width = contentRef.current.scrollWidth; setTotalWidth(width); } }, [activeBlogs]); // Auto-scroll animation useEffect(() => { if (!autoScroll || isPaused || !activeBlogs || activeBlogs.length === 0) { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } return; } let lastTime: number; const animate = (time: number) => { if (!lastTime) lastTime = time; const delta = time - lastTime; lastTime = time; setScrollPosition(prev => { let newPos = prev + (scrollSpeed * delta) / 1000; // Reset when scrolled past content if (newPos > totalWidth) { newPos = -(tickerRef.current?.offsetWidth || 0); } return newPos; }); animationRef.current = requestAnimationFrame(animate); }; animationRef.current = requestAnimationFrame(animate); return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, [autoScroll, isPaused, activeBlogs, scrollSpeed, totalWidth]); if (isLoading) { return (
); } if (!activeBlogs || activeBlogs.length === 0) { return null; // Don't show ticker if no active blogs } const displayBlogs = activeBlogs.slice(0, maxItems); const handlePauseToggle = () => { setIsPaused(!isPaused); }; const handleBlogClick = () => { // Reset scroll position when user interacts setScrollPosition(0); }; return (
{/* Ticker header */}
Live Now {activeBlogs.length} active
{autoScroll && ( )}
{/* Ticker content */}
{displayBlogs.map((blog, index) => (
{blog.title} {blog.updates && blog.updates.length > 0 && ( {blog.updates.length} updates )}
{/* Separator (except after last item) */} {index < displayBlogs.length - 1 && (
•
)}
))} {/* Duplicate content for seamless looping */} {displayBlogs.map((blog, index) => (
{blog.title} {blog.updates && blog.updates.length > 0 && ( {blog.updates.length} updates )}
{index < displayBlogs.length - 1 && (
•
)}
))}
{/* Progress indicator */} {autoScroll && totalWidth > 0 && (
)}
); }