diff --git a/src/components/Library.tsx b/src/components/Library.tsx index ccdee2a..038c0dc 100644 --- a/src/components/Library.tsx +++ b/src/components/Library.tsx @@ -79,7 +79,11 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) { )} {books.map((b) => ( -
loadBook(b.id).then(onOpen)}> +
loadBook(b.id).then(onOpen).catch((e) => setNotice(`Could not open book: ${e}`))} + > {b.title || "Untitled"} {b.author && {b.author}} setPendingDelete(b)} /> diff --git a/src/library.ts b/src/library.ts index e329473..7f39c92 100644 --- a/src/library.ts +++ b/src/library.ts @@ -15,7 +15,11 @@ export async function listBooks(): Promise { export async function loadBook(id: string): Promise { const contents = await invoke("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 { diff --git a/src/model/book.ts b/src/model/book.ts index 5db51dc..dc8017e 100644 --- a/src/model/book.ts +++ b/src/model/book.ts @@ -140,11 +140,19 @@ export function createCover(): Cover { 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 { + const chapters = (Array.isArray(book.chapters) ? book.chapters : []).filter(Boolean); return { ...book, + metadata: { ...DEFAULT_METADATA, ...book.metadata }, + settings: { ...DEFAULT_SETTINGS, ...book.settings }, 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 { schema: "margin/1", id: crypto.randomUUID(), - metadata: { title: "Untitled", subtitle: "", author: "", isbn: "", language: "en" }, + metadata: { ...DEFAULT_METADATA }, theme: "quiet-press", - settings: { trim: "6x9", bleed: true }, + settings: { ...DEFAULT_SETTINGS }, cover: createCover(), chapters: [createChapter("Chapter One")], };