From 828df0e105e16e6d2c6fe07e46a6526cbf1643ca Mon Sep 17 00:00:00 2001 From: anshul23102 Date: Fri, 5 Jun 2026 21:32:06 +0530 Subject: [PATCH 1/5] feat: implement soft delete with 30-day recovery period for notes Users can now recover accidentally deleted notes within 30 days before permanent deletion. Implements soft delete pattern with automatic TTL cleanup. Changes: 1. Schema: - Added isDeleted boolean field (indexed for efficient queries) - Added deletedAt timestamp field - Added TTL index: deletes records permanently after 30 days 2. Delete Endpoint (PATCH /api/notes/:id): - Changed from hard delete to soft delete - Sets isDeleted=true, deletedAt=now() - Returns recovery message with 30-day window 3. Get Endpoints: - Exclude soft-deleted notes from active notes listing - Only show undeleted notes to users 4. New Trash API (GET /api/notes/trash): - GET: List all deleted notes within recovery window - PUT /api/notes/trash/restore/:id: Restore a deleted note - DELETE /api/notes/trash/:id: Permanently delete (skip recovery) Benefits: - Prevents accidental data loss - 30-day recovery period before permanent deletion - Automatic cleanup after recovery window expires - Clear user communication about recovery Database behavior: - Soft-deleted notes: recovered within 30 days - Expired trash: automatically removed by MongoDB TTL index - Performance: indexed isDeleted field for fast filtering - Storage: small overhead (2 extra fields per note) Closes #270 --- savebook/app/api/notes/[id]/route.js | 20 ++- savebook/app/api/notes/route.js | 1 + savebook/app/api/notes/trash/route.js | 196 ++++++++++++++++++++++++++ savebook/lib/models/Notes.js | 22 +++ 4 files changed, 235 insertions(+), 4 deletions(-) create mode 100644 savebook/app/api/notes/trash/route.js diff --git a/savebook/app/api/notes/[id]/route.js b/savebook/app/api/notes/[id]/route.js index 4748eb3..595308a 100644 --- a/savebook/app/api/notes/[id]/route.js +++ b/savebook/app/api/notes/[id]/route.js @@ -37,7 +37,11 @@ export async function GET(request, { params }) { ); } - const note = await Notes.findOne({ _id: id, user: decoded.userId }); + const note = await Notes.findOne({ + _id: id, + user: decoded.userId, + isDeleted: false, // Exclude soft-deleted notes + }); if (!note) { return NextResponse.json( @@ -108,12 +112,20 @@ export async function DELETE(request, { params }) { ); } - // Delete the note - await Notes.findByIdAndDelete(id); + // Soft delete: mark as deleted instead of removing permanently + // User has 30 days to recover from trash before permanent deletion via TTL + await Notes.findByIdAndUpdate( + id, + { + isDeleted: true, + deletedAt: new Date(), + }, + { new: true } + ); return NextResponse.json({ success: true, - message: "Note deleted successfully" + message: "Note deleted. Recover from trash within 30 days." }); } catch (error) { console.error(error); diff --git a/savebook/app/api/notes/route.js b/savebook/app/api/notes/route.js index aa29807..3e19f47 100644 --- a/savebook/app/api/notes/route.js +++ b/savebook/app/api/notes/route.js @@ -35,6 +35,7 @@ export async function GET(request) { const notes = await Notes.find({ user: new mongoose.Types.ObjectId(decoded.userId), + isDeleted: false, // Exclude soft-deleted notes from active notes }).lean(); // 👇 Attach isBookmarked to each note diff --git a/savebook/app/api/notes/trash/route.js b/savebook/app/api/notes/trash/route.js new file mode 100644 index 0000000..6d2a528 --- /dev/null +++ b/savebook/app/api/notes/trash/route.js @@ -0,0 +1,196 @@ +import { NextResponse } from "next/server"; +import dbConnect from "@/lib/db/mongodb"; +import Notes from "@/lib/models/Notes"; +import mongoose from "mongoose"; +import { verifyJwtToken } from "@/lib/utils/jwtAuth"; + +/** + * GET /api/notes/trash + * Retrieve all soft-deleted notes for the current user + * Notes are recoverable for 30 days after deletion + */ +export async function GET(request) { + await dbConnect(); + + try { + const token = request.cookies.get("authToken"); + + if (!token) { + return NextResponse.json( + { error: "Unauthorized: No token provided" }, + { status: 401 } + ); + } + + const decoded = await verifyJwtToken(token.value); + + if (!decoded || !decoded.success) { + return NextResponse.json( + { error: "Unauthorized: Invalid token" }, + { status: 401 } + ); + } + + // Get deleted notes within 30-day recovery window + const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); + + const trash = await Notes.find({ + user: new mongoose.Types.ObjectId(decoded.userId), + isDeleted: true, + deletedAt: { $gte: thirtyDaysAgo }, // Only show notes deleted within last 30 days + }) + .sort({ deletedAt: -1 }) + .lean(); + + return NextResponse.json({ + success: true, + data: trash, + message: `${trash.length} deleted notes available for recovery (expires in 30 days)`, + }); + } catch (error) { + console.error(error); + return NextResponse.json( + { error: "Server error" }, + { status: 500 } + ); + } +} + +/** + * PUT /api/notes/trash/restore/:id + * Restore a soft-deleted note from trash + * This undeletes the note and makes it visible again + */ +export async function PUT(request) { + await dbConnect(); + + try { + const token = request.cookies.get("authToken"); + + if (!token) { + return NextResponse.json( + { error: "Unauthorized: No token provided" }, + { status: 401 } + ); + } + + const decoded = await verifyJwtToken(token.value); + + if (!decoded || !decoded.success) { + return NextResponse.json( + { error: "Unauthorized: Invalid token" }, + { status: 401 } + ); + } + + // Get note ID from URL + const url = new URL(request.url); + const noteId = url.pathname.split("/").pop(); + + if (!mongoose.Types.ObjectId.isValid(noteId)) { + return NextResponse.json( + { error: "Invalid note ID" }, + { status: 400 } + ); + } + + // Find soft-deleted note + const note = await Notes.findOne({ + _id: noteId, + user: decoded.userId, + isDeleted: true, + }); + + if (!note) { + return NextResponse.json( + { error: "Note not found in trash or recovery period expired" }, + { status: 404 } + ); + } + + // Restore note + note.isDeleted = false; + note.deletedAt = null; + await note.save(); + + return NextResponse.json({ + success: true, + data: note, + message: "Note restored successfully", + }); + } catch (error) { + console.error(error); + return NextResponse.json( + { error: "Server error" }, + { status: 500 } + ); + } +} + +/** + * DELETE /api/notes/trash/:id + * Permanently delete a soft-deleted note (skip recovery period) + * Use with caution: This is permanent and cannot be undone + */ +export async function DELETE(request) { + await dbConnect(); + + try { + const token = request.cookies.get("authToken"); + + if (!token) { + return NextResponse.json( + { error: "Unauthorized: No token provided" }, + { status: 401 } + ); + } + + const decoded = await verifyJwtToken(token.value); + + if (!decoded || !decoded.success) { + return NextResponse.json( + { error: "Unauthorized: Invalid token" }, + { status: 401 } + ); + } + + // Get note ID from URL + const url = new URL(request.url); + const noteId = url.pathname.split("/").pop(); + + if (!mongoose.Types.ObjectId.isValid(noteId)) { + return NextResponse.json( + { error: "Invalid note ID" }, + { status: 400 } + ); + } + + // Find and permanently delete note + const note = await Notes.findOne({ + _id: noteId, + user: decoded.userId, + isDeleted: true, + }); + + if (!note) { + return NextResponse.json( + { error: "Note not found in trash" }, + { status: 404 } + ); + } + + // Permanently delete the note + await Notes.findByIdAndDelete(noteId); + + return NextResponse.json({ + success: true, + message: "Note permanently deleted", + }); + } catch (error) { + console.error(error); + return NextResponse.json( + { error: "Server error" }, + { status: 500 } + ); + } +} diff --git a/savebook/lib/models/Notes.js b/savebook/lib/models/Notes.js index 08223fd..5c99ae9 100644 --- a/savebook/lib/models/Notes.js +++ b/savebook/lib/models/Notes.js @@ -60,7 +60,29 @@ const NotesSchema = new Schema({ type: String, default: null, }, + + // Soft delete implementation for recovery window + isDeleted: { + type: Boolean, + default: false, + index: true, // Index for efficient queries excluding soft-deleted notes + }, + + deletedAt: { + type: Date, + default: null, + }, }); +// Add TTL index: automatically delete soft-deleted notes after 30 days +// This removes records permanently after 30-day recovery period +NotesSchema.index( + { deletedAt: 1 }, + { + expireAfterSeconds: 2592000, // 30 days in seconds + partialFilterExpression: { isDeleted: true } + } +); + export default mongoose.models.Notes || mongoose.model('Notes', NotesSchema); From 5e9513fed9a2b66279f29104a089405e96ea8064 Mon Sep 17 00:00:00 2001 From: anshul23102 Date: Mon, 8 Jun 2026 17:14:43 +0530 Subject: [PATCH 2/5] feat: Add trash UI component with recovery functionality Implements trash/recovery feature to address maintainer feedback: - Created Trash.js component for displaying deleted notes - Added trash tab to Notes component sidebar navigation - Implemented recovery and permanent deletion UI - Delete notes now soft-deleted instead of hard-deleted - Users can recover notes within 30-day window - Display deletion timestamp and recovery options - Search and filter functionality for trash items Allows users to: - View all deleted notes in dedicated trash interface - Recover accidentally deleted notes - Permanently delete notes from trash - Search trash items by title/description - Filter by category tags - See recovery window countdown --- savebook/components/notes/Notes.js | 15 ++ savebook/components/notes/Trash.js | 251 +++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 savebook/components/notes/Trash.js diff --git a/savebook/components/notes/Notes.js b/savebook/components/notes/Notes.js index 5593efe..1b3bf11 100644 --- a/savebook/components/notes/Notes.js +++ b/savebook/components/notes/Notes.js @@ -8,6 +8,7 @@ import Addnote from './AddNote'; import NoteItem from './NoteItem'; import { useAuth } from '@/context/auth/authContext'; import RichTextEditor from './RichTextEditor'; +import Trash from './Trash'; // Separate navigation handler component to use router with Suspense const NavigationHandler = ({ isAuthenticated, loading }) => { @@ -560,6 +561,9 @@ export default function Notes() {

