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