Skip to content

YPE-5440 / RNV2-2: Hybrid VerseOfTheDay (native chrome + DOM text) - #183

Open
Dustin-Kelley wants to merge 15 commits into
mainfrom
cursor/hybrid-votd-rnv2-2-b0c5
Open

Dustin-Kelley wants to merge 15 commits into
mainfrom
cursor/hybrid-votd-rnv2-2-b0c5

Conversation

@Dustin-Kelley

@Dustin-Kelley Dustin-Kelley commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Pilot / canonical hybrid VerseOfTheDay for YPE-5440 / RNV2-2.

Native chrome (Card surface, share button, YouVersion attribution, sun icon, title/reference) wraps the DOM BibleTextView for the verse. Scripture stays in WebView for USFM/HTML/CSS. Public VerseOfTheDay props and exported types are unchanged.

This is the quality gate before Phase 4 bulk: native chrome + Project A primitives + the native↔DOM bridge end to end.

IOS
Simulator Screenshot - iPhone 17 Pro - 2026-09-11 at 11 21 32
Simulator Screenshot - iPhone 17 Pro - 2026-09-11 at 11 21 23
Simulator Screenshot - iPhone 17 Pro - 2026-09-11 at 11 21 20
Simulator Screenshot - iPhone 17 Pro - 2026-09-11 at 11 39 07

Android
Screenshot_1789144727
Screenshot_1789144737

Hybrid split

Native

  • Card surface from Project A (Card / Button / Text + tokens)
  • Share button → React Native Share (or consumer onShare)
  • YouVersion / Bible App attribution
  • Title, reference line, sun icon, loading state
  • Light/dark via useTheme (closes the ADR 0021 VOTD exception — system resolves on native, not inside the WebView)

DOM (unchanged scripture path)

  • packages/ui/src/dom/bible-text-view.tsx renders the verse
  • Theme is the resolved light | dark on the in-WebView provider (RNV2-4)
  • Fonts as encoded props only — no --yv-reader-* CSS
  • Highlights still native-owned (HighlightsPaint + Controlled Highlights Latch)
  • Content fetch still fetchBibleContent (no parallel client)

Kept on purpose

  • packages/ui/src/dom/verse-of-the-day.tsx is not deleted. RNV2-10 removes the full-component wrapper.

How to verify

cd packages/ui
pnpm typecheck
pnpm exec jest --testPathPattern='verse-of-the-day|exports.test'

Layer-3 coverage: render, light/dark tokens + bridge theme, native Share / consumer onShare, embed defaults, and the highlights latch against BibleTextViewDom.

Example app: drop-in — <VerseOfTheDay versionId={3034} /> is the same public API.

Screenshots

Skipped. The VM has no YouVersion app key (apps/example/.env is not present), so a live light/dark capture would not render scripture.

Test plan

  • UI typecheck
  • VOTD + exports tests
  • Full UI package test suite (610 passing)
  • pnpm lint
  • Reviewer: example app light/dark + share on a device/simulator with an app key
Open in Web Open in Cursor 

Greptile Summary

This PR converts VerseOfTheDay into a hybrid component with native presentation around DOM-rendered scripture while preserving its public API.

  • Adds native card chrome, theming, loading and retry states, attribution, and sharing.
  • Resolves the daily passage and share source natively while retaining BibleTextViewDom for scripture.
  • Preserves controlled highlights and forwards provider version filters across the native-to-DOM bridge.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/ui/src/native/verse-of-the-day.tsx Replaces the full DOM wrapper with native card chrome around BibleTextViewDom and integrates resolved theming, loading, retry, highlights, attribution, and sharing.
packages/ui/src/native/verse-of-the-day-share.ts Builds filtered native share data while isolating optional version-metadata failures from valid passage responses.
packages/ui/src/native/use-verse-of-the-day-share-source.ts Loads and caches share data with stale-request protection across passage, version, and filter changes.
packages/ui/src/native/use-verse-of-the-day-passage-id.ts Exposes passage resolution status and retry behavior for the native loading and failure states.
packages/ui/src/components/ui/text.tsx Adds the internal eyebrow text variant used by the new native VerseOfTheDay chrome.

Sequence Diagram

sequenceDiagram
  participant App as Consumer App
  participant VOTD as Native VerseOfTheDay
  participant API as Bible Content Client
  participant DOM as BibleTextView DOM
  participant Share as Native Share API
  App->>VOTD: Render props and provider context
  VOTD->>API: Resolve daily passage ID
  API-->>VOTD: Passage ID
  VOTD->>DOM: Passage, resolved theme, filters, highlights
  DOM-->>App: Render scripture in WebView
  VOTD->>API: Load passage text and version metadata
  API-->>VOTD: Share source
  App->>VOTD: Press Share
  VOTD->>Share: Share formatted passage text
Loading

Reviews (9): Last reviewed commit: "Merge branch 'cursor/hybrid-votd-rnv2-2-..." | Re-trigger Greptile

cursoragent and others added 3 commits September 10, 2026 17:26
RNV2-2. Native Card, share, and YouVersion attribution wrap the DOM
BibleTextView so scripture styling stays in WebView. Light/dark resolve
on native before they cross the bridge. Share uses the native Share API.
Public props are unchanged. The full-component DOM wrapper stays for RNV2-10.

Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com>
The share payload came from a separate plain-text fetch, and one failure
left the share button disabled and the reference line blank for the life
of the mount while the DOM view still painted the verse. The hook now
exposes loadShareSource; the button is gated only on the passage id and a
press refetches when the background fetch missed.

