feat: add FileDescriptionContainer for managing file descriptions and integrate with FileActions

This commit is contained in:
ZareMate 2025-05-04 17:00:56 +02:00
parent 344334592e
commit 1d302eb217
Signed by: zaremate
GPG Key ID: 369A0E45E03A81C3
4 changed files with 49 additions and 10 deletions

View File

@ -1,5 +1,6 @@
'use client'; 'use client';
import {useFileActions} from "~/app/_components/FileActions"; import { useRef, useState } from "react";
import { useFileActions } from "~/app/_components/FileActions";
export function FileActionsContainer({ export function FileActionsContainer({
fileId, fileId,
@ -12,7 +13,7 @@ export function FileActionsContainer({
fileUrl: string; fileUrl: string;
isOwner: boolean; isOwner: boolean;
}) { }) {
const { handleDownload, handleCopyUrl, handleRemove } = useFileActions(() => fileId, (description: string) => { const { handleDownload, handleCopyUrl, handleRemove} = useFileActions(() => fileId, (description: string) => {
if (isOwner) { if (isOwner) {
console.log(description); console.log(description);
} }
@ -85,3 +86,35 @@ export function FileActionsContainer({
</div> </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>
);
}

View File

@ -4,7 +4,7 @@ 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}`; const pageUrl = `${env.NEXT_PUBLIC_PAGE_URL}`;
@ -75,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);
@ -83,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);
}; };
@ -99,7 +103,8 @@ export const useFileActions = (
const response = await fetch(`/api/files/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) { if (response.status === 403) {

View File

@ -48,7 +48,8 @@ export async function PUT(req: Request) {
try { try {
const body = (await req.json()) as { id: string; description: string } | null; const body = (await req.json()) as { id: string; description: string } | null;
if (!body?.id || !body.description) { if (!body?.id || body.description === undefined) {
// Allow empty description but ensure id is present
return NextResponse.json({ error: "Invalid request body" }, { status: 400 }); return NextResponse.json({ error: "Invalid request body" }, { status: 400 });
} }

View File

@ -2,7 +2,7 @@ import { notFound } from "next/navigation";
import { FilePreview } from "~/app/_components/FilePreview"; import { FilePreview } from "~/app/_components/FilePreview";
import { HomeButton } from "~/app/_components/HomeButton"; // Import the client component import { HomeButton } from "~/app/_components/HomeButton"; // Import the client component
import { Toaster } from "react-hot-toast"; import { Toaster } from "react-hot-toast";
import { FileActionsContainer } from "~/app/_components/ActionButtons"; // Import the client component 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 {
@ -143,10 +143,10 @@ export default async function FilePreviewContainer({
<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.description || "No description available"} <FileDescriptionContainer fileId={fileDetails.id} fileDescriprtion={fileDetails.description}/>
</p> </div>
<div className="mt-4 flex justify-center"> <div className="mt-4 flex justify-center">
<FileActionsContainer <FileActionsContainer
fileId={fileDetails.id} fileId={fileDetails.id}