"use client"; import Image from "next/image"; import { Download, Eye, FileText, Loader2, Play } from "lucide-react"; import { useCallback, useEffect, useRef, useState } from "react"; import MediaPreviewModal from "@/components/shared/MediaPreviewModal"; import { useNearViewport } from "@/hooks/useNearViewport"; import { apiClient } from "@/lib/api"; import { getMediaKind, type MediaPreview } from "@/lib/media"; import type { TaskAttachment } from "@/types/api"; function formatFileSize(size: number): string { if (size < 1024) return `${size} Б`; if (size < 1024 * 1024) return `${Math.ceil(size / 1024)} КБ`; return `${(size / (1024 * 1024)).toLocaleString("ru-RU", { maximumFractionDigits: 1, })} МБ`; } export default function TaskCommentAttachment({ taskId, attachment, onDownload, }: { taskId: number; attachment: TaskAttachment; onDownload: (attachment: TaskAttachment) => void | Promise; }) { const mediaKind = getMediaKind({ fileName: attachment.file_name, mimeType: attachment.mime_type, }); const image = mediaKind === "image"; const video = mediaKind === "video"; const media = image || video; const [thumbnailSource, setThumbnailSource] = useState(null); const [thumbnailFailed, setThumbnailFailed] = useState(false); const [preview, setPreview] = useState(null); const [previewLoading, setPreviewLoading] = useState(false); const previewUrlRef = useRef(null); const { elementRef, isNearViewport } = useNearViewport(); useEffect(() => { if (!media || !isNearViewport) return; let active = true; let objectUrl: string | null = null; void apiClient.downloadTaskAttachmentThumbnail(taskId, attachment) .then((blob) => { if (!active) return; objectUrl = URL.createObjectURL(blob); setThumbnailSource(objectUrl); }) .catch(() => { if (active) setThumbnailFailed(true); }); return () => { active = false; if (objectUrl) URL.revokeObjectURL(objectUrl); }; }, [attachment, isNearViewport, media, taskId]); useEffect(() => () => { if (previewUrlRef.current) URL.revokeObjectURL(previewUrlRef.current); }, []); const closePreview = useCallback(() => { setPreview(null); if (previewUrlRef.current) { URL.revokeObjectURL(previewUrlRef.current); previewUrlRef.current = null; } }, []); const openPreview = useCallback(async () => { if (!media || previewLoading) return; setPreviewLoading(true); try { const { blob } = await apiClient.downloadTaskAttachment(taskId, attachment); if (previewUrlRef.current) URL.revokeObjectURL(previewUrlRef.current); const source = URL.createObjectURL(blob); previewUrlRef.current = source; setPreview({ type: video ? "video" : "image", src: source, name: attachment.file_name, }); } finally { setPreviewLoading(false); } }, [attachment, media, previewLoading, taskId, video]); if (media) { return (
{preview ? : null}
); } return ( ); }