fix: pwa-sunset follow-ups — /app in-app bounce, snooze tests, invite handoff - #2689
Conversation
universal links (paths: ["*"]) open peanut.me/app inside the app when an installed user scans a download QR — the page then bounced them out to the store they came from. exactly the launch-week cohort.
the pre-prompt carries three dismissal representations (legacy bool, timestamp, flag-conditional 14-day snooze) and none of it was tested — the last open item from the #2591 review rounds.
'Claim your spot' pushed /setup?step=signup during the migration window — correct end state via the gated landing, but one extra hop and inconsistent with the claim/request/pay guest CTAs. also gives the invite surface the migration_guest_cta_shown impression event for the TASK-20939 funnel.
|
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 (4)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThe app redirects native Capacitor sessions to ChangesNative store flows
Notification visibility coverage
Estimated code review effort: 3 (Moderate) | ~20 minutes Mergeability Score: ⚪ Minimal · up to The changes are targeted to the PWA sunset flow and are covered by added tests and reported validation; no actionable merge-blocking risk remains beyond normal checks and review. Sequence Diagram(s)sequenceDiagram
participant Guest
participant InvitesPage
participant useGuestStoreHandoff
participant Signup
Guest->>InvitesPage: Claim your spot
InvitesPage->>InvitesPage: Save invite attribution
InvitesPage->>useGuestStoreHandoff: Intercept guest CTA
useGuestStoreHandoff-->>InvitesPage: Return handled state
InvitesPage-->>Guest: Show handoff modal
InvitesPage--xSignup: Do not navigate to signup
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Code-analysis diffPainscore total: 7159.11 → 7161.17 (+2.06) 🆕 New findings (22)
…and 2 more. ✅ Resolved (20)
📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
- /app guard: isNativeBridge over isCapacitor (capacitor-flavored web builds bake NEXT_PUBLIC_CAPACITOR_BUILD with no bridge — those visitors need the store page), fold redirect into the mount effect, skip the flag subscription in-app, reuse PeanutLoading coverFullScreen - invites: arm the guest impression only when the CTA actually renders (not the invalid-invite view or dead-bare-link redirect) + pin it; save redirect_uri before the store handoff so mid-flow destinations survive the round-trip - notif tests: baseline-true-then-refresh so stays-closed assertions can't pass on leftover module-store state; snooze fixtures derive from NOTIF_PROMPT_SNOOZE_DAYS
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
The three open items from the PR #2591 review audit (TASK-21145), none of which block the flag-flip individually but item 1 is strongly recommended before it:
/appinside the native app routed installed users out to the store. Universal links (paths: ["*"]) openpeanut.me/appinside the app when an installed user scans a download QR — the launch-week cohort exactly. Now:isCapacitor()→router.replace('/home'), loading state while it happens, store redirect and 404 suppressed.useNotificationssnooze conversion had no test. NewuseNotifications.test.tspinsevaluateVisibility's dismissal logic: legacynotifModalClosedbool converts to anotifModalClosedAttimestamp exactly once, flag-off = closed forever, flag-on + expired 14-day snooze = re-ask, flag-on + fresh snooze = closed, granted permission always hides. (The sunset-gate half of this ask shipped in feat(migration): pwa-sunset surfaces behind PostHog flag (native-app migration) #2591 asmigration.utils.test.ts.)useGuestStoreHandofflike the claim/request/pay guest CTAs: desktop gets the scan-to-download QR modal, phones deep-link their store. Also emits themigration_guest_cta_shownimpression (TASK-20939 funnel). Invite cookie/queue bookkeeping still runs first so keep-web signups and post-install link re-taps recover the invite context.Task
TASK-21145 — pwa-sunset follow-ups
Risks / breaking changes
pwa-sunsetPostHog flag is ON (useGuestStoreHandoffand the/apppage both gate on it; the notifications change is test-only). Flag OFF = today's behavior byte-for-byte./appin-app: home instead of a store bounce. No web-path change.QA
npm test— 232 suites / 2979 tests green, including the 7 new dismissal tests and the new interstitial handoff test.npm run typecheck+npm run buildclean.localStorage.setItem('pwa-sunset', 'true')then visit an invite link logged-out → CTA hands off to QR modal (desktop) / store (mobile).Screenshots
N/A (no new visuals) — the interstitial handoff reuses
ScanToDownloadModalexactly as shipped and screenshotted on the other guest surfaces in #2591; the/appin-app path renders the existing loading spinner during the redirect and needs a device to capture.Design notes / accepted trade-offs (review round 1)
Applied from the automated review:
isNativeBridge()overisCapacitor()on/app(capacitor-flavored web previews bakeNEXT_PUBLIC_CAPACITOR_BUILDwith no bridge and still need the store page), guest impression armed only when the CTA actually renders (invalid-invite / dead-bare-link views no longer counted — pinned by test),redirect_urisaved before the store handoff, and stays-closed test assertions hardened against the shared module store.Declined, with reasons:
window.openhandoff can be silently blocked in some in-app webviews — pre-existing feat(migration): pwa-sunset surfaces behind PostHog flag (native-app migration) #2591 behavior shared by all 3 guest surfaces (openStore→openExternalUrl); the fix belongs in that one chokepoint as a follow-up, not per-surface here./app→/homeremap intomapDeepLinkPath— the mapper only runs forappUrlOpenevents; the page-level guard covers every arrival path, and one implementation beats two.useGuestStoreHandoffitself — behavior-preserving refactor across 3 call sites, out of this PR's blast radius; follow-up.Summary by CodeRabbit
New Features
Bug Fixes