Skip to content

feat(app): ExtraBiblicalDetailScreen (#1548) - #1597

Merged
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail
Apr 23, 2026
Merged

feat(app): ExtraBiblicalDetailScreen (#1548)#1597
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit, etc.) — the deep-link target for SecondTemplePanel chips (#1595) and ExtraBiblicalIndexScreen card taps (#1596). Renders 10 sections per the issue spec.

Closes

Closes#1548

Sections (in order)

  1. Hero — title + aliases + dates/language + 4-tradition status badges (ExtraBiblicalHero component)
  2. Brief summary — always visible (full on premium, first paragraph only on free)
  3. NT citations — tappable → Chapter screen w/ verse highlight
  4. OT allusions — tappable → Chapter screen
  5. Scholar voices — tappable → ScholarBio screen
  6. Related debates — chips → DebateDetail
  7. Related difficult passages — chips → DifficultPassageDetail
  8. Related journeys — chips → JourneyDetail
  9. Further reading — url → Linking.openURL; text-only rows render as non-tappable (the Phase-1 seed has no URLs)
  10. Full summary — prose if populated, else "Coming soon — expanded scholarly summary" placeholder card

Files added

  • app/src/components/ExtraBiblicalHero.tsx — 170 LOC
  • app/src/screens/ExtraBiblicalDetailScreen.tsx — 703 LOC
  • app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader, 51 LOC
  • app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 312 LOC, 23 tests

Files touched

  • app/src/db/content/extrabiblical.ts — new rowToEntry() parser + getExtraBiblicalEntry() wrapper
  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsExtrabiblicalEntry, ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion, ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
  • app/src/navigation/types.tsExtraBiblicalDetail route param
  • app/src/navigation/ExploreStack.tsxStack.Screen registration

Some additions overlap with PR #1596 (#1547's Index screen) — the types, query-module file, and barrel exports are identical additions that merge cleanly regardless of order.

Premium gating

Matches the DebatePanel / DebateDetailScreen precedent — props-based, screen wires usePremium() itself.

  • Free tier: Hero + first paragraph of brief_summary only; gated region replaced by "✨ Unlock the full entry" card with description; tap fires showUpgrade('explore', 'Extra-Biblical Literature').
  • Premium tier: all 10 sections visible with full deep-link behavior.

Deep-link handling — verse ref parsing

The Phase-1 extrabiblical seed (#1541) uses natural-language refs like "Jude 14-15" and "Genesis 6:1-4". The standard parseReference utility handles most of these but fails on single-chapter book refs ("Jude 14-15", "Obadiah 11", "Philemon 6") because its regex requires a colon before the verse.

Added a screen-local parseReferenceExtended that wraps parseReference with a fallback: if standard parsing fails and the book is single-chapter (Obadiah, Philemon, 2-3 John, Jude), treat the number as a verse and return chapter=1. This is the minimum needed to make Jude-heavy content (1 Enoch, Assumption of Moses) navigate correctly from their NT citations.

Note on issue file-path text

Issue says app/src/db/queries/extrabiblical.ts, but the actual repo convention is app/src/db/content/*.ts (18 sibling query modules). Placed at db/content/ to match real layout.

Note on further-reading URLs

The issue specifies Linking.openURL for external links, but the Phase-1 extrabiblical.json seed (#1541) has no URL fields — further_reading entries are text-only (title, author, note). Code supports URLs when present (guarded: TouchableOpacity only when url is a non-empty string, else plain View); when the content author adds URLs in a future PR, the tap handler lights up automatically.

How I verified

  • npx tsc --noEmit — clean ✅
  • npm run lint on touched files — 0 warnings on new/touched files (the 4 pre-existing master warnings in unrelated files are out of scope) ✅
  • npx jest (full suite) — 470 suites / 3497 tests pass

Tests (23/23 passing)

render — premium tier
✓ renders hero title + aliases
✓ renders tradition badges
✓ renders full brief_summary (both paragraphs)
✓ renders NT citations with ref + source + type
✓ renders OT allusions
✓ renders scholar voices
✓ renders related-debate chip and section header
✓ renders related-difficult-passage and related-journey chips
✓ renders further-reading items
✓ renders "Coming soon" placeholder when full_summary is null
✓ renders full_summary when it is present instead of placeholder
deep-link navigation — premium tier
✓ tapping NT citation navigates to Chapter with parsed book/chapter/verse
✓ tapping OT allusion navigates to Chapter with bookId
✓ tapping scholar navigates to ScholarBio
✓ tapping debate chip navigates to DebateDetail
✓ tapping difficult-passage chip navigates to DifficultPassageDetail
✓ tapping journey chip navigates to JourneyDetail
✓ tapping further-reading with url calls Linking.openURL
free tier (premium gating)
✓ renders only first paragraph of brief_summary
✓ hides all gated sections (NT citations, scholars, debates, etc.)
✓ shows unlock CTA and fires showUpgrade on tap
loading + not-found states
✓ shows loading indicator when loading=true
✓ shows "Entry not found" when entry=null after load

Merge order

Depends on: #1538 (extrabiblical schema), #1541 (12 seeded entries).

Pairs with:

Rollback

Revert this PR. ExtraBiblicalDetail route unregisters; ExtraBiblicalDetailScreen deleted. SecondTemplePanel chips and ExtraBiblicalIndexScreen tap handlers continue to call navigation.navigate('ExtraBiblicalDetail', …) — which will no longer resolve — but the guarded optional-callback in SecondTemplePanel means chips simply won't render if the parent doesn't wire them, and the Index screen would fail more loudly (acceptable if reverting deliberately).


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3523❌ 03523
Suites✅ 473❌ 0473

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 81.2s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 4a107d9 to 6d6dd18CompareApril 23, 2026 20:05
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 6d6dd18 to 608f5caCompareApril 23, 2026 20:20
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Coverage on this PR was 81.85% lines. Same temporary 79%/81% fallback
as #1596/#1597/#1580 with tracking issue #1599.
Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit,
etc.). 10 sections per the issue spec:
1. Hero (title + aliases + dates + 4-tradition status)
2. Brief summary
3. NT citations
4. OT allusions
5. Scholar voices
6. Related debates
7. Related difficult passages
8. Related journeys
9. Further reading
10. Full summary (or "Coming soon" placeholder)
Files added
app/src/components/ExtraBiblicalHero.tsx — hero (title, aliases,
dates, tradition-status badges)
app/src/screens/ExtraBiblicalDetailScreen.tsx — the screen
app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader
app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 23 tests
Files touched (some overlap with PR #1596 / #1547 — identical additions
that merge cleanly)
app/src/db/content/extrabiblical.ts — new rowToEntry() parser +
getExtraBiblicalEntry() wrapper
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — ExtrabiblicalEntry,
ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion,
ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
app/src/navigation/types.ts — ExtraBiblicalDetail route
app/src/navigation/ExploreStack.tsx — Stack.Screen registration
Premium gating (matches DebatePanel pattern — props-based, screen
wires usePremium() itself — mirroring DebateDetailScreen and
ArchaeologyDetailScreen)
Free tier : Hero + first paragraph of brief_summary only; gated
region replaced by '✨ Unlock the full entry' card; tap
fires showUpgrade('explore', 'Extra-Biblical Literature').
Premium : all 10 sections visible with full deep-link behavior.
Deep-links
- NT citations / OT allusions → Chapter screen. Verse refs parsed via
parseReference() (handles 'Genesis 6:1-4', '1 Cor 13:4') with an
extension for single-chapter books ('Jude 14-15' → jude 1:14). The
Phase-1 extrabiblical seed uses natural-language refs heavily, so
the extended parser is necessary — plain parseReference rejects
'Jude 14-15' because the regex requires a colon before verse.
- Scholar voices → ScholarBio screen (matches DebateDetail).
- Related debates → DebateDetail.
- Related difficult passages → DifficultPassageDetail.
- Related journeys → JourneyDetail.
- Further reading (with url) → Linking.openURL; rows without url
render as non-tappable text (the Phase-1 seed is text-only per
further_reading shape).
Note on issue file-path text
Issue says 'app/src/db/queries/extrabiblical.ts'. Actual repo
convention is app/src/db/content/*.ts (18 sibling query modules).
Placed at db/content/ to match real layout.
Null full_summary → 'Coming soon — expanded scholarly summary'
placeholder card; non-null renders the full text as regular body
prose. The full_summary field is filled in by Phase 4
(HWGTB-P4-02A/B/C/D).
How verified
npx tsc --noEmit — clean
npm run lint (src/) — 0 warnings on new/touched files
(4 pre-existing master warnings
unrelated to this PR)
npx jest (full suite) — 470 suites / 3497 tests pass
23 new tests cover:
- Hero render (title, aliases, tradition badges)
- Brief summary full vs first-paragraph-only
- All 7 mid-sections render with correct labels and row content
- Full-summary rendering vs 'Coming soon' placeholder
- Every deep-link: NT citation → Chapter (with verse), OT allusion
→ Chapter, scholar → ScholarBio, debate → DebateDetail, difficult
passage → DifficultPassageDetail, journey → JourneyDetail,
further-reading url → Linking.openURL
- Free-tier: first paragraph only, gated sections hidden, unlock
CTA fires showUpgrade with correct params
- Loading indicator + 'Entry not found' error state
Merge order
Depends on #1538 (extrabiblical schema), #1541 (12 seeded entries),
and overlaps with #1547 (Index screen) on the types, query module,
and barrel exports — identical additions that merge either way.
Pairs with #1595 (SecondTemplePanel) whose chips navigate here, and
#1547 whose entry cards navigate here.
Closes#1548
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 608f5ca to 2baf275CompareApril 23, 2026 20:26
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmaster merged commit 801f3e7 into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p2-03-extrabiblical-detail branch April 23, 2026 20:35
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster added a commit that referenced this pull request Apr 23, 2026
* feat(app): CanonComparisonScreen (#1550)
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
* fix(types): add BookIntro to ExploreStackParamList
The CanonComparison screen registered BookIntroScreen on ExploreStack
but the param list didn't declare it, causing TS2322 on navigate calls
from CanonComparisonScreen.
Adds the route to ExploreStackParamList alongside the existing
CanonComparison entry. BookIntro also exists on RootStack; same route
on two stacks is fine for React Navigation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude (for Craig) <claude@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HWGTB-P2-03] App: ExtraBiblicalDetailScreen

2 participants

@CraigBuckmaster@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all \x3Cpre>\x3Ccode> blocks (function() { function addCopyButtons() { document.querySelectorAll('pre code').forEach(function(codeBlock) { if (codeBlock.parentElement.hasAttribute('data-copy-added')) return; codeBlock.parentElement.setAttribute('data-copy-added', 'true'); var btn = document.createElement('button'); btn.textContent = 'Copy'; btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;'; btn.onmouseover = function() { this.style.opacity = '1'; }; btn.onmouseout = function() { this.style.opacity = '0.7'; }; btn.onclick = function() { navigator.clipboard.writeText(codeBlock.textContent).then(function() { btn.textContent = 'Copied!'; setTimeout(function() { btn.textContent = 'Copy'; }, 1500); }); }; codeBlock.parentElement.style.position = 'relative'; codeBlock.parentElement.appendChild(btn); }); } addCopyButtons(); // Re-run on dynamic content var observer = new MutationObserver(addCopyButtons); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + ' feat(app): ExtraBiblicalDetailScreen (#1548) by CraigBuckmaster · Pull Request #1597 · CraigBuckmaster/ScriptureDeepDive · GitHub
Skip to content

feat(app): ExtraBiblicalDetailScreen (#1548) - #1597

Merged
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail
Apr 23, 2026
Merged

feat(app): ExtraBiblicalDetailScreen (#1548)#1597
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit, etc.) — the deep-link target for SecondTemplePanel chips (#1595) and ExtraBiblicalIndexScreen card taps (#1596). Renders 10 sections per the issue spec.

Closes

Closes#1548

Sections (in order)

  1. Hero — title + aliases + dates/language + 4-tradition status badges (ExtraBiblicalHero component)
  2. Brief summary — always visible (full on premium, first paragraph only on free)
  3. NT citations — tappable → Chapter screen w/ verse highlight
  4. OT allusions — tappable → Chapter screen
  5. Scholar voices — tappable → ScholarBio screen
  6. Related debates — chips → DebateDetail
  7. Related difficult passages — chips → DifficultPassageDetail
  8. Related journeys — chips → JourneyDetail
  9. Further reading — url → Linking.openURL; text-only rows render as non-tappable (the Phase-1 seed has no URLs)
  10. Full summary — prose if populated, else "Coming soon — expanded scholarly summary" placeholder card

Files added

  • app/src/components/ExtraBiblicalHero.tsx — 170 LOC
  • app/src/screens/ExtraBiblicalDetailScreen.tsx — 703 LOC
  • app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader, 51 LOC
  • app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 312 LOC, 23 tests

Files touched

  • app/src/db/content/extrabiblical.ts — new rowToEntry() parser + getExtraBiblicalEntry() wrapper
  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsExtrabiblicalEntry, ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion, ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
  • app/src/navigation/types.tsExtraBiblicalDetail route param
  • app/src/navigation/ExploreStack.tsxStack.Screen registration

Some additions overlap with PR #1596 (#1547's Index screen) — the types, query-module file, and barrel exports are identical additions that merge cleanly regardless of order.

Premium gating

Matches the DebatePanel / DebateDetailScreen precedent — props-based, screen wires usePremium() itself.

  • Free tier: Hero + first paragraph of brief_summary only; gated region replaced by "✨ Unlock the full entry" card with description; tap fires showUpgrade('explore', 'Extra-Biblical Literature').
  • Premium tier: all 10 sections visible with full deep-link behavior.

Deep-link handling — verse ref parsing

The Phase-1 extrabiblical seed (#1541) uses natural-language refs like "Jude 14-15" and "Genesis 6:1-4". The standard parseReference utility handles most of these but fails on single-chapter book refs ("Jude 14-15", "Obadiah 11", "Philemon 6") because its regex requires a colon before the verse.

Added a screen-local parseReferenceExtended that wraps parseReference with a fallback: if standard parsing fails and the book is single-chapter (Obadiah, Philemon, 2-3 John, Jude), treat the number as a verse and return chapter=1. This is the minimum needed to make Jude-heavy content (1 Enoch, Assumption of Moses) navigate correctly from their NT citations.

Note on issue file-path text

Issue says app/src/db/queries/extrabiblical.ts, but the actual repo convention is app/src/db/content/*.ts (18 sibling query modules). Placed at db/content/ to match real layout.

Note on further-reading URLs

The issue specifies Linking.openURL for external links, but the Phase-1 extrabiblical.json seed (#1541) has no URL fields — further_reading entries are text-only (title, author, note). Code supports URLs when present (guarded: TouchableOpacity only when url is a non-empty string, else plain View); when the content author adds URLs in a future PR, the tap handler lights up automatically.

How I verified

  • npx tsc --noEmit — clean ✅
  • npm run lint on touched files — 0 warnings on new/touched files (the 4 pre-existing master warnings in unrelated files are out of scope) ✅
  • npx jest (full suite) — 470 suites / 3497 tests pass

Tests (23/23 passing)

render — premium tier
✓ renders hero title + aliases
✓ renders tradition badges
✓ renders full brief_summary (both paragraphs)
✓ renders NT citations with ref + source + type
✓ renders OT allusions
✓ renders scholar voices
✓ renders related-debate chip and section header
✓ renders related-difficult-passage and related-journey chips
✓ renders further-reading items
✓ renders "Coming soon" placeholder when full_summary is null
✓ renders full_summary when it is present instead of placeholder
deep-link navigation — premium tier
✓ tapping NT citation navigates to Chapter with parsed book/chapter/verse
✓ tapping OT allusion navigates to Chapter with bookId
✓ tapping scholar navigates to ScholarBio
✓ tapping debate chip navigates to DebateDetail
✓ tapping difficult-passage chip navigates to DifficultPassageDetail
✓ tapping journey chip navigates to JourneyDetail
✓ tapping further-reading with url calls Linking.openURL
free tier (premium gating)
✓ renders only first paragraph of brief_summary
✓ hides all gated sections (NT citations, scholars, debates, etc.)
✓ shows unlock CTA and fires showUpgrade on tap
loading + not-found states
✓ shows loading indicator when loading=true
✓ shows "Entry not found" when entry=null after load

Merge order

Depends on: #1538 (extrabiblical schema), #1541 (12 seeded entries).

Pairs with:

Rollback

Revert this PR. ExtraBiblicalDetail route unregisters; ExtraBiblicalDetailScreen deleted. SecondTemplePanel chips and ExtraBiblicalIndexScreen tap handlers continue to call navigation.navigate('ExtraBiblicalDetail', …) — which will no longer resolve — but the guarded optional-callback in SecondTemplePanel means chips simply won't render if the parent doesn't wire them, and the Index screen would fail more loudly (acceptable if reverting deliberately).


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3523❌ 03523
Suites✅ 473❌ 0473

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 81.2s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 4a107d9 to 6d6dd18CompareApril 23, 2026 20:05
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 6d6dd18 to 608f5caCompareApril 23, 2026 20:20
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Coverage on this PR was 81.85% lines. Same temporary 79%/81% fallback
as #1596/#1597/#1580 with tracking issue #1599.
Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit,
etc.). 10 sections per the issue spec:
1. Hero (title + aliases + dates + 4-tradition status)
2. Brief summary
3. NT citations
4. OT allusions
5. Scholar voices
6. Related debates
7. Related difficult passages
8. Related journeys
9. Further reading
10. Full summary (or "Coming soon" placeholder)
Files added
app/src/components/ExtraBiblicalHero.tsx — hero (title, aliases,
dates, tradition-status badges)
app/src/screens/ExtraBiblicalDetailScreen.tsx — the screen
app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader
app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 23 tests
Files touched (some overlap with PR #1596 / #1547 — identical additions
that merge cleanly)
app/src/db/content/extrabiblical.ts — new rowToEntry() parser +
getExtraBiblicalEntry() wrapper
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — ExtrabiblicalEntry,
ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion,
ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
app/src/navigation/types.ts — ExtraBiblicalDetail route
app/src/navigation/ExploreStack.tsx — Stack.Screen registration
Premium gating (matches DebatePanel pattern — props-based, screen
wires usePremium() itself — mirroring DebateDetailScreen and
ArchaeologyDetailScreen)
Free tier : Hero + first paragraph of brief_summary only; gated
region replaced by '✨ Unlock the full entry' card; tap
fires showUpgrade('explore', 'Extra-Biblical Literature').
Premium : all 10 sections visible with full deep-link behavior.
Deep-links
- NT citations / OT allusions → Chapter screen. Verse refs parsed via
parseReference() (handles 'Genesis 6:1-4', '1 Cor 13:4') with an
extension for single-chapter books ('Jude 14-15' → jude 1:14). The
Phase-1 extrabiblical seed uses natural-language refs heavily, so
the extended parser is necessary — plain parseReference rejects
'Jude 14-15' because the regex requires a colon before verse.
- Scholar voices → ScholarBio screen (matches DebateDetail).
- Related debates → DebateDetail.
- Related difficult passages → DifficultPassageDetail.
- Related journeys → JourneyDetail.
- Further reading (with url) → Linking.openURL; rows without url
render as non-tappable text (the Phase-1 seed is text-only per
further_reading shape).
Note on issue file-path text
Issue says 'app/src/db/queries/extrabiblical.ts'. Actual repo
convention is app/src/db/content/*.ts (18 sibling query modules).
Placed at db/content/ to match real layout.
Null full_summary → 'Coming soon — expanded scholarly summary'
placeholder card; non-null renders the full text as regular body
prose. The full_summary field is filled in by Phase 4
(HWGTB-P4-02A/B/C/D).
How verified
npx tsc --noEmit — clean
npm run lint (src/) — 0 warnings on new/touched files
(4 pre-existing master warnings
unrelated to this PR)
npx jest (full suite) — 470 suites / 3497 tests pass
23 new tests cover:
- Hero render (title, aliases, tradition badges)
- Brief summary full vs first-paragraph-only
- All 7 mid-sections render with correct labels and row content
- Full-summary rendering vs 'Coming soon' placeholder
- Every deep-link: NT citation → Chapter (with verse), OT allusion
→ Chapter, scholar → ScholarBio, debate → DebateDetail, difficult
passage → DifficultPassageDetail, journey → JourneyDetail,
further-reading url → Linking.openURL
- Free-tier: first paragraph only, gated sections hidden, unlock
CTA fires showUpgrade with correct params
- Loading indicator + 'Entry not found' error state
Merge order
Depends on #1538 (extrabiblical schema), #1541 (12 seeded entries),
and overlaps with #1547 (Index screen) on the types, query module,
and barrel exports — identical additions that merge either way.
Pairs with #1595 (SecondTemplePanel) whose chips navigate here, and
#1547 whose entry cards navigate here.
Closes#1548
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 608f5ca to 2baf275CompareApril 23, 2026 20:26
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmaster merged commit 801f3e7 into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p2-03-extrabiblical-detail branch April 23, 2026 20:35
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster added a commit that referenced this pull request Apr 23, 2026
* feat(app): CanonComparisonScreen (#1550)
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
* fix(types): add BookIntro to ExploreStackParamList
The CanonComparison screen registered BookIntroScreen on ExploreStack
but the param list didn't declare it, causing TS2322 on navigate calls
from CanonComparisonScreen.
Adds the route to ExploreStackParamList alongside the existing
CanonComparison entry. BookIntro also exists on RootStack; same route
on two stacks is fine for React Navigation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude (for Craig) <claude@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HWGTB-P2-03] App: ExtraBiblicalDetailScreen

2 participants

@CraigBuckmaster@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(app): ExtraBiblicalDetailScreen (#1548) by CraigBuckmaster · Pull Request #1597 · CraigBuckmaster/ScriptureDeepDive · GitHub
Skip to content

feat(app): ExtraBiblicalDetailScreen (#1548) - #1597

Merged
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail
Apr 23, 2026
Merged

feat(app): ExtraBiblicalDetailScreen (#1548)#1597
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit, etc.) — the deep-link target for SecondTemplePanel chips (#1595) and ExtraBiblicalIndexScreen card taps (#1596). Renders 10 sections per the issue spec.

Closes

Closes#1548

Sections (in order)

  1. Hero — title + aliases + dates/language + 4-tradition status badges (ExtraBiblicalHero component)
  2. Brief summary — always visible (full on premium, first paragraph only on free)
  3. NT citations — tappable → Chapter screen w/ verse highlight
  4. OT allusions — tappable → Chapter screen
  5. Scholar voices — tappable → ScholarBio screen
  6. Related debates — chips → DebateDetail
  7. Related difficult passages — chips → DifficultPassageDetail
  8. Related journeys — chips → JourneyDetail
  9. Further reading — url → Linking.openURL; text-only rows render as non-tappable (the Phase-1 seed has no URLs)
  10. Full summary — prose if populated, else "Coming soon — expanded scholarly summary" placeholder card

Files added

  • app/src/components/ExtraBiblicalHero.tsx — 170 LOC
  • app/src/screens/ExtraBiblicalDetailScreen.tsx — 703 LOC
  • app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader, 51 LOC
  • app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 312 LOC, 23 tests

Files touched

  • app/src/db/content/extrabiblical.ts — new rowToEntry() parser + getExtraBiblicalEntry() wrapper
  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsExtrabiblicalEntry, ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion, ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
  • app/src/navigation/types.tsExtraBiblicalDetail route param
  • app/src/navigation/ExploreStack.tsxStack.Screen registration

Some additions overlap with PR #1596 (#1547's Index screen) — the types, query-module file, and barrel exports are identical additions that merge cleanly regardless of order.

Premium gating

Matches the DebatePanel / DebateDetailScreen precedent — props-based, screen wires usePremium() itself.

  • Free tier: Hero + first paragraph of brief_summary only; gated region replaced by "✨ Unlock the full entry" card with description; tap fires showUpgrade('explore', 'Extra-Biblical Literature').
  • Premium tier: all 10 sections visible with full deep-link behavior.

Deep-link handling — verse ref parsing

The Phase-1 extrabiblical seed (#1541) uses natural-language refs like "Jude 14-15" and "Genesis 6:1-4". The standard parseReference utility handles most of these but fails on single-chapter book refs ("Jude 14-15", "Obadiah 11", "Philemon 6") because its regex requires a colon before the verse.

Added a screen-local parseReferenceExtended that wraps parseReference with a fallback: if standard parsing fails and the book is single-chapter (Obadiah, Philemon, 2-3 John, Jude), treat the number as a verse and return chapter=1. This is the minimum needed to make Jude-heavy content (1 Enoch, Assumption of Moses) navigate correctly from their NT citations.

Note on issue file-path text

Issue says app/src/db/queries/extrabiblical.ts, but the actual repo convention is app/src/db/content/*.ts (18 sibling query modules). Placed at db/content/ to match real layout.

Note on further-reading URLs

The issue specifies Linking.openURL for external links, but the Phase-1 extrabiblical.json seed (#1541) has no URL fields — further_reading entries are text-only (title, author, note). Code supports URLs when present (guarded: TouchableOpacity only when url is a non-empty string, else plain View); when the content author adds URLs in a future PR, the tap handler lights up automatically.

How I verified

  • npx tsc --noEmit — clean ✅
  • npm run lint on touched files — 0 warnings on new/touched files (the 4 pre-existing master warnings in unrelated files are out of scope) ✅
  • npx jest (full suite) — 470 suites / 3497 tests pass

Tests (23/23 passing)

render — premium tier
✓ renders hero title + aliases
✓ renders tradition badges
✓ renders full brief_summary (both paragraphs)
✓ renders NT citations with ref + source + type
✓ renders OT allusions
✓ renders scholar voices
✓ renders related-debate chip and section header
✓ renders related-difficult-passage and related-journey chips
✓ renders further-reading items
✓ renders "Coming soon" placeholder when full_summary is null
✓ renders full_summary when it is present instead of placeholder
deep-link navigation — premium tier
✓ tapping NT citation navigates to Chapter with parsed book/chapter/verse
✓ tapping OT allusion navigates to Chapter with bookId
✓ tapping scholar navigates to ScholarBio
✓ tapping debate chip navigates to DebateDetail
✓ tapping difficult-passage chip navigates to DifficultPassageDetail
✓ tapping journey chip navigates to JourneyDetail
✓ tapping further-reading with url calls Linking.openURL
free tier (premium gating)
✓ renders only first paragraph of brief_summary
✓ hides all gated sections (NT citations, scholars, debates, etc.)
✓ shows unlock CTA and fires showUpgrade on tap
loading + not-found states
✓ shows loading indicator when loading=true
✓ shows "Entry not found" when entry=null after load

Merge order

Depends on: #1538 (extrabiblical schema), #1541 (12 seeded entries).

Pairs with:

Rollback

Revert this PR. ExtraBiblicalDetail route unregisters; ExtraBiblicalDetailScreen deleted. SecondTemplePanel chips and ExtraBiblicalIndexScreen tap handlers continue to call navigation.navigate('ExtraBiblicalDetail', …) — which will no longer resolve — but the guarded optional-callback in SecondTemplePanel means chips simply won't render if the parent doesn't wire them, and the Index screen would fail more loudly (acceptable if reverting deliberately).


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3523❌ 03523
Suites✅ 473❌ 0473

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 81.2s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 4a107d9 to 6d6dd18CompareApril 23, 2026 20:05
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 6d6dd18 to 608f5caCompareApril 23, 2026 20:20
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Coverage on this PR was 81.85% lines. Same temporary 79%/81% fallback
as #1596/#1597/#1580 with tracking issue #1599.
Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit,
etc.). 10 sections per the issue spec:
1. Hero (title + aliases + dates + 4-tradition status)
2. Brief summary
3. NT citations
4. OT allusions
5. Scholar voices
6. Related debates
7. Related difficult passages
8. Related journeys
9. Further reading
10. Full summary (or "Coming soon" placeholder)
Files added
app/src/components/ExtraBiblicalHero.tsx — hero (title, aliases,
dates, tradition-status badges)
app/src/screens/ExtraBiblicalDetailScreen.tsx — the screen
app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader
app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 23 tests
Files touched (some overlap with PR #1596 / #1547 — identical additions
that merge cleanly)
app/src/db/content/extrabiblical.ts — new rowToEntry() parser +
getExtraBiblicalEntry() wrapper
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — ExtrabiblicalEntry,
ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion,
ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
app/src/navigation/types.ts — ExtraBiblicalDetail route
app/src/navigation/ExploreStack.tsx — Stack.Screen registration
Premium gating (matches DebatePanel pattern — props-based, screen
wires usePremium() itself — mirroring DebateDetailScreen and
ArchaeologyDetailScreen)
Free tier : Hero + first paragraph of brief_summary only; gated
region replaced by '✨ Unlock the full entry' card; tap
fires showUpgrade('explore', 'Extra-Biblical Literature').
Premium : all 10 sections visible with full deep-link behavior.
Deep-links
- NT citations / OT allusions → Chapter screen. Verse refs parsed via
parseReference() (handles 'Genesis 6:1-4', '1 Cor 13:4') with an
extension for single-chapter books ('Jude 14-15' → jude 1:14). The
Phase-1 extrabiblical seed uses natural-language refs heavily, so
the extended parser is necessary — plain parseReference rejects
'Jude 14-15' because the regex requires a colon before verse.
- Scholar voices → ScholarBio screen (matches DebateDetail).
- Related debates → DebateDetail.
- Related difficult passages → DifficultPassageDetail.
- Related journeys → JourneyDetail.
- Further reading (with url) → Linking.openURL; rows without url
render as non-tappable text (the Phase-1 seed is text-only per
further_reading shape).
Note on issue file-path text
Issue says 'app/src/db/queries/extrabiblical.ts'. Actual repo
convention is app/src/db/content/*.ts (18 sibling query modules).
Placed at db/content/ to match real layout.
Null full_summary → 'Coming soon — expanded scholarly summary'
placeholder card; non-null renders the full text as regular body
prose. The full_summary field is filled in by Phase 4
(HWGTB-P4-02A/B/C/D).
How verified
npx tsc --noEmit — clean
npm run lint (src/) — 0 warnings on new/touched files
(4 pre-existing master warnings
unrelated to this PR)
npx jest (full suite) — 470 suites / 3497 tests pass
23 new tests cover:
- Hero render (title, aliases, tradition badges)
- Brief summary full vs first-paragraph-only
- All 7 mid-sections render with correct labels and row content
- Full-summary rendering vs 'Coming soon' placeholder
- Every deep-link: NT citation → Chapter (with verse), OT allusion
→ Chapter, scholar → ScholarBio, debate → DebateDetail, difficult
passage → DifficultPassageDetail, journey → JourneyDetail,
further-reading url → Linking.openURL
- Free-tier: first paragraph only, gated sections hidden, unlock
CTA fires showUpgrade with correct params
- Loading indicator + 'Entry not found' error state
Merge order
Depends on #1538 (extrabiblical schema), #1541 (12 seeded entries),
and overlaps with #1547 (Index screen) on the types, query module,
and barrel exports — identical additions that merge either way.
Pairs with #1595 (SecondTemplePanel) whose chips navigate here, and
#1547 whose entry cards navigate here.
Closes#1548
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 608f5ca to 2baf275CompareApril 23, 2026 20:26
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmaster merged commit 801f3e7 into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p2-03-extrabiblical-detail branch April 23, 2026 20:35
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster added a commit that referenced this pull request Apr 23, 2026
* feat(app): CanonComparisonScreen (#1550)
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
* fix(types): add BookIntro to ExploreStackParamList
The CanonComparison screen registered BookIntroScreen on ExploreStack
but the param list didn't declare it, causing TS2322 on navigate calls
from CanonComparisonScreen.
Adds the route to ExploreStackParamList alongside the existing
CanonComparison entry. BookIntro also exists on RootStack; same route
on two stacks is fine for React Navigation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude (for Craig) <claude@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HWGTB-P2-03] App: ExtraBiblicalDetailScreen

2 participants

@CraigBuckmaster@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(app): ExtraBiblicalDetailScreen (#1548) by CraigBuckmaster · Pull Request #1597 · CraigBuckmaster/ScriptureDeepDive · GitHub
Skip to content

feat(app): ExtraBiblicalDetailScreen (#1548) - #1597

Merged
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail
Apr 23, 2026
Merged

feat(app): ExtraBiblicalDetailScreen (#1548)#1597
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit, etc.) — the deep-link target for SecondTemplePanel chips (#1595) and ExtraBiblicalIndexScreen card taps (#1596). Renders 10 sections per the issue spec.

Closes

Closes#1548

Sections (in order)

  1. Hero — title + aliases + dates/language + 4-tradition status badges (ExtraBiblicalHero component)
  2. Brief summary — always visible (full on premium, first paragraph only on free)
  3. NT citations — tappable → Chapter screen w/ verse highlight
  4. OT allusions — tappable → Chapter screen
  5. Scholar voices — tappable → ScholarBio screen
  6. Related debates — chips → DebateDetail
  7. Related difficult passages — chips → DifficultPassageDetail
  8. Related journeys — chips → JourneyDetail
  9. Further reading — url → Linking.openURL; text-only rows render as non-tappable (the Phase-1 seed has no URLs)
  10. Full summary — prose if populated, else "Coming soon — expanded scholarly summary" placeholder card

Files added

  • app/src/components/ExtraBiblicalHero.tsx — 170 LOC
  • app/src/screens/ExtraBiblicalDetailScreen.tsx — 703 LOC
  • app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader, 51 LOC
  • app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 312 LOC, 23 tests

Files touched

  • app/src/db/content/extrabiblical.ts — new rowToEntry() parser + getExtraBiblicalEntry() wrapper
  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsExtrabiblicalEntry, ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion, ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
  • app/src/navigation/types.tsExtraBiblicalDetail route param
  • app/src/navigation/ExploreStack.tsxStack.Screen registration

Some additions overlap with PR #1596 (#1547's Index screen) — the types, query-module file, and barrel exports are identical additions that merge cleanly regardless of order.

Premium gating

Matches the DebatePanel / DebateDetailScreen precedent — props-based, screen wires usePremium() itself.

  • Free tier: Hero + first paragraph of brief_summary only; gated region replaced by "✨ Unlock the full entry" card with description; tap fires showUpgrade('explore', 'Extra-Biblical Literature').
  • Premium tier: all 10 sections visible with full deep-link behavior.

Deep-link handling — verse ref parsing

The Phase-1 extrabiblical seed (#1541) uses natural-language refs like "Jude 14-15" and "Genesis 6:1-4". The standard parseReference utility handles most of these but fails on single-chapter book refs ("Jude 14-15", "Obadiah 11", "Philemon 6") because its regex requires a colon before the verse.

Added a screen-local parseReferenceExtended that wraps parseReference with a fallback: if standard parsing fails and the book is single-chapter (Obadiah, Philemon, 2-3 John, Jude), treat the number as a verse and return chapter=1. This is the minimum needed to make Jude-heavy content (1 Enoch, Assumption of Moses) navigate correctly from their NT citations.

Note on issue file-path text

Issue says app/src/db/queries/extrabiblical.ts, but the actual repo convention is app/src/db/content/*.ts (18 sibling query modules). Placed at db/content/ to match real layout.

Note on further-reading URLs

The issue specifies Linking.openURL for external links, but the Phase-1 extrabiblical.json seed (#1541) has no URL fields — further_reading entries are text-only (title, author, note). Code supports URLs when present (guarded: TouchableOpacity only when url is a non-empty string, else plain View); when the content author adds URLs in a future PR, the tap handler lights up automatically.

How I verified

  • npx tsc --noEmit — clean ✅
  • npm run lint on touched files — 0 warnings on new/touched files (the 4 pre-existing master warnings in unrelated files are out of scope) ✅
  • npx jest (full suite) — 470 suites / 3497 tests pass

Tests (23/23 passing)

render — premium tier
✓ renders hero title + aliases
✓ renders tradition badges
✓ renders full brief_summary (both paragraphs)
✓ renders NT citations with ref + source + type
✓ renders OT allusions
✓ renders scholar voices
✓ renders related-debate chip and section header
✓ renders related-difficult-passage and related-journey chips
✓ renders further-reading items
✓ renders "Coming soon" placeholder when full_summary is null
✓ renders full_summary when it is present instead of placeholder
deep-link navigation — premium tier
✓ tapping NT citation navigates to Chapter with parsed book/chapter/verse
✓ tapping OT allusion navigates to Chapter with bookId
✓ tapping scholar navigates to ScholarBio
✓ tapping debate chip navigates to DebateDetail
✓ tapping difficult-passage chip navigates to DifficultPassageDetail
✓ tapping journey chip navigates to JourneyDetail
✓ tapping further-reading with url calls Linking.openURL
free tier (premium gating)
✓ renders only first paragraph of brief_summary
✓ hides all gated sections (NT citations, scholars, debates, etc.)
✓ shows unlock CTA and fires showUpgrade on tap
loading + not-found states
✓ shows loading indicator when loading=true
✓ shows "Entry not found" when entry=null after load

Merge order

Depends on: #1538 (extrabiblical schema), #1541 (12 seeded entries).

Pairs with:

Rollback

Revert this PR. ExtraBiblicalDetail route unregisters; ExtraBiblicalDetailScreen deleted. SecondTemplePanel chips and ExtraBiblicalIndexScreen tap handlers continue to call navigation.navigate('ExtraBiblicalDetail', …) — which will no longer resolve — but the guarded optional-callback in SecondTemplePanel means chips simply won't render if the parent doesn't wire them, and the Index screen would fail more loudly (acceptable if reverting deliberately).


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3523❌ 03523
Suites✅ 473❌ 0473

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 81.2s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 4a107d9 to 6d6dd18CompareApril 23, 2026 20:05
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 6d6dd18 to 608f5caCompareApril 23, 2026 20:20
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Coverage on this PR was 81.85% lines. Same temporary 79%/81% fallback
as #1596/#1597/#1580 with tracking issue #1599.
Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit,
etc.). 10 sections per the issue spec:
1. Hero (title + aliases + dates + 4-tradition status)
2. Brief summary
3. NT citations
4. OT allusions
5. Scholar voices
6. Related debates
7. Related difficult passages
8. Related journeys
9. Further reading
10. Full summary (or "Coming soon" placeholder)
Files added
app/src/components/ExtraBiblicalHero.tsx — hero (title, aliases,
dates, tradition-status badges)
app/src/screens/ExtraBiblicalDetailScreen.tsx — the screen
app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader
app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 23 tests
Files touched (some overlap with PR #1596 / #1547 — identical additions
that merge cleanly)
app/src/db/content/extrabiblical.ts — new rowToEntry() parser +
getExtraBiblicalEntry() wrapper
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — ExtrabiblicalEntry,
ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion,
ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
app/src/navigation/types.ts — ExtraBiblicalDetail route
app/src/navigation/ExploreStack.tsx — Stack.Screen registration
Premium gating (matches DebatePanel pattern — props-based, screen
wires usePremium() itself — mirroring DebateDetailScreen and
ArchaeologyDetailScreen)
Free tier : Hero + first paragraph of brief_summary only; gated
region replaced by '✨ Unlock the full entry' card; tap
fires showUpgrade('explore', 'Extra-Biblical Literature').
Premium : all 10 sections visible with full deep-link behavior.
Deep-links
- NT citations / OT allusions → Chapter screen. Verse refs parsed via
parseReference() (handles 'Genesis 6:1-4', '1 Cor 13:4') with an
extension for single-chapter books ('Jude 14-15' → jude 1:14). The
Phase-1 extrabiblical seed uses natural-language refs heavily, so
the extended parser is necessary — plain parseReference rejects
'Jude 14-15' because the regex requires a colon before verse.
- Scholar voices → ScholarBio screen (matches DebateDetail).
- Related debates → DebateDetail.
- Related difficult passages → DifficultPassageDetail.
- Related journeys → JourneyDetail.
- Further reading (with url) → Linking.openURL; rows without url
render as non-tappable text (the Phase-1 seed is text-only per
further_reading shape).
Note on issue file-path text
Issue says 'app/src/db/queries/extrabiblical.ts'. Actual repo
convention is app/src/db/content/*.ts (18 sibling query modules).
Placed at db/content/ to match real layout.
Null full_summary → 'Coming soon — expanded scholarly summary'
placeholder card; non-null renders the full text as regular body
prose. The full_summary field is filled in by Phase 4
(HWGTB-P4-02A/B/C/D).
How verified
npx tsc --noEmit — clean
npm run lint (src/) — 0 warnings on new/touched files
(4 pre-existing master warnings
unrelated to this PR)
npx jest (full suite) — 470 suites / 3497 tests pass
23 new tests cover:
- Hero render (title, aliases, tradition badges)
- Brief summary full vs first-paragraph-only
- All 7 mid-sections render with correct labels and row content
- Full-summary rendering vs 'Coming soon' placeholder
- Every deep-link: NT citation → Chapter (with verse), OT allusion
→ Chapter, scholar → ScholarBio, debate → DebateDetail, difficult
passage → DifficultPassageDetail, journey → JourneyDetail,
further-reading url → Linking.openURL
- Free-tier: first paragraph only, gated sections hidden, unlock
CTA fires showUpgrade with correct params
- Loading indicator + 'Entry not found' error state
Merge order
Depends on #1538 (extrabiblical schema), #1541 (12 seeded entries),
and overlaps with #1547 (Index screen) on the types, query module,
and barrel exports — identical additions that merge either way.
Pairs with #1595 (SecondTemplePanel) whose chips navigate here, and
#1547 whose entry cards navigate here.
Closes#1548
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 608f5ca to 2baf275CompareApril 23, 2026 20:26
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmaster merged commit 801f3e7 into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p2-03-extrabiblical-detail branch April 23, 2026 20:35
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster added a commit that referenced this pull request Apr 23, 2026
* feat(app): CanonComparisonScreen (#1550)
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
* fix(types): add BookIntro to ExploreStackParamList
The CanonComparison screen registered BookIntroScreen on ExploreStack
but the param list didn't declare it, causing TS2322 on navigate calls
from CanonComparisonScreen.
Adds the route to ExploreStackParamList alongside the existing
CanonComparison entry. BookIntro also exists on RootStack; same route
on two stacks is fine for React Navigation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude (for Craig) <claude@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HWGTB-P2-03] App: ExtraBiblicalDetailScreen

2 participants

@CraigBuckmaster@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(app): ExtraBiblicalDetailScreen (#1548) by CraigBuckmaster · Pull Request #1597 · CraigBuckmaster/ScriptureDeepDive · GitHub
Skip to content

feat(app): ExtraBiblicalDetailScreen (#1548) - #1597

Merged
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail
Apr 23, 2026
Merged

feat(app): ExtraBiblicalDetailScreen (#1548)#1597
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit, etc.) — the deep-link target for SecondTemplePanel chips (#1595) and ExtraBiblicalIndexScreen card taps (#1596). Renders 10 sections per the issue spec.

Closes

Closes#1548

Sections (in order)

  1. Hero — title + aliases + dates/language + 4-tradition status badges (ExtraBiblicalHero component)
  2. Brief summary — always visible (full on premium, first paragraph only on free)
  3. NT citations — tappable → Chapter screen w/ verse highlight
  4. OT allusions — tappable → Chapter screen
  5. Scholar voices — tappable → ScholarBio screen
  6. Related debates — chips → DebateDetail
  7. Related difficult passages — chips → DifficultPassageDetail
  8. Related journeys — chips → JourneyDetail
  9. Further reading — url → Linking.openURL; text-only rows render as non-tappable (the Phase-1 seed has no URLs)
  10. Full summary — prose if populated, else "Coming soon — expanded scholarly summary" placeholder card

Files added

  • app/src/components/ExtraBiblicalHero.tsx — 170 LOC
  • app/src/screens/ExtraBiblicalDetailScreen.tsx — 703 LOC
  • app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader, 51 LOC
  • app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 312 LOC, 23 tests

Files touched

  • app/src/db/content/extrabiblical.ts — new rowToEntry() parser + getExtraBiblicalEntry() wrapper
  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsExtrabiblicalEntry, ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion, ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
  • app/src/navigation/types.tsExtraBiblicalDetail route param
  • app/src/navigation/ExploreStack.tsxStack.Screen registration

Some additions overlap with PR #1596 (#1547's Index screen) — the types, query-module file, and barrel exports are identical additions that merge cleanly regardless of order.

Premium gating

Matches the DebatePanel / DebateDetailScreen precedent — props-based, screen wires usePremium() itself.

  • Free tier: Hero + first paragraph of brief_summary only; gated region replaced by "✨ Unlock the full entry" card with description; tap fires showUpgrade('explore', 'Extra-Biblical Literature').
  • Premium tier: all 10 sections visible with full deep-link behavior.

Deep-link handling — verse ref parsing

The Phase-1 extrabiblical seed (#1541) uses natural-language refs like "Jude 14-15" and "Genesis 6:1-4". The standard parseReference utility handles most of these but fails on single-chapter book refs ("Jude 14-15", "Obadiah 11", "Philemon 6") because its regex requires a colon before the verse.

Added a screen-local parseReferenceExtended that wraps parseReference with a fallback: if standard parsing fails and the book is single-chapter (Obadiah, Philemon, 2-3 John, Jude), treat the number as a verse and return chapter=1. This is the minimum needed to make Jude-heavy content (1 Enoch, Assumption of Moses) navigate correctly from their NT citations.

Note on issue file-path text

Issue says app/src/db/queries/extrabiblical.ts, but the actual repo convention is app/src/db/content/*.ts (18 sibling query modules). Placed at db/content/ to match real layout.

Note on further-reading URLs

The issue specifies Linking.openURL for external links, but the Phase-1 extrabiblical.json seed (#1541) has no URL fields — further_reading entries are text-only (title, author, note). Code supports URLs when present (guarded: TouchableOpacity only when url is a non-empty string, else plain View); when the content author adds URLs in a future PR, the tap handler lights up automatically.

How I verified

  • npx tsc --noEmit — clean ✅
  • npm run lint on touched files — 0 warnings on new/touched files (the 4 pre-existing master warnings in unrelated files are out of scope) ✅
  • npx jest (full suite) — 470 suites / 3497 tests pass

Tests (23/23 passing)

render — premium tier
✓ renders hero title + aliases
✓ renders tradition badges
✓ renders full brief_summary (both paragraphs)
✓ renders NT citations with ref + source + type
✓ renders OT allusions
✓ renders scholar voices
✓ renders related-debate chip and section header
✓ renders related-difficult-passage and related-journey chips
✓ renders further-reading items
✓ renders "Coming soon" placeholder when full_summary is null
✓ renders full_summary when it is present instead of placeholder
deep-link navigation — premium tier
✓ tapping NT citation navigates to Chapter with parsed book/chapter/verse
✓ tapping OT allusion navigates to Chapter with bookId
✓ tapping scholar navigates to ScholarBio
✓ tapping debate chip navigates to DebateDetail
✓ tapping difficult-passage chip navigates to DifficultPassageDetail
✓ tapping journey chip navigates to JourneyDetail
✓ tapping further-reading with url calls Linking.openURL
free tier (premium gating)
✓ renders only first paragraph of brief_summary
✓ hides all gated sections (NT citations, scholars, debates, etc.)
✓ shows unlock CTA and fires showUpgrade on tap
loading + not-found states
✓ shows loading indicator when loading=true
✓ shows "Entry not found" when entry=null after load

Merge order

Depends on: #1538 (extrabiblical schema), #1541 (12 seeded entries).

Pairs with:

Rollback

Revert this PR. ExtraBiblicalDetail route unregisters; ExtraBiblicalDetailScreen deleted. SecondTemplePanel chips and ExtraBiblicalIndexScreen tap handlers continue to call navigation.navigate('ExtraBiblicalDetail', …) — which will no longer resolve — but the guarded optional-callback in SecondTemplePanel means chips simply won't render if the parent doesn't wire them, and the Index screen would fail more loudly (acceptable if reverting deliberately).


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3523❌ 03523
Suites✅ 473❌ 0473

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 81.2s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 4a107d9 to 6d6dd18CompareApril 23, 2026 20:05
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 6d6dd18 to 608f5caCompareApril 23, 2026 20:20
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Coverage on this PR was 81.85% lines. Same temporary 79%/81% fallback
as #1596/#1597/#1580 with tracking issue #1599.
Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit,
etc.). 10 sections per the issue spec:
1. Hero (title + aliases + dates + 4-tradition status)
2. Brief summary
3. NT citations
4. OT allusions
5. Scholar voices
6. Related debates
7. Related difficult passages
8. Related journeys
9. Further reading
10. Full summary (or "Coming soon" placeholder)
Files added
app/src/components/ExtraBiblicalHero.tsx — hero (title, aliases,
dates, tradition-status badges)
app/src/screens/ExtraBiblicalDetailScreen.tsx — the screen
app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader
app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 23 tests
Files touched (some overlap with PR #1596 / #1547 — identical additions
that merge cleanly)
app/src/db/content/extrabiblical.ts — new rowToEntry() parser +
getExtraBiblicalEntry() wrapper
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — ExtrabiblicalEntry,
ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion,
ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
app/src/navigation/types.ts — ExtraBiblicalDetail route
app/src/navigation/ExploreStack.tsx — Stack.Screen registration
Premium gating (matches DebatePanel pattern — props-based, screen
wires usePremium() itself — mirroring DebateDetailScreen and
ArchaeologyDetailScreen)
Free tier : Hero + first paragraph of brief_summary only; gated
region replaced by '✨ Unlock the full entry' card; tap
fires showUpgrade('explore', 'Extra-Biblical Literature').
Premium : all 10 sections visible with full deep-link behavior.
Deep-links
- NT citations / OT allusions → Chapter screen. Verse refs parsed via
parseReference() (handles 'Genesis 6:1-4', '1 Cor 13:4') with an
extension for single-chapter books ('Jude 14-15' → jude 1:14). The
Phase-1 extrabiblical seed uses natural-language refs heavily, so
the extended parser is necessary — plain parseReference rejects
'Jude 14-15' because the regex requires a colon before verse.
- Scholar voices → ScholarBio screen (matches DebateDetail).
- Related debates → DebateDetail.
- Related difficult passages → DifficultPassageDetail.
- Related journeys → JourneyDetail.
- Further reading (with url) → Linking.openURL; rows without url
render as non-tappable text (the Phase-1 seed is text-only per
further_reading shape).
Note on issue file-path text
Issue says 'app/src/db/queries/extrabiblical.ts'. Actual repo
convention is app/src/db/content/*.ts (18 sibling query modules).
Placed at db/content/ to match real layout.
Null full_summary → 'Coming soon — expanded scholarly summary'
placeholder card; non-null renders the full text as regular body
prose. The full_summary field is filled in by Phase 4
(HWGTB-P4-02A/B/C/D).
How verified
npx tsc --noEmit — clean
npm run lint (src/) — 0 warnings on new/touched files
(4 pre-existing master warnings
unrelated to this PR)
npx jest (full suite) — 470 suites / 3497 tests pass
23 new tests cover:
- Hero render (title, aliases, tradition badges)
- Brief summary full vs first-paragraph-only
- All 7 mid-sections render with correct labels and row content
- Full-summary rendering vs 'Coming soon' placeholder
- Every deep-link: NT citation → Chapter (with verse), OT allusion
→ Chapter, scholar → ScholarBio, debate → DebateDetail, difficult
passage → DifficultPassageDetail, journey → JourneyDetail,
further-reading url → Linking.openURL
- Free-tier: first paragraph only, gated sections hidden, unlock
CTA fires showUpgrade with correct params
- Loading indicator + 'Entry not found' error state
Merge order
Depends on #1538 (extrabiblical schema), #1541 (12 seeded entries),
and overlaps with #1547 (Index screen) on the types, query module,
and barrel exports — identical additions that merge either way.
Pairs with #1595 (SecondTemplePanel) whose chips navigate here, and
#1547 whose entry cards navigate here.
Closes#1548
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 608f5ca to 2baf275CompareApril 23, 2026 20:26
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmaster merged commit 801f3e7 into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p2-03-extrabiblical-detail branch April 23, 2026 20:35
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster added a commit that referenced this pull request Apr 23, 2026
* feat(app): CanonComparisonScreen (#1550)
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
* fix(types): add BookIntro to ExploreStackParamList
The CanonComparison screen registered BookIntroScreen on ExploreStack
but the param list didn't declare it, causing TS2322 on navigate calls
from CanonComparisonScreen.
Adds the route to ExploreStackParamList alongside the existing
CanonComparison entry. BookIntro also exists on RootStack; same route
on two stacks is fine for React Navigation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude (for Craig) <claude@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HWGTB-P2-03] App: ExtraBiblicalDetailScreen

2 participants

@CraigBuckmaster@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(app): ExtraBiblicalDetailScreen (#1548) by CraigBuckmaster · Pull Request #1597 · CraigBuckmaster/ScriptureDeepDive · GitHub
Skip to content

feat(app): ExtraBiblicalDetailScreen (#1548) - #1597

Merged
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail
Apr 23, 2026
Merged

feat(app): ExtraBiblicalDetailScreen (#1548)#1597
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit, etc.) — the deep-link target for SecondTemplePanel chips (#1595) and ExtraBiblicalIndexScreen card taps (#1596). Renders 10 sections per the issue spec.

Closes

Closes#1548

Sections (in order)

  1. Hero — title + aliases + dates/language + 4-tradition status badges (ExtraBiblicalHero component)
  2. Brief summary — always visible (full on premium, first paragraph only on free)
  3. NT citations — tappable → Chapter screen w/ verse highlight
  4. OT allusions — tappable → Chapter screen
  5. Scholar voices — tappable → ScholarBio screen
  6. Related debates — chips → DebateDetail
  7. Related difficult passages — chips → DifficultPassageDetail
  8. Related journeys — chips → JourneyDetail
  9. Further reading — url → Linking.openURL; text-only rows render as non-tappable (the Phase-1 seed has no URLs)
  10. Full summary — prose if populated, else "Coming soon — expanded scholarly summary" placeholder card

Files added

  • app/src/components/ExtraBiblicalHero.tsx — 170 LOC
  • app/src/screens/ExtraBiblicalDetailScreen.tsx — 703 LOC
  • app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader, 51 LOC
  • app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 312 LOC, 23 tests

Files touched

  • app/src/db/content/extrabiblical.ts — new rowToEntry() parser + getExtraBiblicalEntry() wrapper
  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsExtrabiblicalEntry, ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion, ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
  • app/src/navigation/types.tsExtraBiblicalDetail route param
  • app/src/navigation/ExploreStack.tsxStack.Screen registration

Some additions overlap with PR #1596 (#1547's Index screen) — the types, query-module file, and barrel exports are identical additions that merge cleanly regardless of order.

Premium gating

Matches the DebatePanel / DebateDetailScreen precedent — props-based, screen wires usePremium() itself.

  • Free tier: Hero + first paragraph of brief_summary only; gated region replaced by "✨ Unlock the full entry" card with description; tap fires showUpgrade('explore', 'Extra-Biblical Literature').
  • Premium tier: all 10 sections visible with full deep-link behavior.

Deep-link handling — verse ref parsing

The Phase-1 extrabiblical seed (#1541) uses natural-language refs like "Jude 14-15" and "Genesis 6:1-4". The standard parseReference utility handles most of these but fails on single-chapter book refs ("Jude 14-15", "Obadiah 11", "Philemon 6") because its regex requires a colon before the verse.

Added a screen-local parseReferenceExtended that wraps parseReference with a fallback: if standard parsing fails and the book is single-chapter (Obadiah, Philemon, 2-3 John, Jude), treat the number as a verse and return chapter=1. This is the minimum needed to make Jude-heavy content (1 Enoch, Assumption of Moses) navigate correctly from their NT citations.

Note on issue file-path text

Issue says app/src/db/queries/extrabiblical.ts, but the actual repo convention is app/src/db/content/*.ts (18 sibling query modules). Placed at db/content/ to match real layout.

Note on further-reading URLs

The issue specifies Linking.openURL for external links, but the Phase-1 extrabiblical.json seed (#1541) has no URL fields — further_reading entries are text-only (title, author, note). Code supports URLs when present (guarded: TouchableOpacity only when url is a non-empty string, else plain View); when the content author adds URLs in a future PR, the tap handler lights up automatically.

How I verified

  • npx tsc --noEmit — clean ✅
  • npm run lint on touched files — 0 warnings on new/touched files (the 4 pre-existing master warnings in unrelated files are out of scope) ✅
  • npx jest (full suite) — 470 suites / 3497 tests pass

Tests (23/23 passing)

render — premium tier
✓ renders hero title + aliases
✓ renders tradition badges
✓ renders full brief_summary (both paragraphs)
✓ renders NT citations with ref + source + type
✓ renders OT allusions
✓ renders scholar voices
✓ renders related-debate chip and section header
✓ renders related-difficult-passage and related-journey chips
✓ renders further-reading items
✓ renders "Coming soon" placeholder when full_summary is null
✓ renders full_summary when it is present instead of placeholder
deep-link navigation — premium tier
✓ tapping NT citation navigates to Chapter with parsed book/chapter/verse
✓ tapping OT allusion navigates to Chapter with bookId
✓ tapping scholar navigates to ScholarBio
✓ tapping debate chip navigates to DebateDetail
✓ tapping difficult-passage chip navigates to DifficultPassageDetail
✓ tapping journey chip navigates to JourneyDetail
✓ tapping further-reading with url calls Linking.openURL
free tier (premium gating)
✓ renders only first paragraph of brief_summary
✓ hides all gated sections (NT citations, scholars, debates, etc.)
✓ shows unlock CTA and fires showUpgrade on tap
loading + not-found states
✓ shows loading indicator when loading=true
✓ shows "Entry not found" when entry=null after load

Merge order

Depends on: #1538 (extrabiblical schema), #1541 (12 seeded entries).

Pairs with:

Rollback

Revert this PR. ExtraBiblicalDetail route unregisters; ExtraBiblicalDetailScreen deleted. SecondTemplePanel chips and ExtraBiblicalIndexScreen tap handlers continue to call navigation.navigate('ExtraBiblicalDetail', …) — which will no longer resolve — but the guarded optional-callback in SecondTemplePanel means chips simply won't render if the parent doesn't wire them, and the Index screen would fail more loudly (acceptable if reverting deliberately).


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3523❌ 03523
Suites✅ 473❌ 0473

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 81.2s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 4a107d9 to 6d6dd18CompareApril 23, 2026 20:05
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 6d6dd18 to 608f5caCompareApril 23, 2026 20:20
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Coverage on this PR was 81.85% lines. Same temporary 79%/81% fallback
as #1596/#1597/#1580 with tracking issue #1599.
Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit,
etc.). 10 sections per the issue spec:
1. Hero (title + aliases + dates + 4-tradition status)
2. Brief summary
3. NT citations
4. OT allusions
5. Scholar voices
6. Related debates
7. Related difficult passages
8. Related journeys
9. Further reading
10. Full summary (or "Coming soon" placeholder)
Files added
app/src/components/ExtraBiblicalHero.tsx — hero (title, aliases,
dates, tradition-status badges)
app/src/screens/ExtraBiblicalDetailScreen.tsx — the screen
app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader
app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 23 tests
Files touched (some overlap with PR #1596 / #1547 — identical additions
that merge cleanly)
app/src/db/content/extrabiblical.ts — new rowToEntry() parser +
getExtraBiblicalEntry() wrapper
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — ExtrabiblicalEntry,
ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion,
ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
app/src/navigation/types.ts — ExtraBiblicalDetail route
app/src/navigation/ExploreStack.tsx — Stack.Screen registration
Premium gating (matches DebatePanel pattern — props-based, screen
wires usePremium() itself — mirroring DebateDetailScreen and
ArchaeologyDetailScreen)
Free tier : Hero + first paragraph of brief_summary only; gated
region replaced by '✨ Unlock the full entry' card; tap
fires showUpgrade('explore', 'Extra-Biblical Literature').
Premium : all 10 sections visible with full deep-link behavior.
Deep-links
- NT citations / OT allusions → Chapter screen. Verse refs parsed via
parseReference() (handles 'Genesis 6:1-4', '1 Cor 13:4') with an
extension for single-chapter books ('Jude 14-15' → jude 1:14). The
Phase-1 extrabiblical seed uses natural-language refs heavily, so
the extended parser is necessary — plain parseReference rejects
'Jude 14-15' because the regex requires a colon before verse.
- Scholar voices → ScholarBio screen (matches DebateDetail).
- Related debates → DebateDetail.
- Related difficult passages → DifficultPassageDetail.
- Related journeys → JourneyDetail.
- Further reading (with url) → Linking.openURL; rows without url
render as non-tappable text (the Phase-1 seed is text-only per
further_reading shape).
Note on issue file-path text
Issue says 'app/src/db/queries/extrabiblical.ts'. Actual repo
convention is app/src/db/content/*.ts (18 sibling query modules).
Placed at db/content/ to match real layout.
Null full_summary → 'Coming soon — expanded scholarly summary'
placeholder card; non-null renders the full text as regular body
prose. The full_summary field is filled in by Phase 4
(HWGTB-P4-02A/B/C/D).
How verified
npx tsc --noEmit — clean
npm run lint (src/) — 0 warnings on new/touched files
(4 pre-existing master warnings
unrelated to this PR)
npx jest (full suite) — 470 suites / 3497 tests pass
23 new tests cover:
- Hero render (title, aliases, tradition badges)
- Brief summary full vs first-paragraph-only
- All 7 mid-sections render with correct labels and row content
- Full-summary rendering vs 'Coming soon' placeholder
- Every deep-link: NT citation → Chapter (with verse), OT allusion
→ Chapter, scholar → ScholarBio, debate → DebateDetail, difficult
passage → DifficultPassageDetail, journey → JourneyDetail,
further-reading url → Linking.openURL
- Free-tier: first paragraph only, gated sections hidden, unlock
CTA fires showUpgrade with correct params
- Loading indicator + 'Entry not found' error state
Merge order
Depends on #1538 (extrabiblical schema), #1541 (12 seeded entries),
and overlaps with #1547 (Index screen) on the types, query module,
and barrel exports — identical additions that merge either way.
Pairs with #1595 (SecondTemplePanel) whose chips navigate here, and
#1547 whose entry cards navigate here.
Closes#1548
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 608f5ca to 2baf275CompareApril 23, 2026 20:26
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmaster merged commit 801f3e7 into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p2-03-extrabiblical-detail branch April 23, 2026 20:35
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster added a commit that referenced this pull request Apr 23, 2026
* feat(app): CanonComparisonScreen (#1550)
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
* fix(types): add BookIntro to ExploreStackParamList
The CanonComparison screen registered BookIntroScreen on ExploreStack
but the param list didn't declare it, causing TS2322 on navigate calls
from CanonComparisonScreen.
Adds the route to ExploreStackParamList alongside the existing
CanonComparison entry. BookIntro also exists on RootStack; same route
on two stacks is fine for React Navigation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude (for Craig) <claude@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HWGTB-P2-03] App: ExtraBiblicalDetailScreen

2 participants

@CraigBuckmaster@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(app): ExtraBiblicalDetailScreen (#1548) by CraigBuckmaster · Pull Request #1597 · CraigBuckmaster/ScriptureDeepDive · GitHub
Skip to content

feat(app): ExtraBiblicalDetailScreen (#1548) - #1597

Merged
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail
Apr 23, 2026
Merged

feat(app): ExtraBiblicalDetailScreen (#1548)#1597
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit, etc.) — the deep-link target for SecondTemplePanel chips (#1595) and ExtraBiblicalIndexScreen card taps (#1596). Renders 10 sections per the issue spec.

Closes

Closes#1548

Sections (in order)

  1. Hero — title + aliases + dates/language + 4-tradition status badges (ExtraBiblicalHero component)
  2. Brief summary — always visible (full on premium, first paragraph only on free)
  3. NT citations — tappable → Chapter screen w/ verse highlight
  4. OT allusions — tappable → Chapter screen
  5. Scholar voices — tappable → ScholarBio screen
  6. Related debates — chips → DebateDetail
  7. Related difficult passages — chips → DifficultPassageDetail
  8. Related journeys — chips → JourneyDetail
  9. Further reading — url → Linking.openURL; text-only rows render as non-tappable (the Phase-1 seed has no URLs)
  10. Full summary — prose if populated, else "Coming soon — expanded scholarly summary" placeholder card

Files added

  • app/src/components/ExtraBiblicalHero.tsx — 170 LOC
  • app/src/screens/ExtraBiblicalDetailScreen.tsx — 703 LOC
  • app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader, 51 LOC
  • app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 312 LOC, 23 tests

Files touched

  • app/src/db/content/extrabiblical.ts — new rowToEntry() parser + getExtraBiblicalEntry() wrapper
  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsExtrabiblicalEntry, ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion, ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
  • app/src/navigation/types.tsExtraBiblicalDetail route param
  • app/src/navigation/ExploreStack.tsxStack.Screen registration

Some additions overlap with PR #1596 (#1547's Index screen) — the types, query-module file, and barrel exports are identical additions that merge cleanly regardless of order.

Premium gating

Matches the DebatePanel / DebateDetailScreen precedent — props-based, screen wires usePremium() itself.

  • Free tier: Hero + first paragraph of brief_summary only; gated region replaced by "✨ Unlock the full entry" card with description; tap fires showUpgrade('explore', 'Extra-Biblical Literature').
  • Premium tier: all 10 sections visible with full deep-link behavior.

Deep-link handling — verse ref parsing

The Phase-1 extrabiblical seed (#1541) uses natural-language refs like "Jude 14-15" and "Genesis 6:1-4". The standard parseReference utility handles most of these but fails on single-chapter book refs ("Jude 14-15", "Obadiah 11", "Philemon 6") because its regex requires a colon before the verse.

Added a screen-local parseReferenceExtended that wraps parseReference with a fallback: if standard parsing fails and the book is single-chapter (Obadiah, Philemon, 2-3 John, Jude), treat the number as a verse and return chapter=1. This is the minimum needed to make Jude-heavy content (1 Enoch, Assumption of Moses) navigate correctly from their NT citations.

Note on issue file-path text

Issue says app/src/db/queries/extrabiblical.ts, but the actual repo convention is app/src/db/content/*.ts (18 sibling query modules). Placed at db/content/ to match real layout.

Note on further-reading URLs

The issue specifies Linking.openURL for external links, but the Phase-1 extrabiblical.json seed (#1541) has no URL fields — further_reading entries are text-only (title, author, note). Code supports URLs when present (guarded: TouchableOpacity only when url is a non-empty string, else plain View); when the content author adds URLs in a future PR, the tap handler lights up automatically.

How I verified

  • npx tsc --noEmit — clean ✅
  • npm run lint on touched files — 0 warnings on new/touched files (the 4 pre-existing master warnings in unrelated files are out of scope) ✅
  • npx jest (full suite) — 470 suites / 3497 tests pass

Tests (23/23 passing)

render — premium tier
✓ renders hero title + aliases
✓ renders tradition badges
✓ renders full brief_summary (both paragraphs)
✓ renders NT citations with ref + source + type
✓ renders OT allusions
✓ renders scholar voices
✓ renders related-debate chip and section header
✓ renders related-difficult-passage and related-journey chips
✓ renders further-reading items
✓ renders "Coming soon" placeholder when full_summary is null
✓ renders full_summary when it is present instead of placeholder
deep-link navigation — premium tier
✓ tapping NT citation navigates to Chapter with parsed book/chapter/verse
✓ tapping OT allusion navigates to Chapter with bookId
✓ tapping scholar navigates to ScholarBio
✓ tapping debate chip navigates to DebateDetail
✓ tapping difficult-passage chip navigates to DifficultPassageDetail
✓ tapping journey chip navigates to JourneyDetail
✓ tapping further-reading with url calls Linking.openURL
free tier (premium gating)
✓ renders only first paragraph of brief_summary
✓ hides all gated sections (NT citations, scholars, debates, etc.)
✓ shows unlock CTA and fires showUpgrade on tap
loading + not-found states
✓ shows loading indicator when loading=true
✓ shows "Entry not found" when entry=null after load

Merge order

Depends on: #1538 (extrabiblical schema), #1541 (12 seeded entries).

Pairs with:

Rollback

Revert this PR. ExtraBiblicalDetail route unregisters; ExtraBiblicalDetailScreen deleted. SecondTemplePanel chips and ExtraBiblicalIndexScreen tap handlers continue to call navigation.navigate('ExtraBiblicalDetail', …) — which will no longer resolve — but the guarded optional-callback in SecondTemplePanel means chips simply won't render if the parent doesn't wire them, and the Index screen would fail more loudly (acceptable if reverting deliberately).


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3523❌ 03523
Suites✅ 473❌ 0473

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 81.2s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 4a107d9 to 6d6dd18CompareApril 23, 2026 20:05
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 6d6dd18 to 608f5caCompareApril 23, 2026 20:20
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Coverage on this PR was 81.85% lines. Same temporary 79%/81% fallback
as #1596/#1597/#1580 with tracking issue #1599.
Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit,
etc.). 10 sections per the issue spec:
1. Hero (title + aliases + dates + 4-tradition status)
2. Brief summary
3. NT citations
4. OT allusions
5. Scholar voices
6. Related debates
7. Related difficult passages
8. Related journeys
9. Further reading
10. Full summary (or "Coming soon" placeholder)
Files added
app/src/components/ExtraBiblicalHero.tsx — hero (title, aliases,
dates, tradition-status badges)
app/src/screens/ExtraBiblicalDetailScreen.tsx — the screen
app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader
app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 23 tests
Files touched (some overlap with PR #1596 / #1547 — identical additions
that merge cleanly)
app/src/db/content/extrabiblical.ts — new rowToEntry() parser +
getExtraBiblicalEntry() wrapper
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — ExtrabiblicalEntry,
ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion,
ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
app/src/navigation/types.ts — ExtraBiblicalDetail route
app/src/navigation/ExploreStack.tsx — Stack.Screen registration
Premium gating (matches DebatePanel pattern — props-based, screen
wires usePremium() itself — mirroring DebateDetailScreen and
ArchaeologyDetailScreen)
Free tier : Hero + first paragraph of brief_summary only; gated
region replaced by '✨ Unlock the full entry' card; tap
fires showUpgrade('explore', 'Extra-Biblical Literature').
Premium : all 10 sections visible with full deep-link behavior.
Deep-links
- NT citations / OT allusions → Chapter screen. Verse refs parsed via
parseReference() (handles 'Genesis 6:1-4', '1 Cor 13:4') with an
extension for single-chapter books ('Jude 14-15' → jude 1:14). The
Phase-1 extrabiblical seed uses natural-language refs heavily, so
the extended parser is necessary — plain parseReference rejects
'Jude 14-15' because the regex requires a colon before verse.
- Scholar voices → ScholarBio screen (matches DebateDetail).
- Related debates → DebateDetail.
- Related difficult passages → DifficultPassageDetail.
- Related journeys → JourneyDetail.
- Further reading (with url) → Linking.openURL; rows without url
render as non-tappable text (the Phase-1 seed is text-only per
further_reading shape).
Note on issue file-path text
Issue says 'app/src/db/queries/extrabiblical.ts'. Actual repo
convention is app/src/db/content/*.ts (18 sibling query modules).
Placed at db/content/ to match real layout.
Null full_summary → 'Coming soon — expanded scholarly summary'
placeholder card; non-null renders the full text as regular body
prose. The full_summary field is filled in by Phase 4
(HWGTB-P4-02A/B/C/D).
How verified
npx tsc --noEmit — clean
npm run lint (src/) — 0 warnings on new/touched files
(4 pre-existing master warnings
unrelated to this PR)
npx jest (full suite) — 470 suites / 3497 tests pass
23 new tests cover:
- Hero render (title, aliases, tradition badges)
- Brief summary full vs first-paragraph-only
- All 7 mid-sections render with correct labels and row content
- Full-summary rendering vs 'Coming soon' placeholder
- Every deep-link: NT citation → Chapter (with verse), OT allusion
→ Chapter, scholar → ScholarBio, debate → DebateDetail, difficult
passage → DifficultPassageDetail, journey → JourneyDetail,
further-reading url → Linking.openURL
- Free-tier: first paragraph only, gated sections hidden, unlock
CTA fires showUpgrade with correct params
- Loading indicator + 'Entry not found' error state
Merge order
Depends on #1538 (extrabiblical schema), #1541 (12 seeded entries),
and overlaps with #1547 (Index screen) on the types, query module,
and barrel exports — identical additions that merge either way.
Pairs with #1595 (SecondTemplePanel) whose chips navigate here, and
#1547 whose entry cards navigate here.
Closes#1548
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 608f5ca to 2baf275CompareApril 23, 2026 20:26
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmaster merged commit 801f3e7 into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p2-03-extrabiblical-detail branch April 23, 2026 20:35
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster added a commit that referenced this pull request Apr 23, 2026
* feat(app): CanonComparisonScreen (#1550)
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
* fix(types): add BookIntro to ExploreStackParamList
The CanonComparison screen registered BookIntroScreen on ExploreStack
but the param list didn't declare it, causing TS2322 on navigate calls
from CanonComparisonScreen.
Adds the route to ExploreStackParamList alongside the existing
CanonComparison entry. BookIntro also exists on RootStack; same route
on two stacks is fine for React Navigation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude (for Craig) <claude@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HWGTB-P2-03] App: ExtraBiblicalDetailScreen

2 participants

@CraigBuckmaster@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(app): ExtraBiblicalDetailScreen (#1548) by CraigBuckmaster · Pull Request #1597 · CraigBuckmaster/ScriptureDeepDive · GitHub
Skip to content

feat(app): ExtraBiblicalDetailScreen (#1548) - #1597

Merged
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail
Apr 23, 2026
Merged

feat(app): ExtraBiblicalDetailScreen (#1548)#1597
CraigBuckmaster merged 1 commit into
masterfrom
feature/hwgtb-p2-03-extrabiblical-detail

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit, etc.) — the deep-link target for SecondTemplePanel chips (#1595) and ExtraBiblicalIndexScreen card taps (#1596). Renders 10 sections per the issue spec.

Closes

Closes#1548

Sections (in order)

  1. Hero — title + aliases + dates/language + 4-tradition status badges (ExtraBiblicalHero component)
  2. Brief summary — always visible (full on premium, first paragraph only on free)
  3. NT citations — tappable → Chapter screen w/ verse highlight
  4. OT allusions — tappable → Chapter screen
  5. Scholar voices — tappable → ScholarBio screen
  6. Related debates — chips → DebateDetail
  7. Related difficult passages — chips → DifficultPassageDetail
  8. Related journeys — chips → JourneyDetail
  9. Further reading — url → Linking.openURL; text-only rows render as non-tappable (the Phase-1 seed has no URLs)
  10. Full summary — prose if populated, else "Coming soon — expanded scholarly summary" placeholder card

Files added

  • app/src/components/ExtraBiblicalHero.tsx — 170 LOC
  • app/src/screens/ExtraBiblicalDetailScreen.tsx — 703 LOC
  • app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader, 51 LOC
  • app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 312 LOC, 23 tests

Files touched

  • app/src/db/content/extrabiblical.ts — new rowToEntry() parser + getExtraBiblicalEntry() wrapper
  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsExtrabiblicalEntry, ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion, ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
  • app/src/navigation/types.tsExtraBiblicalDetail route param
  • app/src/navigation/ExploreStack.tsxStack.Screen registration

Some additions overlap with PR #1596 (#1547's Index screen) — the types, query-module file, and barrel exports are identical additions that merge cleanly regardless of order.

Premium gating

Matches the DebatePanel / DebateDetailScreen precedent — props-based, screen wires usePremium() itself.

  • Free tier: Hero + first paragraph of brief_summary only; gated region replaced by "✨ Unlock the full entry" card with description; tap fires showUpgrade('explore', 'Extra-Biblical Literature').
  • Premium tier: all 10 sections visible with full deep-link behavior.

Deep-link handling — verse ref parsing

The Phase-1 extrabiblical seed (#1541) uses natural-language refs like "Jude 14-15" and "Genesis 6:1-4". The standard parseReference utility handles most of these but fails on single-chapter book refs ("Jude 14-15", "Obadiah 11", "Philemon 6") because its regex requires a colon before the verse.

Added a screen-local parseReferenceExtended that wraps parseReference with a fallback: if standard parsing fails and the book is single-chapter (Obadiah, Philemon, 2-3 John, Jude), treat the number as a verse and return chapter=1. This is the minimum needed to make Jude-heavy content (1 Enoch, Assumption of Moses) navigate correctly from their NT citations.

Note on issue file-path text

Issue says app/src/db/queries/extrabiblical.ts, but the actual repo convention is app/src/db/content/*.ts (18 sibling query modules). Placed at db/content/ to match real layout.

Note on further-reading URLs

The issue specifies Linking.openURL for external links, but the Phase-1 extrabiblical.json seed (#1541) has no URL fields — further_reading entries are text-only (title, author, note). Code supports URLs when present (guarded: TouchableOpacity only when url is a non-empty string, else plain View); when the content author adds URLs in a future PR, the tap handler lights up automatically.

How I verified

  • npx tsc --noEmit — clean ✅
  • npm run lint on touched files — 0 warnings on new/touched files (the 4 pre-existing master warnings in unrelated files are out of scope) ✅
  • npx jest (full suite) — 470 suites / 3497 tests pass

Tests (23/23 passing)

render — premium tier
✓ renders hero title + aliases
✓ renders tradition badges
✓ renders full brief_summary (both paragraphs)
✓ renders NT citations with ref + source + type
✓ renders OT allusions
✓ renders scholar voices
✓ renders related-debate chip and section header
✓ renders related-difficult-passage and related-journey chips
✓ renders further-reading items
✓ renders "Coming soon" placeholder when full_summary is null
✓ renders full_summary when it is present instead of placeholder
deep-link navigation — premium tier
✓ tapping NT citation navigates to Chapter with parsed book/chapter/verse
✓ tapping OT allusion navigates to Chapter with bookId
✓ tapping scholar navigates to ScholarBio
✓ tapping debate chip navigates to DebateDetail
✓ tapping difficult-passage chip navigates to DifficultPassageDetail
✓ tapping journey chip navigates to JourneyDetail
✓ tapping further-reading with url calls Linking.openURL
free tier (premium gating)
✓ renders only first paragraph of brief_summary
✓ hides all gated sections (NT citations, scholars, debates, etc.)
✓ shows unlock CTA and fires showUpgrade on tap
loading + not-found states
✓ shows loading indicator when loading=true
✓ shows "Entry not found" when entry=null after load

Merge order

Depends on: #1538 (extrabiblical schema), #1541 (12 seeded entries).

Pairs with:

Rollback

Revert this PR. ExtraBiblicalDetail route unregisters; ExtraBiblicalDetailScreen deleted. SecondTemplePanel chips and ExtraBiblicalIndexScreen tap handlers continue to call navigation.navigate('ExtraBiblicalDetail', …) — which will no longer resolve — but the guarded optional-callback in SecondTemplePanel means chips simply won't render if the parent doesn't wire them, and the Index screen would fail more loudly (acceptable if reverting deliberately).


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3523❌ 03523
Suites✅ 473❌ 0473

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 81.2s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 4a107d9 to 6d6dd18CompareApril 23, 2026 20:05
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 6d6dd18 to 608f5caCompareApril 23, 2026 20:20
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Coverage on this PR was 81.85% lines. Same temporary 79%/81% fallback
as #1596/#1597/#1580 with tracking issue #1599.
Detail view for a single extrabiblical entry (1 Enoch, Jubilees, Tobit,
etc.). 10 sections per the issue spec:
1. Hero (title + aliases + dates + 4-tradition status)
2. Brief summary
3. NT citations
4. OT allusions
5. Scholar voices
6. Related debates
7. Related difficult passages
8. Related journeys
9. Further reading
10. Full summary (or "Coming soon" placeholder)
Files added
app/src/components/ExtraBiblicalHero.tsx — hero (title, aliases,
dates, tradition-status badges)
app/src/screens/ExtraBiblicalDetailScreen.tsx — the screen
app/src/hooks/useExtraBiblicalEntry.ts — single-entry loader
app/__tests__/screens/ExtraBiblicalDetailScreen.test.tsx — 23 tests
Files touched (some overlap with PR #1596 / #1547 — identical additions
that merge cleanly)
app/src/db/content/extrabiblical.ts — new rowToEntry() parser +
getExtraBiblicalEntry() wrapper
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — ExtrabiblicalEntry,
ExtrabiblicalNTCitation, ExtrabiblicalOTAllusion,
ExtrabiblicalScholarVoice, ExtrabiblicalFurtherReading
app/src/navigation/types.ts — ExtraBiblicalDetail route
app/src/navigation/ExploreStack.tsx — Stack.Screen registration
Premium gating (matches DebatePanel pattern — props-based, screen
wires usePremium() itself — mirroring DebateDetailScreen and
ArchaeologyDetailScreen)
Free tier : Hero + first paragraph of brief_summary only; gated
region replaced by '✨ Unlock the full entry' card; tap
fires showUpgrade('explore', 'Extra-Biblical Literature').
Premium : all 10 sections visible with full deep-link behavior.
Deep-links
- NT citations / OT allusions → Chapter screen. Verse refs parsed via
parseReference() (handles 'Genesis 6:1-4', '1 Cor 13:4') with an
extension for single-chapter books ('Jude 14-15' → jude 1:14). The
Phase-1 extrabiblical seed uses natural-language refs heavily, so
the extended parser is necessary — plain parseReference rejects
'Jude 14-15' because the regex requires a colon before verse.
- Scholar voices → ScholarBio screen (matches DebateDetail).
- Related debates → DebateDetail.
- Related difficult passages → DifficultPassageDetail.
- Related journeys → JourneyDetail.
- Further reading (with url) → Linking.openURL; rows without url
render as non-tappable text (the Phase-1 seed is text-only per
further_reading shape).
Note on issue file-path text
Issue says 'app/src/db/queries/extrabiblical.ts'. Actual repo
convention is app/src/db/content/*.ts (18 sibling query modules).
Placed at db/content/ to match real layout.
Null full_summary → 'Coming soon — expanded scholarly summary'
placeholder card; non-null renders the full text as regular body
prose. The full_summary field is filled in by Phase 4
(HWGTB-P4-02A/B/C/D).
How verified
npx tsc --noEmit — clean
npm run lint (src/) — 0 warnings on new/touched files
(4 pre-existing master warnings
unrelated to this PR)
npx jest (full suite) — 470 suites / 3497 tests pass
23 new tests cover:
- Hero render (title, aliases, tradition badges)
- Brief summary full vs first-paragraph-only
- All 7 mid-sections render with correct labels and row content
- Full-summary rendering vs 'Coming soon' placeholder
- Every deep-link: NT citation → Chapter (with verse), OT allusion
→ Chapter, scholar → ScholarBio, debate → DebateDetail, difficult
passage → DifficultPassageDetail, journey → JourneyDetail,
further-reading url → Linking.openURL
- Free-tier: first paragraph only, gated sections hidden, unlock
CTA fires showUpgrade with correct params
- Loading indicator + 'Entry not found' error state
Merge order
Depends on #1538 (extrabiblical schema), #1541 (12 seeded entries),
and overlaps with #1547 (Index screen) on the types, query module,
and barrel exports — identical additions that merge either way.
Pairs with #1595 (SecondTemplePanel) whose chips navigate here, and
#1547 whose entry cards navigate here.
Closes#1548
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p2-03-extrabiblical-detail branch from 608f5ca to 2baf275CompareApril 23, 2026 20:26
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
@CraigBuckmaster
CraigBuckmaster merged commit 801f3e7 into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p2-03-extrabiblical-detail branch April 23, 2026 20:35
CraigBuckmaster pushed a commit that referenced this pull request Apr 23, 2026
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
CraigBuckmaster added a commit that referenced this pull request Apr 23, 2026
* feat(app): CanonComparisonScreen (#1550)
Interactive side-by-side of the four major canon traditions. The
conversion-driving screen for the "How We Got The Bible" bundle —
readers see the Protestant, Catholic, Orthodox, and Ethiopian canons
next to each other, with book-diff highlighting, founding-council
timelines, and tap-through to per-book detail.
Files added
app/src/db/content/canonTraditions.ts — data access layer
(getAllCanonTraditions, getCanonTraditionById, defensive
sqlite_master table-exists guard)
app/src/hooks/useCanonTraditions.ts — loader + book-membership
computation (common vs subset book ids per tradition)
app/src/components/CanonColumn.tsx — one tradition's column
(header, scrollable book list with per-book tradition badges,
expandable distinctives, locked-preview variant for free tier)
app/src/screens/CanonComparisonScreen.tsx — the screen
app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests
Files touched
app/src/db/content/index.ts — barrel export
app/src/types/meta.ts — CanonTradition,
CanonListSection, CanonDistinctive, CanonFormationEvent,
CanonTraditionRow
app/src/navigation/types.ts — CanonComparison,
ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for
ExploreStack (ExtraBiblical routes typed ahead for cross-stack
consistency with #1596/#1597; BookIntro added so the canon-column
book tap can route without cross-tab hops)
app/src/navigation/ExploreStack.tsx — CanonComparison +
BookIntro Stack.Screen registrations via lazySuspense()
Layout — useWindowDimensions() driven (no third-party orientation
library, no Dimensions.addEventListener per the issue guidance)
Landscape (width > height): horizontal scroll across 4 columns,
each 320px wide with vertical book lists
Portrait (width <= height): tabs at top, one column visible at
a time, tap-to-switch
Column rendering
Header : tradition label + book count + first 2 formation events
Book list : scrollable; section headers (OT / NT / etc.); books in
all 4 traditions render in neutral style; books unique to a
subset carry gold-tinted tradition-id badges (Prot / Cath / EO /
Eth) showing which traditions contain them
Distinctives: expandable block at the bottom (2-4 entries from
the authored content)
Tap biblical book (present in books table) → BookIntro
Tap extrabiblical book (not in books table) → ExtraBiblicalDetail
(HWGTB-P2-03 / PR #1597)
Header
Back button + title "Canon Comparison" + right-aligned "How did
this happen?" CTA → JourneyDetail journeyId='canon-formation'
(HWGTB-P3-02 / #1551 — when the journey lands this activates)
Formation timeline footer
Expandable; merges all formation_events from all 4 traditions,
sorted by year, annotated with the source tradition's label.
Renders "{N} events across the four traditions" header.
Premium gating
Free tier: Protestant + Catholic columns fully visible. Orthodox
+ Ethiopian columns render header + first 5 rows with a gold
"✨ Unlock full canon" CTA that fires showUpgrade('feature',
'Canon Comparison'). Premium tier: all 4 columns fully readable.
Portrait tab labels show a 🔒 indicator for gated tabs.
How verified
npx tsc --noEmit — clean
npx eslint (touched files) — 0 warnings
npx jest (full suite) — 470 suites / 3486 tests pass
12 new tests cover:
- portrait: 4 tradition tabs render, active column renders,
tab tap switches visible column
- landscape: all 4 columns render simultaneously (no tabs)
- header "How did this happen?" CTA + navigation to canon-formation
journey
- premium gating: free-tier 🔒 on Orthodox/Ethiopian tabs, unlock
CTA fires showUpgrade, premium user sees no locks
- formation timeline: footer renders event count, expand shows
merged events sorted by year with tradition annotations
Merge order
Depends on #1539 (canon_traditions schema) and #1542 (the 4
seeded traditions). Works against a pre-#1539 DB: the table-exists
guard returns [] and the screen shows 'Canon data not yet
available' cleanly.
Pairs with #1548 (PR #1597 — ExtraBiblicalDetail) for extrabib
taps, and #1551 (canon-formation journey) for the header CTA.
Follow-up activation
When #1598's HowWeGotTheBibleLandingScreen ships alongside this,
flip its canonComparisonReady = true constant so the landing
entry activates live nav (currently rendered as 'Coming soon').
Closes#1550
* fix(types): add BookIntro to ExploreStackParamList
The CanonComparison screen registered BookIntroScreen on ExploreStack
but the param list didn't declare it, causing TS2322 on navigate calls
from CanonComparisonScreen.
Adds the route to ExploreStackParamList alongside the existing
CanonComparison entry. BookIntro also exists on RootStack; same route
on two stacks is fine for React Navigation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude (for Craig) <claude@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HWGTB-P2-03] App: ExtraBiblicalDetailScreen

2 participants

@CraigBuckmaster@claude