Repository navigation
feat(connect): objectos://connect links and the QR scanner (screen 6) - #168
Merged
Merged
Conversation
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>
…onnect link 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: 维护者速读
席位意见: 「不会静默连接」这条有测试钉住。消融两腿:去掉链接分支,2 个用例转红;去掉返回入口,2 个用例转红。连接链接参数不对时回启动页(dev 选项 A),席位接受,因为不新增界面,也不把链接里多带的数据填进来。建议合并。 席位核对
|
os-justin
pushed a commit
that referenced
this pull request
Nov 2, 2026
Stack #168's connect-by-QR work with the ten-locale shell: the picker keeps the scan button, link prefill and way-back from #168 and the RTL direction from #169; #168's new keys (serverConfig.scan, serverConfig.backTo, scanner.*, permissions.camera) are carried into ja ko de fr es pt ru; app.config.ts localizes the iOS camera prompt in all ten languages. Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
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 #156
Clause-②: no
Metadata-protocol: none
维护者速读
objectos://connect?server=…链接(二维码或邀请邮件)会打开选择页,填好地址并自动探测。expo-camera,必须重新出包(EAS build),OTA 更新带不上它;任何链接或扫码都要用户在确认页点“连接并登录”才会保存,不会静默换服务器。链接冷启动到选择页且已有保存的服务器时,地址页会出现“返回 该服务器”按钮(安卓返回键同效),回到原服务器的 Console。回滚:revert 本 PR 后重新出包。lib/shell/incoming-link.ts(connectRequestFromLink的拒收规则)、app/server-config.tsx(带server参数进入时自动探测的 effect,以及showWayBack返回原服务器的条件)、app.config.ts(相机插件与三语权限文案)。npx jest --ci __tests__/lib/shell/incoming-link.test.ts __tests__/components/QrScanner.test.tsx __tests__/components/ServerConfigScreen.test.tsx,预期 3 个测试文件全部通过(29 + 15 + 21 个用例)。objectos://connect链接、拒绝相机权限。What changes
objectos://connectlinks (lib/shell/incoming-link.ts).connectRequestFromLinkreads the singleserverparam and normalizes it withnormalizeServerUrl.redirectIncomingPathsends a connect link to the picker as/server-config?server=…&n=…, never to the Console, so the user always sees the probe and the confirm step. A link with noserver, a repeatedserver, any other param, a path afterconnect, or malformed encoding is refused and lands on the start screen, like every other unknown link. An address that does not normalize (plain http to a public host, not a URL, empty) still opens the picker, with the raw value in the field and its error shown.components/shell/QrScanner.tsx, routeapp/connect-scan.tsx). The full-screenexpo-camerascanner reads QR codes only on the back camera, and has close and torch controls, the hint, the note, and "Enter address manually". The component knows nothing about servers. The caller passesaccept, the refusal text, and the optional note and manual action, so the bridgescan()(Bridge:scan()— barcode / QR scanning for Console fields #146) can reuse it as is. The route passesserverAddressFromCode, which acceptsobjectos://connectcodes plus a plainhttps://server URL; anything else shows "This isn't a connect QR code" and scanning continues. The first accepted code goes back to the picker withrouter.dismissTo, and the scanner stops reading. Camera permission is requested on open. If it is refused for good, the screen offers Settings and manual entry instead.app/server-config.tsx: scan button, prefill, and the way back). The scan button is the address field'srightSlot. Aserversearch param fills the field and runsrunProbe.nchanges on each request, so scanning the same code twice probes twice.router.canGoBack()is false, which is the case when a connect link cold-starts the app), the address step shows a "Back to" button naming the saved host (new keyserverConfig.backToin en, zh and ar). Android back on that step does the same thing. Both callrouter.dismissTo("/console")and save nothing. The confirm step is unchanged: "Not this server" still returns to the address step with the address kept, and the way back sits there. With no saved server, or when the picker was pushed over the Console, nothing is added. The screen-5 "where to find the address" copy is unchanged; the swap to Console's mobile-app page waits for Console: a "Mobile app" page under the user's settings that shows a connect QR code for the ObjectOS mobile app objectstack#29314.app.config.ts). Theexpo-cameraplugin is configured for the camera only:microphonePermission: falseandrecordAudioAndroid: false. The iOS permission text ispermissions.camerafrom each locale bundle (en, zh aszh-Hans, ar), written into the plugin and the per-languageInfoPlist.stringsthroughlocales.expo-camera~55.0.18, which is the versionexpo@55.0.26'sbundledNativeModules.jsonpins for this SDK (pnpmresolved 55.0.23).serverConfig.scan,serverConfig.backTo,scanner.*andpermissions.cameratolocales/{en,zh,ar}.json. Languages: follow the phone's language in the platform's 11 locales (ruling F of objectstack#29349) #159 (ten locales) had not landed when this was pushed (mainat29c25df), so there are three bundles.docs/HYBRID-SHELL.md, Server selection now has a "Connect by QR code or link" section, plus updated Layout and Testing lists.What the user sees when a server is already saved
Today there is one saved server; saved connections are #152.
Tests
__tests__/lib/shell/incoming-link.test.ts: link parsing is covered for valid, encoded, unencoded, missingserver, extra params, repeatedserver, http on a public host (insecure), http on a LAN host, malformed encoding, a non-connecthost and a white-label scheme. Also covered:serverAddressFromCodeaccept and refuse,serverConfigHref, and theredirectIncomingPathconnect cases.__tests__/components/QrScanner.test.tsxmocksexpo-camera. It covers permission loading, undetermined (asks once on open and again from the button), and blocked (Settings and manual entry, no request). It also covers refused codes (the message stays while the code is in view and clearsREJECT_MESSAGE_MSafter), the accepted code handed over once with reading stopped, torch on and off, close, and manual entry. Screen 6 itself is tested for its copy, refusing a code with an extra param, returning connect and https codes to the picker, and close or manual going back.__tests__/components/ServerConfigScreen.test.tsx: the scan button opens/connect-scan. A linked address is probed and reaches confirm with nothing saved. With a server already saved, the user is still asked, and confirming replaces it. An unusable linked address shows in the field with its error. A newnprobes the same address again. For the way back, there are four branches. (1) Saved server and no back stack: the button names the saved host; on the confirm step "Not this server" keeps the address and the button then appears; pressing it callsdismissTo("/console")and saves nothing. (2) Android back on that step does the same. (3) Saved server pushed over the Console: no button and no back handler. (4) No saved server: no button and no back handler.beforeAllwarm-up render with a 30 s timeout, covering both permission branches.Acceptance notes
objectosscheme is accepted, as the card specifies. An incoming link is read on whatever scheme the OS delivered it on, as the existingopenandconsolelinks are, so white-label builds (EXPO_PUBLIC_APP_SCHEME) work.localesalso writesvalues-b+en,values-b+zh+Hansandvalues-b+arfolders with an emptystrings.xml, because the Android camera prompt is system text. Measured in anexpo prebuildof this branch. Harmless.minorwithMetadata-protocol: none, following the feat(server-picker): probe ObjectOS discovery and confirm the server before connecting #165 changeset already in this repo. This repo has no Check Changeset gate.Verification
All local gates ran on head
0f04604, after mergingorigin/mainat29c25df. The commands are the same asci.ymlandlint-typecheck.yml.Gates: tsc, eslint, jest --ci --coverage (all exit 0)
npx tsc --noEmit: exit 0npx eslint . --ext .ts,.tsx: exit 0npx jest --ci --coverage: exit 0. Test Suites 31 passed of 31, Tests 411 passed of 411, coverage thresholds met.incoming-link.tshas 100 % lines and 98.18 % branches;QrScanner.tsxhas 100 % lines and 95.34 % branches.pnpm install --frozen-lockfile: exit 0Cold transform cache, as on a fresh CI runner
npx jest --ci --coverage --no-cache --maxWorkers=2 --verbose: exit 0, 411 of 411. The way-back tests took 5 to 23 ms each. The warm-upbeforeAllabsorbs the first-render load.Ablation: connect links no longer reach the picker
The ablation deleted the connect branch of
redirectIncomingPathon the committed head. The anchor occurrence count went from 1 to 0, and the marker count was 1. Result:incoming-link.test.tsexit 1 with 2 failed of 29: "opens the server picker for a connect link, never the Console" and "opens the picker with the raw value when the connect address is unusable". The file was restored withgit checkout HEAD. The blob hash matched HEAD (dd704c1) andgit diff HEADwas empty. The rerun exited 0 with 29 of 29.Ablation: the way back removed
The ablation replaced
showWayBackwithfalseon the committed head0f04604. The anchor occurrence count went from 1 to 0, and the marker count was 1. Result:ServerConfigScreen.test.tsxexit 1 with 2 failed of 21: "cold-started onto the picker with a server saved: offers it, and confirm's 'Not this server' keeps it" and "Android back on the address step does the same". The file was restored withgit checkout HEAD. The blob hash matched HEAD (fcfd873), andgit diff HEADandgit statuswere both empty. The rerun exited 0 with 21 of 21.Native config: expo prebuild of this branch, in a throwaway copy
ios/ObjectOS/Info.plisthasNSCameraUsageDescriptionwith the English text and noNSMicrophoneUsageDescription.en.lproj,zh-Hans.lprojandar.lprojeach haveInfoPlist.stringswith that language'sNSCameraUsageDescription.AndroidManifest.xmlhasandroid.permission.CAMERAand noRECORD_AUDIO.Not verified: device checks for the maintainer.
objectos://connect?server=…code. The confirm step should show your server.objectos://connectlink from an email or Notes, once with the app running and once with it closed. The picker should open, probe, and confirm. With a server already saved and the app closed, tap "Not this server". "Back to" the saved host (and Android back) should return to that server's Console.Generated by Claude Code