Preferences in Settings and a home page the user chooses
Build and Push Docker Image / build-and-push (push) Failing after 20s
Build and Push Docker Image / build-and-push (push) Failing after 20s
Settings gets a Preferences section: theme, default Bible, the Bible new reading plans start in, verse layout, commentary, text size, reading width, audio narrator (the last three stay per device; text size and narrator are now remembered). A Home page setting (My Studies, Bible Reader or Reading Plans) decides where the app opens and where every Home button goes; already being there means My Studies. Back arrows are labelled Home. Tests cover every page leading back. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
760c1e568b
commit
8c4f54e033
15 files changed
+401
-23
No files matched your search
@@ -1,6 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
import { useApp } from '../context/AppContext.js';
|
||||
import PageShell from '../components/study/PageShell.jsx';
|
||||
import { READER_TRANSLATIONS } from '../hooks/useBibleReader.js';
|
||||
import { COMMENTARIES } from '../hooks/useVerseStudy.js';
|
||||
import { READER_THEMES } from '../components/reader/readerThemes.js';
|
||||
import {
|
||||
startMfaSetup,
|
||||
confirmMfaSetup,
|
||||
@@ -12,10 +15,46 @@ import {
|
||||
} from '../syncService.js';
|
||||
import { exportJournalDocx, printJournalPdf } from '../utils/journalExport.js';
|
||||
|
||||
const FONT_SIZES = [['S', 0.875], ['M', 1], ['L', 1.125], ['XL', 1.25], ['XXL', 1.5]];
|
||||
const selectCls = 'rounded-xl border border-[var(--r-line)] bg-[var(--r-bg)] px-3 py-2 text-sm text-[var(--r-text)] focus:outline-none focus:ring-2 focus:ring-[var(--r-line)]';
|
||||
|
||||
// One labelled preference: what it is, whether it follows you between devices, and the control.
|
||||
function PrefRow({ label, hint, synced, children }) {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center justify-between gap-x-6 gap-y-2 border-t border-[var(--r-line)] py-3 first:border-t-0 first:pt-0">
|
||||
<div className="min-w-0 flex-1 basis-56">
|
||||
<p className="text-sm font-medium">{label}</p>
|
||||
<p className="text-xs text-[var(--r-sub)]">{hint} <span className="whitespace-nowrap">· {synced ? 'Synced to your account' : 'This device only'}</span></p>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Segmented({ label, options, value, onChange }) {
|
||||
return (
|
||||
<div role="group" aria-label={label} className="flex overflow-hidden rounded-xl border border-[var(--r-line)]">
|
||||
{options.map(([id, text]) => (
|
||||
<button key={String(id)} type="button" aria-pressed={value === id} onClick={() => onChange(id)}
|
||||
className={`px-3 py-1.5 text-sm ${value === id ? 'bg-[var(--r-text)] text-[var(--r-bg)]' : 'text-[var(--r-sub)] hover:bg-black/5'}`}>{text}</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SettingsPage() {
|
||||
const {
|
||||
authUser, setAuthUser,
|
||||
goHome,
|
||||
readerTheme, setReaderTheme,
|
||||
readerTranslation, setReaderTranslation,
|
||||
planTranslationPref, setPlanTranslationPref,
|
||||
startPagePref, setStartPagePref,
|
||||
readerLayout, setReaderLayout,
|
||||
readerFontSize, setReaderFontSize,
|
||||
readerWide, setReaderWide,
|
||||
readerCommentary, setReaderCommentary,
|
||||
audioNarrator, setAudioNarrator,
|
||||
} = useApp();
|
||||
const [mfaSetup, setMfaSetup] = useState(null); // { secret, qrCodeDataUrl } while setup is in progress
|
||||
const [mfaSetupCode, setMfaSetupCode] = useState('');
|
||||
@@ -179,6 +218,49 @@ export default function SettingsPage() {
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section aria-label="Preferences" className="rounded-2xl border border-[var(--r-line)] bg-[var(--r-bar)] p-6">
|
||||
<h2 className="text-base font-medium">Preferences</h2>
|
||||
<p className="mb-4 mt-1 text-sm text-[var(--r-sub)]">How the app looks and reads. Study projects always use the Berean Standard Bible.</p>
|
||||
<PrefRow label="Home page" hint="Where the app opens, and where the Home buttons take you." synced>
|
||||
<Segmented label="Home page" options={[['studies', 'My Studies'], ['reader', 'Bible Reader'], ['plans', 'Reading Plans']]} value={startPagePref} onChange={setStartPagePref} />
|
||||
</PrefRow>
|
||||
<PrefRow label="Theme" hint="Paper, sepia or dark." synced>
|
||||
<Segmented label="Theme" options={Object.entries(READER_THEMES).map(([id, t]) => [id, t.label])} value={readerTheme} onChange={setReaderTheme} />
|
||||
</PrefRow>
|
||||
<PrefRow label="Default Bible" hint="What the Reader opens in." synced>
|
||||
<select aria-label="Default Bible" value={readerTranslation} onChange={(e) => setReaderTranslation(e.target.value)} className={selectCls}>
|
||||
{READER_TRANSLATIONS.map((t) => <option key={t.id} value={t.id}>{t.id} · {t.name}</option>)}
|
||||
</select>
|
||||
</PrefRow>
|
||||
<PrefRow label="New reading plans" hint="The Bible a plan starts in. You can change it per plan." synced>
|
||||
<select aria-label="New reading plans" value={planTranslationPref} onChange={(e) => setPlanTranslationPref(e.target.value)} className={selectCls}>
|
||||
<option value="same">Same as my default Bible</option>
|
||||
{READER_TRANSLATIONS.map((t) => <option key={t.id} value={t.id}>{t.id} · {t.name}</option>)}
|
||||
</select>
|
||||
</PrefRow>
|
||||
<PrefRow label="Verse layout" hint="Verses run together like a page, or one to a line." synced>
|
||||
<Segmented label="Verse layout" options={[['flow', 'Flowing'], ['verses', 'One per line']]} value={readerLayout} onChange={setReaderLayout} />
|
||||
</PrefRow>
|
||||
<PrefRow label="Commentary" hint="The one shown first in the Reader and the Study stage." synced>
|
||||
<select aria-label="Commentary" value={readerCommentary} onChange={(e) => setReaderCommentary(e.target.value)} className={selectCls}>
|
||||
{COMMENTARIES.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
|
||||
</select>
|
||||
</PrefRow>
|
||||
<PrefRow label="Text size" hint="Reader text.">
|
||||
<Segmented label="Text size" options={FONT_SIZES.map(([l, n]) => [n, l])} value={readerFontSize} onChange={setReaderFontSize} />
|
||||
</PrefRow>
|
||||
<PrefRow label="Reading width" hint="One column, or two on a wide screen.">
|
||||
<Segmented label="Reading width" options={[[false, 'Column'], [true, 'Two columns']]} value={readerWide} onChange={setReaderWide} />
|
||||
</PrefRow>
|
||||
<PrefRow label="Audio narrator" hint="For the Berean Standard Bible audio.">
|
||||
<select aria-label="Audio narrator" value={audioNarrator} onChange={(e) => setAudioNarrator(e.target.value)} className={selectCls}>
|
||||
<option value="david">David</option>
|
||||
<option value="hays">Hays</option>
|
||||
<option value="souer">Souer</option>
|
||||
</select>
|
||||
</PrefRow>
|
||||
</section>
|
||||
|
||||
<section className="rounded-2xl border border-[var(--r-line)] bg-[var(--r-bar)] p-6 space-y-4">
|
||||
<div>
|
||||
<h2 className="text-base font-medium text-[var(--r-text)]">Change password</h2>
|
||||
|
||||
Reference in new issue
Block a user