feat: enhance file handling and preview features
- Added new dependencies for file type handling and markdown support in package.json. - Updated FileGrid component to use file extension for FilePreview. - Refactored FilePreview component to determine file type based on extension, supporting various media types including images, audio, video, text, archives, and code files. - Created a utility function getFileType to centralize MIME type determination based on file extensions. - Updated API route to utilize the new getFileType function for serving files with correct MIME types. - Added Tailwind CSS configuration with typography plugin for improved styling.
This commit is contained in:
parent
af978c98b8
commit
ad24c76a70
3030
package-lock.json
generated
3030
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -24,15 +24,21 @@
|
||||
"@auth/prisma-adapter": "^2.7.2",
|
||||
"@prisma/client": "^6.5.0",
|
||||
"@t3-oss/env-nextjs": "^0.12.0",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tanstack/react-query": "^5.69.0",
|
||||
"@trpc/client": "^11.0.0",
|
||||
"@trpc/react-query": "^11.0.0",
|
||||
"@trpc/server": "^11.0.0",
|
||||
"dompurify": "^3.2.5",
|
||||
"mermaid": "^11.6.0",
|
||||
"next": "^15.2.3",
|
||||
"next-auth": "5.0.0-beta.25",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"react-markdown": "^10.1.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"server-only": "^0.0.1",
|
||||
"superjson": "^2.2.1",
|
||||
"zod": "^3.24.2"
|
||||
|
||||
@ -103,7 +103,7 @@ export default function FileGrid({ session }: FileGridProps) {
|
||||
key={file.id}
|
||||
className="flex place-content-end max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20"
|
||||
>
|
||||
{fileType !== "unknown" && <div className=" self-center max-w-50"><FilePreview fileId={file.id} fileType={fileType} /></div>}
|
||||
{<div className=" self-center max-w-50"><FilePreview fileId={file.id} fileType={file.extension} /></div>}
|
||||
|
||||
<button onClick={() => router.push(pageUrl + file.url)}>
|
||||
<h3 className="text-2xl font-bold">{file.name}</h3>
|
||||
|
||||
@ -51,8 +51,37 @@ export function FilePreview({ fileId, fileType }: FilePreviewProps) {
|
||||
if (!mediaSrc) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
const getFileType = (extension: string): string => {
|
||||
const fileTypes: Record<string, string> = {
|
||||
".mp4": "video/mp4",
|
||||
".webm": "video/webm",
|
||||
".ogg": "video/ogg",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".png": "image/png",
|
||||
".gif": "image/gif",
|
||||
".svg": "image/svg+xml",
|
||||
".mp3": "audio/mpeg",
|
||||
".wav": "audio/wav",
|
||||
".zip": "archive/zip",
|
||||
".rar": "archive/rar",
|
||||
".pdf": "text/pdf",
|
||||
".txt": "text/plain",
|
||||
".c" : "code/c",
|
||||
".cpp" : "code/cpp",
|
||||
".py" : "code/python",
|
||||
".js" : "code/javascript",
|
||||
".html" : "code/html",
|
||||
".css" : "code/css",
|
||||
".md" : "markdown/markdown",
|
||||
".json" : "code/json",
|
||||
".xml" : "code/xml",
|
||||
".csv" : "code/csv",
|
||||
};
|
||||
return fileTypes[extension] || "unknown";
|
||||
};
|
||||
|
||||
if (fileType.startsWith("video")) {
|
||||
if (getFileType(fileType).startsWith("video")) {
|
||||
return (
|
||||
<video
|
||||
controls
|
||||
@ -63,7 +92,7 @@ export function FilePreview({ fileId, fileType }: FilePreviewProps) {
|
||||
</video>
|
||||
);
|
||||
}
|
||||
if (fileType.startsWith("audio")) {
|
||||
if (getFileType(fileType).startsWith("audio")) {
|
||||
return (
|
||||
<audio
|
||||
controls
|
||||
@ -74,6 +103,29 @@ export function FilePreview({ fileId, fileType }: FilePreviewProps) {
|
||||
</audio>
|
||||
);
|
||||
}
|
||||
if (getFileType(fileType).startsWith("image")) {
|
||||
return <img src={mediaSrc} alt="Media preview" className="max-w-full max-h-96 rounded-lg shadow-md" />;
|
||||
}
|
||||
|
||||
return <img src={mediaSrc} alt="Media preview" className="max-w-full max-h-96 rounded-lg shadow-md" />;
|
||||
if (getFileType(fileType).startsWith("text")) {
|
||||
return (
|
||||
<img src="/icons/files/text.svg" alt="Text file preview" className="max-w-full max-h-96 rounded-lg invert" />
|
||||
);
|
||||
}
|
||||
if (getFileType(fileType).startsWith("archive")) {
|
||||
return (
|
||||
<img src="/icons/files/archive.svg" alt="Archive file preview" className="max-w-full max-h-96 rounded-lg invert" />
|
||||
);
|
||||
}
|
||||
if (getFileType(fileType).startsWith("code")) {
|
||||
return (
|
||||
<img src="/icons/files/code.svg" alt="Code file preview" className="max-w-full max-h-96 rounded-lg invert" />
|
||||
);
|
||||
}
|
||||
// if (getFileType(fileType).startsWith("markdown")) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
|
||||
import path from "path";
|
||||
import { promises as fs } from "fs";
|
||||
import { db } from "~/server/db";
|
||||
import { getFileType } from "~/utils/fileType";
|
||||
|
||||
export async function GET(req: Request) {
|
||||
const url = new URL(req.url);
|
||||
@ -27,25 +28,8 @@ export async function GET(req: Request) {
|
||||
// Read the file from the filesystem
|
||||
const fileBuffer = await fs.readFile(filePath);
|
||||
|
||||
const mimeType = file.extension === ".mp4"
|
||||
? "video/mp4"
|
||||
: file.extension === ".webm"
|
||||
? "video/webm"
|
||||
: file.extension === ".ogg"
|
||||
? "video/ogg"
|
||||
: file.extension === ".jpg" || file.extension === ".jpeg"
|
||||
? "image/jpeg"
|
||||
: file.extension === ".png"
|
||||
? "image/png"
|
||||
: file.extension === ".gif"
|
||||
? "image/gif"
|
||||
: file.extension === ".svg"
|
||||
? "image/svg+xml"
|
||||
: file.extension === ".mp3"
|
||||
? "audio/mpeg"
|
||||
: file.extension === ".wav"
|
||||
? "audio/wav"
|
||||
: "application/octet-stream";
|
||||
const mimeType = getFileType(path.extname(file.name)); // Get the MIME type based on the file extension
|
||||
|
||||
|
||||
// Return the file as a binary response
|
||||
return new Response(fileBuffer, {
|
||||
|
||||
31
src/utils/fileType.ts
Normal file
31
src/utils/fileType.ts
Normal file
@ -0,0 +1,31 @@
|
||||
// This function takes a file name as input and returns the file type based on its extension.
|
||||
|
||||
export function getFileType(extension: string): string {
|
||||
const fileTypes: Record<string, string> = {
|
||||
".mp4": "video/mp4",
|
||||
".webm": "video/webm",
|
||||
".ogg": "video/ogg",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".png": "image/png",
|
||||
".gif": "image/gif",
|
||||
".svg": "image/svg+xml",
|
||||
".mp3": "audio/mpeg",
|
||||
".wav": "audio/wav",
|
||||
".zip": "archive/zip",
|
||||
".rar": "archive/rar",
|
||||
".pdf": "text/pdf",
|
||||
".txt": "text/plain",
|
||||
".c": "code/c",
|
||||
".cpp": "code/cpp",
|
||||
".py": "code/python",
|
||||
".js": "code/javascript",
|
||||
".html": "code/html",
|
||||
".css": "code/css",
|
||||
".md": "markdown/markdown",
|
||||
".json": "code/json",
|
||||
".xml": "code/xml",
|
||||
".csv": "code/csv",
|
||||
};
|
||||
return fileTypes[extension] || "unknown";
|
||||
};
|
||||
6
tailwind.config.ts
Normal file
6
tailwind.config.ts
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{js,ts,jsx,tsx}", // Ensure your paths are correct
|
||||
],
|
||||
plugins: [require("@tailwindcss/typography")],
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user