Fix plan days with no chapters, add a way out of ink mode, harden ntfy reminders
Build and Push Docker Image / build-and-push (push) Failing after 19s

Plans can no longer have more days than chapters (existing ones heal), and
opening a plan never strands the Reader on an empty day. The Reader shows
Done drawing while inking (Escape also exits). Reminders use the user's own
timezone, send within a 3 hour grace window, and Settings has a Send test
notification button.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-09-29 10:34:46 -04:00
1 parent ecb1c701fa
commit 297f2c7e84
14 files changed
+335 -58

No files matched your search

+6 -4
View File
@@ -4,9 +4,9 @@
// Saved locally right away; synced to the server via an effect that watches
// `state` (see below) so sync success/failure can actually be tracked and
// shown, instead of being a fire-and-forget call buried inside setState.
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { getRemoteReadingProgress, saveRemoteReadingProgress } from '../syncService.js';
import { localDateString, registerCustomPlans } from '../utils/readingPlans.js';
import { clampPlanDays, localDateString, normalizeCustomPlan, registerCustomPlans } from '../utils/readingPlans.js';
const STORAGE_KEY = 'readingPlanProgress';
@@ -46,6 +46,8 @@ export function useReadingPlans() {
// Keeps the readingPlans.js lookup (getPlan/getPlanSchedule) in sync with
// this user's custom plans before any consumer renders this commit.
registerCustomPlans(state.customPlans);
// What the rest of the app sees: plans with more days than chapters are clamped.
const customPlans = useMemo(() => state.customPlans.map(normalizeCustomPlan), [state.customPlans]);
// Persist locally + sync to the server whenever state changes. Runs as a
// real effect (not inside the setState updater) so the network call has a
@@ -198,7 +200,7 @@ export function useReadingPlans() {
category: 'My Plans',
icon: '✨',
image: image || undefined,
days: Math.max(1, Math.round(days)),
days: clampPlanDays(books, chapterRanges, days),
books,
chapterRanges: chapterRanges && Object.keys(chapterRanges).length > 0 ? chapterRanges : undefined,
custom: true,
@@ -279,7 +281,7 @@ export function useReadingPlans() {
return {
planProgress: state.progress,
customPlans: state.customPlans,
customPlans,
readingActivity: state.activity,
readingPlanSyncStatus: syncStatus,
readingPlanSyncError: syncError,