import React, { useState, useEffect, useRef } from 'react'; import { ChevronLeft, ChevronRight, Volume2, VolumeX, X } from 'lucide-react'; import { motion } from 'motion/react'; import { Post } from '../types'; import { cn, formatDateSafe } from '../lib/utils'; interface StoryViewerProps { stories: Post[]; onClose: () => void; profilePic: string | null; /** Highlight name, shown in place of the date when viewing a highlight. */ title?: string; } export const StoryViewer: React.FC = ({ stories, onClose, profilePic, title }) => { const [currentStoryIndex, setCurrentStoryIndex] = useState(0); const [progress, setProgress] = useState(0); // Start muted: Safari and Firefox refuse to autoplay audible media, which // would stall the reel on its first video. const [isMuted, setIsMuted] = useState(true); const videoRef = useRef(null); const story = stories[currentStoryIndex]; const primary = story?.media?.[0]; useEffect(() => { setProgress(0); let duration = 5000; const interval = 50; const updateProgress = () => { if (primary?.type === 'video' && videoRef.current) { const currentTime = videoRef.current.currentTime; const totalTime = videoRef.current.duration; if (totalTime) { setProgress((currentTime / totalTime) * 100); } } else { setProgress(prev => { const step = (interval / duration) * 100; if (prev >= 100) return 100; return prev + step; }); } }; const timer = setInterval(() => { updateProgress(); }, interval); return () => clearInterval(timer); }, [currentStoryIndex, primary]); useEffect(() => { if (progress >= 100) { if (currentStoryIndex < stories.length - 1) { setCurrentStoryIndex(prev => prev + 1); } else { onClose(); } } }, [progress, currentStoryIndex, stories.length, onClose]); const nextStory = () => { if (currentStoryIndex < stories.length - 1) { setCurrentStoryIndex(prev => prev + 1); } else { onClose(); } }; const prevStory = () => { if (currentStoryIndex > 0) { setCurrentStoryIndex(prev => prev - 1); } }; // An empty or exhausted reel has nothing to show; bail before dereferencing. if (!story || !primary) return null; return (
e.stopPropagation()} >
100 ? '1px' : (stories.length > 50 ? '2px' : '4px') }} > {stories.map((_, i) => (
))}
{profilePic ? ( ) : ( {story.username[0]} )}
{story.username} {title && {title}} {formatDateSafe(story.date, 'MMM d')}
{primary.type === 'video' && ( )}
{primary.type === 'video' ? (
{story.caption && (
{story.caption}
)}
); };