Read entire bible

This commit is contained in:
h202-wq
2026-08-19 08:20:17 -04:00
parent 1a79f44a0e
commit 35630dd41f
11 changed files with 1080 additions and 134 deletions
+110
View File
@@ -0,0 +1,110 @@
import { useCallback, useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import CandleCanvas from './Candle'
import { type Verse } from './bible'
import { daySeed, loadKJV, msUntilRollover, pickForDay } from './verse'
type Phase = 'sealed' | 'revealing' | 'revealed'
function formatCountdown(ms: number): string {
const s = Math.max(0, Math.ceil(ms / 1000))
const h = Math.floor(s / 3600)
const m = Math.floor((s % 3600) / 60)
const sec = s % 60
return `${String(h).padStart(2, '0')}h ${String(m).padStart(2, '0')}m ${String(sec).padStart(2, '0')}s`
}
export default function Home() {
const [phase, setPhase] = useState<Phase>('sealed')
const [verse, setVerse] = useState<Verse | null>(null)
const [countdown, setCountdown] = useState('')
const [error, setError] = useState(false)
useEffect(() => {
const update = () => setCountdown(formatCountdown(msUntilRollover()))
update()
const id = window.setInterval(update, 1000)
return () => window.clearInterval(id)
}, [])
const seek = useCallback(async () => {
if (phase !== 'sealed') return
setPhase('revealing')
try {
const verses = await loadKJV()
setVerse(pickForDay(verses, daySeed()))
setPhase('revealed')
} catch {
setError(true)
setPhase('sealed')
}
}, [phase])
const today = new Date().toLocaleDateString(undefined, {
year: 'numeric',
month: 'long',
day: 'numeric',
})
return (
<div className="app">
<CandleCanvas />
<nav className="home-nav" aria-label="Site navigation">
<Link to="/read/Genesis/1">Read</Link>
<Link to="/search">Search</Link>
</nav>
<header className="app-header">
<h1 className="title">Walk By Faith</h1>
<p className="tagline">
For we walk by faith, not by sight. <span className="tagline-ref"> 2 Corinthians 5:7</span>
</p>
</header>
<main className="app-main">
{phase !== 'revealed' && (
<section className="sealed" aria-label="Today's verse is sealed">
<div className="seal">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path
d="M12 1.8C12 1.8 4.6 9.4 4.6 15a7.4 7.4 0 0 0 14.8 0c0-5.6-7.4-13.2-7.4-13.2z"
fill="none"
stroke="currentColor"
strokeWidth="1.1"
/>
<path
d="M12 8.4c0 0-3 4.2-3 7a3 3 0 0 0 6 0c0-2.8-3-7-3-7z"
fill="currentColor"
/>
</svg>
</div>
<p className="seal-caption">The verse for this day is sealed</p>
<button className="seek-btn" onClick={seek} disabled={phase === 'revealing'}>
{error ? 'Relight the lamp' : phase === 'revealing' ? 'Lighting the lamp…' : 'Seek the verse'}
</button>
</section>
)}
{phase === 'revealed' && verse && (
<section className="revealed" aria-live="polite">
<p className="reveal-label">The verse for {today}</p>
<div className="verse">
<p className="verse-text">{verse.text}</p>
<p className="verse-ref">
<Link to={`/read/${verse.reference.split(' ')[0]}/1`}>{verse.reference}</Link>
</p>
</div>
</section>
)}
</main>
<footer className="app-footer">
<p className="countdown">
A new verse is appointed in <strong>{countdown}</strong>
</p>
<p className="foot-note">King James Version · Public Domain · The New Testament</p>
</footer>
</div>
)
}