| import React from "react"; | |
| import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Download, Loader2 } from "lucide-react"; | |
| import { DocumentManager } from "@/lib/document/manager"; | |
| import { FilePreviewDialogProps } from "../types"; | |
| import { toast } from "sonner"; | |
| export const FilePreviewDialog = React.memo(({ document: fileDoc, onClose }: FilePreviewDialogProps) => { | |
| const [content, setContent] = React.useState<string | null>(null); | |
| const [isLoading, setIsLoading] = React.useState(true); | |
| const [error, setError] = React.useState<string | null>(null); | |
| const documentManager = React.useMemo(() => DocumentManager.getInstance(), []); | |
| React.useEffect(() => { | |
| if (!fileDoc) return; | |
| const loadContent = async () => { | |
| try { | |
| setIsLoading(true); | |
| setError(null); | |
| const file = await documentManager.getDocument(fileDoc.id); | |
| if (fileDoc.type === "image") { | |
| const reader = new FileReader(); | |
| reader.onload = () => setContent(reader.result as string); | |
| reader.readAsDataURL(file); | |
| } else if (fileDoc.type === "pdf") { | |
| const url = URL.createObjectURL(file); | |
| setContent(url); | |
| return () => URL.revokeObjectURL(url); | |
| } else { | |
| const text = await file.text(); | |
| setContent(text); | |
| } | |
| } catch (err) { | |
| console.error(err); | |
| setError("Failed to load file content"); | |
| } finally { | |
| setIsLoading(false); | |
| } | |
| }; | |
| loadContent(); | |
| }, [fileDoc, documentManager]); | |
| const handleDownload = React.useCallback(async () => { | |
| if (!fileDoc) return; | |
| try { | |
| const file = await documentManager.getDocument(fileDoc.id); | |
| const url = URL.createObjectURL(file); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = fileDoc.name; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } catch (err) { | |
| console.error(err); | |
| toast.error("Failed to download file"); | |
| } | |
| }, [fileDoc, documentManager]); | |
| if (!fileDoc) return null; | |
| const renderContent = () => { | |
| if (isLoading) { | |
| return ( | |
| <div className="flex items-center justify-center h-full"> | |
| <Loader2 className="h-8 w-8 animate-spin" /> | |
| </div> | |
| ); | |
| } | |
| if (error) { | |
| return ( | |
| <div className="flex items-center justify-center h-full text-destructive"> | |
| {error} | |
| </div> | |
| ); | |
| } | |
| if (content) { | |
| if (fileDoc.type === "image") { | |
| return ( | |
| <div className="flex items-center justify-center p-4"> | |
| <img src={content} alt={fileDoc.name} className="max-w-full max-h-full object-contain" /> | |
| </div> | |
| ); | |
| } | |
| if (fileDoc.type === "pdf") { | |
| return <iframe src={content} className="h-full w-full rounded-md bg-muted/50" />; | |
| } | |
| return ( | |
| <pre className="p-4 whitespace-pre-wrap font-mono text-sm"> | |
| {content} | |
| </pre> | |
| ); | |
| } | |
| return null; | |
| }; | |
| return ( | |
| <Dialog open={!!fileDoc} onOpenChange={onClose}> | |
| <DialogContent className="max-w-4xl max-h-[80vh] h-full flex flex-col"> | |
| <DialogHeader className="flex flex-row items-center justify-between"> | |
| <div className="space-y-1"> | |
| <DialogTitle>{fileDoc.name}</DialogTitle> | |
| <DialogDescription> | |
| Type: {fileDoc.type} • Created: {new Date(fileDoc.createdAt).toLocaleString()} | |
| </DialogDescription> | |
| </div> | |
| <Button onClick={handleDownload} variant="outline" size="sm" className="gap-2"> | |
| <Download className="h-4 w-4" /> | |
| Download | |
| </Button> | |
| </DialogHeader> | |
| {renderContent()} | |
| </DialogContent> | |
| </Dialog> | |
| ); | |
| }); | |
| FilePreviewDialog.displayName = "FilePreviewDialog"; |