Invite-only sign-up, present mode, series, handouts, templates, dictation, progress, sharing
Build and Push Docker Image / build-and-push (push) Successful in 6m47s
Build and Push Docker Image / build-and-push (push) Successful in 6m47s
- Admin: sign-up Open / Invite only / Closed with single-use invite links - Present mode for scripts (auto-scroll) and outlines (step through, pace vs planned minutes) - Series: group studies on the dashboard, filter by series, calendar file for a study's sessions - Lesson handout: printable page per session (passage, outline, questions, writing lines) - Save a chunk's notes as your own study template (synced) - Voice dictation for notes and scripts (browser speech recognition) - Reading progress: map of the 66 books and a calendar of days read, synced and merged across devices - Export highlights and bookmarks as Markdown or Word, grouped by named colour - Verse of the day push to the ntfy topic (needs IQBIBLE_API_KEY) - Sharing: co-teacher editing via single-use invite links; read-only links can expire and take a passcode Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
88bc59bac0
commit
54cb1c217d
73 files changed
+2739
-134
No files matched your search
@@ -102,7 +102,7 @@ function initialChapterIndex(project) {
|
||||
return index >= 0 ? index : 0;
|
||||
}
|
||||
|
||||
export function useChunkBuilder({ project, setProject, updateProject, allChunks, setCurrentPage }) {
|
||||
export function useChunkBuilder({ project, setProject, updateProject, allChunks, setCurrentPage, userTemplates = [] }) {
|
||||
const { toast } = useToast();
|
||||
const [setup, setSetup] = useState(() => initialSetupForm(project));
|
||||
const [titleEdited, setTitleEdited] = useState(false);
|
||||
@@ -138,7 +138,10 @@ export function useChunkBuilder({ project, setProject, updateProject, allChunks,
|
||||
const activeChapter = project?.chapters[activeChapterIndex] ?? null;
|
||||
|
||||
const handleSetupField = (field, value) => {
|
||||
if (field === 'book') {
|
||||
if (field === 'template') {
|
||||
// a template of your own is copied into the study, so it keeps working wherever the study goes
|
||||
setSetup((current) => ({ ...current, template: value, templateDef: userTemplates.find((t) => t.id === value) }));
|
||||
} else if (field === 'book') {
|
||||
const selected = bookOptions.find((book) => book.abbrev === value) ?? bookOptions[0];
|
||||
setSetup((current) => ({
|
||||
...current,
|
||||
@@ -195,6 +198,8 @@ export function useChunkBuilder({ project, setProject, updateProject, allChunks,
|
||||
title: setup.title,
|
||||
translation: setup.translation,
|
||||
templateId: setup.template ?? 'blank',
|
||||
...(setup.templateDef ? { templateDef: setup.templateDef } : {}), // a copy of your own template
|
||||
|
||||
lastEdited: Date.now(),
|
||||
selectedChunkId: null,
|
||||
chapters: [
|
||||
@@ -237,7 +242,7 @@ export function useChunkBuilder({ project, setProject, updateProject, allChunks,
|
||||
&& (c.spilloverEndVerse ?? null) === (spilloverEndVerse ?? null),
|
||||
);
|
||||
if (overlap) return ch;
|
||||
const newChunk = blankChunk(start, end, spilloverEndVerse, current.templateId);
|
||||
const newChunk = blankChunk(start, end, spilloverEndVerse, current.templateDef ?? current.templateId);
|
||||
return { ...ch, chunks: [...ch.chunks, newChunk] };
|
||||
});
|
||||
// Find the newly added chunk id
|
||||
@@ -286,7 +291,7 @@ export function useChunkBuilder({ project, setProject, updateProject, allChunks,
|
||||
skippedCount += 1;
|
||||
return;
|
||||
}
|
||||
const newChunk = blankChunk(start, end, spilloverEndVerse, current.templateId);
|
||||
const newChunk = blankChunk(start, end, spilloverEndVerse, current.templateDef ?? current.templateId);
|
||||
existing.add(key);
|
||||
newChunks.push(newChunk);
|
||||
addedCount += 1;
|
||||
@@ -577,12 +582,12 @@ export function useChunkBuilder({ project, setProject, updateProject, allChunks,
|
||||
if (i >= 0) {
|
||||
const c = chunks[i];
|
||||
chunks[i] = { ...c, endVerse: n, spilloverEndVerse: null };
|
||||
chunks.splice(i + 1, 0, blankChunk(n + 1, c.endVerse, c.spilloverEndVerse ?? null, current.templateId));
|
||||
chunks.splice(i + 1, 0, blankChunk(n + 1, c.endVerse, c.spilloverEndVerse ?? null, current.templateDef ?? current.templateId));
|
||||
return { ...ch, chunks };
|
||||
}
|
||||
if (chunks.some((c) => c.startVerse <= n && n <= c.endVerse)) return ch; // already a boundary
|
||||
const start = Math.max(0, ...chunks.filter((c) => c.endVerse < n).map((c) => c.endVerse)) + 1;
|
||||
const created = blankChunk(start, n, null, current.templateId);
|
||||
const created = blankChunk(start, n, null, current.templateDef ?? current.templateId);
|
||||
selected = created.id;
|
||||
const at = chunks.findIndex((c) => c.startVerse > n);
|
||||
chunks.splice(at < 0 ? chunks.length : at, 0, created);
|
||||
|
||||
Reference in new issue
Block a user