feat(invites): emit invited_by in invite links, keep code as an alias - #2828
feat(invites): emit invited_by in invite links, keep code as an alias#28280xkkonrad wants to merge 3 commits into
Conversation
New links (share links, guest-door nav, invite-flow URLs) emit /invite?invited_by=<username> — the param names what the value is. Every reader (InvitesPage, /setup deep-link persistence, native openDeepLink) accepts both params via inviteCodeFromParams; legacy ?code= wins when both are present, so every previously shared link, QR code, and campaign URL keeps its exact behavior. No API change: the backend still receives the same bare-username invite code.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Code-analysis diffPainscore total: 7451.71 → 7453.41 (+1.7) 🆕 New findings (35)
…and 15 more. ✅ Resolved (34)
…and 14 more. |
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
…rect Review of #2828 found two readers the alias missed: - src/app/invite/page.tsx generateMetadata read only ?code=, so every new ?invited_by= link unfurled as "Invites | Peanut / Invalid invite link" with the generic image (verified on the Vercel preview). Now goes through inviteCodeFromParams like the client readers. - next.config.js root-domain hand-off was keyed on campaign|campaignTag|code only, so peanut.me/?invited_by=alice landed on the marketing page with the param dropped. Also: an empty ?code= falls through to ?invited_by= instead of masking it; stale ?code= comments updated. Tests cover both readers.
Adversarial review — 26 AugVerified on this PR's Vercel preview + prod curls; full grep for every other Fixed in de3cfc1 (this branch)
Fixed elsewhere
Not changed, noted
|
There was a problem hiding this comment.
Chip review — changes requested
The new reader paths are internally consistent and CI is green, but emitting only invited_by breaks invite attribution when those links open in an older installed native app.
Findings
- MAJOR · src/utils/general.utils.ts:822 · Keep new invite links readable by older native builds
A user on this release can share /invite?invited_by=alice; because /invite is an iOS/Android App Link, a recipient with the current older binary opens it in that bundle. At the supplied base SHA, both useNativeAppLinks and /setup read only code, so the app reaches signup without persisting inviteCode and the inviter is never accepted. Emit both invited_by and code with identical values for a transition window, or gate the writer until supported native versions can read invited_by; add a version-skew regression test.
Checked clean
- Verified the detached worktree HEAD and exact supplied base/merge base.
- Read the trusted PR title and description without fetching issue or review comments.
- Checked CI at the exact head: unit, e2e, typecheck, eslint, format, deploy, and aggregate gates succeeded; ping was skipped.
- Traced invited_by and legacy code precedence through metadata, InvitesPage, setup, the native route mapper, and the native link listener.
- Compared the new writers with base-SHA native readers and the iOS/Android App Link declarations; this exposed the version-skew attribution loss.
- Confirmed the API still accepts bare usernames and historical INVITESYOU forms, and checked the intentional legacy merchant-code writer.
- Reviewed redirect safety, off-host native-link rejection, empty-parameter behavior, and changed tests for reachable regressions.
Second opinion skipped: openrouter-timeout.
Exact head: 8e5f6e4f0ea4 · Context: repo, mono, ci
| export const generateInviteCodeLink = (username: string) => { | ||
| const inviteCode = toInviteCode(username) | ||
| const inviteLink = shareableUrl(`/invite?code=${inviteCode}`) | ||
| const inviteLink = shareableUrl(`/invite?invited_by=${inviteCode}`) |
There was a problem hiding this comment.
MAJOR: Keep new invite links readable by older native builds
A user on this release can share /invite?invited_by=alice; because /invite is an iOS/Android App Link, a recipient with the current older binary opens it in that bundle. At the supplied base SHA, both useNativeAppLinks and /setup read only code, so the app reaches signup without persisting inviteCode and the inviter is never accepted. Emit both invited_by and code with identical values for a transition window, or gate the writer until supported native versions can read invited_by; add a version-skew regression test.
|
@0xkkonrad — this one is Worth doing soon rather than later: #2876 brings the design system to No migration work needed on this branch either way: it's the only feature PR in the current queue with zero DS debt — no legacy palette classes, no stock text sizes. Once the conflict is resolved it can merge straight away, and the integration branch absorbs it. |
What
Invite links now emit
/invite?invited_by=<username>instead of/invite?code=<username>. Thecodeparam stays supported forever as a read-side alias — every previously shared link, QR code, X post, and campaign URL keeps its exact behavior.Why
The value in the param is the inviter's username, so the param should say so.
codestays because years of shared links carry it, and because the code slot also transports non-username values (offramp, legacyINVITESYOUsuffixes, campaign slugs).How
inviteCodeFromParams()ininvite-code.utils.tsis the single reader:params.get('code') ?? params.get('invited_by'). Legacycodewins when both are present, so a pre-existing link keeps its behavior even with the new param appended — the alias is purely additive.InvitesPage,/setupdeep-link cookie persistence, nativeopenDeepLink(useNativeAppLinks).invited_by:generateInviteCodeLink(),inviteFlowUrl(), and the PublicProfile guest-door navigation./invites/validateand/invites/acceptstill receive the same bare-username code in the body.INVITE_CODE_TO_CAMPAIGN_MAP/ badge-campaign resolution are untouched — they key off the resolved value, not the param name.link_type: 'invite_code'wire value unchanged (registered constant, PostHog continuity).Tests
inviteCodeFromParams(both params, code-wins precedence, absent case).general.utils,invite-flow-url,PublicProfile,BadgeDetailModal).?code=deep-link tests kept as-is and now prove the alias; addedinvited_byvariants for native route mapping andInvitesPage.tsc --noEmitclean.