Repository navigation
feat(bridge): scan() — native barcode / QR scanner for Console fields - #170
Merged
Merged
Conversation
Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
…TL scanner root Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
Answers go only to the document that made the call: the per-document call id covers a replaced page, and a re-created WebView is never sent one. An in-app navigation keeps its open calls. Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
Contributor
Author
|
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv Seat verdict: ACCEPT — Head-sha: 维护者速读
席位意见: 范围与卡面一致,复用扫码组件,没有另做一份。复核第一轮发现一处:原生侧按 席位核对
|
os-justin
pushed a commit
that referenced
this pull request
Nov 2, 2026
Reconciles #170 (bridge scan(), per-WebView answer guard) with the load state machine. #170's documentRef and the onMessage answer guard stay; the counter is bumped in the machine's `recreate` effect, the one place a new WebView is created, and nowhere tied to onLoadStart (still unwired, as in the #167 reconcile). Two #170 tests drove a load start through the `onLoadStart` prop; they now deliver it the way react-native-webview's WebViewShared does (onLoadStart if wired, then onNavigationStateChange). Docs stack #170's Scanning codes and wire-format text beside Load states. Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Nov 2, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #146
Clause-②: no
Metadata-protocol: none
维护者速读
ObjectStackNative.scan(),打开原生全屏扫码界面(带手电筒和关闭按钮),拿回条码或二维码的文字;用户关闭则返回null。objectos://connect?server=…links and the native scanner screen #156 已合入的扫码界面,不新增任何权限或插件配置。components/shell/ConsoleWebView.tsx(onMessage 里丢弃过期应答的判断)、lib/shell/bridge/handler.ts(scan 参数校验)、app/bridge-scan.tsx(每种退出方式都会结算调用)。npx jest --ci __tests__/lib/shell __tests__/components/QrScanner.test.tsx __tests__/components/ConsoleWebView.test.tsx,预期全部通过。scan()的 Console 页面,扫一个条码,确认文字回填;关闭扫码界面时返回null;拒绝相机权限后调用以failed失败。What changes
scan(opts?: { formats?: string[] }): Promise<string | null>, announced as capabilityscan, following "Adding a method":BRIDGE_METHODS, the handler with argument validation, the bootstrap wrapper, and the docs (docs/HYBRID-SHELL.md, new "Scanning codes" section).expo-camera'sBarcodeTypenames (SCAN_FORMATSinprotocol.ts; a compile-time pin in the protocol test keeps them insideBarcodeType). Withoutformats, every listed format is read. An empty list, an unknown name, a non-string, a non-array or any extra option isbad_args, and no scanner opens.QrScannerfrom Connect by QR code:objectos://connect?server=…links and the native scanner screen #156, reused rather than forked. Its props grow by three optional ones, all needed by the bridge use:barcodeTypes(defaults to["qr"], so screen 6 is unchanged),permissionText(the default panel copy speaks of entering the address), andonPermissionStatus.app/bridge-scan.tsx. It reads the open scan fromlib/shell/bridge/scan-session.ts, never from its URL. Every way out settles the call: the close control, and Android back and the iOS swipe through the unmount. Opened with no scan (a link straight to the route), it closes at once.failed("Camera permission denied"). No new error type.scan()while one is open isfailed("A scan is already open"), and the open scan is left alone, so a double tap cannot close the scanner under the user.scangoes through the sametrustedcheck as every method; a page on another origin gets no answer and no scanner (tested).Answers stay with the calling document
A scan stays open for as long as the user takes, and the page can be replaced in the meantime: a reload, recovery after a failure, or a redirect. Bridge answers are now scoped to the document that made the call. Call ids carry a per-document token, and the shell sends no answer to a re-created WebView. An in-app navigation does not count as a new document, so a call open across a route change still settles. The tests below pin both ends.
Landing points outside the claimed file surface
app/console.tsx: one line added (plus its import). It is the real producer of the bridge'scapabilities, whichConsoleWebViewonly passes through, soscanhas to be wired there.components/shell/ConsoleWebView.tsx: the document guard above. The answer plumbing needs it to meet "the answer must not be injected into a different document".origin/mainwas merged in, not rebased. Key parity was verified across the ten.app/connect-scan.tsx: not touched. The RTL root direction sits inQrScanner, which is the root of both scan routes.Tests
Gates on head f856d04, each run under the verify lock, with exit codes captured before any pipe:
npx tsc --noEmit: exit 0npx eslint . --ext .ts,.tsx: exit 0, 0 warningsnpx jest --ci --coverage: exit 0, Test Suites 33/33, Tests 519/519npx jest --ci --no-cache --maxWorkers=2 __tests__/components/QrScanner.test.tsx __tests__/components/ConsoleWebView.test.tsx: exit 0, Tests 56/56. The scanner tests reuse the file's existingbeforeAllwarm-up.New coverage:
bad_argsshapes, a foreign origin, and through the real session: text, cancel tonull, denied tofailed, busy tofailed;scan-session.test.ts;ConsoleWebView: an answer injected while its document is live, still settled after an in-app navigation, dropped after a re-created WebView;QrScannerprops andBridgeScanScreen: formats, copy, text, close, unmount, denied by close and by unmount, closed before the prompt, opened with no scan;ar.Ablations (each mutation verified on disk by scripts/ablation-replace.mjs, restored to the HEAD blob, git diff HEAD empty)
if (!result || documentRef.current !== caller) return;replaced byif (!result) return;: 2 failed, 25 passed. Red: the two document-scoping cases.doc + "." + seqreplaced by"c" + seq: 2 failed, 26 passed. Red: the two per-document id cases.QrScannercamera root withoutstyle={{ direction }}: 2 failed, 27 passed. Red: the camera RTL case and the two-route RTL case.Mobile's jest runs the TypeScript sources through babel-jest (no
dist/), so there was no build step to prove.Acceptance notes
formats= every listed format. The card does not say. This reading matchesscan()for "barcode / QR" field entry; a caller that wants QR only passes["qr"].deniedisfailed. Leaving it before the prompt is answered (statusundetermined) isnull. After Settings, the permission hook does not re-read on foreground (expo-modules-coreusePermissionreads once on mount), so a Settings round trip needs the scan reopened.directionreaches a pushed screen across react-native-screens; the test renderer has no screen boundary to measure it on. As asked for that case, feat(i18n): ship the Console's ten locales and follow the phone language #169's root pattern (useLayoutDirection) is now on bothQrScannerroots.permissions.camera, all ten bundles) now names both uses ("QR codes and barcodes, such as the code that connects it to your server"). The old text named only the connect code, which a Console field scan would contradict. The plugin config is untouched.Verification
Head f5f3225 (patch round 1 on top of f856d04, which merged
origin/mainaef6993). Not verified on a device.Generated by Claude Code