diff --git a/apps/web/src/app/(shares)/s/[alias]/components/files-table.tsx b/apps/web/src/app/(shares)/s/[alias]/components/files-table.tsx index c4dda69..186071b 100644 --- a/apps/web/src/app/(shares)/s/[alias]/components/files-table.tsx +++ b/apps/web/src/app/(shares)/s/[alias]/components/files-table.tsx @@ -2,12 +2,12 @@ import { useState } from "react"; import { IconDownload, IconEye } from "@tabler/icons-react"; import { useTranslations } from "next-intl"; +import { FilePreviewModal } from "@/components/modals/file-preview-modal"; import { Button } from "@/components/ui/button"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { getFileIcon } from "@/utils/file-icons"; import { formatFileSize } from "@/utils/format-file-size"; import { ShareFilesTableProps } from "../types"; -import { ShareFilePreviewModal } from "./share-file-preview-modal"; export function ShareFilesTable({ files, onDownload }: ShareFilesTableProps) { const t = useTranslations(); @@ -99,14 +99,7 @@ export function ShareFilesTable({ files, onDownload }: ShareFilesTableProps) { - {selectedFile && ( - - )} + {selectedFile && } ); } diff --git a/apps/web/src/app/(shares)/s/[alias]/components/share-file-preview-modal.tsx b/apps/web/src/app/(shares)/s/[alias]/components/share-file-preview-modal.tsx deleted file mode 100644 index 47d5504..0000000 --- a/apps/web/src/app/(shares)/s/[alias]/components/share-file-preview-modal.tsx +++ /dev/null @@ -1,320 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { IconDownload } from "@tabler/icons-react"; -import { useTranslations } from "next-intl"; -import { toast } from "sonner"; - -import { CustomAudioPlayer } from "@/components/audio/custom-audio-player"; -import { AspectRatio } from "@/components/ui/aspect-ratio"; -import { Button } from "@/components/ui/button"; -import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { getDownloadUrl } from "@/http/endpoints"; -import { getFileIcon } from "@/utils/file-icons"; - -interface ShareFilePreviewModalProps { - isOpen: boolean; - onClose: () => void; - file: { - name: string; - objectName: string; - type?: string; - }; - onDownload: (objectName: string, fileName: string) => void; -} - -export function ShareFilePreviewModal({ isOpen, onClose, file, onDownload }: ShareFilePreviewModalProps) { - const t = useTranslations(); - const [previewUrl, setPreviewUrl] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const [videoBlob, setVideoBlob] = useState(null); - const [pdfAsBlob, setPdfAsBlob] = useState(false); - const [downloadUrl, setDownloadUrl] = useState(null); - const [pdfLoadFailed, setPdfLoadFailed] = useState(false); - const [isLoadingPreview, setIsLoadingPreview] = useState(false); - - useEffect(() => { - if (isOpen && file.objectName && !isLoadingPreview) { - setIsLoading(true); - setPreviewUrl(null); - setVideoBlob(null); - setPdfAsBlob(false); - setDownloadUrl(null); - setPdfLoadFailed(false); - loadPreview(); - } - }, [file.objectName, isOpen]); - - useEffect(() => { - return () => { - if (previewUrl && previewUrl.startsWith("blob:")) { - URL.revokeObjectURL(previewUrl); - } - if (videoBlob && videoBlob.startsWith("blob:")) { - URL.revokeObjectURL(videoBlob); - } - }; - }, [previewUrl, videoBlob]); - - useEffect(() => { - if (!isOpen) { - if (previewUrl && previewUrl.startsWith("blob:")) { - URL.revokeObjectURL(previewUrl); - setPreviewUrl(null); - } - if (videoBlob && videoBlob.startsWith("blob:")) { - URL.revokeObjectURL(videoBlob); - setVideoBlob(null); - } - } - }, [isOpen]); - - const loadPreview = async () => { - if (!file.objectName || isLoadingPreview) return; - - setIsLoadingPreview(true); - try { - const encodedObjectName = encodeURIComponent(file.objectName); - const response = await getDownloadUrl(encodedObjectName); - const url = response.data.url; - - setDownloadUrl(url); - - const fileType = getFileType(); - - if (fileType === "video") { - await loadVideoPreview(url); - } else if (fileType === "audio") { - await loadAudioPreview(url); - } else if (fileType === "pdf") { - await loadPdfPreview(url); - } else { - setPreviewUrl(url); - } - } catch (error) { - console.error("Failed to load preview:", error); - toast.error(t("filePreview.loadError")); - } finally { - setIsLoading(false); - setIsLoadingPreview(false); - } - }; - - const loadVideoPreview = async (url: string) => { - try { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const blob = await response.blob(); - const blobUrl = URL.createObjectURL(blob); - setVideoBlob(blobUrl); - } catch (error) { - console.error("Failed to load video as blob:", error); - setPreviewUrl(url); - } - }; - - const loadAudioPreview = async (url: string) => { - try { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const blob = await response.blob(); - const blobUrl = URL.createObjectURL(blob); - setPreviewUrl(blobUrl); - } catch (error) { - console.error("Failed to load audio as blob:", error); - setPreviewUrl(url); - } - }; - - const loadPdfPreview = async (url: string) => { - try { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const blob = await response.blob(); - const finalBlob = new Blob([blob], { type: "application/pdf" }); - const blobUrl = URL.createObjectURL(finalBlob); - setPreviewUrl(blobUrl); - setPdfAsBlob(true); - } catch (error) { - console.error("Failed to load PDF as blob:", error); - setPreviewUrl(url); - setTimeout(() => { - if (!pdfLoadFailed && !pdfAsBlob) { - handlePdfLoadError(); - } - }, 4000); - } - }; - - const handlePdfLoadError = async () => { - if (pdfLoadFailed || pdfAsBlob) return; - - setPdfLoadFailed(true); - - if (downloadUrl) { - setTimeout(() => { - loadPdfPreview(downloadUrl); - }, 500); - } - }; - - const handleDownload = () => { - onDownload(file.objectName, file.name); - }; - - const getFileType = () => { - const extension = file.name.split(".").pop()?.toLowerCase(); - - if (extension === "pdf") return "pdf"; - if (["jpg", "jpeg", "png", "gif", "webp", "svg", "bmp", "tiff"].includes(extension || "")) return "image"; - if (["mp3", "wav", "ogg", "m4a", "aac", "flac"].includes(extension || "")) return "audio"; - if (["mp4", "webm", "ogg", "mov", "avi", "mkv", "wmv", "flv", "m4v"].includes(extension || "")) return "video"; - - return "other"; - }; - - const renderPreview = () => { - const fileType = getFileType(); - const { icon: FileIcon, color } = getFileIcon(file.name); - - if (isLoading) { - return ( -
-
-

{t("filePreview.loading")}

-
- ); - } - - const mediaUrl = fileType === "video" ? videoBlob : previewUrl; - - if (!mediaUrl && (fileType === "video" || fileType === "audio")) { - return ( -
- -

{t("filePreview.notAvailable")}

-

{t("filePreview.downloadToView")}

-
- ); - } - - if (!previewUrl && fileType !== "video") { - return ( -
- -

{t("filePreview.notAvailable")}

-

{t("filePreview.downloadToView")}

-
- ); - } - - switch (fileType) { - case "pdf": - return ( - -
- {pdfAsBlob ? ( -