Nothing mounts VerseOfTheDayDom after the hybrid rewrite, so remove its
ImplKey and registerDefault. The DOM file stays for RNV2-10.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Dustin-Kelley
Dustin-Kelley marked this pull request as ready for review September 11, 2026 16:25
Comment thread packages/ui/src/native/verse-of-the-day-share.ts

@bmanquen bmanquen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two-axis review (standards + spec vs YPE-5440). Inline comments below mark what must be addressed before merging; everything else found was judgement-call level.

Comment thread packages/ui/src/native/verse-of-the-day.tsx Outdated
Comment thread packages/ui/src/native/verse-of-the-day.tsx Outdated
Comment thread packages/ui/src/native/verse-of-the-day.tsx
A failed VOTD request left a spinner with Share disabled. Split loading
from failure so the card can recover.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cameronapak cameronapak left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

YPE-5440

Summary

Standards: 0 must-fix. Spec: 3 must-fix. Primary concern: native sharing bypasses provider version restrictions.

For Agents
  • CI: passing
  • Bot review: clear
  • Event: REQUEST_CHANGES
  • HEAD: 9372c1e

Written by Code Reviewer bot on behalf of Cam.

Comment thread packages/ui/src/native/verse-of-the-day-share.ts
Comment thread packages/ui/src/native/verse-of-the-day-share.ts
Comment thread packages/ui/src/native/verse-of-the-day.tsx
Dustin-Kelley and others added 5 commits September 14, 2026 11:01
Native share still fetches and builds a payload for excluded, non-permitted, and language-filtered versions.

Co-authored-by: Cursor <cursoragent@cursor.com>
Native header and Share fetched a version the DOM verse body would refuse. Share now uses the same permit, exclude, and language rules as the web SDK, with the provider lists as arguments.

Co-authored-by: Cursor <cursoragent@cursor.com>
onShare currently fires on Expo web, which changes the public callback from the old DOM wrapper.

Co-authored-by: Cursor <cursoragent@cursor.com>
The old wrapper left onShare unset on web so the Web Share flow ran. The hybrid chrome called the host callback on every platform.

Co-authored-by: Cursor <cursoragent@cursor.com>
Hybrid VOTD already resolves through useTheme. The ADR still described the old WebView-owned system path.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cameronapak cameronapak left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

YPE-5440

Summary

Standards: 0 must-fix. Spec: 2 must-fix. Primary concern: version restrictions can still be bypassed, and the default web Share flow is now inert.

For Agents
  • CI: passing
  • Bot review: clear
  • Event: REQUEST_CHANGES
  • HEAD: a246882

Written by Code Reviewer bot on behalf of Cam.

Comment thread packages/ui/src/native/verse-of-the-day.tsx Outdated
Comment thread packages/ui/src/native/use-verse-of-the-day-share-source.ts Outdated
Comment thread packages/ui/src/native/__tests__/verse-of-the-day-share.test.ts
Dustin-Kelley and others added 2 commits September 15, 2026 08:19
Language allowlists now check version metadata before fetching passage
text. In-flight share results also key on the filter lists so a slower
permissive request cannot restore a refused header or payload.

Co-authored-by: Cursor <cursoragent@cursor.com>
Typecheck failed because renderHook's rerender requires the hook props
argument. Match the existing wrapper pattern with rerender(undefined).

Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com>
Dustin-Kelley and others added 3 commits September 15, 2026 08:29
Keep onShare native-only. On web the header still loads share text and
uses navigator.share, or the clipboard when that API is missing.

Co-authored-by: Cursor <cursoragent@cursor.com>
renderHook.rerender requires an argument under tsconfig.test.json, so
the filter-tightening case never typechecked.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cameronapak cameronapak left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

YPE-5440

Summary

Standards: 0 must-fix. Spec: 1 must-fix. Primary concern: an in-flight Share press can send a newly refused version.

For Agents
  • CI: passing
  • Bot review: clear
  • Event: REQUEST_CHANGES
  • HEAD: fc33ed3

Written by Code Reviewer bot on behalf of Cam.

) {
setResult({ passageId, versionId, filters, data })
}
return data

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: A Share press can still send a version that the current filters refuse.

For Agents: reject a stale Share retry

If a press starts loadShareSource under permissive provider filters and those filters tighten before the fetch settles, sameFilters prevents setResult, but this unconditional return data still gives the old payload to handleShare. That handler can then invoke the consumer callback, browser Share, or native Share.share for a now-refused version. Return null when the request snapshot no longer matches currentRef, and test an in-flight Share press after a filter change. The new hook test only checks shareSource state.

Written by Code Reviewer bot on behalf of Cam.

const themeContext = useTheme()
const { t } = useSdkTranslation()
const resolvedTheme = useTheme(theme ?? background)
const [sampledDayOfYear] = useState(() => getDayOfYear(new Date()))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Pinning the day when the card mounts keeps the verse and its Share text on the same date across midnight.

For Agents: pinned calendar day

The sampled day feeds native passage resolution and the DOM verse bridge instead of allowing each surface to resolve “today” independently.

Written by Code Reviewer bot on behalf of Cam.

Sign up for free to 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.

4 participants