diff --git a/app/pages/page.tsx b/app/pages/page.tsx index b2dcac59..c3d65aac 100644 --- a/app/pages/page.tsx +++ b/app/pages/page.tsx @@ -71,6 +71,12 @@ export default function CarnetPage() { // Cache for Nextcloud folders const foldersCache = useRef<{ folders: string[]; timestamp: number } | null>(null); + + // Cache for notes list (Panel 2) + const notesCache = useRef>({}); + + // Cache for note content (Panel 3) + const noteContentCache = useRef>({}); // Clear folder cache on component mount to ensure fresh data useEffect(() => { @@ -288,13 +294,63 @@ export default function CarnetPage() { const folderLowercase = selectedFolder.toLowerCase(); console.log(`Fetching notes from folder: ${folderLowercase}`); - // Use direct storage API instead of adapter + // Check in-memory cache first + const cacheKey = `${session?.user?.id}-${folderLowercase}`; + const cachedNotes = notesCache.current[cacheKey]; + const CACHE_EXPIRATION = 5 * 60 * 1000; // 5 minutes in milliseconds + + if (cachedNotes && (Date.now() - cachedNotes.timestamp) < CACHE_EXPIRATION) { + console.log(`Using cached notes for ${folderLowercase} folder`); + setNotes(cachedNotes.notes); + setIsLoadingNotes(false); + return; + } + + // Check localStorage cache if in-memory cache is not available + try { + const localStorageKey = `notes-cache-${cacheKey}`; + const storedCache = localStorage.getItem(localStorageKey); + + if (storedCache) { + const { notes, timestamp } = JSON.parse(storedCache); + + if ((Date.now() - timestamp) < CACHE_EXPIRATION) { + console.log(`Using localStorage cached notes for ${folderLowercase} folder`); + setNotes(notes); + + // Update in-memory cache + notesCache.current[cacheKey] = { notes, timestamp }; + + setIsLoadingNotes(false); + return; + } + } + } catch (error) { + console.error('Error accessing localStorage notes cache:', error); + } + + // Use direct storage API instead of adapter if cache is not available or expired const response = await fetch(`/api/storage/files?folder=${folderLowercase}`); if (response.ok) { const data = await response.json(); console.log(`Fetched ${data.length} notes from ${folderLowercase} folder`); + + // Update state setNotes(data); + + // Update both caches + const newTimestamp = Date.now(); + notesCache.current[cacheKey] = { notes: data, timestamp: newTimestamp }; + + try { + localStorage.setItem(`notes-cache-${cacheKey}`, JSON.stringify({ + notes: data, + timestamp: newTimestamp + })); + } catch (error) { + console.error('Error saving notes to localStorage:', error); + } } else { console.error('Error fetching notes:', await response.text()); setNotes([]); @@ -335,6 +391,39 @@ export default function CarnetPage() { }); if (response.ok) { + // Invalidate the cache for this folder to ensure fresh data on next fetch + const cacheKey = `${session?.user?.id}-${selectedFolder.toLowerCase()}`; + + // Remove from in-memory cache + if (notesCache.current[cacheKey]) { + delete notesCache.current[cacheKey]; + } + + // Remove from localStorage cache + try { + localStorage.removeItem(`notes-cache-${cacheKey}`); + } catch (error) { + console.error('Error removing notes from localStorage:', error); + } + + // Update the content cache for this note + if (payload.id) { + noteContentCache.current[payload.id] = { + content: payload.content, + timestamp: Date.now() + }; + + // Update localStorage cache + try { + localStorage.setItem(`note-content-${payload.id}`, JSON.stringify({ + content: payload.content, + timestamp: Date.now() + })); + } catch (error) { + console.error('Error saving note content to localStorage:', error); + } + } + // Refresh the list of notes fetchNotes(); } else { diff --git a/components/carnet/editor.tsx b/components/carnet/editor.tsx index 113c16c9..e3df9e9a 100644 --- a/components/carnet/editor.tsx +++ b/components/carnet/editor.tsx @@ -31,6 +31,10 @@ export const Editor: React.FC = ({ note, onSave, currentFolder = 'N const saveTimeout = useRef(); const router = useRouter(); const { data: session, status } = useSession(); + + // Content cache for notes + const contentCache = useRef>({}); + const CACHE_EXPIRATION = 15 * 60 * 1000; // 15 minutes in milliseconds useEffect(() => { // Redirect to login if not authenticated @@ -44,8 +48,42 @@ export const Editor: React.FC = ({ note, onSave, currentFolder = 'N if (note?.id) { setIsLoading(true); setError(null); + + // First check in-memory cache + const cachedContent = contentCache.current[note.id]; + if (cachedContent && (Date.now() - cachedContent.timestamp) < CACHE_EXPIRATION) { + console.log(`Using cached content for note ${note.title}`); + setContent(cachedContent.content); + setIsLoading(false); + return; + } + + // Then check localStorage cache try { - const response = await fetch(`/api/nextcloud/files/content?id=${note.id}`); + const localStorageKey = `note-content-${note.id}`; + const storedCache = localStorage.getItem(localStorageKey); + + if (storedCache) { + const { content, timestamp } = JSON.parse(storedCache); + + if ((Date.now() - timestamp) < CACHE_EXPIRATION) { + console.log(`Using localStorage cached content for note ${note.title}`); + setContent(content); + + // Update in-memory cache + contentCache.current[note.id] = { content, timestamp }; + + setIsLoading(false); + return; + } + } + } catch (error) { + console.error('Error accessing localStorage content cache:', error); + } + + // If cache miss, fetch from API + try { + const response = await fetch(`/api/storage/files/content?path=${encodeURIComponent(note.id)}`); if (response.status === 401) { console.error('Authentication error, redirecting to login'); router.push('/signin'); @@ -57,6 +95,19 @@ export const Editor: React.FC = ({ note, onSave, currentFolder = 'N } const data = await response.json(); setContent(data.content); + + // Update both caches + const newTimestamp = Date.now(); + contentCache.current[note.id] = { content: data.content, timestamp: newTimestamp }; + + try { + localStorage.setItem(`note-content-${note.id}`, JSON.stringify({ + content: data.content, + timestamp: newTimestamp + })); + } catch (error) { + console.error('Error saving content to localStorage:', error); + } } catch (error) { console.error('Error fetching note content:', error); setError('Failed to load note content. Please try again later.'); @@ -77,7 +128,7 @@ export const Editor: React.FC = ({ note, onSave, currentFolder = 'N setTitle(''); setContent(''); } - }, [note, router]); + }, [note, router, CACHE_EXPIRATION]); const handleTitleChange = (e: React.ChangeEvent) => { setTitle(e.target.value); @@ -109,11 +160,14 @@ export const Editor: React.FC = ({ note, onSave, currentFolder = 'N setIsSaving(true); setError(null); try { - const endpoint = note?.id ? '/api/nextcloud/files' : '/api/nextcloud/files'; + // Construct the full note ID if it doesn't exist yet + const noteId = note?.id || `user-${session.user.id}/${currentFolder.toLowerCase()}/${title}${title.endsWith('.md') ? '' : '.md'}`; + + const endpoint = '/api/storage/files'; const method = note?.id ? 'PUT' : 'POST'; console.log('Saving note:', { - id: note?.id, + id: noteId, title, folder: currentFolder, contentLength: content.length @@ -125,10 +179,10 @@ export const Editor: React.FC = ({ note, onSave, currentFolder = 'N 'Content-Type': 'application/json', }, body: JSON.stringify({ - id: note?.id, + id: noteId, title, content, - folder: currentFolder + folder: currentFolder.toLowerCase() }), }); @@ -151,6 +205,26 @@ export const Editor: React.FC = ({ note, onSave, currentFolder = 'N const savedNote = await response.json(); console.log('Note saved successfully:', savedNote); + + // Update content cache after successful save + const newTimestamp = Date.now(); + + // Update in-memory cache + contentCache.current[noteId] = { + content, + timestamp: newTimestamp + }; + + // Update localStorage cache + try { + localStorage.setItem(`note-content-${noteId}`, JSON.stringify({ + content, + timestamp: newTimestamp + })); + } catch (error) { + console.error('Error updating content cache in localStorage:', error); + } + setError(null); onSave?.({ ...savedNote,