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
@@ -78,6 +78,23 @@ export default function ReaderPage() {
|
||||
.filter(Boolean);
|
||||
}, [planProgress, readerBookAbbrev, readerChapter]);
|
||||
|
||||
// Auto-marks matching plan days as read after a dwell period on the chapter,
|
||||
// so you don't have to remember to tap "Mark read" yourself. Resets whenever
|
||||
// you navigate to a different book/chapter; a manual unmark isn't re-marked.
|
||||
const planMatchesRef = useRef(planMatches);
|
||||
planMatchesRef.current = planMatches;
|
||||
const [autoMarkedNotice, setAutoMarkedNotice] = useState('');
|
||||
useEffect(() => {
|
||||
const timer = window.setTimeout(() => {
|
||||
const justMarked = planMatchesRef.current.filter((m) => !m.done);
|
||||
if (justMarked.length === 0) return;
|
||||
justMarked.forEach((m) => toggleDay(m.progressId, m.dayIndex, m.plan.days));
|
||||
setAutoMarkedNotice(`✓ Marked read — ${justMarked.map((m) => m.plan.title).join(', ')}`);
|
||||
window.setTimeout(() => setAutoMarkedNotice(''), 4000);
|
||||
}, 20000);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [readerBookAbbrev, readerChapter, toggleDay]);
|
||||
|
||||
const [readerDrawMode, setReaderDrawMode] = useState(false);
|
||||
const [readerWideLayout, setReaderWideLayout] = useState(() => localStorage.getItem('reader-wide') === '1');
|
||||
const readerPageInkStrokes = readerInkByPage?.[`${readerBookAbbrev}_${readerChapter}`] ?? NO_STROKES;
|
||||
@@ -354,6 +371,12 @@ export default function ReaderPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{autoMarkedNotice && (
|
||||
<div className="mb-4 rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-2 text-sm font-semibold text-emerald-700">
|
||||
{autoMarkedNotice}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tool strip: font size · cross-refs · search */}
|
||||
<div className="mb-4 flex flex-wrap items-center gap-2 rounded-3xl border border-slate-200 bg-white px-4 py-3 shadow-panel">
|
||||
<span className="text-xs text-slate-500 mr-1">Text size</span>
|
||||
|
||||
Reference in new issue
Block a user