Reading plans: integrated into the Reader, redesigned, with per-plan translation
Build and Push Docker Image / build-and-push (push) Failing after 19s
Build and Push Docker Image / build-and-push (push) Failing after 19s
Reader: plan strip with a dot per chapter, plan sheet (day picker, chapter ticks, translation, streak, other plans), end-of-day card that steps through the day and rolls into the next, and chapter-level auto-marking. Each plan remembers its own translation (BSB, ESV, NLT, CSB, NASB, NET) and the Reader follows it while a plan is open. Plans home redesigned in the reader's themes with a Continue card, in-progress plans, a filterable catalog, and sheets for details and the custom-plan builder. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c11b6a60bf
commit
b1bc1c8ea4
17 files changed
+1205
-863
No files matched your search
+39
-3
@@ -1,4 +1,5 @@
|
||||
import { lazy, Suspense, useEffect, useRef, useState } from 'react';
|
||||
import { getPlanSchedule } from './utils/readingPlans.js';
|
||||
import { AppContext, useApp } from './context/AppContext.js';
|
||||
import { useAudio } from './hooks/useAudio.js';
|
||||
import { useAuth } from './hooks/useAuth.js';
|
||||
@@ -38,9 +39,15 @@ const App = () => {
|
||||
const saveTimerRef = useRef(null);
|
||||
const [syncStatus, setSyncStatus] = useState(''); // '' | 'syncing' | 'synced' | 'error'
|
||||
const [studyBuildBusy, setStudyBuildBusy] = useState(false);
|
||||
const reader = useBibleReader(currentPage);
|
||||
const audio = useAudio(reader.readerBookAbbrev, reader.readerChapter, reader.readerTranslation);
|
||||
const readingPlans = useReadingPlans();
|
||||
// The plan day currently open in the Reader ({ progressId, dayIndex }) — set when
|
||||
// you read from a plan, cleared when you leave the Reader. While set, the Reader
|
||||
// uses that plan's own translation.
|
||||
const [readerPlan, setReaderPlan] = useState(null);
|
||||
const readerPlanProgress = readerPlan ? readingPlans.planProgress.find((p) => p.id === readerPlan.progressId) ?? null : null;
|
||||
const reader = useBibleReader(currentPage, readerPlanProgress?.translation ?? null);
|
||||
const audio = useAudio(reader.readerBookAbbrev, reader.readerChapter, reader.readerTranslation);
|
||||
useEffect(() => { if (currentPage !== 'reader') setReaderPlan(null); }, [currentPage]);
|
||||
const { authUser, setAuthUser, authServerDown, signIn, logout } = useAuth({
|
||||
onSignIn: (user) => {
|
||||
setProjectIndex(switchStorageUser(user.id));
|
||||
@@ -120,12 +127,37 @@ const App = () => {
|
||||
|
||||
const openReadingPlans = () => setCurrentPage('reading-plans');
|
||||
|
||||
// Jumps straight into a specific plan day's first chapter and opens the reader.
|
||||
// Jumps straight into a specific chapter and opens the reader (no plan context).
|
||||
const readReadingPlanDay = (bookAbbrev, chapter) => {
|
||||
reader.jumpToReaderVerse(bookAbbrev, chapter, 1);
|
||||
setCurrentPage('reader');
|
||||
};
|
||||
|
||||
// Opens the Reader inside a plan: on `ref` (or the day's first unread chapter),
|
||||
// in the plan's own translation, with the plan strip and end-of-day card active.
|
||||
const openPlanReading = (progressId, dayIndex, ref) => {
|
||||
const progress = readingPlans.planProgress.find((p) => p.id === progressId);
|
||||
if (!progress) return;
|
||||
const refs = getPlanSchedule(progress.planId)[dayIndex] ?? [];
|
||||
const done = progress.completedDays.includes(dayIndex);
|
||||
const read = new Set(done ? [] : progress.chaptersRead?.[dayIndex] ?? []);
|
||||
const target = ref ?? refs.find((r) => !read.has(`${r.book}.${r.chapter}`)) ?? refs[0];
|
||||
setReaderPlan({ progressId, dayIndex });
|
||||
if (target) {
|
||||
reader.setReaderBookAbbrev(target.book);
|
||||
reader.setReaderChapter(target.chapter);
|
||||
reader.setReaderSelectedVerse(null);
|
||||
reader.setReaderCrossRefs(null);
|
||||
}
|
||||
setCurrentPage('reader');
|
||||
};
|
||||
|
||||
// Changing the translation while a plan is open changes that plan's translation.
|
||||
const setReaderTranslation = (id) => {
|
||||
if (readerPlan) readingPlans.setPlanTranslation(readerPlan.progressId, id);
|
||||
else reader.setReaderTranslation(id);
|
||||
};
|
||||
|
||||
// Starts book-mode audio playback at a specific plan day's chapter, then
|
||||
// shows the Home audio player. Uses handlePlayBookAudioFrom (not the reader
|
||||
// player) so this works even without opening the reader first.
|
||||
@@ -233,6 +265,10 @@ const App = () => {
|
||||
...audio,
|
||||
// Reader
|
||||
...reader,
|
||||
readerPlan, setReaderPlan,
|
||||
readerPlanActive: !!readerPlanProgress,
|
||||
openPlanReading,
|
||||
setReaderTranslation,
|
||||
// Setup (chunk-builder state lives in SetupPage via useChunkBuilder)
|
||||
project, setProject,
|
||||
allChunks,
|
||||
|
||||
Reference in new issue
Block a user