Improve download viewer with grouping and inline previews
Group downloads and scraps by type in Entry and Release details Add FileViewer component for .txt, .nfo, image, and PDF previews Update download API to support inline view with correct MIME types Signed-off-by: Junie@lucy.xalior.com
This commit is contained in:
90
src/components/FileViewer.tsx
Normal file
90
src/components/FileViewer.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Modal, Button, Spinner } from "react-bootstrap";
|
||||
|
||||
type FileViewerProps = {
|
||||
url: string;
|
||||
title: string;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export default function FileViewer({ url, title, onClose }: FileViewerProps) {
|
||||
const [content, setContent] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const isText = title.toLowerCase().endsWith(".txt") || title.toLowerCase().endsWith(".nfo");
|
||||
const isImage = title.toLowerCase().match(/\.(png|jpg|jpeg|gif)$/);
|
||||
const isPdf = title.toLowerCase().endsWith(".pdf");
|
||||
|
||||
const viewUrl = url.includes("?") ? `${url}&view=1` : `${url}?view=1`;
|
||||
|
||||
useState(() => {
|
||||
if (isText) {
|
||||
fetch(viewUrl)
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error("Failed to load file");
|
||||
return res.text();
|
||||
})
|
||||
.then((text) => {
|
||||
setContent(text);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setError(err.message);
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal show size="xl" onHide={onClose} centered scrollable>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>{title}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body className="p-0 bg-dark text-light" style={{ minHeight: "300px" }}>
|
||||
{loading && (
|
||||
<div className="d-flex justify-content-center align-items-center" style={{ minHeight: "300px" }}>
|
||||
<Spinner animation="border" variant="light" />
|
||||
</div>
|
||||
)}
|
||||
{error && (
|
||||
<div className="p-4 text-center">
|
||||
<p className="text-danger">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
{!loading && !error && (
|
||||
<>
|
||||
{isText && (
|
||||
<pre className="p-3 m-0" style={{ whiteSpace: "pre-wrap", wordBreak: "break-all", fontSize: "0.9rem", color: "#ccc" }}>
|
||||
{content}
|
||||
</pre>
|
||||
)}
|
||||
{isImage && (
|
||||
<div className="text-center p-3">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={viewUrl} alt={title} className="img-fluid" style={{ maxHeight: "80vh" }} />
|
||||
</div>
|
||||
)}
|
||||
{isPdf && (
|
||||
<iframe src={viewUrl} style={{ width: "100%", height: "80vh", border: "none" }} title={title} />
|
||||
)}
|
||||
{!isText && !isImage && !isPdf && (
|
||||
<div className="p-4 text-center">
|
||||
<p>Preview not available for this file type.</p>
|
||||
<a href={url} className="btn btn-primary">Download File</a>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={onClose}>Close</Button>
|
||||
<a href={url} className="btn btn-success" download>Download</a>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user