Compare commits
	
		
			3 Commits
		
	
	
		
			8bc35d979d
			...
			1d302eb217
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1d302eb217 | |||
| 344334592e | |||
| 5ec3e011e1 | 
							
								
								
									
										120
									
								
								src/app/_components/ActionButtons.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/app/_components/ActionButtons.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | |||||||
|  | 'use client'; | ||||||
|  | import { useRef, useState } from "react"; | ||||||
|  | import { useFileActions } from "~/app/_components/FileActions"; | ||||||
|  | 
 | ||||||
|  | export function FileActionsContainer({ | ||||||
|  |   fileId, | ||||||
|  |   fileName, | ||||||
|  |   fileUrl, | ||||||
|  |   isOwner, | ||||||
|  | }: { | ||||||
|  |   fileId: string; | ||||||
|  |   fileName: string; | ||||||
|  |   fileUrl: string; | ||||||
|  |   isOwner: boolean; | ||||||
|  | }) { | ||||||
|  |   const { handleDownload, handleCopyUrl, handleRemove} = useFileActions(() => fileId, (description: string) => { | ||||||
|  |     if (isOwner) { | ||||||
|  |       console.log(description); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div className="flex self-center gap-2"> | ||||||
|  |       {/* Download Button */} | ||||||
|  |       <button | ||||||
|  |         onClick={() => handleDownload(fileId, fileName)} | ||||||
|  |         className="flex items-center justify-center rounded-full bg-blue-500 p-2 hover:bg-blue-600" | ||||||
|  |       > | ||||||
|  |         <svg | ||||||
|  |           xmlns="http://www.w3.org/2000/svg" | ||||||
|  |           fill="none" | ||||||
|  |           viewBox="0 0 24 24" | ||||||
|  |           strokeWidth={2} | ||||||
|  |           stroke="currentColor" | ||||||
|  |           className="h-5 w-5 text-white" | ||||||
|  |         > | ||||||
|  |           <path | ||||||
|  |             strokeLinecap="round" | ||||||
|  |             strokeLinejoin="round" | ||||||
|  |             d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2M7 10l5 5m0 0l5-5m-5 5V3" | ||||||
|  |           /> | ||||||
|  |         </svg> | ||||||
|  |       </button> | ||||||
|  | 
 | ||||||
|  |       {/* Copy URL Button */} | ||||||
|  |       <button | ||||||
|  |         onClick={() => handleCopyUrl(fileUrl)} | ||||||
|  |         className="flex items-center justify-center rounded-full bg-green-500 p-2 hover:bg-green-600" | ||||||
|  |       > | ||||||
|  |         <svg | ||||||
|  |           xmlns="http://www.w3.org/2000/svg" | ||||||
|  |           fill="none" | ||||||
|  |           viewBox="0 0 24 24" | ||||||
|  |           strokeWidth={2} | ||||||
|  |           stroke="currentColor" | ||||||
|  |           className="h-5 w-5 text-white" | ||||||
|  |         > | ||||||
|  |           <path | ||||||
|  |             strokeLinecap="round" | ||||||
|  |             strokeLinejoin="round" | ||||||
|  |             d="M8 9h8m-6 4h4m-7 8h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v12a2 2 0 002 2z" | ||||||
|  |           /> | ||||||
|  |         </svg> | ||||||
|  |       </button> | ||||||
|  | 
 | ||||||
|  |       {/* Remove Button */} | ||||||
|  |       <button | ||||||
|  |         onClick={() => handleRemove(fileId)} | ||||||
|  |         className="flex items-center justify-center rounded-full bg-red-500 p-2 hover:bg-red-600" | ||||||
|  |       > | ||||||
|  |         <svg | ||||||
|  |           xmlns="http://www.w3.org/2000/svg" | ||||||
|  |           fill="none" | ||||||
|  |           viewBox="0 0 24 24" | ||||||
|  |           strokeWidth={2} | ||||||
|  |           stroke="currentColor" | ||||||
|  |           className="h-5 w-5 text-white" | ||||||
|  |         > | ||||||
|  |           <path | ||||||
|  |             strokeLinecap="round" | ||||||
|  |             strokeLinejoin="round" | ||||||
|  |             d="M6 18L18 6M6 6l12 12" | ||||||
|  |           /> | ||||||
|  |         </svg> | ||||||
|  |       </button> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function FileDescriptionContainer({ | ||||||
|  |   fileId, | ||||||
|  |   fileDescriprtion, | ||||||
|  | }: { | ||||||
|  |   fileId: string; | ||||||
|  |   fileDescriprtion?: string; | ||||||
|  | }) { | ||||||
|  | 
 | ||||||
|  |   const [description, setDescription] = useState(fileDescriprtion || ""); // Add state for description
 | ||||||
|  |   const { handleDescriptionChange } = useFileActions(() => {}, (description: string) => { | ||||||
|  |     setDescription(description); | ||||||
|  |     return undefined; | ||||||
|  |   }, fileId); // Wrap setDescription in a function
 | ||||||
|  |   const debounceTimer = useRef<NodeJS.Timeout | null>(null); // Initialize debounce timer
 | ||||||
|  | 
 | ||||||
|  |   const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { | ||||||
|  |     handleDescriptionChange(e, debounceTimer); // Pass the debounce timer
 | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div className="flex self-center gap-2"> | ||||||
|  |       <textarea | ||||||
|  |         className="w-full h-24 p-2 border rounded-md bg-gray-800 text-white" | ||||||
|  |         value={description} // Use state value
 | ||||||
|  |         onChange={handleChange} | ||||||
|  |         placeholder="Enter file description..." | ||||||
|  |         maxLength={200} // Limit to 200 characters
 | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @ -1,12 +1,13 @@ | |||||||
| import toast from "react-hot-toast"; | import toast from "react-hot-toast"; | ||||||
| import { env } from "~/env.js"; | import { env } from "~/env.js"; | ||||||
|  | import { notifyClients } from "~/utils/notifyClients"; | ||||||
| 
 | 
 | ||||||
| export const useFileActions = ( | export const useFileActions = ( | ||||||
|   setFiles: (callback: (prevFiles: any[]) => any[]) => void, |   setFiles: (callback: (prevFiles: any[]) => any[]) => void, | ||||||
|   setDescription?: (description: string) => void, |   setDescription?: (description: string) => undefined, | ||||||
|   fileId?: string |   fileId?: string | ||||||
| ) => { | ) => { | ||||||
|   const pageUrl = `${env.NEXT_PUBLIC_PAGE_URL}/share?id=`; |   const pageUrl = `${env.NEXT_PUBLIC_PAGE_URL}`; | ||||||
| 
 | 
 | ||||||
|   // Handle file download
 |   // Handle file download
 | ||||||
|   const handleDownload = async (fileId: string, fileName: string) => { |   const handleDownload = async (fileId: string, fileName: string) => { | ||||||
| @ -44,15 +45,25 @@ export const useFileActions = ( | |||||||
|   // Remove a file
 |   // Remove a file
 | ||||||
|   const handleRemove = async (fileId: string) => { |   const handleRemove = async (fileId: string) => { | ||||||
|     try { |     try { | ||||||
|       const response = await fetch(`/api/remove`, { |       const response = await fetch(`/api/files/remove`, { | ||||||
|         method: "DELETE", |         method: "DELETE", | ||||||
|         headers: { "Content-Type": "application/json" }, |         headers: { "Content-Type": "application/json" }, | ||||||
|         body: JSON.stringify({ id: fileId }), |         body: JSON.stringify({ id: fileId }), | ||||||
|       }); |       }); | ||||||
|  | 
 | ||||||
|  |       if (response.status === 403) { | ||||||
|  |         toast.error("You are not authorized to remove this file."); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       if (!response.ok) throw new Error("Failed to delete file"); |       if (!response.ok) throw new Error("Failed to delete file"); | ||||||
| 
 | 
 | ||||||
|       setFiles((prevFiles) => prevFiles.filter((file) => file.id !== fileId)); |       setFiles((prevFiles) => prevFiles.filter((file) => file.id !== fileId)); | ||||||
|       toast.success("File removed successfully!"); |       toast.success("File removed successfully!"); | ||||||
|  |       // Go to the home page after removing the file
 | ||||||
|  |       window.location.href = `${pageUrl}/`; | ||||||
|  |       notifyClients({ type: "file-removed", fileId }); | ||||||
|  | 
 | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
|       console.error(err); |       console.error(err); | ||||||
|       toast.error("Failed to remove file."); |       toast.error("Failed to remove file."); | ||||||
| @ -64,7 +75,9 @@ export const useFileActions = ( | |||||||
|     e: React.ChangeEvent<HTMLTextAreaElement>, |     e: React.ChangeEvent<HTMLTextAreaElement>, | ||||||
|     debounceTimer: React.RefObject<NodeJS.Timeout | null> |     debounceTimer: React.RefObject<NodeJS.Timeout | null> | ||||||
|   ) => { |   ) => { | ||||||
|     if (!setDescription) return; |     if (setDescription === undefined) {console.error("setDescription function is not provided")  | ||||||
|  |       return; | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     const newDescription = e.target.value; |     const newDescription = e.target.value; | ||||||
|     setDescription(newDescription); |     setDescription(newDescription); | ||||||
| @ -72,7 +85,9 @@ export const useFileActions = ( | |||||||
|     if (debounceTimer.current) { |     if (debounceTimer.current) { | ||||||
|       clearTimeout(debounceTimer.current); |       clearTimeout(debounceTimer.current); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     debounceTimer.current = setTimeout(() => { |     debounceTimer.current = setTimeout(() => { | ||||||
|  |       console.log("Calling handleDescriptionSave"); // Debug log
 | ||||||
|       handleDescriptionSave(newDescription); |       handleDescriptionSave(newDescription); | ||||||
|     }, 1000); |     }, 1000); | ||||||
|   }; |   }; | ||||||
| @ -85,14 +100,22 @@ export const useFileActions = ( | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|       const response = await fetch(`/api/share?id=${encodeURIComponent(fileId)}`, { |       const response = await fetch(`/api/files/share?id=${encodeURIComponent(fileId)}`, { | ||||||
|         method: "PUT", |         method: "PUT", | ||||||
|         headers: { "Content-Type": "application/json" }, |         headers: { "Content-Type": "application/json" }, | ||||||
|         body: JSON.stringify({ description }), |         // pass the fileId and description in the request body
 | ||||||
|  |         body: JSON.stringify({ description, id: fileId }), | ||||||
|       }); |       }); | ||||||
|  | 
 | ||||||
|  |       if (response.status === 403) { | ||||||
|  |         toast.error("You are not authorized to modify this file's description."); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       if (!response.ok) throw new Error("Failed to update description"); |       if (!response.ok) throw new Error("Failed to update description"); | ||||||
| 
 | 
 | ||||||
|       toast.success("Description updated successfully!"); |       toast.success("Description updated successfully!"); | ||||||
|  |       notifyClients({ type: "file-updated", fileId }); | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
|       console.error(err); |       console.error(err); | ||||||
|       toast.error("Failed to update description."); |       toast.error("Failed to update description."); | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ export function FilePreview({ fileId, fileType }: FilePreviewProps) { | |||||||
| 
 | 
 | ||||||
|     const fetchMedia = async () => { |     const fetchMedia = async () => { | ||||||
|       try { |       try { | ||||||
|         const response = await fetch(`/api/serv?id=${encodeURIComponent(fileId)}`); |         const response = await fetch(`/api/files/serv?id=${encodeURIComponent(fileId)}`); | ||||||
|         if (!response.ok) { |         if (!response.ok) { | ||||||
|           throw new Error("Failed to fetch media"); |           throw new Error("Failed to fetch media"); | ||||||
|         } |         } | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								src/app/_components/HomeButton.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/app/_components/HomeButton.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | "use client"; | ||||||
|  | 
 | ||||||
|  | export function HomeButton() { | ||||||
|  |   return ( | ||||||
|  |     <button | ||||||
|  |       onClick={() => (window.location.href = "/")} | ||||||
|  |       className="rounded-full bg-white/10 px-4 py-2 font-semibold hover:bg-white/20" | ||||||
|  |     > | ||||||
|  |       Home | ||||||
|  |     </button> | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @ -2,6 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| import { useState, useRef } from "react"; | import { useState, useRef } from "react"; | ||||||
| import toast, { Toaster } from "react-hot-toast"; | import toast, { Toaster } from "react-hot-toast"; | ||||||
|  | import { notifyClients } from "~/utils/notifyClients"; | ||||||
| 
 | 
 | ||||||
| export default function UploadForm() { | export default function UploadForm() { | ||||||
|   const [file, setFile] = useState<File | null>(null); |   const [file, setFile] = useState<File | null>(null); | ||||||
| @ -42,6 +43,7 @@ export default function UploadForm() { | |||||||
|         if (xhr.status === 200) { |         if (xhr.status === 200) { | ||||||
|           const response: { url: string } = JSON.parse(xhr.responseText); // Explicitly type the response
 |           const response: { url: string } = JSON.parse(xhr.responseText); // Explicitly type the response
 | ||||||
|           setUploadedFileUrl(response.url); // Assume the API returns the uploaded file URL
 |           setUploadedFileUrl(response.url); // Assume the API returns the uploaded file URL
 | ||||||
|  |           notifyClients({type: "file-uploaded", fileUrl: response.url}); // Notify other clients about the new file
 | ||||||
|           toast.success("File uploaded successfully!"); |           toast.success("File uploaded successfully!"); | ||||||
| 
 | 
 | ||||||
|           // Clear the file input and reset state
 |           // Clear the file input and reset state
 | ||||||
|  | |||||||
| @ -22,8 +22,12 @@ export async function DELETE(req: Request) { | |||||||
|       where: { id: body.id }, |       where: { id: body.id }, | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     if (!resource || resource.uploadedById !== session.user.id) { |     if (!resource) { | ||||||
|       return NextResponse.json({ error: "Resource not found or unauthorized" }, { status: 404 }); |       return NextResponse.json({ error: "File not found" }, { status: 404 }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (resource.uploadedById !== session.user.id) { | ||||||
|  |       return NextResponse.json({ error: "You are not authorized to delete this file" }, { status: 403 }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const filePath = path.join(process.cwd(), "uploads", path.basename(body.id)); |     const filePath = path.join(process.cwd(), "uploads", path.basename(body.id)); | ||||||
| @ -37,9 +41,9 @@ export async function DELETE(req: Request) { | |||||||
| 
 | 
 | ||||||
|     notifyClients({ type: "file-removed", fileId: body.id }); |     notifyClients({ type: "file-removed", fileId: body.id }); | ||||||
| 
 | 
 | ||||||
|     return NextResponse.json({ message: "Resource deleted successfully" }); |     return NextResponse.json({ message: "File deleted successfully" }); | ||||||
|   } catch (error) { |   } catch (error) { | ||||||
|     console.error("Error deleting resource:", error); |     console.error("Error deleting file:", error); | ||||||
|     return NextResponse.json({ error: "Failed to delete resource" }, { status: 500 }); |     return NextResponse.json({ error: "Failed to delete file" }, { status: 500 }); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -41,23 +41,38 @@ export async function GET(req: Request) { | |||||||
| 
 | 
 | ||||||
| export async function PUT(req: Request) { | export async function PUT(req: Request) { | ||||||
|   const session = await auth(); |   const session = await auth(); | ||||||
|   const url = new URL(req.url); |  | ||||||
|   const fileId = url.searchParams.get("id"); |  | ||||||
|   const { description = "" } = await req.json(); |  | ||||||
| 
 | 
 | ||||||
|   if (!fileId) { |   if (!session?.user) { | ||||||
|     return NextResponse.json({ error: "File name is required" }, { status: 400 }); |     return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   try { |   try { | ||||||
|     const file = await db.file.update({ |     const body = (await req.json()) as { id: string; description: string } | null; | ||||||
|       where: { id: fileId }, |     if (!body?.id || body.description === undefined) { | ||||||
|       data: { description }, |       // Allow empty description but ensure id is present
 | ||||||
|  |       return NextResponse.json({ error: "Invalid request body" }, { status: 400 }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const resource = await db.file.findUnique({ | ||||||
|  |       where: { id: body.id }, | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     return NextResponse.json(file); |     if (!resource) { | ||||||
|  |       return NextResponse.json({ error: "File not found" }, { status: 404 }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (resource.uploadedById !== session.user.id) { | ||||||
|  |       return NextResponse.json({ error: "You are not authorized to modify this file" }, { status: 403 }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     await db.file.update({ | ||||||
|  |       where: { id: body.id }, | ||||||
|  |       data: { description: body.description }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return NextResponse.json({ message: "Description updated successfully" }); | ||||||
|   } catch (error) { |   } catch (error) { | ||||||
|     console.error("Error updating file description:", error); |     console.error("Error updating description:", error); | ||||||
|     return NextResponse.json({ error: "Failed to update file description" }, { status: 500 }); |     return NextResponse.json({ error: "Failed to update description" }, { status: 500 }); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -1,10 +1,8 @@ | |||||||
| "use client"; | import { notFound } from "next/navigation"; | ||||||
| 
 |  | ||||||
| import { useEffect, useState, useRef, Suspense } from "react"; |  | ||||||
| import { useSearchParams, useRouter } from "next/navigation"; |  | ||||||
| import toast, { Toaster } from "react-hot-toast"; |  | ||||||
| import { FilePreview } from "~/app/_components/FilePreview"; | import { FilePreview } from "~/app/_components/FilePreview"; | ||||||
| import { useFileActions } from "~/app/_components/FileActions"; | import { HomeButton } from "~/app/_components/HomeButton"; // Import the client component
 | ||||||
|  | import { Toaster } from "react-hot-toast"; | ||||||
|  | import { FileActionsContainer, FileDescriptionContainer } from "~/app/_components/ActionButtons"; // Import the client component
 | ||||||
| import Head from "next/head"; | import Head from "next/head"; | ||||||
| 
 | 
 | ||||||
| interface FileDetails { | interface FileDetails { | ||||||
| @ -20,75 +18,45 @@ interface FileDetails { | |||||||
|   description: string; |   description: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function FilePreviewContainerContent() { | async function fetchFileDetails(fileId: string): Promise<FileDetails | null> { | ||||||
|   const searchParams = useSearchParams(); |   try { | ||||||
|   const router = useRouter(); |     const response = await fetch( | ||||||
|   const fileId = searchParams.get("id"); |       `${process.env.NEXT_PUBLIC_PAGE_URL}/api/files/share?id=${encodeURIComponent( | ||||||
|   const [fileDetails, setFileDetails] = useState<FileDetails | null>(null); |         fileId | ||||||
|   const [error, setError] = useState<string | null>(null); |       )}`,
 | ||||||
|   const [description, setDescription] = useState<string>(""); |       { cache: "no-store" } | ||||||
|   const debounceTimer = useRef<NodeJS.Timeout | null>(null); |  | ||||||
| 
 |  | ||||||
|   const { handleDescriptionChange, handleCopyUrl, handleDownload, handleRemove } = useFileActions( |  | ||||||
|     () => {}, |  | ||||||
|     setDescription, |  | ||||||
|     fileId || undefined |  | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|   const getFileType = (extension: string): string => { |     if (!response.ok) { | ||||||
|     const fileTypes: Record<string, string> = { |       return null; | ||||||
|       ".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", |  | ||||||
|     }; |  | ||||||
|     return fileTypes[extension] || "unknown"; |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     if (!fileId) { |  | ||||||
|       setError("File name is required."); |  | ||||||
|       return; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const fetchFileDetails = async () => { |     return response.json(); | ||||||
|       try { |  | ||||||
|         const response = await fetch(`/api/share?id=${encodeURIComponent(fileId)}`); |  | ||||||
|         if (!response.ok) throw new Error("Failed to fetch file details"); |  | ||||||
| 
 |  | ||||||
|         const data: FileDetails = await response.json(); |  | ||||||
|         setFileDetails(data); |  | ||||||
|         setDescription(data.description); |  | ||||||
|   } catch (err) { |   } catch (err) { | ||||||
|         console.error(err); |     console.error("Failed to fetch file details:", err); | ||||||
|         setError("Failed to load file details."); |     return null; | ||||||
|   } |   } | ||||||
|     }; | } | ||||||
| 
 | 
 | ||||||
|     fetchFileDetails(); | export default async function FilePreviewContainer({ | ||||||
|   }, [fileId]); |   searchParams, | ||||||
|  | }: { | ||||||
|  |   searchParams: { id?: string }; | ||||||
|  | }) { | ||||||
|  |   const fileId = searchParams.id; | ||||||
| 
 | 
 | ||||||
|   if (error) { |   if (!fileId) { | ||||||
|     return <div className="text-red-500">{error}</div>; |     notFound(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   const fileDetails = await fetchFileDetails(fileId); | ||||||
|  | 
 | ||||||
|   if (!fileDetails) { |   if (!fileDetails) { | ||||||
|     return ( |     return ( | ||||||
|       <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> |       <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> | ||||||
|         <Toaster position="top-right" reverseOrder={false} /> |         <Toaster position="top-right" reverseOrder={false} /> | ||||||
|         <div className="absolute top-4 left-4"> |         <div className="absolute top-4 left-4"> | ||||||
|           <button |           <HomeButton /> | ||||||
|             onClick={() => router.push("/")} |  | ||||||
|             className="rounded-full bg-white/10 px-4 py-2 font-semibold hover:bg-white/20" |  | ||||||
|           > |  | ||||||
|             Home |  | ||||||
|           </button> |  | ||||||
|         </div> |         </div> | ||||||
|         <div className="container flex flex-col items-center gap-12 px-4 py-16"> |         <div className="container flex flex-col items-center gap-12 px-4 py-16"> | ||||||
|           <h1 className="text-5xl font-extrabold tracking-tight sm:text-[5rem]"> |           <h1 className="text-5xl font-extrabold tracking-tight sm:text-[5rem]"> | ||||||
| @ -122,16 +90,19 @@ function FilePreviewContainerContent() { | |||||||
|           ).toLocaleString()}`}
 |           ).toLocaleString()}`}
 | ||||||
|         /> |         /> | ||||||
|         <meta property="og:type" content="website" /> |         <meta property="og:type" content="website" /> | ||||||
|  |         <meta | ||||||
|  |           property="og:image" | ||||||
|  |           content={`${process.env.NEXT_PUBLIC_PAGE_URL}/api/files/serv?id=${fileId}`} | ||||||
|  |         /> | ||||||
|  |         <meta property="og:image:alt" content={`${fileDetails.name} preview`} /> | ||||||
|  |         <meta | ||||||
|  |           property="og:url" | ||||||
|  |           content={`${process.env.NEXT_PUBLIC_PAGE_URL}/share?id=${fileId}`} | ||||||
|  |         /> | ||||||
|       </Head> |       </Head> | ||||||
|       <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> |       <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> | ||||||
|         <Toaster position="top-right" reverseOrder={false} /> |  | ||||||
|         <div className="absolute top-4 left-4"> |         <div className="absolute top-4 left-4"> | ||||||
|           <button |           <HomeButton /> {/* Use the client component */} | ||||||
|             onClick={() => router.push("/")} |  | ||||||
|             className="rounded-full bg-white/10 px-4 py-2 font-semibold hover:bg-white/20" |  | ||||||
|           > |  | ||||||
|             Home |  | ||||||
|           </button> |  | ||||||
|         </div> |         </div> | ||||||
|         <div className="container flex flex-col items-center gap-12 px-4 py-16"> |         <div className="container flex flex-col items-center gap-12 px-4 py-16"> | ||||||
|           <h1 className="text-5xl font-extrabold tracking-tight sm:text-[5rem]"> |           <h1 className="text-5xl font-extrabold tracking-tight sm:text-[5rem]"> | ||||||
| @ -139,7 +110,10 @@ function FilePreviewContainerContent() { | |||||||
|           </h1> |           </h1> | ||||||
|           <div className="mt-6"> |           <div className="mt-6"> | ||||||
|             {fileDetails.type !== "unknown" && ( |             {fileDetails.type !== "unknown" && ( | ||||||
|               <FilePreview fileId={fileDetails.id} fileType={getFileType(fileDetails.type)} /> |               <FilePreview | ||||||
|  |                 fileId={fileDetails.id} | ||||||
|  |                 fileType={fileDetails.type} | ||||||
|  |               /> | ||||||
|             )} |             )} | ||||||
|           </div> |           </div> | ||||||
|           <div className="bg-white/10 shadow-md rounded-lg p-6 w-full max-w-md text-white"> |           <div className="bg-white/10 shadow-md rounded-lg p-6 w-full max-w-md text-white"> | ||||||
| @ -169,81 +143,18 @@ function FilePreviewContainerContent() { | |||||||
|               <strong>Upload Date:</strong>{" "} |               <strong>Upload Date:</strong>{" "} | ||||||
|               {new Date(fileDetails.uploadDate).toLocaleString()} |               {new Date(fileDetails.uploadDate).toLocaleString()} | ||||||
|             </p> |             </p> | ||||||
|             <p> |             <div> | ||||||
|               <strong>Description:</strong>{" "} |               <strong>Description:</strong>{" "} | ||||||
|               {fileDetails.isOwner ? ( |               <FileDescriptionContainer fileId={fileDetails.id} fileDescriprtion={fileDetails.description}/> | ||||||
|                 <textarea |             </div> | ||||||
|                   value={description} |             <div className="mt-4 flex justify-center"> | ||||||
|                   onChange={(e) => handleDescriptionChange(e, debounceTimer)} |               <FileActionsContainer | ||||||
|                   className="w-full h-24 p-2 bg-white/10 rounded-lg text-white" |                 fileId={fileDetails.id} | ||||||
|                 /> |                 fileName={fileDetails.name} | ||||||
|               ) : fileDetails.description === "" ? ( |                 fileUrl={fileDetails.url} | ||||||
|                 <span>No description available</span> |                 isOwner={fileDetails.isOwner} | ||||||
|               ) : ( |               /> | ||||||
|                 <span>{fileDetails.description}</span> |  | ||||||
|               )} |  | ||||||
|             </p> |  | ||||||
|             {fileDetails.isOwner && ( |  | ||||||
|               <div className="flex place-content-center gap-4 mt-4"> |  | ||||||
|                 <button |  | ||||||
|                   onClick={() => handleDownload(fileDetails.id, fileDetails.name)} |  | ||||||
|                   className="rounded-full bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 flex items-center gap-2" |  | ||||||
|                 > |  | ||||||
|                   <svg |  | ||||||
|                   xmlns="http://www.w3.org/2000/svg" |  | ||||||
|                   fill="none" |  | ||||||
|                   viewBox="0 0 24 24" |  | ||||||
|                   strokeWidth={2} |  | ||||||
|                   stroke="currentColor" |  | ||||||
|                   className="h-5 w-5 text-white" |  | ||||||
|                 > |  | ||||||
|                   <path |  | ||||||
|                     strokeLinecap="round" |  | ||||||
|                     strokeLinejoin="round" |  | ||||||
|                     d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2M7 10l5 5m0 0l5-5m-5 5V3" |  | ||||||
|                   /> |  | ||||||
|                 </svg> |  | ||||||
|                 </button> |  | ||||||
|                 <button |  | ||||||
|                   onClick={() => handleCopyUrl(fileDetails.url)} |  | ||||||
|                   className="rounded-full bg-green-500 px-4 py-2 text-white hover:bg-green-600 flex items-center gap-2" |  | ||||||
|                 > |  | ||||||
|                   <svg |  | ||||||
|                   xmlns="http://www.w3.org/2000/svg" |  | ||||||
|                   fill="none" |  | ||||||
|                   viewBox="0 0 24 24" |  | ||||||
|                   strokeWidth={2} |  | ||||||
|                   stroke="currentColor" |  | ||||||
|                   className="h-5 w-5 text-white" |  | ||||||
|                 > |  | ||||||
|                   <path |  | ||||||
|                     strokeLinecap="round" |  | ||||||
|                     strokeLinejoin="round" |  | ||||||
|                     d="M8 9h8m-6 4h4m-7 8h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v12a2 2 0 002 2z" |  | ||||||
|                   /> |  | ||||||
|                 </svg> |  | ||||||
|                 </button> |  | ||||||
|                 <button |  | ||||||
|                   onClick={() => handleRemove(fileDetails.id)} |  | ||||||
|                   className="rounded-full bg-red-500 px-4 py-2 text-white hover:bg-red-600 flex items-center gap-2" |  | ||||||
|                 > |  | ||||||
|                   <svg |  | ||||||
|                   xmlns="http://www.w3.org/2000/svg" |  | ||||||
|                   fill="none" |  | ||||||
|                   viewBox="0 0 24 24" |  | ||||||
|                   strokeWidth={2} |  | ||||||
|                   stroke="currentColor" |  | ||||||
|                   className="h-5 w-5 text-white" |  | ||||||
|                 > |  | ||||||
|                   <path |  | ||||||
|                     strokeLinecap="round" |  | ||||||
|                     strokeLinejoin="round" |  | ||||||
|                     d="M6 18L18 6M6 6l12 12" |  | ||||||
|                   /> |  | ||||||
|                 </svg> |  | ||||||
|                 </button> |  | ||||||
|             </div> |             </div> | ||||||
|             )} |  | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </main> |       </main> | ||||||
| @ -251,11 +162,3 @@ function FilePreviewContainerContent() { | |||||||
|   ); |   ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default function FilePreviewContainer() { |  | ||||||
|   return ( |  | ||||||
|     <Suspense fallback={<div>Loading...</div>}> |  | ||||||
|       <FilePreviewContainerContent /> |  | ||||||
|     </Suspense> |  | ||||||
|   ); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user