feat(ui): BibleReaderNavigation chapter jumps (YPE-5745) - #182
Conversation
20ff5ba to
1e2cb42
Compare
1e2cb42 to
8a6e214
Compare
34106a2 to
6d5ad53
Compare
YPE-5706: temporary Search HTTP in expo-core until platform-core ships SearchClient. Public hook and types only; wrapper stays internal. Co-authored-by: Cursor <cursoragent@cursor.com>
YPE-5706: hook must throw when used without YouVersionProvider. Co-authored-by: Cursor <cursoragent@cursor.com>
Delete the local Search HTTP client and wrap SearchClient from
@youversion/platform-core 2.13.0. Thrown errors map to the existing
RN Result kinds. Verse hits use id. Query lists are { queries }.
Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com>
The USFM tests already pin verse-only three-segment parsing. The exports tests already pin the public surface. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com>
Digit-only USFM chapter and verse segments can parse past Number.MAX_SAFE_INTEGER. Require a positive safe integer. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com>
Public request/focusReference object so a host can jump the Reader before mount. Scroll-to-verse waits. YPE-5745. Co-authored-by: Cursor <cursoragent@cursor.com>
6d5ad53 to
77db50d
Compare
Keep Search's Core USFM parser. Combine the native toolbar with BibleReaderNavigation so a pending jump paints on the first frame. Co-authored-by: Cursor <cursoragent@cursor.com>
Consume the pending BibleReaderNavigation request after commit so a retried render cannot drop the host's chapter jump. Co-authored-by: Cursor <cursoragent@cursor.com>
Call request when creating BibleReaderNavigation so a parent re-render does not queue the same jump again. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the first-paint overlay, but wait to set book/chapter/version so a discarded render cannot notify the host of a chapter that never showed. Co-authored-by: Cursor <cursoragent@cursor.com>
cameronapak
left a comment
There was a problem hiding this comment.
Review
Summary
Standards: 2 must-fix. Spec: 0 must-fix. Primary concerns: the exported class exposes reader-owned queue controls, and a combined jump persists intermediate Reader Location snapshots.
Review evidence
- Scope: revision 28af625, YPE-5745, PR history, resolved threads, public declarations, Reader Location behavior, and the Search follow-up boundary.
- Method: independent Standards, Spec, and Correctness passes, followed by source-level verification of each finding.
| Behavior or check | Method / command | Result | Evidence source |
|---|---|---|---|
| Navigation requirements | Jira issue and comments, README, CONTEXT.md, implementation and tests | Chapter-only scope and YPE-5748 deferral are satisfied | Coordinator |
| Public API surface | Generated packages/ui/build/native/bible-reader-navigation.d.ts |
Reader-owned getters, subscription, and consume methods are public | Coordinator |
| Reader Location commit | Traced navigation setters, useControllableState, Zustand persistence, and applyReaderLocation |
One jump writes separate field patches instead of one combined snapshot | Coordinator |
| Tests | pnpm test -- --runInBand |
731 passed, 1 skipped | Coordinator |
| Static checks | pnpm lint; pnpm typecheck |
Passed | Coordinator |
- Limits: no native-device run; this PR changes coordination rather than visual presentation.
- CI and bot review: all current GitHub checks passed; prior bot findings are resolved and outdated.
- Event: REQUEST_CHANGES.
Written by Code Reviewer bot on behalf of Cam.
Keep request and focusReference as the host API. Move subscribe, snapshot, and consume helpers off the exported class so a host cannot clear a jump before the Reader sees it. Drop unused consumePending. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com>
Write book, chapter, and version in a single Reader Location patch so an uncontrolled jump cannot save a mixed book with the old chapter. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com>
Hosts should pass one BibleReaderNavigation to one mounted Reader. Broadcast across stacked readers is left for a later issue if needed. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com>
cameronapak
left a comment
There was a problem hiding this comment.
Review
Summary
Standards: 0 must-fix. Spec: 0 must-fix. Primary concern: none. The previous public-API and Reader Location findings are resolved.
Review evidence
- Scope: revision 548bcb4, full PR behavior, changes since the prior review, resolved threads, YPE-5745, and the YPE-5748 boundary.
- Method: traced the queue encapsulation, generated declaration surface, external-store consumption, controlled and uncontrolled Reader paths, combined persistence, and single-Reader ownership documentation.
| Behavior or check | Method / command | Result | Evidence source |
|---|---|---|---|
| Public navigation API | Built package and inspected generated declaration | Only request and focusReference are public |
Coordinator |
| Combined Reader Location | Source trace and focused regression test | One complete patch persists after commit | Coordinator |
| Navigation and Reader tests | Focused Jest suites | 14 passed | Coordinator |
| Full test suite | pnpm test -- --runInBand |
730 passed, 1 skipped | Coordinator |
| Static checks and build | pnpm lint, UI typecheck, UI build |
Passed | Coordinator |
- Limits: no native-device run; the change is coordination rather than visual presentation. Existing asynchronous test-harness warnings remain pre-existing and non-blocking.
- CI and bot review: all current GitHub checks passed; all prior review threads are resolved.
- Event: APPROVE.
Written by Code Reviewer bot on behalf of Cam.
| ) { | ||
| return | ||
| } | ||
| commitReaderLocationPatch({ |
There was a problem hiding this comment.
praise: Persisting the complete destination before the individual setters keeps Reader Location coherent without losing the first-paint overlay.
For Agents: atomic navigation commit
This resolves the prior mixed-location failure while preserving controlled callbacks and the consume-after-commit safety. The added regression test also checks the observable patch count and final stored values, so a future return to per-field writes will fail clearly.
Written by Code Reviewer bot on behalf of Cam.
* feat(ui): hybridize BibleCard chrome with DOM BibleTextView YPE-5830. Native Card owns header, version control, copyright, and maxWidth. Scripture still paints through BibleTextViewDom. * fix: clear BibleCard chrome metadata before refetch * fix: guard BibleCard version picker until language tag loads Disable the version control and skip onVersionPickerPress when chrome metadata has no languageTag, so consumers never receive an empty languageId. * fix: invalidate BibleCard chrome metadata synchronously on transition Track which reference/versionId loaded metadata and return null during render when inputs change, so chrome cannot flash stale values before the refetch effect runs. * chore: upgrade example app and packages to Expo SDK 57 Bump the example dev client to expo@57.0.22 with RN 0.86.3, pin @expo/dom-webview@57.0.1, and move publishable package peers to >=57.0.0 <58.0.0. Add pnpm overrides for transitive Expo deps still inside the supply-chain cooldown window. * chore: bump expo to 57.0.23 for iOS 27 lifecycle backport Pick up the SDK patch without enabling ios.enableSceneSupport; workspace override and example app pin expo@57.0.23 exactly. * fix(example): enable iOS 27 UIScene lifecycle for Xcode 27 builds Opt in via expo-build-properties enableSceneSupport and bump the plugin to 57.0.20, which implements the scene delegate wiring required alongside expo@57.0.23 on SDK 57. * fix(ui): tighten Reanimated/Worklets peers for RN 0.86 Raise peer floors so consumers on React Native 0.86 cannot resolve Reanimated 4.3.x or Worklets 0.8.x, which only declare support through 0.85. Add a major changeset recording the Expo 57 peer requirement. * fix: sync lockfile with Reanimated/Worklets peer floors 5970824 raised packages/ui peer floors to >=4.4.0 / >=0.9.1 but did not regenerate pnpm-lock.yaml, causing frozen-lockfile CI to fail. Co-authored-by: Cameron Llewellyn <cameron.b.llewellyn@gmail.com> * docs: align READMEs with Expo SDK 57 peer contract Consumer-facing docs still listed SDK 56 while published peers require 57. * fix(ui): address BibleCard hybrid chrome review feedback Apply provider version filters to chrome metadata, restore abbreviation and web background theming, gate the version picker only for consumer handlers, and keep Expo web from showing a dead built-in picker trigger. Co-authored-by: Cameron Llewellyn <cameron.b.llewellyn@gmail.com> * fix(ui): clear BibleCard chrome when version filters change Include provider filter lists in the chrome metadata key so a tighten without reference/versionId change drops prohibited chrome immediately, matching the reference-transition safety net. Co-authored-by: Cameron Llewellyn <cameron.b.llewellyn@gmail.com> * fix(ui): keep 600px inner column for BibleCard maxWidth 100% Match the Web SDK full-bleed contract: the Card surface fills the parent while header, scripture, and footer stay in a centered 600px column. Co-authored-by: Cameron Llewellyn <cameron.b.llewellyn@gmail.com> * fix(ui): clear BibleCard chrome when content client changes Include fetchBibleContent in the chrome metadata key so an appKey/apiHost swap drops stale reference/copyright/language before the new lookup resolves. Co-authored-by: Cameron Llewellyn <cameron.b.llewellyn@gmail.com> * feat(core): wrap platform-core SearchClient (YPE-5746) (#180) * feat(core): add placeholder Search client YPE-5706: temporary Search HTTP in expo-core until platform-core ships SearchClient. Public hook and types only; wrapper stays internal. Co-authored-by: Cursor <cursoragent@cursor.com> * test(core): cover useSearch outside provider YPE-5706: hook must throw when used without YouVersionProvider. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(core): wrap platform-core SearchClient (YPE-5746) Delete the local Search HTTP client and wrap SearchClient from @youversion/platform-core 2.13.0. Thrown errors map to the existing RN Result kinds. Verse hits use id. Query lists are { queries }. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * style(core): drop restated Search comments The USFM tests already pin verse-only three-segment parsing. The exports tests already pin the public surface. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(core): reject unsafe integers in bibleReferenceFromUsfm Digit-only USFM chapter and verse segments can parse past Number.MAX_SAFE_INTEGER. Require a positive safe integer. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(core): reject malformed USFM book identifiers Require a 3-character uppercase alphanumeric book code so punctuation, whitespace, and overlong ids return null. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * feat(ui): BibleReaderNavigation chapter jumps (YPE-5745) (#182) * feat(core): add placeholder Search client YPE-5706: temporary Search HTTP in expo-core until platform-core ships SearchClient. Public hook and types only; wrapper stays internal. Co-authored-by: Cursor <cursoragent@cursor.com> * test(core): cover useSearch outside provider YPE-5706: hook must throw when used without YouVersionProvider. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(core): wrap platform-core SearchClient (YPE-5746) Delete the local Search HTTP client and wrap SearchClient from @youversion/platform-core 2.13.0. Thrown errors map to the existing RN Result kinds. Verse hits use id. Query lists are { queries }. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * style(core): drop restated Search comments The USFM tests already pin verse-only three-segment parsing. The exports tests already pin the public surface. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(core): reject unsafe integers in bibleReferenceFromUsfm Digit-only USFM chapter and verse segments can parse past Number.MAX_SAFE_INTEGER. Require a positive safe integer. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * feat(ui): add BibleReaderNavigation for chapter jumps Public request/focusReference object so a host can jump the Reader before mount. Scroll-to-verse waits. YPE-5745. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): keep chapter jumps across discarded renders Consume the pending BibleReaderNavigation request after commit so a retried render cannot drop the host's chapter jump. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(ui): request the chapter jump once in the host example Call request when creating BibleReaderNavigation so a parent re-render does not queue the same jump again. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): apply reader jumps after the paint commits Keep the first-paint overlay, but wait to set book/chapter/version so a discarded render cannot notify the host of a chapter that never showed. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): hide reader navigation queue controls Keep request and focusReference as the host API. Move subscribe, snapshot, and consume helpers off the exported class so a host cannot clear a jump before the Reader sees it. Drop unused consumePending. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): persist reader jumps as one location Write book, chapter, and version in a single Reader Location patch so an uncontrolled jump cannot save a mixed book with the old chapter. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * docs(ui): note one Reader per navigation object Hosts should pass one BibleReaderNavigation to one mounted Reader. Broadcast across stacked readers is left for a later issue if needed. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * feat(ui): match native Reader chrome to Swift Drop the avatar row. Half-pills, overlay chapter buttons, and a More menu keep the same host presses. Search stays off this PR. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): lift Reader overlay nav above the iOS tab bar Prev and next sat 24pt from the reader bottom, on SampleApp tabs, so those presses hit Bible or Profile instead of changing chapter. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(ui): retarget Reader chrome to Swift PR 268 Previous and next live in the chapter capsule. Overlay circles are gone so the toolbar matches the current Swift header. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): polish v2 Reader toolbar chrome Match Figma capsules, a softer halo, side padding, and More-menu icons so the native row reads like the v2 file. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): finish v2 toolbar leftovers from self-review Cap the version pill so Select version cannot squeeze the chapter row. Drop unused GearIcon and avatar copy. Open More while auth is restoring. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(ui): delete unused Avatar primitive The v2 toolbar no longer mounts it. Drop the component, its tests, and the internal barrel export. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): even the Reader toolbar gaps around More Size More to the icon so the 8px gap matches the space between the chapter and version chips. Co-authored-by: Cursor <cursoragent@cursor.com> * revert(ui): keep userAvatarAlt in synced locale catalogs Locale JSON is generated from platform-localization. Dropping the unused key here fails Greptile. Leave the string until that repo deletes it. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): match More menu icons to Swift glyphs Use San Francisco textformat.size for Fonts & Settings and person for sign in/out. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(ui): restack Reader Search onto Swift chrome Search belongs in the 5953 spacer. The old row put it between version and gear, and that chrome is gone. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): keep 5745 navigation consume API on Search rebase Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): keep combined Reader Location jumps after Search rebase Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(ui): delete unused components/ui PersonIcon The More menu still uses native/icons/person-icon.tsx. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): stop the Reader example from jumping every render The sample called navigation.request after every ReaderScreen render, so a copied example reset the reader to John 3 on unrelated updates. The request now happens once, inside the same useMemo that creates the navigation object. Drop the hand-written Spanish search strings. Missing keys fall back to English until Crowdin returns them. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * YPE-5833 / RNV2-6: compose YouVersionAuthButton on design-system Button (#194) * feat(ui)!: compose YouVersionAuthButton on design-system Button YPE-5833 / RNV2-6. The branded control is now Button + Button.Text with the Bible App logo as a child. background still forces the light or dark token scheme. BREAKING CHANGE: YouVersionAuthButton no longer accepts outline, radius, or size. Short, icon, and rectangular variants are gone. The canonical look is a filled, rounded Button with the full localized label. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): keep Trans props off Button.Text in YouVersionAuthButton Wrap the i18n Trans node inside Button.Text instead of using it as Trans parent, so extra Trans props are not forwarded onto the label. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): address YouVersionAuthButton review findings Drop the unlinked example preview route. Collapse sign-in versus sign-out to one decision. Document that core majors only because the packages version together. Cover theme isolation and the bold Trans face. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): pin YouVersionAuthButton fill to scheme background Button default primary is red in dark and hides the Bible App logo. Read tokens via useTokens inside the forced ThemeContext. Cover sign-in error logging and keep outline/radius/size off the public props type. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * docs: match native i18n Trans example to auth button The contributing snippet still taught parent={Text} and a hand-written fontWeight. The auth button nests Trans in Button.Text and bolds with sansFace. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * refactor(ui): read i18n inside YouVersionAuthButton surface AuthButtonSurface already sits under the forced scheme. It can call useSdkTranslation itself instead of taking i18n as a pass-through prop. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): treat empty auth button text as omitted An empty text prop used to fall back to the localized Trans label. Nullish coalescing made it a blank control. Scope the i18n sansFace note to primitives so sheets can keep fontWeight. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): outline YouVersionAuthButton and let the label wrap Bake a scheme border into the locked look (light 1px, dark 2px) without bringing back the outline prop. Override Button.Text to two lines and drop the fixed height so a long locale can wrap. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * refactor(ui): extract AuthButtonLabel from YouVersionAuthButton Keep Trans named Trans. Early-return when the caller passes text. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * chore(changeset): ship YouVersionAuthButton DS work as patch Drop core from the changeset; it has no API change. The fixed group still versions both packages together. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * refactor(ui): bold AuthButtonLabel with design-system Text Trans bold uses DS Text plus sansFace. Pin typography.sm so the brand name stays on the Button.Text size. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): let YouVersionAuthButton label wrap on web Give Button.Text flex: 1 so a narrow parent wraps to two lines instead of ellipsizing on one. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * docs: add PR 194 Expo web QA screenshots for local download Temporary PNGs so Dustin can pull them via the GitHub API onto his Desktop. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * docs: remove temporary PR 194 screenshot drop folder Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): restore YouVersionAuthButton post-DS-9 shape on Button Keep the old padding, logo gap, and no default border while composing the design-system Button. Label color comes from Button context. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): match YouVersionAuthButton chrome to Swift Compose Button outline, hug content, and use Swift padding, gap, and logo size so hosts place the pill. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * chore: sync react native localization (#200) Co-authored-by: camrun91 <16566029+camrun91@users.noreply.github.com> * refactor(ui): inline the Reader Search field The design-system Input had one caller, and that caller turned its border off. The sheet now uses TextInput directly. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * feat(ui): v2 toolbar (YPE-5953) (#202) * feat(ui): match native Reader chrome to Swift Drop the avatar row. Half-pills, overlay chapter buttons, and a More menu keep the same host presses. Search stays off this PR. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): lift Reader overlay nav above the iOS tab bar Prev and next sat 24pt from the reader bottom, on SampleApp tabs, so those presses hit Bible or Profile instead of changing chapter. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(ui): retarget Reader chrome to Swift PR 268 Previous and next live in the chapter capsule. Overlay circles are gone so the toolbar matches the current Swift header. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): polish v2 Reader toolbar chrome Match Figma capsules, a softer halo, side padding, and More-menu icons so the native row reads like the v2 file. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): finish v2 toolbar leftovers from self-review Cap the version pill so Select version cannot squeeze the chapter row. Drop unused GearIcon and avatar copy. Open More while auth is restoring. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(ui): delete unused Avatar primitive The v2 toolbar no longer mounts it. Drop the component, its tests, and the internal barrel export. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): even the Reader toolbar gaps around More Size More to the icon so the 8px gap matches the space between the chapter and version chips. Co-authored-by: Cursor <cursoragent@cursor.com> * revert(ui): keep userAvatarAlt in synced locale catalogs Locale JSON is generated from platform-localization. Dropping the unused key here fails Greptile. Leave the string until that repo deletes it. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): match More menu icons to Swift glyphs Use San Francisco textformat.size for Fonts & Settings and person for sign in/out. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(ui): delete unused components/ui PersonIcon The More menu still uses native/icons/person-icon.tsx. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): address review feedback on host toolbar presses The release note said hosts keep the same presses. Settings and sign-in now open from More, and the retired avatar test ids are named. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): address review feedback on capsule clipping Light mode keeps the shadow on the outer pill. Labels and press fills clip on an inner view. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): address review feedback on the More hit target The More pressable is 44 points. The icon stays 24, and the test checks that frame. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * feat(ui): polish Reader Search open state Focus the field when the sheet opens, match the search pill to the other toolbar capsules, and show three trending and recent queries. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(ui): simplify styles in BibleReaderSearchSheet * fix(ui): use synced Reader Search strings The locale check blocks a hand-edited catalog. Call the synced keys, and read the focus spy from its context so typecheck accepts it. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(ui): YPE-5836: Add native Bible chapter picker (#204) * feat(ui): add native Bible chapter picker Amp-Thread-ID: https://ampcode.com/threads/T-01a0c4d9-366a-702e-a0d8-e56d50e888bc Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com> * fix(ui): refine native chapter picker Amp-Thread-ID: https://ampcode.com/threads/T-01a0c4d9-366a-702e-a0d8-e56d50e888bc Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com> * fix(ui): polish chapter picker interactions Amp-Thread-ID: https://ampcode.com/threads/T-01a0c4d9-366a-702e-a0d8-e56d50e888bc Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com> * fix(ui): preserve chapter picker sheet theme Amp-Thread-ID: https://ampcode.com/threads/T-01a0c5d4-3d6c-712f-b216-cb65fb75357f Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com> * fix(ui): address chapter picker review feedback Keep the selected-book fade on one color, avoid reloading the catalog when provider filters are recreated with the same contents, and scroll the selected chapter back into view after an order change. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): ignore chapter picks for a revoked Bible version Drop the loaded catalog as soon as provider filters change, and refuse a selection when the current filters no longer allow that version. Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> * fix(ui): drop the Reader Search capsule Search is a plain 24pt icon with 8pt of padding. The toolbar row uses 16pt on the leading edge and no trailing pad. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): inset the Reader More control from the trailing edge The row had no trailing pad, so More sat on the screen edge. Add 8pt on that side. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): keep a trailing space after a submitted search Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): retry a search page when its hits cannot be titled A later page with hits and no titles stays retryable instead of marking those verses seen. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): label search retry buttons Retry The failed search and the page-error control were saying Error. Co-authored-by: Cursor <cursoragent@cursor.com> * test(ui): press a bad search result on the real sheet The reader mock never called the sheet, so a bad id could still navigate and the test would pass. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(ui): say Cancel in the Reader Search changeset The header already uses the Cancel string. The release note was still saying Done. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(ui): delete the unused font settings icon The More menu still uses AaIcon, and nothing imported this file. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): narrow the search view before retrying in the test Typecheck rejected footer on the full search view after the await. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(ui): focus a verse across the Expo DOM bridge A Search tap should scroll to the verse. The WebView can only take a plain object, so the DOM wrapper builds BibleReaderNavigation and calls focusReference when seq changes. * fix(ui): drop the example John 3:16 focus control The example Reader is not a focus demo. Search still focuses through the Reader's own navigation. * fix(ui): keep a repeated or rejected verse focus from moving the reader A WebView reload was jumping back to the last focused verse, and a passage the Web SDK rejects was clearing the page. * fix(ui): type the verse focus mocks with jest's two type arguments tsc rejects a single type argument on jest.fn, which failed the PR typecheck. * fix(ui): apply a verse focus queued before the Reader mounts The WebView treated the first focus number as already handled, so a focus waiting at startup never scrolled. A later reload still skips that same number. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): show the suggestion spinner only after the request starts The debounce wait stays blank. The spinner keeps the list height so the sheet does not collapse. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): skip a repeat search for the same query and version Pressing Search again after results land was clearing the list and fetching it again. A failed search still retries. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): announce a failed search next to Retry A failed search and a failed next page only showed Retry. Screen readers now hear Error when that state appears. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): load the next search page at the last five rows The list waited until the last fifth of the viewport. It now requests the next page once a row in the last five is on screen. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): finish remaining Search review notes Give the field a search role, grow Search and Clear to 44pt hits, and document that Search taps and focusReference scroll and dim. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): address Search review follow-ups Prevent verse focus from replaying after a WebView reload, skip a whitespace-only repeat search, and give Retry a 44pt hit target. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): match Search clear to the picker field Use the same 18pt ClearIcon, trailing placement, and 10pt hit slop as the native picker search field. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): apply verse focus only after the WebView reports it A focus requested while the WebView is loading was marked applied on the native side and then skipped when the WebView became ready. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): show completed search results again after a whitespace edit Pressing Search on the same query restores the page already loaded and keeps the typed space. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): give Search Clear a 44pt touch target The 18pt icon stays. The button itself is 44pt so the extra area does not cover the text field. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): drop a void call the linter rejects The focus report already returns nothing, so the void operator is not allowed. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): let Search delete whitespace after restoring results setQuery ignored any edit equal to the submitted query. A restored page keeps the typed text ("hope "), so backspacing to "hope" was dropped and the field snapped back. Compare against the displayed field text instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ui): keep the last of two quick Search edits Compare each edit against the reducer's current state instead of the last render, so a type-then-delete before a re-render is not dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ui): keep paging Search past a page of repeated verses A next page whose hits were all already shown added no rows, so the list never scrolled to ask again and results stopped early. Follow the new cursor instead; stop if the API hands back the same one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ui): stop Search paging when cursors cycle Following the cursor past repeat-only pages looped forever if the API cycled cursors (A -> B -> A). Remember every fetched cursor for the result set and stop when a repeat-only page points back at one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ui): dedupe the first page of Search results Later pages already dropped repeated hits, but page one used the raw list, so a repeated id rendered two rows with one key and fetched the passage twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ui): keep Search focus and theme with the Reader The navigation object stores the applied focus sequence, so remounting the Reader does not send an acknowledged verse to the Web SDK again. Search content uses the Reader theme, so result text matches the sheet when the provider scheme differs. * fix(ui): scope verse focus to the navigation object A replacement navigation object restarts its focus sequence at 1. The WebView now tracks which object that sequence belongs to, so the new object's first focus still runs once. Co-authored-by: Cursor <cursoragent@cursor.com> * test(ui): show a late verse-focus ack marks the replacement reader The callback still accepts only the sequence. After a navigation swap, that sequence is stored on the reader now on screen. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): ignore a verse-focus ack from another navigation stream Expo delivers the action to the reader on screen. The acknowledgment names its stream, so a late reply from the previous navigation object does not mark the replacement. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Dustin Kelley <141975656+Dustin-Kelley@users.noreply.github.com> Co-authored-by: Dustin Kelley <Dustin-Kelley@users.noreply.github.com> Co-authored-by: Dustin Kelley <Kelleydustin2018@gmail.com> Co-authored-by: platform-localization-pr-bot[bot] <294853593+platform-localization-pr-bot[bot]@users.noreply.github.com> Co-authored-by: camrun91 <16566029+camrun91@users.noreply.github.com> Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com> Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Summary
BibleReaderNavigationwithrequestandfocusReference. Host can submit beforeBibleReadermounts. One pending request; newer replaces older; Reader consumes once.book/chapter/versionId). No Web SDK change. Scroll-to-verse and focus are not in this PR.Stack
Search(feat(core): wrap platform-core SearchClient (YPE-5746) #180)SearchYPE-5747 (scroll/focus) is out of this stack.
Test plan
focusReferencechanges chapter the same way asrequestBibleReaderHandlestill onlyrefreshHighlightspnpm typecheck/pnpm test/pnpm lintfrom worktree rootMade with Cursor
The PR appears safe to merge.
Summary
The PR adds a public imperative navigation object that lets hosts queue one chapter jump before or after a native Bible Reader mounts.
BibleReaderNavigationand its factory from the UI package.Diagram
Reviews (6) · Last reviewed commit: "docs(ui): note one Reader per navigation..."