Add reading plans: catalog, progress tracking, and custom plans

Replaces the old single-book reading goal with a full Reading Plans page:
9 built-in plans (Whole Bible, OT/NT, topical) with cover photos and
color-themed cards, day-by-day checklists synced to the server per user,
inline "mark today read" in the Bible reader, and user-created plans with
their own book picker and an optional uploaded cover photo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5 committed 2026-09-23 12:51:28 -04:00
1 parent f1026dc194
commit 600b3d3ab9
21 files changed
+1166 -159

No files matched your search

+34
View File
@@ -81,6 +81,16 @@ export function initDb() {
);
`);
// Reading plan progress — one row per user, holding the whole list of
// in-progress/completed plans as JSON (small; no need to normalize).
db.exec(`
CREATE TABLE IF NOT EXISTS reading_progress (
user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
data TEXT NOT NULL,
updated_at INTEGER NOT NULL
);
`);
console.log(`SQLite database ready at ${DB_PATH}`);
}
@@ -182,6 +192,30 @@ export function setReaderInkPage(userId, bookAbbrev, chapter, strokes) {
`).run(userId, bookAbbrev, Number(chapter), JSON.stringify(strokes), Date.now());
}
// ---------------------------------------------------------------------------
// Reading plan progress — cross-device sync for started/completed reading plans
// ---------------------------------------------------------------------------
/** Returns the user's reading-plan data ({ progress, customPlans }, parsed JSON), or an empty shell. */
export function getReadingProgress(userId) {
const row = db.prepare('SELECT data FROM reading_progress WHERE user_id = ?').get(userId);
if (!row) return { progress: [], customPlans: [] };
try {
return JSON.parse(row.data);
} catch {
return { progress: [], customPlans: [] };
}
}
/** Overwrites the user's whole reading-plan progress list. */
export function setReadingProgress(userId, progress) {
db.prepare(`
INSERT INTO reading_progress (user_id, data, updated_at)
VALUES (?, ?, ?)
ON CONFLICT(user_id) DO UPDATE SET data = excluded.data, updated_at = excluded.updated_at
`).run(userId, JSON.stringify(progress), Date.now());
}
/**
* Assigns any pre-existing, unowned projects (from before multi-user support)
* to the given user. Intended to run once, right after the first account is created.