mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 19:17:03 +00:00
feat(editor): persist active chapter per book
Remember which chapter was open per book and restore it on reopen, falling back to the first chapter when the saved id is no longer valid. Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
This commit is contained in:
1 parent
c0da4c53db
commit
0bc3e36ee6
2 files changed
+33
-1
No files matched your search
@@ -25,3 +25,23 @@ export function savePosition(bookId: string, chapterId: string, position: Chapte
|
|||||||
(all[bookId] ||= {})[chapterId] = position;
|
(all[bookId] ||= {})[chapterId] = position;
|
||||||
localStorage.setItem(KEY, JSON.stringify(all));
|
localStorage.setItem(KEY, JSON.stringify(all));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ACTIVE_KEY = "margin-active-chapter";
|
||||||
|
|
||||||
|
function readActive(): Record<string, string> {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem(ACTIVE_KEY) || "{}") as Record<string, string>;
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadActiveChapter(bookId: string): string | null {
|
||||||
|
return readActive()[bookId] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveActiveChapter(bookId: string, chapterId: string): void {
|
||||||
|
const all = readActive();
|
||||||
|
all[bookId] = chapterId;
|
||||||
|
localStorage.setItem(ACTIVE_KEY, JSON.stringify(all));
|
||||||
|
}
|
||||||
+13
-1
@@ -12,6 +12,7 @@ import {
|
|||||||
createPage,
|
createPage,
|
||||||
normalizeBook,
|
normalizeBook,
|
||||||
} from "../model/book";
|
} from "../model/book";
|
||||||
|
import { loadActiveChapter, saveActiveChapter } from "../editor/positions";
|
||||||
|
|
||||||
export const COVER_ID = "__cover__";
|
export const COVER_ID = "__cover__";
|
||||||
|
|
||||||
@@ -50,7 +51,10 @@ export const useBook = create<BookState>((set) => ({
|
|||||||
setNotice: (message) => set({ notice: message }),
|
setNotice: (message) => set({ notice: message }),
|
||||||
openBook: (book) => {
|
openBook: (book) => {
|
||||||
const normalized = normalizeBook(book);
|
const normalized = normalizeBook(book);
|
||||||
set({ book: normalized, activeChapterId: normalized.chapters[0]?.id ?? "", dirty: false });
|
const saved = loadActiveChapter(normalized.id);
|
||||||
|
const valid = saved === COVER_ID || normalized.chapters.some((c) => c.id === saved);
|
||||||
|
const activeChapterId = valid ? (saved as string) : normalized.chapters[0]?.id ?? "";
|
||||||
|
set({ book: normalized, activeChapterId, dirty: false });
|
||||||
},
|
},
|
||||||
closeBook: () => set({ book: null, activeChapterId: "", dirty: false }),
|
closeBook: () => set({ book: null, activeChapterId: "", dirty: false }),
|
||||||
setActiveChapter: (id) => set({ activeChapterId: id }),
|
setActiveChapter: (id) => set({ activeChapterId: id }),
|
||||||
@@ -132,3 +136,11 @@ export const useBook = create<BookState>((set) => ({
|
|||||||
set((s) => (s.book ? { dirty: true, book: { ...s.book, cover: { ...s.book.cover, ...patch } } } : {})),
|
set((s) => (s.book ? { dirty: true, book: { ...s.book, cover: { ...s.book.cover, ...patch } } } : {})),
|
||||||
markSaved: () => set({ dirty: false }),
|
markSaved: () => set({ dirty: false }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
let lastActive = "";
|
||||||
|
useBook.subscribe((s) => {
|
||||||
|
if (s.book && s.activeChapterId && s.activeChapterId !== lastActive) {
|
||||||
|
lastActive = s.activeChapterId;
|
||||||
|
saveActiveChapter(s.book.id, s.activeChapterId);
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in new issue
Block a user