This commit is contained in:
lendry
2026-07-10 12:21:44 +03:00
parent a0966e7ba2
commit f00f3d411d
8 changed files with 395 additions and 54 deletions

View File

@@ -1,7 +1,8 @@
'use client';
import { useEffect, useRef, useState } from 'react';
import { Loader2, Pause, Play } from 'lucide-react';
import { Loader2, Maximize2, Pause, Play } from 'lucide-react';
import { ChatVideoLightbox } from '@/components/chat/chat-video-lightbox';
import { cn } from '@/lib/utils';
function formatDuration(seconds: number) {
@@ -16,17 +17,20 @@ export function ChatVideoPlayer({
src,
durationMs,
fileName,
variant = 'theirs'
variant = 'theirs',
enableFullscreen = true
}: {
src: string;
durationMs?: number;
fileName?: string;
variant?: 'mine' | 'theirs';
enableFullscreen?: boolean;
}) {
const videoRef = useRef<HTMLVideoElement | null>(null);
const [playing, setPlaying] = useState(false);
const [loading, setLoading] = useState(true);
const [duration, setDuration] = useState(durationMs ? durationMs / 1000 : 0);
const [fullscreenOpen, setFullscreenOpen] = useState(false);
useEffect(() => {
const video = videoRef.current;
@@ -65,43 +69,74 @@ export function ChatVideoPlayer({
}
}
function openFullscreen(event: React.MouseEvent<HTMLButtonElement>) {
event.stopPropagation();
videoRef.current?.pause();
setPlaying(false);
setFullscreenOpen(true);
}
return (
<div
className={cn(
'relative max-w-[320px] overflow-hidden rounded-xl',
variant === 'mine' ? 'bg-[#d9f7c8]/40' : 'bg-black/5'
)}
>
<video
ref={videoRef}
src={src}
className="max-h-72 w-full bg-black object-contain"
playsInline
preload="metadata"
onClick={togglePlayback}
/>
<button
type="button"
onClick={togglePlayback}
className="absolute inset-0 flex items-center justify-center bg-black/15 transition hover:bg-black/25"
aria-label={playing ? 'Пауза' : 'Воспроизвести'}
>
{loading ? (
<Loader2 className="h-10 w-10 animate-spin text-white" />
) : playing ? (
<Pause className="h-10 w-10 text-white drop-shadow" />
) : (
<Play className="h-10 w-10 text-white drop-shadow" />
<>
<div
className={cn(
'group relative max-w-[320px] overflow-hidden rounded-xl',
variant === 'mine' ? 'bg-[#d9f7c8]/40' : 'bg-black/5'
)}
</button>
<div className="absolute bottom-2 left-2 rounded-full bg-black/55 px-2 py-0.5 text-[11px] text-white">
{formatDuration(duration)}
</div>
{fileName ? (
<div className="truncate px-2 py-1 text-xs text-[#667085]" title={fileName}>
{fileName}
data-chat-interactive="true"
>
<video
ref={videoRef}
src={src}
className="max-h-72 w-full bg-black object-contain"
playsInline
preload="metadata"
onClick={togglePlayback}
/>
<button
type="button"
onClick={togglePlayback}
className="absolute inset-0 flex items-center justify-center bg-black/15 transition hover:bg-black/25"
aria-label={playing ? 'Пауза' : 'Воспроизвести'}
>
{loading ? (
<Loader2 className="h-10 w-10 animate-spin text-white" />
) : playing ? (
<Pause className="h-10 w-10 text-white drop-shadow" />
) : (
<Play className="h-10 w-10 text-white drop-shadow" />
)}
</button>
{enableFullscreen ? (
<button
type="button"
onClick={openFullscreen}
className="absolute right-2 top-2 flex h-8 w-8 items-center justify-center rounded-full bg-black/50 text-white opacity-0 transition group-hover:opacity-100 hover:bg-black/70"
aria-label="Открыть на весь экран"
title="На весь экран"
>
<Maximize2 className="h-4 w-4" />
</button>
) : null}
<div className="absolute bottom-2 left-2 rounded-full bg-black/55 px-2 py-0.5 text-[11px] text-white">
{formatDuration(duration)}
</div>
{fileName ? (
<div className="truncate px-2 py-1 text-xs text-[#667085]" title={fileName}>
{fileName}
</div>
) : null}
</div>
{enableFullscreen ? (
<ChatVideoLightbox
open={fullscreenOpen}
src={src}
fileName={fileName}
durationMs={durationMs}
onClose={() => setFullscreenOpen(false)}
/>
) : null}
</div>
</>
);
}