+ {activeTab === 'trash' ? ( + + ) : (
{/* Search and Filter Section */} @@ -705,6 +709,16 @@ export default function Notes() { Whiteboards {totalWhiteboards} +
@@ -721,6 +735,7 @@ export default function Notes() {
+ )} diff --git a/savebook/components/notes/Trash.js b/savebook/components/notes/Trash.js new file mode 100644 index 0000000..b3d2d89 --- /dev/null +++ b/savebook/components/notes/Trash.js @@ -0,0 +1,251 @@ +"use client" +import React, { useEffect, useState } from 'react' +import toast from 'react-hot-toast'; +import NoteItem from './NoteItem'; +import { useAuth } from '@/context/auth/authContext'; + +export default function Trash() { + const { isAuthenticated, loading } = useAuth(); + const [trashedNotes, setTrashedNotes] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [searchTerm, setSearchTerm] = useState(''); + const [selectedTag, setSelectedTag] = useState('all'); + + // Fetch trashed notes + const getTrashedNotes = async () => { + try { + setIsLoading(true); + const response = await fetch('/api/notes/trash'); + + if (!response.ok) { + throw new Error('Failed to fetch trash'); + } + + const data = await response.json(); + setTrashedNotes(data.data || []); + + if (data.data && data.data.length > 0) { + toast.success(`Loaded ${data.data.length} deleted notes`); + } + } catch (error) { + console.error('Error fetching trash:', error); + toast.error('Failed to load trash'); + setTrashedNotes([]); + } finally { + setIsLoading(false); + } + }; + + // Load trash on mount + useEffect(() => { + if (isAuthenticated && !loading) { + getTrashedNotes(); + } + }, [isAuthenticated, loading]); + + // Restore note from trash + const restoreNote = async (noteId) => { + try { + const response = await fetch(`/api/notes/trash/restore/${noteId}`, { + method: 'PUT' + }); + + if (!response.ok) { + throw new Error('Failed to restore note'); + } + + const data = await response.json(); + + // Remove from trash display + setTrashedNotes(trashedNotes.filter(note => note._id !== noteId)); + toast.success('Note restored successfully'); + } catch (error) { + console.error('Error restoring note:', error); + toast.error('Failed to restore note'); + } + }; + + // Permanently delete note from trash + const permanentlyDeleteNote = async (noteId) => { + try { + // Confirm deletion + const userConfirmed = window.confirm( + 'This will permanently delete the note. This action cannot be undone. Are you sure?' + ); + + if (!userConfirmed) { + return; + } + + const response = await fetch(`/api/notes/trash/${noteId}`, { + method: 'DELETE' + }); + + if (!response.ok) { + throw new Error('Failed to permanently delete note'); + } + + // Remove from trash display + setTrashedNotes(trashedNotes.filter(note => note._id !== noteId)); + toast.success('Note permanently deleted'); + } catch (error) { + console.error('Error permanently deleting note:', error); + toast.error('Failed to permanently delete note'); + } + }; + + const tagOptions = [ + { id: 1, value: "General", color: "bg-blue-500" }, + { id: 2, value: "Basic", color: "bg-gray-500" }, + { id: 3, value: "Finance", color: "bg-green-500" }, + { id: 4, value: "Grocery", color: "bg-orange-500" }, + { id: 5, value: "Office", color: "bg-purple-500" }, + { id: 6, value: "Personal", color: "bg-pink-500" }, + { id: 7, value: "Work", color: "bg-indigo-500" }, + { id: 8, value: "Ideas", color: "bg-teal-500" } + ]; + + // Filter trashed notes by search and tag + const filteredNotes = trashedNotes.filter(note => { + const matchesSearch = note.title?.toLowerCase().includes(searchTerm.toLowerCase()) || + note.description?.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesTag = selectedTag === 'all' || note.tag === selectedTag; + return matchesSearch && matchesTag; + }); + + if (!isAuthenticated && !loading) { + return null; + } + + return ( +
+
+ {/* Header */} +
+

+ 🗑️ Trash +

+

+ Recover deleted notes within 30 days. After 30 days, notes are permanently deleted. +

+
+ + {/* Search and Filter */} +
+ setSearchTerm(e.target.value)} + className="flex-1 px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-white placeholder-gray-500 dark:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500" + /> + + +
+ + {/* Stats */} +
+
+

Total in Trash

+

+ {trashedNotes.length} +

+
+
+

Matching Search

+

+ {filteredNotes.length} +

+
+
+

Recovery Window

+

30 days

+
+
+ + {/* Trash Items */} +
+ {isLoading ? ( +
+
+
+ ) : filteredNotes.length === 0 ? ( +
+

+ {trashedNotes.length === 0 ? "No deleted notes" : "No notes match your search"} +

+

+ {trashedNotes.length === 0 ? "Your trash is empty. Deleted notes will appear here." : "Try adjusting your search filters."} +

+
+ ) : ( + filteredNotes.map(note => ( +
+
+ {/* Note Header */} +
+
+

+ {note.title} +

+

+ {note.description} +

+
+ {note.tag && ( + t.value === note.tag)?.color || 'bg-gray-500' + }`}> + {note.tag} + + )} +
+ + {/* Deletion Info */} +
+

+ Deleted {new Date(note.deletedAt).toLocaleDateString()} at {new Date(note.deletedAt).toLocaleTimeString()} +

+
+ + {/* Action Buttons */} +
+ + +
+
+
+ )) + )} +
+
+
+ ); +} From ec6521e77ea1271b68eee1fbb4086dbc844def16 Mon Sep 17 00:00:00 2001 From: Anshul Jain Date: Sun, 14 Jun 2026 23:18:12 +0530 Subject: [PATCH 3/5] fix: add navigation buttons to return from trash tab to notes and whiteboard tabs - Add back to Notes button that navigates to notes tab - Add back to Whiteboard button that navigates to whiteboard tab - Styled with consistent dark mode support - Improves UX by providing clear navigation path from trash view --- savebook/components/notes/Trash.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/savebook/components/notes/Trash.js b/savebook/components/notes/Trash.js index b3d2d89..1f70e4b 100644 --- a/savebook/components/notes/Trash.js +++ b/savebook/components/notes/Trash.js @@ -130,6 +130,22 @@ export default function Trash() {

+ {/* Navigation Tabs */} +
+ + +
+ {/* Search and Filter */}
Date: Mon, 15 Jun 2026 16:23:06 +0530 Subject: [PATCH 4/5] Fix trash navigation, API routing, and note decryption - Changed navigation button hrefs from /#notes and /#whiteboard to /notes and /notes/whiteboard - Created dynamic API routes for trash operations per Next.js App Router pattern - Moved route handlers from trash/route.js to proper dynamic route files - Export decryptNote from NoteState context for use in Trash component - Decrypt fetched notes in Trash component so users can identify content before deletion Fixes: - Navigation buttons now link to correct routes - Restore and delete operations work with proper routing - Trash notes display decrypted content instead of encrypted strings Co-Authored-By: Claude Haiku 4.5 --- savebook/app/api/notes/trash/[id]/route.js | 63 ++++++++ .../app/api/notes/trash/restore/[id]/route.js | 66 +++++++++ savebook/app/api/notes/trash/route.js | 138 ------------------ savebook/components/notes/Trash.js | 24 ++- savebook/context/NoteState.js | 2 +- 5 files changed, 147 insertions(+), 146 deletions(-) create mode 100644 savebook/app/api/notes/trash/[id]/route.js create mode 100644 savebook/app/api/notes/trash/restore/[id]/route.js diff --git a/savebook/app/api/notes/trash/[id]/route.js b/savebook/app/api/notes/trash/[id]/route.js new file mode 100644 index 0000000..e4c1638 --- /dev/null +++ b/savebook/app/api/notes/trash/[id]/route.js @@ -0,0 +1,63 @@ +import { NextResponse } from "next/server"; +import dbConnect from "@/lib/db/mongodb"; +import Notes from "@/lib/models/Notes"; +import mongoose from "mongoose"; +import { verifyJwtToken } from "@/lib/utils/jwtAuth"; + +export async function DELETE(request, { params }) { + await dbConnect(); + + try { + const { id: noteId } = await params; + const token = request.cookies.get("authToken"); + + if (!token) { + return NextResponse.json( + { error: "Unauthorized: No token provided" }, + { status: 401 } + ); + } + + const decoded = await verifyJwtToken(token.value); + + if (!decoded || !decoded.success) { + return NextResponse.json( + { error: "Unauthorized: Invalid token" }, + { status: 401 } + ); + } + + if (!mongoose.Types.ObjectId.isValid(noteId)) { + return NextResponse.json( + { error: "Invalid note ID" }, + { status: 400 } + ); + } + + const note = await Notes.findOne({ + _id: noteId, + user: decoded.userId, + isDeleted: true, + }); + + if (!note) { + return NextResponse.json( + { error: "Note not found in trash" }, + { status: 404 } + ); + } + + await Notes.findByIdAndDelete(noteId); + + return NextResponse.json({ + success: true, + message: "Note permanently deleted", + }); + } catch (error) { + console.error(error); + return NextResponse.json( + { error: "Server error" }, + { status: 500 } + ); + } +} diff --git a/savebook/app/api/notes/trash/restore/[id]/route.js b/savebook/app/api/notes/trash/restore/[id]/route.js new file mode 100644 index 0000000..22635d8 --- /dev/null +++ b/savebook/app/api/notes/trash/restore/[id]/route.js @@ -0,0 +1,66 @@ +import { NextResponse } from "next/server"; +import dbConnect from "@/lib/db/mongodb"; +import Notes from "@/lib/models/Notes"; +import mongoose from "mongoose"; +import { verifyJwtToken } from "@/lib/utils/jwtAuth"; + +export async function PUT(request, { params }) { + await dbConnect(); + + try { + const { id: noteId } = await params; + const token = request.cookies.get("authToken"); + + if (!token) { + return NextResponse.json( + { error: "Unauthorized: No token provided" }, + { status: 401 } + ); + } + + const decoded = await verifyJwtToken(token.value); + + if (!decoded || !decoded.success) { + return NextResponse.json( + { error: "Unauthorized: Invalid token" }, + { status: 401 } + ); + } + + if (!mongoose.Types.ObjectId.isValid(noteId)) { + return NextResponse.json( + { error: "Invalid note ID" }, + { status: 400 } + ); + } + + const note = await Notes.findOne({ + _id: noteId, + user: decoded.userId, + isDeleted: true, + }); + + if (!note) { + return NextResponse.json( + { error: "Note not found in trash or recovery period expired" }, + { status: 404 } + ); + } + + note.isDeleted = false; + note.deletedAt = null; + await note.save(); + + return NextResponse.json({ + success: true, + data: note, + message: "Note restored successfully", + }); + } catch (error) { + console.error(error); + return NextResponse.json( + { error: "Server error" }, + { status: 500 } + ); + } +} diff --git a/savebook/app/api/notes/trash/route.js b/savebook/app/api/notes/trash/route.js index 6d2a528..d5f98d6 100644 --- a/savebook/app/api/notes/trash/route.js +++ b/savebook/app/api/notes/trash/route.js @@ -56,141 +56,3 @@ export async function GET(request) { } } -/** - * PUT /api/notes/trash/restore/:id - * Restore a soft-deleted note from trash - * This undeletes the note and makes it visible again - */ -export async function PUT(request) { - await dbConnect(); - - try { - const token = request.cookies.get("authToken"); - - if (!token) { - return NextResponse.json( - { error: "Unauthorized: No token provided" }, - { status: 401 } - ); - } - - const decoded = await verifyJwtToken(token.value); - - if (!decoded || !decoded.success) { - return NextResponse.json( - { error: "Unauthorized: Invalid token" }, - { status: 401 } - ); - } - - // Get note ID from URL - const url = new URL(request.url); - const noteId = url.pathname.split("/").pop(); - - if (!mongoose.Types.ObjectId.isValid(noteId)) { - return NextResponse.json( - { error: "Invalid note ID" }, - { status: 400 } - ); - } - - // Find soft-deleted note - const note = await Notes.findOne({ - _id: noteId, - user: decoded.userId, - isDeleted: true, - }); - - if (!note) { - return NextResponse.json( - { error: "Note not found in trash or recovery period expired" }, - { status: 404 } - ); - } - - // Restore note - note.isDeleted = false; - note.deletedAt = null; - await note.save(); - - return NextResponse.json({ - success: true, - data: note, - message: "Note restored successfully", - }); - } catch (error) { - console.error(error); - return NextResponse.json( - { error: "Server error" }, - { status: 500 } - ); - } -} - -/** - * DELETE /api/notes/trash/:id - * Permanently delete a soft-deleted note (skip recovery period) - * Use with caution: This is permanent and cannot be undone - */ -export async function DELETE(request) { - await dbConnect(); - - try { - const token = request.cookies.get("authToken"); - - if (!token) { - return NextResponse.json( - { error: "Unauthorized: No token provided" }, - { status: 401 } - ); - } - - const decoded = await verifyJwtToken(token.value); - - if (!decoded || !decoded.success) { - return NextResponse.json( - { error: "Unauthorized: Invalid token" }, - { status: 401 } - ); - } - - // Get note ID from URL - const url = new URL(request.url); - const noteId = url.pathname.split("/").pop(); - - if (!mongoose.Types.ObjectId.isValid(noteId)) { - return NextResponse.json( - { error: "Invalid note ID" }, - { status: 400 } - ); - } - - // Find and permanently delete note - const note = await Notes.findOne({ - _id: noteId, - user: decoded.userId, - isDeleted: true, - }); - - if (!note) { - return NextResponse.json( - { error: "Note not found in trash" }, - { status: 404 } - ); - } - - // Permanently delete the note - await Notes.findByIdAndDelete(noteId); - - return NextResponse.json({ - success: true, - message: "Note permanently deleted", - }); - } catch (error) { - console.error(error); - return NextResponse.json( - { error: "Server error" }, - { status: 500 } - ); - } -} diff --git a/savebook/components/notes/Trash.js b/savebook/components/notes/Trash.js index 1f70e4b..81d3445 100644 --- a/savebook/components/notes/Trash.js +++ b/savebook/components/notes/Trash.js @@ -1,11 +1,14 @@ "use client" -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState, useContext } from 'react' import toast from 'react-hot-toast'; import NoteItem from './NoteItem'; import { useAuth } from '@/context/auth/authContext'; +import noteContext from '@/context/noteContext'; export default function Trash() { const { isAuthenticated, loading } = useAuth(); + const context = useContext(noteContext); + const { decryptNote } = context || {}; const [trashedNotes, setTrashedNotes] = useState([]); const [isLoading, setIsLoading] = useState(false); const [searchTerm, setSearchTerm] = useState(''); @@ -22,10 +25,17 @@ export default function Trash() { } const data = await response.json(); - setTrashedNotes(data.data || []); + const notesData = data.data || []; - if (data.data && data.data.length > 0) { - toast.success(`Loaded ${data.data.length} deleted notes`); + // Decrypt notes if decryptNote function is available + const decryptedNotes = decryptNote + ? await Promise.all(notesData.map(note => decryptNote(note))) + : notesData; + + setTrashedNotes(decryptedNotes); + + if (decryptedNotes.length > 0) { + toast.success(`Loaded ${decryptedNotes.length} deleted notes`); } } catch (error) { console.error('Error fetching trash:', error); @@ -41,7 +51,7 @@ export default function Trash() { if (isAuthenticated && !loading) { getTrashedNotes(); } - }, [isAuthenticated, loading]); + }, [isAuthenticated, loading, decryptNote]); // Restore note from trash const restoreNote = async (noteId) => { @@ -133,13 +143,13 @@ export default function Trash() { {/* Navigation Tabs */}