feat(app): CanonComparisonScreen (#1550) - #1600

Merged
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison
Apr 23, 2026
Merged

feat(app): CanonComparisonScreen (#1550)#1600
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

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.

Closes

Closes#1550

Files added

  • app/src/db/content/canonTraditions.ts — data access layer with defensive sqlite_master table-exists guard (110 LOC)
  • app/src/hooks/useCanonTraditions.ts — loader + book-membership computation (87 LOC)
  • app/src/components/CanonColumn.tsx — one tradition's column, reused in both layouts (395 LOC)
  • app/src/screens/CanonComparisonScreen.tsx — the screen itself (567 LOC)
  • app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests

Files touched

  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsCanonTradition, CanonListSection, CanonDistinctive, CanonFormationEvent, CanonTraditionRow
  • app/src/navigation/types.tsCanonComparison, ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for ExploreStackParamList
  • app/src/navigation/ExploreStack.tsxCanonComparison + BookIntroStack.Screen registrations via lazySuspense()

Layout — useWindowDimensions() driven

Per issue guidance (no third-party orientation lib, no Dimensions.addEventListener):

  • 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

Column rendering (CanonColumn.tsx)

  • Header: tradition label + book count + first 2 formation events as a terse "founding councils" subtitle
  • Book list: scrollable; section headers (OT / NT / etc. from canon_list[].section); 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 a biblical book (present in books table) → BookIntro
  • Tap an extrabiblical book (not in books table) → ExtraBiblicalDetail (HWGTB-P2-03 / PR feat(app): ExtraBiblicalDetailScreen (#1548) #1597)

Header

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 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 include a 🔒 indicator for gated tabs so free-tier users see the lock state at a glance without having to tap.

How I verified

  • npx tsc --noEmit — clean ✅
  • npx eslint on touched files with --max-warnings 00 warnings
  • npx jest (full suite) — 470 suites / 3486 tests pass

Tests (12/12 passing)

portrait layout
✓ renders all 4 tradition tabs
✓ renders the active column (Protestant by default)
✓ tapping a tradition tab switches the visible column
landscape layout
✓ renders all 4 columns simultaneously (no tabs)
book-diff highlighting
✓ renders the "How did this happen?" header CTA
✓ tapping "How did this happen?" navigates to JourneyDetail canon-formation
premium gating
✓ free user sees 🔒 on Orthodox + Ethiopian tabs
✓ free user selecting Orthodox sees the unlock CTA
✓ free user tap on Orthodox unlock CTA fires showUpgrade
✓ premium user does NOT see 🔒 on Orthodox / Ethiopian tabs
formation timeline footer
✓ renders the formation timeline header with event count
✓ expanding the timeline shows merged events sorted by year

Merge order

Depends on:

Pairs with:

Rollback

Revert this PR. CanonComparison route unregisters; BookIntro route removal from ExploreStack is additive (still registered in ReadStack). The CanonColumn component and query module disappear — no other screen references them.


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

⚠️Tests: Could not parse results

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch 2 times, most recently from 5b85122 to 2a04075CompareApril 23, 2026 20:20
@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3535❌ 03535
Suites✅ 474❌ 0474

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 86.5s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from 2a04075 to f53beb9CompareApril 23, 2026 20:28
claudeand others added 2 commits April 23, 2026 20:36
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
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.
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from f53beb9 to fd5214aCompareApril 23, 2026 20:36
@CraigBuckmaster
CraigBuckmaster merged commit 953e02c into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p3-01-canon-comparison branch April 23, 2026 20:39
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-P3-01] App: CanonComparisonScreen

2 participants

@CraigBuckmaster@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n 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;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(app): CanonComparisonScreen (#1550) - #1600

Merged
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison
Apr 23, 2026
Merged

feat(app): CanonComparisonScreen (#1550)#1600
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

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.

Closes

Closes#1550

Files added

  • app/src/db/content/canonTraditions.ts — data access layer with defensive sqlite_master table-exists guard (110 LOC)
  • app/src/hooks/useCanonTraditions.ts — loader + book-membership computation (87 LOC)
  • app/src/components/CanonColumn.tsx — one tradition's column, reused in both layouts (395 LOC)
  • app/src/screens/CanonComparisonScreen.tsx — the screen itself (567 LOC)
  • app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests

Files touched

  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsCanonTradition, CanonListSection, CanonDistinctive, CanonFormationEvent, CanonTraditionRow
  • app/src/navigation/types.tsCanonComparison, ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for ExploreStackParamList
  • app/src/navigation/ExploreStack.tsxCanonComparison + BookIntroStack.Screen registrations via lazySuspense()

Layout — useWindowDimensions() driven

Per issue guidance (no third-party orientation lib, no Dimensions.addEventListener):

  • 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

Column rendering (CanonColumn.tsx)

  • Header: tradition label + book count + first 2 formation events as a terse "founding councils" subtitle
  • Book list: scrollable; section headers (OT / NT / etc. from canon_list[].section); 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 a biblical book (present in books table) → BookIntro
  • Tap an extrabiblical book (not in books table) → ExtraBiblicalDetail (HWGTB-P2-03 / PR feat(app): ExtraBiblicalDetailScreen (#1548) #1597)

Header

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 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 include a 🔒 indicator for gated tabs so free-tier users see the lock state at a glance without having to tap.

How I verified

  • npx tsc --noEmit — clean ✅
  • npx eslint on touched files with --max-warnings 00 warnings
  • npx jest (full suite) — 470 suites / 3486 tests pass

Tests (12/12 passing)

portrait layout
✓ renders all 4 tradition tabs
✓ renders the active column (Protestant by default)
✓ tapping a tradition tab switches the visible column
landscape layout
✓ renders all 4 columns simultaneously (no tabs)
book-diff highlighting
✓ renders the "How did this happen?" header CTA
✓ tapping "How did this happen?" navigates to JourneyDetail canon-formation
premium gating
✓ free user sees 🔒 on Orthodox + Ethiopian tabs
✓ free user selecting Orthodox sees the unlock CTA
✓ free user tap on Orthodox unlock CTA fires showUpgrade
✓ premium user does NOT see 🔒 on Orthodox / Ethiopian tabs
formation timeline footer
✓ renders the formation timeline header with event count
✓ expanding the timeline shows merged events sorted by year

Merge order

Depends on:

Pairs with:

Rollback

Revert this PR. CanonComparison route unregisters; BookIntro route removal from ExploreStack is additive (still registered in ReadStack). The CanonColumn component and query module disappear — no other screen references them.


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

⚠️Tests: Could not parse results

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch 2 times, most recently from 5b85122 to 2a04075CompareApril 23, 2026 20:20
@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3535❌ 03535
Suites✅ 474❌ 0474

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 86.5s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from 2a04075 to f53beb9CompareApril 23, 2026 20:28
claudeand others added 2 commits April 23, 2026 20:36
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
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.
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from f53beb9 to fd5214aCompareApril 23, 2026 20:36
@CraigBuckmaster
CraigBuckmaster merged commit 953e02c into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p3-01-canon-comparison branch April 23, 2026 20:39
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-P3-01] App: CanonComparisonScreen

2 participants

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

feat(app): CanonComparisonScreen (#1550) - #1600

Merged
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison
Apr 23, 2026
Merged

feat(app): CanonComparisonScreen (#1550)#1600
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

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.

Closes

Closes#1550

Files added

  • app/src/db/content/canonTraditions.ts — data access layer with defensive sqlite_master table-exists guard (110 LOC)
  • app/src/hooks/useCanonTraditions.ts — loader + book-membership computation (87 LOC)
  • app/src/components/CanonColumn.tsx — one tradition's column, reused in both layouts (395 LOC)
  • app/src/screens/CanonComparisonScreen.tsx — the screen itself (567 LOC)
  • app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests

Files touched

  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsCanonTradition, CanonListSection, CanonDistinctive, CanonFormationEvent, CanonTraditionRow
  • app/src/navigation/types.tsCanonComparison, ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for ExploreStackParamList
  • app/src/navigation/ExploreStack.tsxCanonComparison + BookIntroStack.Screen registrations via lazySuspense()

Layout — useWindowDimensions() driven

Per issue guidance (no third-party orientation lib, no Dimensions.addEventListener):

  • 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

Column rendering (CanonColumn.tsx)

  • Header: tradition label + book count + first 2 formation events as a terse "founding councils" subtitle
  • Book list: scrollable; section headers (OT / NT / etc. from canon_list[].section); 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 a biblical book (present in books table) → BookIntro
  • Tap an extrabiblical book (not in books table) → ExtraBiblicalDetail (HWGTB-P2-03 / PR feat(app): ExtraBiblicalDetailScreen (#1548) #1597)

Header

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 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 include a 🔒 indicator for gated tabs so free-tier users see the lock state at a glance without having to tap.

How I verified

  • npx tsc --noEmit — clean ✅
  • npx eslint on touched files with --max-warnings 00 warnings
  • npx jest (full suite) — 470 suites / 3486 tests pass

Tests (12/12 passing)

portrait layout
✓ renders all 4 tradition tabs
✓ renders the active column (Protestant by default)
✓ tapping a tradition tab switches the visible column
landscape layout
✓ renders all 4 columns simultaneously (no tabs)
book-diff highlighting
✓ renders the "How did this happen?" header CTA
✓ tapping "How did this happen?" navigates to JourneyDetail canon-formation
premium gating
✓ free user sees 🔒 on Orthodox + Ethiopian tabs
✓ free user selecting Orthodox sees the unlock CTA
✓ free user tap on Orthodox unlock CTA fires showUpgrade
✓ premium user does NOT see 🔒 on Orthodox / Ethiopian tabs
formation timeline footer
✓ renders the formation timeline header with event count
✓ expanding the timeline shows merged events sorted by year

Merge order

Depends on:

Pairs with:

Rollback

Revert this PR. CanonComparison route unregisters; BookIntro route removal from ExploreStack is additive (still registered in ReadStack). The CanonColumn component and query module disappear — no other screen references them.


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

⚠️Tests: Could not parse results

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch 2 times, most recently from 5b85122 to 2a04075CompareApril 23, 2026 20:20
@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3535❌ 03535
Suites✅ 474❌ 0474

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 86.5s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from 2a04075 to f53beb9CompareApril 23, 2026 20:28
claudeand others added 2 commits April 23, 2026 20:36
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
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.
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from f53beb9 to fd5214aCompareApril 23, 2026 20:36
@CraigBuckmaster
CraigBuckmaster merged commit 953e02c into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p3-01-canon-comparison branch April 23, 2026 20:39
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-P3-01] App: CanonComparisonScreen

2 participants

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

feat(app): CanonComparisonScreen (#1550) - #1600

Merged
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison
Apr 23, 2026
Merged

feat(app): CanonComparisonScreen (#1550)#1600
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

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.

Closes

Closes#1550

Files added

  • app/src/db/content/canonTraditions.ts — data access layer with defensive sqlite_master table-exists guard (110 LOC)
  • app/src/hooks/useCanonTraditions.ts — loader + book-membership computation (87 LOC)
  • app/src/components/CanonColumn.tsx — one tradition's column, reused in both layouts (395 LOC)
  • app/src/screens/CanonComparisonScreen.tsx — the screen itself (567 LOC)
  • app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests

Files touched

  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsCanonTradition, CanonListSection, CanonDistinctive, CanonFormationEvent, CanonTraditionRow
  • app/src/navigation/types.tsCanonComparison, ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for ExploreStackParamList
  • app/src/navigation/ExploreStack.tsxCanonComparison + BookIntroStack.Screen registrations via lazySuspense()

Layout — useWindowDimensions() driven

Per issue guidance (no third-party orientation lib, no Dimensions.addEventListener):

  • 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

Column rendering (CanonColumn.tsx)

  • Header: tradition label + book count + first 2 formation events as a terse "founding councils" subtitle
  • Book list: scrollable; section headers (OT / NT / etc. from canon_list[].section); 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 a biblical book (present in books table) → BookIntro
  • Tap an extrabiblical book (not in books table) → ExtraBiblicalDetail (HWGTB-P2-03 / PR feat(app): ExtraBiblicalDetailScreen (#1548) #1597)

Header

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 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 include a 🔒 indicator for gated tabs so free-tier users see the lock state at a glance without having to tap.

How I verified

  • npx tsc --noEmit — clean ✅
  • npx eslint on touched files with --max-warnings 00 warnings
  • npx jest (full suite) — 470 suites / 3486 tests pass

Tests (12/12 passing)

portrait layout
✓ renders all 4 tradition tabs
✓ renders the active column (Protestant by default)
✓ tapping a tradition tab switches the visible column
landscape layout
✓ renders all 4 columns simultaneously (no tabs)
book-diff highlighting
✓ renders the "How did this happen?" header CTA
✓ tapping "How did this happen?" navigates to JourneyDetail canon-formation
premium gating
✓ free user sees 🔒 on Orthodox + Ethiopian tabs
✓ free user selecting Orthodox sees the unlock CTA
✓ free user tap on Orthodox unlock CTA fires showUpgrade
✓ premium user does NOT see 🔒 on Orthodox / Ethiopian tabs
formation timeline footer
✓ renders the formation timeline header with event count
✓ expanding the timeline shows merged events sorted by year

Merge order

Depends on:

Pairs with:

Rollback

Revert this PR. CanonComparison route unregisters; BookIntro route removal from ExploreStack is additive (still registered in ReadStack). The CanonColumn component and query module disappear — no other screen references them.


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

⚠️Tests: Could not parse results

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch 2 times, most recently from 5b85122 to 2a04075CompareApril 23, 2026 20:20
@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3535❌ 03535
Suites✅ 474❌ 0474

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 86.5s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from 2a04075 to f53beb9CompareApril 23, 2026 20:28
claudeand others added 2 commits April 23, 2026 20:36
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
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.
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from f53beb9 to fd5214aCompareApril 23, 2026 20:36
@CraigBuckmaster
CraigBuckmaster merged commit 953e02c into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p3-01-canon-comparison branch April 23, 2026 20:39
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-P3-01] App: CanonComparisonScreen

2 participants

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

feat(app): CanonComparisonScreen (#1550) - #1600

Merged
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison
Apr 23, 2026
Merged

feat(app): CanonComparisonScreen (#1550)#1600
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

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.

Closes

Closes#1550

Files added

  • app/src/db/content/canonTraditions.ts — data access layer with defensive sqlite_master table-exists guard (110 LOC)
  • app/src/hooks/useCanonTraditions.ts — loader + book-membership computation (87 LOC)
  • app/src/components/CanonColumn.tsx — one tradition's column, reused in both layouts (395 LOC)
  • app/src/screens/CanonComparisonScreen.tsx — the screen itself (567 LOC)
  • app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests

Files touched

  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsCanonTradition, CanonListSection, CanonDistinctive, CanonFormationEvent, CanonTraditionRow
  • app/src/navigation/types.tsCanonComparison, ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for ExploreStackParamList
  • app/src/navigation/ExploreStack.tsxCanonComparison + BookIntroStack.Screen registrations via lazySuspense()

Layout — useWindowDimensions() driven

Per issue guidance (no third-party orientation lib, no Dimensions.addEventListener):

  • 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

Column rendering (CanonColumn.tsx)

  • Header: tradition label + book count + first 2 formation events as a terse "founding councils" subtitle
  • Book list: scrollable; section headers (OT / NT / etc. from canon_list[].section); 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 a biblical book (present in books table) → BookIntro
  • Tap an extrabiblical book (not in books table) → ExtraBiblicalDetail (HWGTB-P2-03 / PR feat(app): ExtraBiblicalDetailScreen (#1548) #1597)

Header

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 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 include a 🔒 indicator for gated tabs so free-tier users see the lock state at a glance without having to tap.

How I verified

  • npx tsc --noEmit — clean ✅
  • npx eslint on touched files with --max-warnings 00 warnings
  • npx jest (full suite) — 470 suites / 3486 tests pass

Tests (12/12 passing)

portrait layout
✓ renders all 4 tradition tabs
✓ renders the active column (Protestant by default)
✓ tapping a tradition tab switches the visible column
landscape layout
✓ renders all 4 columns simultaneously (no tabs)
book-diff highlighting
✓ renders the "How did this happen?" header CTA
✓ tapping "How did this happen?" navigates to JourneyDetail canon-formation
premium gating
✓ free user sees 🔒 on Orthodox + Ethiopian tabs
✓ free user selecting Orthodox sees the unlock CTA
✓ free user tap on Orthodox unlock CTA fires showUpgrade
✓ premium user does NOT see 🔒 on Orthodox / Ethiopian tabs
formation timeline footer
✓ renders the formation timeline header with event count
✓ expanding the timeline shows merged events sorted by year

Merge order

Depends on:

Pairs with:

Rollback

Revert this PR. CanonComparison route unregisters; BookIntro route removal from ExploreStack is additive (still registered in ReadStack). The CanonColumn component and query module disappear — no other screen references them.


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

⚠️Tests: Could not parse results

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch 2 times, most recently from 5b85122 to 2a04075CompareApril 23, 2026 20:20
@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3535❌ 03535
Suites✅ 474❌ 0474

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 86.5s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from 2a04075 to f53beb9CompareApril 23, 2026 20:28
claudeand others added 2 commits April 23, 2026 20:36
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
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.
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from f53beb9 to fd5214aCompareApril 23, 2026 20:36
@CraigBuckmaster
CraigBuckmaster merged commit 953e02c into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p3-01-canon-comparison branch April 23, 2026 20:39
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-P3-01] App: CanonComparisonScreen

2 participants

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

feat(app): CanonComparisonScreen (#1550) - #1600

Merged
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison
Apr 23, 2026
Merged

feat(app): CanonComparisonScreen (#1550)#1600
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

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.

Closes

Closes#1550

Files added

  • app/src/db/content/canonTraditions.ts — data access layer with defensive sqlite_master table-exists guard (110 LOC)
  • app/src/hooks/useCanonTraditions.ts — loader + book-membership computation (87 LOC)
  • app/src/components/CanonColumn.tsx — one tradition's column, reused in both layouts (395 LOC)
  • app/src/screens/CanonComparisonScreen.tsx — the screen itself (567 LOC)
  • app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests

Files touched

  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsCanonTradition, CanonListSection, CanonDistinctive, CanonFormationEvent, CanonTraditionRow
  • app/src/navigation/types.tsCanonComparison, ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for ExploreStackParamList
  • app/src/navigation/ExploreStack.tsxCanonComparison + BookIntroStack.Screen registrations via lazySuspense()

Layout — useWindowDimensions() driven

Per issue guidance (no third-party orientation lib, no Dimensions.addEventListener):

  • 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

Column rendering (CanonColumn.tsx)

  • Header: tradition label + book count + first 2 formation events as a terse "founding councils" subtitle
  • Book list: scrollable; section headers (OT / NT / etc. from canon_list[].section); 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 a biblical book (present in books table) → BookIntro
  • Tap an extrabiblical book (not in books table) → ExtraBiblicalDetail (HWGTB-P2-03 / PR feat(app): ExtraBiblicalDetailScreen (#1548) #1597)

Header

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 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 include a 🔒 indicator for gated tabs so free-tier users see the lock state at a glance without having to tap.

How I verified

  • npx tsc --noEmit — clean ✅
  • npx eslint on touched files with --max-warnings 00 warnings
  • npx jest (full suite) — 470 suites / 3486 tests pass

Tests (12/12 passing)

portrait layout
✓ renders all 4 tradition tabs
✓ renders the active column (Protestant by default)
✓ tapping a tradition tab switches the visible column
landscape layout
✓ renders all 4 columns simultaneously (no tabs)
book-diff highlighting
✓ renders the "How did this happen?" header CTA
✓ tapping "How did this happen?" navigates to JourneyDetail canon-formation
premium gating
✓ free user sees 🔒 on Orthodox + Ethiopian tabs
✓ free user selecting Orthodox sees the unlock CTA
✓ free user tap on Orthodox unlock CTA fires showUpgrade
✓ premium user does NOT see 🔒 on Orthodox / Ethiopian tabs
formation timeline footer
✓ renders the formation timeline header with event count
✓ expanding the timeline shows merged events sorted by year

Merge order

Depends on:

Pairs with:

Rollback

Revert this PR. CanonComparison route unregisters; BookIntro route removal from ExploreStack is additive (still registered in ReadStack). The CanonColumn component and query module disappear — no other screen references them.


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

⚠️Tests: Could not parse results

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch 2 times, most recently from 5b85122 to 2a04075CompareApril 23, 2026 20:20
@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3535❌ 03535
Suites✅ 474❌ 0474

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 86.5s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from 2a04075 to f53beb9CompareApril 23, 2026 20:28
claudeand others added 2 commits April 23, 2026 20:36
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
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.
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from f53beb9 to fd5214aCompareApril 23, 2026 20:36
@CraigBuckmaster
CraigBuckmaster merged commit 953e02c into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p3-01-canon-comparison branch April 23, 2026 20:39
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-P3-01] App: CanonComparisonScreen

2 participants

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

feat(app): CanonComparisonScreen (#1550) - #1600

Merged
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison
Apr 23, 2026
Merged

feat(app): CanonComparisonScreen (#1550)#1600
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

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.

Closes

Closes#1550

Files added

  • app/src/db/content/canonTraditions.ts — data access layer with defensive sqlite_master table-exists guard (110 LOC)
  • app/src/hooks/useCanonTraditions.ts — loader + book-membership computation (87 LOC)
  • app/src/components/CanonColumn.tsx — one tradition's column, reused in both layouts (395 LOC)
  • app/src/screens/CanonComparisonScreen.tsx — the screen itself (567 LOC)
  • app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests

Files touched

  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsCanonTradition, CanonListSection, CanonDistinctive, CanonFormationEvent, CanonTraditionRow
  • app/src/navigation/types.tsCanonComparison, ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for ExploreStackParamList
  • app/src/navigation/ExploreStack.tsxCanonComparison + BookIntroStack.Screen registrations via lazySuspense()

Layout — useWindowDimensions() driven

Per issue guidance (no third-party orientation lib, no Dimensions.addEventListener):

  • 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

Column rendering (CanonColumn.tsx)

  • Header: tradition label + book count + first 2 formation events as a terse "founding councils" subtitle
  • Book list: scrollable; section headers (OT / NT / etc. from canon_list[].section); 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 a biblical book (present in books table) → BookIntro
  • Tap an extrabiblical book (not in books table) → ExtraBiblicalDetail (HWGTB-P2-03 / PR feat(app): ExtraBiblicalDetailScreen (#1548) #1597)

Header

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 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 include a 🔒 indicator for gated tabs so free-tier users see the lock state at a glance without having to tap.

How I verified

  • npx tsc --noEmit — clean ✅
  • npx eslint on touched files with --max-warnings 00 warnings
  • npx jest (full suite) — 470 suites / 3486 tests pass

Tests (12/12 passing)

portrait layout
✓ renders all 4 tradition tabs
✓ renders the active column (Protestant by default)
✓ tapping a tradition tab switches the visible column
landscape layout
✓ renders all 4 columns simultaneously (no tabs)
book-diff highlighting
✓ renders the "How did this happen?" header CTA
✓ tapping "How did this happen?" navigates to JourneyDetail canon-formation
premium gating
✓ free user sees 🔒 on Orthodox + Ethiopian tabs
✓ free user selecting Orthodox sees the unlock CTA
✓ free user tap on Orthodox unlock CTA fires showUpgrade
✓ premium user does NOT see 🔒 on Orthodox / Ethiopian tabs
formation timeline footer
✓ renders the formation timeline header with event count
✓ expanding the timeline shows merged events sorted by year

Merge order

Depends on:

Pairs with:

Rollback

Revert this PR. CanonComparison route unregisters; BookIntro route removal from ExploreStack is additive (still registered in ReadStack). The CanonColumn component and query module disappear — no other screen references them.


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

⚠️Tests: Could not parse results

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch 2 times, most recently from 5b85122 to 2a04075CompareApril 23, 2026 20:20
@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3535❌ 03535
Suites✅ 474❌ 0474

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 86.5s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from 2a04075 to f53beb9CompareApril 23, 2026 20:28
claudeand others added 2 commits April 23, 2026 20:36
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
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.
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from f53beb9 to fd5214aCompareApril 23, 2026 20:36
@CraigBuckmaster
CraigBuckmaster merged commit 953e02c into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p3-01-canon-comparison branch April 23, 2026 20:39
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-P3-01] App: CanonComparisonScreen

2 participants

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

feat(app): CanonComparisonScreen (#1550) - #1600

Merged
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison
Apr 23, 2026
Merged

feat(app): CanonComparisonScreen (#1550)#1600
CraigBuckmaster merged 2 commits into
masterfrom
feature/hwgtb-p3-01-canon-comparison

Conversation

@CraigBuckmaster

Copy link
Copy Markdown
Owner

What this does

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.

Closes

Closes#1550

Files added

  • app/src/db/content/canonTraditions.ts — data access layer with defensive sqlite_master table-exists guard (110 LOC)
  • app/src/hooks/useCanonTraditions.ts — loader + book-membership computation (87 LOC)
  • app/src/components/CanonColumn.tsx — one tradition's column, reused in both layouts (395 LOC)
  • app/src/screens/CanonComparisonScreen.tsx — the screen itself (567 LOC)
  • app/__tests__/screens/CanonComparisonScreen.test.tsx — 12 tests

Files touched

  • app/src/db/content/index.ts — barrel export
  • app/src/types/meta.tsCanonTradition, CanonListSection, CanonDistinctive, CanonFormationEvent, CanonTraditionRow
  • app/src/navigation/types.tsCanonComparison, ExtraBiblicalIndex, ExtraBiblicalDetail, BookIntro typed for ExploreStackParamList
  • app/src/navigation/ExploreStack.tsxCanonComparison + BookIntroStack.Screen registrations via lazySuspense()

Layout — useWindowDimensions() driven

Per issue guidance (no third-party orientation lib, no Dimensions.addEventListener):

  • 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

Column rendering (CanonColumn.tsx)

  • Header: tradition label + book count + first 2 formation events as a terse "founding councils" subtitle
  • Book list: scrollable; section headers (OT / NT / etc. from canon_list[].section); 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 a biblical book (present in books table) → BookIntro
  • Tap an extrabiblical book (not in books table) → ExtraBiblicalDetail (HWGTB-P2-03 / PR feat(app): ExtraBiblicalDetailScreen (#1548) #1597)

Header

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 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 include a 🔒 indicator for gated tabs so free-tier users see the lock state at a glance without having to tap.

How I verified

  • npx tsc --noEmit — clean ✅
  • npx eslint on touched files with --max-warnings 00 warnings
  • npx jest (full suite) — 470 suites / 3486 tests pass

Tests (12/12 passing)

portrait layout
✓ renders all 4 tradition tabs
✓ renders the active column (Protestant by default)
✓ tapping a tradition tab switches the visible column
landscape layout
✓ renders all 4 columns simultaneously (no tabs)
book-diff highlighting
✓ renders the "How did this happen?" header CTA
✓ tapping "How did this happen?" navigates to JourneyDetail canon-formation
premium gating
✓ free user sees 🔒 on Orthodox + Ethiopian tabs
✓ free user selecting Orthodox sees the unlock CTA
✓ free user tap on Orthodox unlock CTA fires showUpgrade
✓ premium user does NOT see 🔒 on Orthodox / Ethiopian tabs
formation timeline footer
✓ renders the formation timeline header with event count
✓ expanding the timeline shows merged events sorted by year

Merge order

Depends on:

Pairs with:

Rollback

Revert this PR. CanonComparison route unregisters; BookIntro route removal from ExploreStack is additive (still registered in ReadStack). The CanonColumn component and query module disappear — no other screen references them.


Generated by Claude Code

@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

⚠️Tests: Could not parse results

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch 2 times, most recently from 5b85122 to 2a04075CompareApril 23, 2026 20:20
@github-actions

github-actionsBot commented Apr 23, 2026

Copy link
Copy Markdown

Test Results

✅ All tests passed

PassedFailedTotal
Tests✅ 3535❌ 03535
Suites✅ 474❌ 0474

Coverage

StatementsBranchesFunctionsLines

⏱️ Duration: 86.5s

@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from 2a04075 to f53beb9CompareApril 23, 2026 20:28
claudeand others added 2 commits April 23, 2026 20:36
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
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.
@CraigBuckmaster
CraigBuckmasterforce-pushed the feature/hwgtb-p3-01-canon-comparison branch from f53beb9 to fd5214aCompareApril 23, 2026 20:36
@CraigBuckmaster
CraigBuckmaster merged commit 953e02c into masterApr 23, 2026
6 checks passed
@CraigBuckmaster
CraigBuckmaster deleted the feature/hwgtb-p3-01-canon-comparison branch April 23, 2026 20:39
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-P3-01] App: CanonComparisonScreen

2 participants

@CraigBuckmaster@claude