56 lines
1.9 KiB
TypeScript
56 lines
1.9 KiB
TypeScript
// components/utilities/files/FilePreview.tsx
|
|
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { rawUrl, isPreviewableImage, isPreviewableText, isPreviewablePdf } from "./api";
|
|
|
|
export default function FilePreview({ path, mime, name }: { path: string; mime?: string | null; name?: string }) {
|
|
const [text, setText] = useState<string>("");
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
async function load() {
|
|
setText("");
|
|
if (isPreviewableText(mime, name)) {
|
|
try {
|
|
const res = await fetch(rawUrl(path), { cache: "no-store" });
|
|
const t = await res.text();
|
|
if (!cancelled) setText(t.slice(0, 100_000)); // safety cap
|
|
} catch {
|
|
if (!cancelled) setText("Unable to load text preview.");
|
|
}
|
|
}
|
|
}
|
|
load();
|
|
return () => { cancelled = true; };
|
|
}, [path, mime, name]);
|
|
|
|
if (isPreviewableImage(mime, name)) {
|
|
return (
|
|
<div className="rounded-md border overflow-hidden">
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
<img src={rawUrl(path)} alt={name || ""} className="w-full max-h-[60vh] object-contain bg-muted" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (isPreviewablePdf(mime, name)) {
|
|
return (
|
|
<iframe
|
|
src={rawUrl(path)}
|
|
className="w-full h-[60vh] rounded-md border"
|
|
title={name || "PDF preview"}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (isPreviewableText(mime, name)) {
|
|
return (
|
|
<pre className="rounded-md border bg-muted/40 p-3 overflow-auto max-h-[60vh] text-xs whitespace-pre-wrap">
|
|
{text || "Loading…"}
|
|
</pre>
|
|
);
|
|
}
|
|
|
|
return <div className="text-sm text-zinc-400">No preview available.</div>;
|
|
}
|