Skip to content

feat(onboarding): invite 3 friends, get free Plus funnel step#6366

Draft
tsahimatsliah wants to merge 1 commit into
claude/website-sharing-visibility-be6b32from
claude/onboarding-invite-reward
Draft

feat(onboarding): invite 3 friends, get free Plus funnel step#6366
tsahimatsliah wants to merge 1 commit into
claude/website-sharing-visibility-be6b32from
claude/onboarding-invite-reward

Conversation

@tsahimatsliah

@tsahimatsliah tsahimatsliah commented Jul 22, 2026

Copy link
Copy Markdown
Member

What

A new onboarding funnel step — "Invite 3 friends, get 1 month of Plus on us" — that fires referral intent at the highest-motivation moment of the funnel. Part of the sharing-visibility initiative (PR 20), based on PR #6343.

  • New FunnelStepType.InviteFriends ('inviteFriends') + FunnelStepInviteFriends typed step, registered in FunnelStepper's stepComponentMap.
  • Reuses the existing referral infrastructure: useReferralCampaign(ReferralCampaignKey.Generic), InviteLinkInput, SocialShareList (native share sheet feature-detected on mobile via useShareOrCopyLink), and the existing GivebackConfettiBurst for the 3/3 celebration.
  • Progress UI (0/3 friends joined) reads referredUsersCount; reaching the target swaps to a celebratory reveal (confetti + headline) with a Continue CTA.
  • Skip is always available — an in-step "I'll do this later" tertiary button fires the standard skip transition; onboarding is never hard-gated on inviting.
  • After any invite action (copy / social share / native share) the primary CTA becomes "Continue" so inviting never dead-ends the funnel.
  • Reward framing is a copy parameter with {count} / {reward} placeholders (default "1 month"), so the 1-month vs 3-months arms are A/B-able from the funnel JSON alone — no frontend change needed.
  • Measurable via standard funnel events: new FunnelTargetId values (invite friends, invite friends copy, invite friends share, invite friends skip) on the tracked elements, plus the standard transition events carrying { referredUsersCount, targetReached, invited } details. Existing LogEvent.CopyReferralLink / LogEvent.InviteReferral fire with TargetId.Onboarding (no log.ts changes needed).

Flag

onboarding_invite_reward (featureOnboardingInviteReward), default false. Evaluated via useConditionalFeature with shouldEvaluate only when a logged-in user mounts a funnel containing the step, so enrollment matches exposure. Intentionally not gated on useSharingVisibility() — this is an onboarding experiment with its own lifecycle. Flag off (or step absent from the funnel JSON) → the step auto-registers as skipped and the funnel behaves exactly as before.

⚠️ Backend dependencies — cannot ship without

This PR is frontend-only by explicit decision. Two hard backend dependencies must land before this can be enabled for anyone:

1. Funnel JSON (/boot/funnels/:key)

Onboarding steps are emitted by the backend funnel JSON. The new step will not appear in any real flow until the backend adds it. Required shape:

{
  "id": "invite_friends",
  "type": "inviteFriends",
  "parameters": {
    // all optional — sensible defaults ship in the component
    "headline": "Invite {count} friends, get {reward} of Plus on us",
    "explainer": "...",
    "celebrationHeadline": "...",
    "celebrationExplainer": "...",
    "celebrationCta": "Continue",
    "reward": "1 month",      // A/B lever: "1 month" vs "3 months" (copy only)
    "targetCount": 3
  },
  "transitions": [
    { "on": "complete", "destination": "<next-step-or-finish>" },
    { "on": "skip", "destination": "<next-step-or-finish>" }  // REQUIRED — the in-step skip button only renders when this transition exists
  ]
}

The step must be placed after registration (it auto-skips for anonymous users because referral links are per-user).

2. Plus grant mechanism

There is no frontend path to grant PlusisPlus is backend-set and this PR deliberately never fakes or sets any Plus state client-side. Backend needs a new reward mechanism. The frontend contract it should build against:

  • What counts as a successful invite: the existing referredUsersCount semantics of the generic referral campaign (referralCampaign(referralOrigin: "generic")) — a user who signs up through the inviter's referral link/token. The step reads this exact field for progress, so grant eligibility and the progress UI stay in sync by construction.
  • Grant trigger: when a user's generic referredUsersCount reaches the target (3), backend grants the reward automatically (server-side observer on referral attribution — not triggered by any frontend call). The step's complete transition details ({ referredUsersCount, targetReached, invited }) are analytics, not a grant request.
  • Reward: free daily.dev Plus for the configured duration — default 1 month, with 3 months as the planned A/B arm. The frontend only renders the reward copy parameter; the actual granted duration must be configured backend-side to match the arm the funnel JSON is serving.
  • Frontend expectations after grant: user.isPlus flips via boot as it does today; no new client API is required. (A follow-up notification/email when the reward lands would be a backend/product nicety, not required by this UI.)

Testing

  • packages/shared/src/features/onboarding/steps/FunnelInviteFriends.spec.tsx — 12 tests: progress from referredUsersCount (0/3, 1/3), reward-copy A/B parameter, copy → clipboard + toast, mobile → navigator.share, skip transition (+ JSON-provided skip copy), funnel target ids, 3/3 celebration → complete, flag-off / anonymous auto-skip registration, flag-on registration.
  • FunnelStepper.spec.tsx — 2 new tests: the registered step renders inside the stepper with the real flag plumbing on, and renders nothing with the flag off; all 18 pre-existing stepper tests untouched and passing (no stepComponentMap regression).
  • Storybook: stories/components/onboarding/FunnelInviteFriends.stories.tsx — 0/3, 1/3, 3/3 celebration, 3-months reward arm, and missing-skip-transition states (renders the unguarded step; flag gating is Jest-verified since the repo-wide Storybook flag mock can't represent flag-off).
  • node ./scripts/typecheck-strict-changed.js ✅ · pnpm --filter shared lint ✅ · NODE_ENV=test pnpm --filter shared test → 295 suites / 1999 tests ✅ · NODE_ENV=test pnpm --filter webapp test → 44 suites / 297 tests ✅

🤖 Generated with Claude Code

Preview domain

https://claude-onboarding-invite-reward.preview.app.daily.dev

Adds a FunnelStepType.InviteFriends onboarding step that fires referral
intent at the highest-motivation moment: invite friends via the generic
referral campaign, with a 0/3 progress indicator from referredUsersCount
and a confetti celebration when the target is reached. Skip is always
available, the reward copy ({reward} placeholder, default "1 month") is
A/B-able straight from the funnel JSON, and the whole step is gated
behind the new onboarding_invite_reward flag (default false).

Frontend-only: the step needs the backend funnel JSON to include it and
a backend Plus-grant mechanism to award the reward; no Plus state is
ever set client-side.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
daily-webapp Ready Ready Preview Jul 22, 2026 4:36pm
storybook Building Building Preview Jul 22, 2026 4:36pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant