import { create } from 'zustand'; import { createNote as persistNewNote, getNote, listNotes, updateNote as persistNote, type MobileNote } from '../api/notes'; import { enqueueNoteCreate, enqueueNoteUpdate } from '../lib/offline-queue'; function createOfflineNoteId(): string { const randomUUID = globalThis.crypto && typeof globalThis.crypto.randomUUID === 'function' ? globalThis.crypto.randomUUID.bind(globalThis.crypto) : null; if (randomUUID) { return randomUUID(); } return `offline-${Date.now()}`; } export type NotesState = { notes: MobileNote[]; selectedNote: MobileNote | null; isLoading: boolean; hydrate: () => Promise; openNote: (id: string) => Promise; saveDraft: (workspaceId: string | null, title: string, body: string) => Promise; updateNote: (id: string, title: string, body: string) => Promise; }; export const useNotesStore = create((set, get) => ({ notes: [], selectedNote: null, isLoading: false, async hydrate() { set({ isLoading: true }); const notes = await listNotes(); set({ notes, isLoading: false }); }, async openNote(id: string) { set({ isLoading: true }); const current = get().notes.find((note: MobileNote) => note.id === id); if (!current) { set({ selectedNote: null, isLoading: false }); return; } const note = await getNote(id, current.workspaceId); set({ selectedNote: note, isLoading: false }); }, async saveDraft(workspaceId: string | null, title: string, body: string) { if (!workspaceId) { return false; } set({ isLoading: true }); try { const created = await persistNewNote(workspaceId, title, body); set({ notes: [created, ...get().notes], selectedNote: created, isLoading: false, }); } catch { const queuedNote: MobileNote = { id: createOfflineNoteId(), workspaceId, workspaceName: workspaceId, title: title.trim() || 'Untitled draft', body, status: 'draft', updatedAt: new Date().toISOString(), }; enqueueNoteCreate({ id: queuedNote.id, workspaceId, title: queuedNote.title, body, }); set({ notes: [queuedNote, ...get().notes], selectedNote: queuedNote, isLoading: false, }); } return true; }, async updateNote(id: string, title: string, body: string) { const nextTitle = title.trim() || 'Untitled draft'; const current = get().notes.find((note: MobileNote) => note.id === id); if (!current) { return; } if (current.workspaceId === 'drafts') { const updated: MobileNote = { ...current, title: nextTitle, body, updatedAt: new Date().toISOString(), }; set({ notes: get().notes.map((note: MobileNote) => (note.id === id ? updated : note)), selectedNote: updated, }); return; } set({ isLoading: true }); try { const updated = await persistNote(id, current.workspaceId, nextTitle, body); set({ notes: get().notes.map((note: MobileNote) => (note.id === id ? updated : note)), selectedNote: updated, isLoading: false, }); } catch { const updated: MobileNote = { ...current, title: nextTitle, body, updatedAt: new Date().toISOString(), }; enqueueNoteUpdate({ id, workspaceId: current.workspaceId, title: nextTitle, body, }); set({ notes: get().notes.map((note: MobileNote) => (note.id === id ? updated : note)), selectedNote: updated, isLoading: false, }); } }, }));