Add streaks, today view, custom plan editing/chapter ranges, and ntfy reminders
Build and Push Docker Image / build-and-push (push) Failing after 21s
Build and Push Docker Image / build-and-push (push) Failing after 21s
Reading plans now track a cross-plan activity log for streaks, surface a combined "today's reading" view on both Home and the Reading Plans page, and let custom plans be edited after creation (including narrowing a book to a specific chapter range). The reader auto-marks a plan's day read after a dwell period, active plans that fall behind their pace get a "catch me up" banner, and a new Settings section wires up daily reading reminders via ntfy.sh, checked once a minute by a small server-side scheduler. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
600b3d3ab9
commit
1dd6aef6a5
10 files changed
+711
-141
No files matched your search
+42
-1
@@ -63,6 +63,17 @@ export function initDb() {
|
||||
db.exec('ALTER TABLE users ADD COLUMN podcast_name TEXT');
|
||||
}
|
||||
|
||||
// Older databases predate ntfy.sh daily reading reminders.
|
||||
if (!userCols.some((c) => c.name === 'ntfy_url')) {
|
||||
db.exec(`
|
||||
ALTER TABLE users ADD COLUMN ntfy_url TEXT;
|
||||
ALTER TABLE users ADD COLUMN ntfy_topic TEXT;
|
||||
ALTER TABLE users ADD COLUMN reminder_time TEXT;
|
||||
ALTER TABLE users ADD COLUMN reminder_enabled INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE users ADD COLUMN last_reminder_date TEXT;
|
||||
`);
|
||||
}
|
||||
|
||||
// Older databases predate shareable read-only links.
|
||||
if (!projectCols.some((c) => c.name === 'share_token')) {
|
||||
db.exec('ALTER TABLE projects ADD COLUMN share_token TEXT');
|
||||
@@ -125,6 +136,7 @@ function parseUserRow(row) {
|
||||
return {
|
||||
...row,
|
||||
totpEnabled: !!row.totpEnabled,
|
||||
reminderEnabled: !!row.reminderEnabled,
|
||||
backupCodeHashes: row.backupCodesRaw ? JSON.parse(row.backupCodesRaw) : [],
|
||||
};
|
||||
}
|
||||
@@ -132,7 +144,9 @@ function parseUserRow(row) {
|
||||
const USER_SELECT = `
|
||||
SELECT id, email, password_hash AS passwordHash, created_at AS createdAt,
|
||||
totp_secret AS totpSecret, totp_enabled AS totpEnabled, backup_codes AS backupCodesRaw,
|
||||
podcast_name AS podcastName
|
||||
podcast_name AS podcastName,
|
||||
ntfy_url AS ntfyUrl, ntfy_topic AS ntfyTopic, reminder_time AS reminderTime,
|
||||
reminder_enabled AS reminderEnabled, last_reminder_date AS lastReminderDate
|
||||
FROM users
|
||||
`;
|
||||
|
||||
@@ -163,6 +177,33 @@ export function setPodcastName(userId, podcastName) {
|
||||
db.prepare('UPDATE users SET podcast_name = ? WHERE id = ?').run(podcastName || null, userId);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Daily reading reminders (sent via ntfy.sh — see server/reminders.js)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Saves a user's ntfy reminder settings ({ ntfyUrl, ntfyTopic, reminderTime, reminderEnabled }). */
|
||||
export function setReminderSettings(userId, { ntfyUrl, ntfyTopic, reminderTime, reminderEnabled }) {
|
||||
db.prepare(`
|
||||
UPDATE users SET ntfy_url = ?, ntfy_topic = ?, reminder_time = ?, reminder_enabled = ?
|
||||
WHERE id = ?
|
||||
`).run(ntfyUrl || null, ntfyTopic || null, reminderTime || null, reminderEnabled ? 1 : 0, userId);
|
||||
}
|
||||
|
||||
/** Every account with reminders turned on and a topic configured — checked once a minute. */
|
||||
export function getUsersWithRemindersEnabled() {
|
||||
return db.prepare(`
|
||||
SELECT id, email, ntfy_url AS ntfyUrl, ntfy_topic AS ntfyTopic, reminder_time AS reminderTime,
|
||||
last_reminder_date AS lastReminderDate
|
||||
FROM users
|
||||
WHERE reminder_enabled = 1 AND ntfy_topic IS NOT NULL AND reminder_time IS NOT NULL
|
||||
`).all();
|
||||
}
|
||||
|
||||
/** Marks that a user's reminder was sent today, so it isn't sent twice. */
|
||||
export function setLastReminderDate(userId, dateString) {
|
||||
db.prepare('UPDATE users SET last_reminder_date = ? WHERE id = ?').run(dateString, userId);
|
||||
}
|
||||
|
||||
/** Rewrites the remaining backup-code hashes after one is used (single-use codes). */
|
||||
export function setBackupCodeHashes(userId, backupCodeHashes) {
|
||||
db.prepare('UPDATE users SET backup_codes = ? WHERE id = ?').run(JSON.stringify(backupCodeHashes), userId);
|
||||
|
||||
Reference in new issue
Block a user