refactor(store): nullable open book with open/close for library nav

This commit is contained in:
pj committed 2026-06-20 15:09:13 -04:00
1 parent 4896e10aba
commit a6404847d1
1 file changed
+36 -47
+36 -47
View File
@@ -1,61 +1,50 @@
import { create } from "zustand"; import { create } from "zustand";
import type { JSONContent } from "@tiptap/core"; import type { JSONContent } from "@tiptap/core";
import { type Book, type BookMetadata, type BookSettings, createChapter, starterBook } from "../model/book"; import { type Book, type BookMetadata, type BookSettings, createChapter } from "../model/book";
interface BookState { interface BookState {
book: Book; book: Book | null;
activeChapterId: string; activeChapterId: string;
path: string | null;
dirty: boolean; dirty: boolean;
openBook: (book: Book) => void;
closeBook: () => void;
setActiveChapter: (id: string) => void; setActiveChapter: (id: string) => void;
setChapterContent: (id: string, content: JSONContent) => void; setChapterContent: (id: string, content: JSONContent) => void;
setChapterTitle: (id: string, title: string) => void; setChapterTitle: (id: string, title: string) => void;
addChapter: () => void; addChapter: () => void;
setMetadata: (patch: Partial<BookMetadata>) => void; setMetadata: (patch: Partial<BookMetadata>) => void;
setSettings: (patch: Partial<BookSettings>) => void; setSettings: (patch: Partial<BookSettings>) => void;
replaceBook: (book: Book, path: string) => void; markSaved: () => void;
markSaved: (path: string) => void;
} }
export const useBook = create<BookState>((set) => { export const useBook = create<BookState>((set) => ({
const book = starterBook(); book: null,
return { activeChapterId: "",
book, dirty: false,
activeChapterId: book.chapters[0].id, openBook: (book) => set({ book, activeChapterId: book.chapters[0]?.id ?? "", dirty: false }),
path: null, closeBook: () => set({ book: null, activeChapterId: "", dirty: false }),
dirty: false, setActiveChapter: (id) => set({ activeChapterId: id }),
setActiveChapter: (id) => set({ activeChapterId: id }), setChapterContent: (id, content) =>
setChapterContent: (id, content) => set((s) =>
set((s) => ({ s.book
dirty: true, ? { dirty: true, book: { ...s.book, chapters: s.book.chapters.map((c) => (c.id === id ? { ...c, content } : c)) } }
book: { : {}
...s.book, ),
chapters: s.book.chapters.map((c) => (c.id === id ? { ...c, content } : c)), setChapterTitle: (id, title) =>
}, set((s) =>
})), s.book
setChapterTitle: (id, title) => ? { dirty: true, book: { ...s.book, chapters: s.book.chapters.map((c) => (c.id === id ? { ...c, title } : c)) } }
set((s) => ({ : {}
dirty: true, ),
book: { addChapter: () =>
...s.book, set((s) => {
chapters: s.book.chapters.map((c) => (c.id === id ? { ...c, title } : c)), if (!s.book) return {};
}, const chapter = createChapter();
})), return { activeChapterId: chapter.id, dirty: true, book: { ...s.book, chapters: [...s.book.chapters, chapter] } };
addChapter: () => }),
set((s) => { setMetadata: (patch) =>
const chapter = createChapter(); set((s) => (s.book ? { dirty: true, book: { ...s.book, metadata: { ...s.book.metadata, ...patch } } } : {})),
return { setSettings: (patch) =>
activeChapterId: chapter.id, set((s) => (s.book ? { dirty: true, book: { ...s.book, settings: { ...s.book.settings, ...patch } } } : {})),
dirty: true, markSaved: () => set({ dirty: false }),
book: { ...s.book, chapters: [...s.book.chapters, chapter] }, }));
};
}),
setMetadata: (patch) =>
set((s) => ({ dirty: true, book: { ...s.book, metadata: { ...s.book.metadata, ...patch } } })),
setSettings: (patch) =>
set((s) => ({ dirty: true, book: { ...s.book, settings: { ...s.book.settings, ...patch } } })),
replaceBook: (book, path) =>
set({ book, activeChapterId: book.chapters[0]?.id ?? "", path, dirty: false }),
markSaved: (path) => set({ path, dirty: false }),
};
});