// "Save for offline": fetch chapters ahead of time so they open without a connection. Fetching is // all it takes — the service worker (public/sw.js) keeps every Bible-text response it sees. Only the // free translations (BSB, NET) can be saved; the licensed ones' terms don't allow bulk copies. import { CHAPTER_COUNTS } from './bibleUtils.jsx'; import { fetchChapter, isLicensed } from './bibleText.js'; const KEY = 'offline-saved'; // { 'BSB': ['JHN', 'PSA'] }: whole books saved on this device, per translation. export function loadSavedBooks() { try { const raw = JSON.parse(localStorage.getItem(KEY) || '{}'); return raw && typeof raw === 'object' && !Array.isArray(raw) ? raw : {}; } catch { return {}; } } export function markBookSaved(translation, book) { const all = loadSavedBooks(); all[translation] = [...new Set([...(all[translation] ?? []), book])]; try { localStorage.setItem(KEY, JSON.stringify(all)); } catch { /* storage full or unavailable */ } return all; } export const canSaveOffline = (translation) => !isLicensed(translation); export const bookRefs = (book) => Array.from({ length: CHAPTER_COUNTS[book] ?? 0 }, (_, i) => ({ book, chapter: i + 1 })); export const bibleRefs = () => Object.keys(CHAPTER_COUNTS).flatMap(bookRefs); // Fetches every chapter in `refs` ([{ book, chapter }]) a few at a time, calling onProgress(done, total) // as each lands. Resolves { done, failed } — a chapter that fails doesn't stop the rest. Pass `signal` // to stop early. export async function downloadChapters(translation, refs, { onProgress, signal, concurrency = 4 } = {}) { const queue = [...refs]; let done = 0; let failed = 0; const worker = async () => { while (queue.length > 0 && !signal?.aborted) { const { book, chapter } = queue.shift(); try { await fetchChapter(translation, book, chapter, { signal }); done += 1; } catch { if (!signal?.aborted) failed += 1; } onProgress?.(done + failed, refs.length); } }; await Promise.all(Array.from({ length: Math.min(concurrency, refs.length) }, worker)); return { done, failed }; }