feat(native): deferred deep linking through the store install (TASK-20772) - #2560
feat(native): deferred deep linking through the store install (TASK-20772)#2560kushagrasarathe wants to merge 2 commits into
Conversation
…0772) Carry locale + invite code + campaign tag + destination path from mobile web through the app-store hop. Android rides the Play Install Referrer (app-local InstallReferrerPlugin, resolve-once + 5s timeout); iOS rides a clipboard hand-off double-gated by prompt-free UIPasteboard checks (hasStrings + detectPatterns probable-web-url) so unrelated clipboard content never triggers the paste alert. One-shot restore on first launch: 30-day normalized inviteCode/campaignTag cookies (setup flow already reads them), locale persisted under the app-locale key the upcoming in-app i18n (on dev) reads — @capacitor/preferences added so the preference lands in native storage and the plugin ships in this binary — and navigation to the destination only when no real deep link landed. Restore is not awaited in init so a pending paste prompt or slow referrer service can never block the push listener or splash hide. Base is main (not dev) so the native release pipeline can cut a testable binary; back-merge to dev will need a small locale-handling reconcile with dev's src/i18n/app (marked in deferred-link.ts).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds deferred deep-link payload generation, Android and iOS restoration, native navigation coordination, a guarded development page, Capacitor Preferences support, and comprehensive tests. ChangesDeferred deep-linking
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested labels: Sequence Diagram(s)sequenceDiagram
participant WebPage
participant Store
participant NativeApp
participant DeferredContext
WebPage->>Store: Open handoff URL with deferred payload
Store->>NativeApp: Install application
NativeApp->>DeferredContext: Restore referrer or clipboard payload
DeferredContext-->>NativeApp: Return destination and locale
NativeApp->>NativeApp: Navigate when no launch deep link handled
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed: dependency version conflict. Check your lock file or package.json. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
Code-analysis diffPainscore total: 6309.2 → 6324.39 (+15.19) 🆕 New findings (27)
…and 7 more. ✅ Resolved (21)
…and 1 more. 📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/dev/deferred/page.tsx`:
- Around line 92-114: Reset the copied state when building a new payload in the
deferred page’s payload-generation button. Update the onClick handler that calls
buildDeferredPayload to also set copied to false, while preserving the existing
setCopied(true) behavior after copyIOSHandoff completes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 530cb036-dd58-415a-979d-78df4ff5291d
⛔ Files ignored due to path filters (7)
android/app/build.gradleis excluded by!android/**android/app/capacitor.build.gradleis excluded by!android/**android/app/src/main/java/me/peanut/wallet/InstallReferrerPlugin.javais excluded by!android/**android/app/src/main/java/me/peanut/wallet/MainActivity.javais excluded by!android/**android/capacitor.settings.gradleis excluded by!android/**ios/App/App/ClipboardDetectPlugin.swiftis excluded by!ios/**pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
package.jsonsrc/app/dev/deferred/page.tsxsrc/constants/general.consts.tssrc/hooks/__tests__/useNativePlugins.test.tsxsrc/hooks/useNativePlugins.tssrc/utils/__tests__/deferred-link.test.tssrc/utils/clipboard-detect.tssrc/utils/deferred-link.ts
…s, PIX exit, verification tasks Cherry-picks the open native-facing work onto the release branch: - KYC now routes every native entry point through the Sumsub Cordova SDK instead of the WebSDK (peanut-ui #2562) — the one change here that genuinely requires a new binary rather than an OTA. - Deferred deep linking survives the store install, so a link followed before install lands on its destination on first launch (#2560, TASK-20772). - PIX deposit "Done" exits to home from both the completed and processing states instead of dropping the user into a new deposit (#2548). - Pending Bridge verification tasks card on home, dismissible and resurfaced under Unlocked regions (#2549). - Native demo/passkey follow-ups: the awaited token clear in clearAuthState plus the two callers it changed (#2517 — the rest shipped in 1.0.40). - Documents left running on a superseded deployment now reload (#2563). - Android safe zone sized from natively measured insets. Not carried: the CSP allow-list gaps (#2564). It builds on the #2519 collector rework — a /api/csp-report route and ten commits that are not on this branch — and the policy is report-only, so the gaps cost report fidelity, not behaviour.
🤖 High-effort code review — 10 verified findingsAutomated multi-agent review (4 finders + independent verification per finding; 22 candidates, 3 refuted). The happy path works, but the restore path breaks in several everyday situations. 🔴 Blockers1. Existing users get locked out of login ( 2. Unmapped dests navigate to routes that don't exist ( 3. Transient Android referrer failure burns the one-shot flag ( 4. Late restore yanks users out of mid-onboarding ( 🟡 Should fix5. iOS re-prompts after kill-during-prompt ( 6. 7. First-launch race: invite gate vs. referrer read ( 8. 🧹 Cleanup9. Dev page forks the prod wall ( 10. Inline invite normalization duplicates 🤖 Generated with Claude Code |
Summary
TASK-20772 — deferred deep linking v1 (DIY). When a mobile-web user is bounced to the Play Store / App Store, their context (locale, invite code, campaign tag, destination path) now survives the install into the native app:
&referrer=<payload>to the Play URL; a new app-local Capacitor plugin (InstallReferrerPlugin.java, Play Install Referrer library) reads it back on first launch. Deterministic, first-party.https://peanut.me/?pnutdl=1&…to the clipboard; first launch reads it via@capacitor/clipboard, gated by the existing prompt-freeclipboardHasStrings()so organic installs with an empty clipboard never see the iOS paste prompt.deferredLinkConsumedflag): sets the existinginviteCode/campaignTagcookies (setup flow already reads them), persists the normalized locale under theapp-localekey that dev's in-app i18n (src/i18n/app/locale-store.ts) reads — localStorage +@capacitor/preferences(dep added; the plugin ships in this binary, which dev's i18n also needs to OTA onto it) — and navigates to the destination only when the launch wasn't already a real deep link. Unsupported language tags resolve to null so a garbage payload can never override the device language./dev/deferredtest page only — the user-facing download modal (TASK-20769) will consumebuildDeferredPayload/playStoreUrlWithReferrer/copyIOSHandoffwhen it lands.Design notes / accepted trade-offs
localStorage(survives Capgo OTA; resets on reinstall — which is exactly fresh-install semantics).hasStrings+ newdetectPatternsprobable-web-URL inClipboardDetectPlugin.swift): organic installs with unrelated clipboard text (a password, a draft) are never prompted — only a clipboard that plausibly holds the hand-off URL is worth the alert./es-419/…) are stripped from dests on both sides — those routes don't exist in the native static export.utm_source=google-play…) is rejected by thepnutdl=1marker.Risks / breaking changes
ClipboardDetectPlugin.swift— rides the next IPA (iOS is mid-submission; no binaries in the field). On a binary without the method the JS skips the clipboard read entirely: no prompt, no restore, status quo.QA
deferred-link.test.ts): build→parse round-trip, Play URL encoding hop, iOS hand-off form, organic-referrer rejection, one-shot consume, off-origin dest rejection, locale normalization.:app:compileDebugJavaWithJavacgreen with the new plugin./dev/deferredshows restored state); Android via Play internal testing track with a&referrer=install link; sideloaded builds verified to no-op.Screenshots: N/A (no visible change — dev-only page + native plumbing)
Summary by CodeRabbit