mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 19:17:03 +00:00
fix(ui): handle library-open errors
Opening a book card swallowed failures: a corrupt or unreadable file failed silently and normalizeBook could throw on a missing chapters array. Surface load errors as a toast, throw a clear message on unparseable JSON, and make normalizeBook tolerant of missing chapters, metadata and settings. Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk
This commit is contained in:
1 parent
9eabef9041
commit
4e5ea6d855
3 files changed
+21
-5
No files matched your search
@@ -79,7 +79,11 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{books.map((b) => (
|
{books.map((b) => (
|
||||||
<div key={b.id} className="card card-book" onClick={() => loadBook(b.id).then(onOpen)}>
|
<div
|
||||||
|
key={b.id}
|
||||||
|
className="card card-book"
|
||||||
|
onClick={() => loadBook(b.id).then(onOpen).catch((e) => setNotice(`Could not open book: ${e}`))}
|
||||||
|
>
|
||||||
<span className="card-title">{b.title || "Untitled"}</span>
|
<span className="card-title">{b.title || "Untitled"}</span>
|
||||||
{b.author && <span className="card-author">{b.author}</span>}
|
{b.author && <span className="card-author">{b.author}</span>}
|
||||||
<RowMenu label="Book options" className="card-menu" onDelete={() => setPendingDelete(b)} />
|
<RowMenu label="Book options" className="card-menu" onDelete={() => setPendingDelete(b)} />
|
||||||
|
|||||||
+5
-1
@@ -15,7 +15,11 @@ export async function listBooks(): Promise<BookSummary[]> {
|
|||||||
|
|
||||||
export async function loadBook(id: string): Promise<Book> {
|
export async function loadBook(id: string): Promise<Book> {
|
||||||
const contents = await invoke<string>("load_book", { id });
|
const contents = await invoke<string>("load_book", { id });
|
||||||
return JSON.parse(contents) as Book;
|
try {
|
||||||
|
return JSON.parse(contents) as Book;
|
||||||
|
} catch {
|
||||||
|
throw new Error("the book file is corrupt or unreadable");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveBook(book: Book): Promise<void> {
|
export async function saveBook(book: Book): Promise<void> {
|
||||||
|
|||||||
+11
-3
@@ -140,11 +140,19 @@ export function createCover(): Cover {
|
|||||||
return { kind: "default", image: "", bg: COVER_PALETTES[0].bg, ink: COVER_PALETTES[0].ink };
|
return { kind: "default", image: "", bg: COVER_PALETTES[0].bg, ink: COVER_PALETTES[0].ink };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_METADATA: BookMetadata = { title: "Untitled", subtitle: "", author: "", isbn: "", language: "en" };
|
||||||
|
const DEFAULT_SETTINGS: BookSettings = { trim: "6x9", bleed: true };
|
||||||
|
|
||||||
export function normalizeBook(book: Book): Book {
|
export function normalizeBook(book: Book): Book {
|
||||||
|
const chapters = (Array.isArray(book.chapters) ? book.chapters : []).filter(Boolean);
|
||||||
return {
|
return {
|
||||||
...book,
|
...book,
|
||||||
|
metadata: { ...DEFAULT_METADATA, ...book.metadata },
|
||||||
|
settings: { ...DEFAULT_SETTINGS, ...book.settings },
|
||||||
cover: book.cover ? { ...createCover(), ...book.cover } : createCover(),
|
cover: book.cover ? { ...createCover(), ...book.cover } : createCover(),
|
||||||
chapters: book.chapters.map((c) => (c.updatedAt ? c : { ...c, updatedAt: Date.now() })),
|
chapters: (chapters.length ? chapters : [createChapter()]).map((c) =>
|
||||||
|
c.updatedAt ? c : { ...c, updatedAt: Date.now() },
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,9 +160,9 @@ export function createBook(): Book {
|
|||||||
return {
|
return {
|
||||||
schema: "margin/1",
|
schema: "margin/1",
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
metadata: { title: "Untitled", subtitle: "", author: "", isbn: "", language: "en" },
|
metadata: { ...DEFAULT_METADATA },
|
||||||
theme: "quiet-press",
|
theme: "quiet-press",
|
||||||
settings: { trim: "6x9", bleed: true },
|
settings: { ...DEFAULT_SETTINGS },
|
||||||
cover: createCover(),
|
cover: createCover(),
|
||||||
chapters: [createChapter("Chapter One")],
|
chapters: [createChapter("Chapter One")],
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in new issue
Block a user