'use client'; import { useCallback, useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { Loader2, Pause, Play, Volume2, VolumeX, X } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; function formatDuration(seconds: number) { if (!Number.isFinite(seconds) || seconds < 0) return '0:00'; const whole = Math.floor(seconds); const mins = Math.floor(whole / 60); const secs = whole % 60; return `${mins}:${secs.toString().padStart(2, '0')}`; } export function ChatVideoLightbox({ open, src, fileName, durationMs, onClose }: { open: boolean; src: string; fileName?: string; durationMs?: number; onClose: () => void; }) { const videoRef = useRef(null); const hideControlsTimerRef = useRef | null>(null); const [mounted, setMounted] = useState(false); const [playing, setPlaying] = useState(false); const [loading, setLoading] = useState(true); const [muted, setMuted] = useState(false); const [progress, setProgress] = useState(0); const [duration, setDuration] = useState(durationMs ? durationMs / 1000 : 0); const [controlsVisible, setControlsVisible] = useState(true); const [scrubbing, setScrubbing] = useState(false); useEffect(() => { setMounted(true); }, []); const revealControls = useCallback(() => { setControlsVisible(true); if (hideControlsTimerRef.current) clearTimeout(hideControlsTimerRef.current); hideControlsTimerRef.current = setTimeout(() => { if (playing) setControlsVisible(false); }, 2800); }, [playing]); const togglePlayback = useCallback(() => { const video = videoRef.current; if (!video) return; if (video.paused) { void video.play(); } else { video.pause(); } revealControls(); }, [revealControls]); useEffect(() => { if (!open) return; function handleKeyDown(event: KeyboardEvent) { if (event.key === 'Escape') onClose(); if (event.key === ' ') { event.preventDefault(); togglePlayback(); } } window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [open, onClose, togglePlayback]); useEffect(() => { if (!open || typeof document === 'undefined') return; const previousOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = previousOverflow; }; }, [open]); useEffect(() => { const video = videoRef.current; if (!video || !open) return; const onLoaded = () => { if (Number.isFinite(video.duration) && video.duration > 0) { setDuration(video.duration); } setLoading(false); }; const onTimeUpdate = () => { if (!scrubbing && video.duration) { setProgress(video.currentTime / video.duration); } }; const onPlay = () => { setPlaying(true); revealControls(); }; const onPause = () => { setPlaying(false); setControlsVisible(true); }; const onEnded = () => { setPlaying(false); setControlsVisible(true); }; video.addEventListener('loadedmetadata', onLoaded); video.addEventListener('timeupdate', onTimeUpdate); video.addEventListener('play', onPlay); video.addEventListener('pause', onPause); video.addEventListener('ended', onEnded); return () => { video.removeEventListener('loadedmetadata', onLoaded); video.removeEventListener('timeupdate', onTimeUpdate); video.removeEventListener('play', onPlay); video.removeEventListener('pause', onPause); video.removeEventListener('ended', onEnded); }; }, [open, revealControls, scrubbing, src]); useEffect(() => { if (!open) return; const video = videoRef.current; if (!video) return; setLoading(true); setProgress(0); setPlaying(false); video.src = src; video.load(); void video.play().catch(() => undefined); }, [open, src]); useEffect(() => { return () => { if (hideControlsTimerRef.current) clearTimeout(hideControlsTimerRef.current); }; }, []); function seekTo(ratio: number) { const video = videoRef.current; if (!video || !video.duration) return; const next = Math.min(Math.max(ratio, 0), 1); video.currentTime = next * video.duration; setProgress(next); } if (!open || !mounted) return null; return createPortal(

{fileName?.trim() || 'Видео'}

Полноэкранный просмотр

{ setScrubbing(true); seekTo(Number(event.target.value) / 1000); }} onMouseUp={() => setScrubbing(false)} onTouchEnd={() => setScrubbing(false)} aria-label="Позиция воспроизведения" />
{formatDuration(progress * duration)} / {formatDuration(duration)}
, document.body ); }