- StudyPage owns layout/tabs/collapsed sections, tags, DOCX script import, chunk navigation, keyboard shortcuts, and the selected-chunk values derived from the project. - useStudyResources: commentary + interlinear for the selected chunk's chapter. These now load only on the study page (they previously also fetched while on Setup). - useCrossRefs: typed refs, dataset suggestions, verse text. - useWordStudy: word entries, Strong's lookups, suggestion picker. - Fix: study-page messages (word suggestions, DOCX import, lookups) set a status that only SetupPage displayed, so they were never seen. They now show as a toast on the study page. - copyVerse moves into ReaderPage. - Tests: toast, arrow-key navigation, cross-refs, tags. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
77 lines
3.3 KiB
JavaScript
77 lines
3.3 KiB
JavaScript
// Commentary and interlinear text for the chapter of the selected chunk.
|
|
// Each loads only while its section is expanded; results are cached for the visit.
|
|
import { useEffect, useState } from 'react';
|
|
|
|
const commentaryCache = {}; // "commentaryId/BOOK/chapter" -> data
|
|
const interlinearCache = {}; // BOOK -> whole-book interlinear data
|
|
|
|
export function useStudyResources({ project, selectedChunk, selectedChunkChapterIndex, collapsedSections }) {
|
|
const [commentarySource, setCommentarySource] = useState('matthew-henry');
|
|
const [commentaryData, setCommentaryData] = useState(null); // { content: [...] } for current chapter
|
|
const [commentaryLoading, setCommentaryLoading] = useState(false);
|
|
const [commentaryError, setCommentaryError] = useState('');
|
|
const [interlinearData, setInterlinearData] = useState(null);
|
|
const [interlinearLoading, setInterlinearLoading] = useState(false);
|
|
const [interlinearError, setInterlinearError] = useState('');
|
|
|
|
const loadInterlinearChapter = async (bookAbbrev, chapterNumber) => {
|
|
if (interlinearCache[bookAbbrev]) return interlinearCache[bookAbbrev];
|
|
const res = await fetch(`/interlinear/${bookAbbrev}.json`);
|
|
if (!res.ok) throw new Error('No interlinear data for this book.');
|
|
const data = await res.json();
|
|
interlinearCache[bookAbbrev] = data;
|
|
return data;
|
|
};
|
|
|
|
const loadCommentaryChapter = async (commentaryId, bookAbbrev, chapterNumber) => {
|
|
const cacheKey = `${commentaryId}/${bookAbbrev}/${chapterNumber}`;
|
|
if (commentaryCache[cacheKey]) return commentaryCache[cacheKey];
|
|
const res = await fetch(`https://bible.helloao.org/api/c/${commentaryId}/${bookAbbrev}/${chapterNumber}.json`);
|
|
const contentType = res.headers.get('content-type') || '';
|
|
if (!res.ok || !contentType.includes('json')) throw new Error(`No ${commentaryId} commentary for this chapter.`);
|
|
const data = await res.json();
|
|
commentaryCache[cacheKey] = data;
|
|
return data;
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (collapsedSections.commentary) return;
|
|
if (selectedChunkChapterIndex < 0) return;
|
|
const chapter = project.chapters[selectedChunkChapterIndex];
|
|
setCommentaryLoading(true);
|
|
setCommentaryError('');
|
|
loadCommentaryChapter(commentarySource, chapter.bookAbbrev, chapter.chapter)
|
|
.then((data) => setCommentaryData(data))
|
|
.catch((err) => {
|
|
setCommentaryData(null);
|
|
setCommentaryError(err.message);
|
|
})
|
|
.finally(() => setCommentaryLoading(false));
|
|
}, [commentarySource, selectedChunk?.id, selectedChunkChapterIndex, collapsedSections.commentary]);
|
|
|
|
useEffect(() => {
|
|
if (collapsedSections.interlinear) return;
|
|
if (selectedChunkChapterIndex < 0) return;
|
|
const chapter = project.chapters[selectedChunkChapterIndex];
|
|
setInterlinearLoading(true);
|
|
setInterlinearError('');
|
|
loadInterlinearChapter(chapter.bookAbbrev, chapter.chapter)
|
|
.then((data) => setInterlinearData(data?.[String(chapter.chapter)] ?? null))
|
|
.catch((err) => {
|
|
setInterlinearData(null);
|
|
setInterlinearError(err.message);
|
|
})
|
|
.finally(() => setInterlinearLoading(false));
|
|
}, [selectedChunk?.id, selectedChunkChapterIndex, collapsedSections.interlinear]);
|
|
|
|
return {
|
|
commentarySource, setCommentarySource,
|
|
commentaryData,
|
|
commentaryLoading,
|
|
commentaryError,
|
|
interlinearData,
|
|
interlinearLoading,
|
|
interlinearError,
|
|
};
|
|
}
|