Skip to content

Commit abc1877

Browse files
cameronapakclaude
andauthored
feat(ui): host-owned verse actions on BibleReader (#307)
* feat(ui): host-owned verse actions on BibleReader (YPE-2894) Three additive, non-breaking changes to BibleReader.Root so a host can own the verse-action UI. Every default is unchanged. - verseActions?: 'popover' | 'none' gates the built-in VerseActionPopover. - clearSelectionSignal?: number clears the selection from outside; a counter rather than a ref handle because Expo DOM props must be serializable. - BibleReaderVerseSelection gains reference and shareData; the highlight intent payload stays byte-identical via Omit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(ui): refresh live verse selection when book/version metadata resolves Greptile P1 on #307: `reference` and `shareData` were a snapshot of `useBooks` / `useVersion` taken when the selection was made. A selection made before that metadata resolved left the host holding the USFM book code and a version-less copy/share payload until the user reselected — visible to any host on `verseActions="none"` rendering its own UI. `Content` now re-emits the live selection when the localized book title or version abbreviation changes. Both refs seed from the mount values, so the first effect run is never an emit, and the empty-selection guard keeps navigation (which clears first) from re-emitting a dead selection. `onVerseSelect` therefore can fire twice for one user action inside that race window, with the same `verses`. Documented on the prop, the `reference` field, and the changeset: the payload is the current state of the selection, not one event per action. Not gated on `verseActions="none"` on purpose — payload freshness that varies by mode is a worse trap than an extra call. Adds 4 cases: a re-emit for each of the two metadata sources (both fail without the fix), plus negative controls for no live selection and for a settled-metadata re-render. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(ui): correct the verseActions="none" highlight-intent contract The built-in popover is the only trigger for handleHighlight / handleClearHighlight, so verseActions="none" - which stops it mounting - also stops onHighlightApply / onHighlightRemove from ever firing. The prop docs and the changeset claimed the opposite, which would have sent a host down a dead path: set "none", pass onHighlightApply, render its own swatches, and never hear a tap. A host in this mode owns `highlights` and paints by updating that prop. Docs only, plus a test that holds the contract honest. No behavior change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(ui): make the verseActions="none" intent test non-vacuous The assertion was that neither highlight callback fires after selecting two verses - but selecting a verse never emits an intent in either mode, a swatch has to be clicked. The test passed with verseActions="popover" too, so it locked nothing. Now a differential: the popover arm clicks Apply and Clear and proves both callbacks fire, then the same props and script under 'none' assert they cannot. Verified by mutation - removing the `verseActions !== 'none'` guard fails the test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 1da7184 commit abc1877

3 files changed

Lines changed: 605 additions & 32 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@youversion/platform-react-ui': minor
3+
---
4+
5+
`BibleReader` can now hand verse actions to the host (YPE-2894). Three additive changes; **every default is unchanged**, so existing consumers see identical behavior.
6+
7+
- New `BibleReader.Root` prop `verseActions?: 'popover' | 'none'`. `'popover'` is the default and renders the built-in `VerseActionPopover` exactly as before. `'none'` renders no verse-action UI at all while keeping everything else intact — verses still select and paint, `onVerseSelect` still fires with `reference` and `shareData` populated, and the Copy / Share payload is still built. Highlight intents are the exception: the built-in popover is their only trigger, so `onHighlightApply` / `onHighlightRemove` do **not** fire in this mode — a host owning the action UI drives highlights by updating the `highlights` prop directly. Use it when the host owns the action UI (a React Native host presenting a native bottom sheet, where a second in-WebView popover would stack on top of it).
8+
- New `BibleReader.Root` prop `clearSelectionSignal?: number`. Any change to the value clears the current verse selection from outside the reader, emitting `onVerseSelect` with `verses: []`. The value at mount is the baseline, so mounting never clears. It is a counter rather than an imperative `ref` handle because Expo DOM components accept only serializable props. Needed with `verseActions="none"`, where nothing inside the reader clears the selection any more.
9+
- `BibleReaderVerseSelection` (the `onVerseSelect` payload) gains two fields: `reference`, the localized display reference **without** the version abbreviation (`Hebrews 11:4`, falling back to the USFM book code until `useBooks` resolves, `''` on clear), and `shareData`, the same `BibleReaderShareData` the popover's Copy / Share buttons produce (`null` on clear). `reference` and `shareData.reference` deliberately differ — copied text keeps the version (`Hebrews 11:4 BSB`), a UI label does not. When the localized book title or version abbreviation resolves while a selection is live, the reader re-emits `onVerseSelect` with the same `verses` and the refreshed fields, so a host never sticks on the fallback: treat the payload as the current state of the selection, not as one event per user action.
10+
11+
`BibleReaderHighlightIntent` is unchanged: it no longer spreads the full selection type, so intents still carry identity and color only.

0 commit comments

Comments
 (0)