feat(filesharing): one-tap Yivi app open on iOS (pre-warmed session)#321
Conversation
Builds on #319. On iOS a Yivi Universal Link only opens the app inside a genuine user gesture, so #319's two-tap flow needs a second tap on Yivi's own button. This pre-warms the signing session with pg.prepareSign() as soon as the compose form is valid, then renders "Send" as a real <a href> to the resulting app URL — so a single tap opens the app. The pre-resolved signing keys are fed into encrypt() via signingKeys, reusing the disclosure instead of starting a second session. - Pre-warm lifecycle driven by an $effect: warm while composing + form valid, tear down otherwise; re-warm on window focus after the session's TTL lapses. - Hidden host (#prewarm-yivi) drives the invisible widget; the user interacts with the send anchor. - Best-effort: until the URL is ready (or on desktop, or if the session lapsed) it falls back to #319's two-tap button/QR flow. Requires @e4a/pg-js >= 2.2.0 (prepareSign + EncryptInput.signingKeys); bumped dependency and lockfile to 2.2.0.
c65a6d8 to
3430744
Compare
There was a problem hiding this comment.
Post-merge review — PR already merged by @rubenhensen; noting for the record.
Reconciled the handed-over review findings and a focused rule sweep (WCAG 2.2 AA, conventional PR title, i18n, dep-bump) against the actual diff. CI was green (svelte-check 0/0, eslint clean, 14/14 unit, build ok) and the PR title is conventional.
- One non-blocking UX note inline.
- A handed-over i18n finding was dropped: it referenced
fallback/Decrypt.svelte, which is not part of this diff, and this PR does localize its newoneTapWaitingstring in both en.json and nl.json. - A spinner
aria-hiddena11y nit was dropped: the new spinner matches the pre-existing spinner pattern already in this file (line 595), so it is carried-forward debt, not a regression here.
| </p> | ||
| {/if} | ||
| </div> | ||
| {:else if isMobileDevice && yiviAppUrl && canEncrypt} |
There was a problem hiding this comment.
Non-blocking (follow-up): this one-tap <a href> branch renders only the send label. The powered by Yivi attribution (line 664) and the Sign with Yivi on another device QR chip (line 684) live only in the {:else} button branch. So once yiviAppUrl + canEncrypt are ready on iOS, a user whose Yivi app is not on this phone loses the cross-device QR fallback, and the Yivi attribution disappears. Consider rendering both in the one-tap branch too.
What
Collapses the iOS "sign and send" flow to a single tap. Follows up #319, which fixed the "opens Safari instead of the app" bug but still needs a second tap on Yivi's own button (a Universal Link only hands off to the app inside a genuine user gesture, and the app URL doesn't exist until the session starts).
This pre-warms the Yivi signing session with
pg.prepareSign()as soon as the compose form is valid, so the app deep-link URL is ready before the tap. "Send" is then a real<a href>to that URL — one tap opens the app. The pre-resolved signing keys are fed intoencrypt({ signingKeys }), reusing the disclosure instead of starting a second session.How
$effect): warm while composing +canEncrypt; tear down otherwise. A second$effectre-warms onwindowfocus after the session's TTL lapses.preparedSignis intentionally non-reactive so a persistent failure can't create a re-warm loop.yiviAppUrlis ready, render an<a href>(onSignOneTapruns alongside the navigation and must notpreventDefault). Otherwise the normal button.#prewarm-yividrives the invisible widget; the user only ever touches the send anchor.onetapwaiting sheet: after the tap, shows "Complete the steps in the Yivi app, then return here" while the disclosure completes; on cancel/timeout it recovers via the existing Yivi disclosure aborts when the app is closed mid-flow and does not recover #294 interrupted-banner path.startEncryption()gained an optionalsigningKeysparam; when omitted it behaves exactly as before (and retires any idle pre-warm session).Verification
svelte-check0 errors/0 warnings;eslintclean; Svelte MCP autofixer no issues;npm run test:unit14/14; fullvite buildsucceeds (against a locally-linked pg-js build).The actual iOS Universal Link hand-off can't be exercised by automated tests — needs a device once pg-js is released:
open.yivi.appin Safari). Complete disclosure, return to Safari → upload proceeds.