Skip to content

Feat/onboarding v2 ab test#5835

Open
capJavert wants to merge 17 commits intomainfrom
feat/onboarding-v2-ab-test
Open

Feat/onboarding v2 ab test#5835
capJavert wants to merge 17 commits intomainfrom
feat/onboarding-v2-ab-test

Conversation

@capJavert
Copy link
Copy Markdown
Contributor

@capJavert capJavert commented Apr 4, 2026

Changes

  • clean branch
  • does not touch any top level layout stuff (to avoid conflicts/breaking)
  • does not touch original onboarding or freyja for now, we will cleanup if this wins
  • behind feature tag for ab test
  • also asks for elevated private repos for import (we can remove)
  • only open point is error handling if we don't detect any keywords in flows

API: dailydotdev/daily-api#3775

Events

Did you introduce any new tracking events?

Experiment

Did you introduce any new experiments?

Manual Testing

Caution

Please make sure existing components are not breaking/affected by this PR

Preview domain

https://feat-onboarding-v2-ab-test.preview.app.daily.dev

capJavert and others added 3 commits April 4, 2026 17:21
Port onboarding v2 redesign from feat/redesign-onboarding branch as a
self-contained component, served on /onboarding behind the onboarding_v2
GrowthBook feature flag. No shared layout components modified.

- Add featureOnboardingV2 flag and mobileAppUrl constant
- Create OnboardingV2 component with hero, tag clouds, feed preview, auth flow
- Wire A/B test in onboarding page via dynamic import + feature flag
- Hide experience level in auth forms when flag is on (gated on both trigger + flag)
- Wrap feed preview with local SearchProvider/FeedLayoutProvider

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add static JSX header bar and collapsed sidebar to OnboardingV2 that
visually match the branch's minimal onboarding look without modifying
any shared layout components. Header shows logo + login/signup buttons,
sidebar shows greyed-out nav icons (pointer-events-none).

Also adds SearchProvider wrapper around the feed preview to fix
MainFeedLayout runtime error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Login opens the auth modal with login display, signup opens the signup
chooser. Both use the v2 component's own auth flow instead of
showLogin which redirects to /onboarding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@capJavert capJavert self-assigned this Apr 4, 2026
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 4, 2026

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

Project Deployment Actions Updated (UTC)
daily-webapp Ready Ready Preview Apr 8, 2026 3:56pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
storybook Ignored Ignored Apr 8, 2026 3:56pm

Request Review

@capJavert capJavert force-pushed the feat/onboarding-v2-ab-test branch from 6beb83e to 2b88e49 Compare April 4, 2026 15:28
- Rewrite import flow: async/await + CSS transition, no setInterval
- Auth first, then import: chooser buttons open auth, onSuccessfulRegistration triggers import
- Add completeAction (CompletedOnboarding, EditTag, ContentTypes) after tags saved
- Persist aiPrompt and signupContext via usePersistentContext (survives OAuth redirect)
- Add logged-in redirect: complete → redirectToApp, incomplete → show chooser
- Use ?step=complete query param to guard extension promo after completion
- Persist extensionSeen flag so promo only shows once
- Remove dead code: topics panel, SELECTABLE_TOPICS, intersection observer, recommendedTopics
- Remove 5-second auto-redirect from feedReadyState
- Fix redirect to use redirectToApp

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
capJavert and others added 2 commits April 8, 2026 11:15
Move ~1125 lines of <style jsx global> CSS (animations, keyframes,
feed limits, parallax, glassmorphism) to a separate component.
All class names are onb- prefixed, no collision risk.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…flow

Step-based refactor:
- Replace 6 boolean states with single OnboardingStep enum
- Simplify body overflow, engagement animation, JSX conditionals
- Split routing into query param handler + logged-in redirect
- Remove dead code: importFlowSourceRef, onbSignup effect, 'manual' context

Animation extraction:
- Create useOnboardingAnimations hook (mount, tags, feed, parallax,
  hero tracking, engagement floater, confetti, browser detection, body overflow)

Auth flow fixes:
- Add autoTriggerProvider prop to AuthOptionsProps for GitHub auto-trigger
- onSuccessfulRegistration triggers import based on signupContext
- Routing effect guarded by step (blocks during auth/importing/extension/complete)
- Completion actions fire on seniority selection, not prematurely
- Experience level saved via UPDATE_USER_PROFILE_MUTATION on seniority pick

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- OnboardingRegistrationForm: MemberAlready style gated on flag + trigger
- RegistrationForm: title gradient/size gated on flag
- Both fall back to original v1 styles when flag is off

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@capJavert
Copy link
Copy Markdown
Contributor Author

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 8, 2026

Claude finished @capJavert's task in 2m 22s —— View job


Code Review

  • Gather context and read changed files
  • Review OnboardingV2.tsx
  • Review OnboardingV2Styles.tsx
  • Review useOnboardingAnimations.ts
  • Review auth component changes
  • Review supporting changes (featureManagement, constants, graphql)
  • Post final review

Overall this is a well-structured A/B experiment — isolated behind a flag, ssr: false dynamic import, no layout changes. The UX flow and animation work is impressive. Below are issues worth addressing before or after ship.


Bugs / Correctness

1. Fire-and-forget mutation in handleExperienceLevelSelect

OnboardingV2.tsx:335 — The gqlClient.request() is not awaited and has no error handling. If this silently fails, the user's experience level is never saved:

// ❌ fire-and-forget
gqlClient.request(UPDATE_USER_PROFILE_MUTATION, {
  data: { experienceLevel: UserExperienceLevel[level] },
});

Since you're already in an async function you can await it, or at minimum .catch(console.error) for the experiment.

Fix this →


2. startImportFlow ignores API errors silently

OnboardingV2.tsx:279-293 — When the API call to requestGitHubProfileTags() / requestOnboardingProfileTags() fails, the code does nothing:

const [apiResult] = await Promise.allSettled([...]);
if (apiResult.status === 'fulfilled') {
  setAiPrompt('');
  setSignupContext(null);
}
// rejected → falls through to setGithubImportPhase('awaitingSeniority')
// user proceeds as if the profile was imported

The user still reaches the seniority screen without their profile being set up. At minimum, show an error state or toast.


3. Inner setTimeout calls in finishing phase are not cleaned up

OnboardingV2.tsx:450-462 — Only the outermost exitTimer is tracked by the useEffect cleanup. The two nested setTimeout calls fire unconditionally if the component unmounts mid-animation:

return () => clearTimeout(exitTimer); // ✅ outer only
// inner timeouts (600ms + 350ms) are leaked

Use useRef to track all three timers and clear them in cleanup.

Fix this →


Design / Architecture

4. OnboardingV2 not wrapped by ErrorBoundary or Toast

onboarding.tsx:375-377:

if (isOnboardingV2) {
  return <OnboardingV2 />;   // ❌ no ErrorBoundary, no Toast
}
return (
  <JotaiProvider>
    <ErrorBoundary feature="onboarding">   // ✅ V1 is protected

Any unhandled render error in V2 will crash the whole page. At minimum wrap it in <ErrorBoundary>.


5. featureOnboardingV2 defaults to isDevelopment

featureManagement.ts:155:

export const featureOnboardingV2 = new Feature('onboarding_v2', isDevelopment);

This enables V2 for all local dev by default. Developers working on the V1 funnel won't realize V2 is silently activated. Every other feature flag in this file defaults to false. Consider defaulting to false and having developers explicitly set it via GrowthBook SDK override.


6. GitHub icon SVG path hardcoded three times

The GitHub icon path (M12 2C6.477 2 2 6.477 2 12...) appears inline in the hero CTA, chooser modal, and import overlay. There's already a ChromeIcon import but no GitHubIcon usage. There is likely one in the icons package. Using it (or extracting a local GithubSvg const) removes ~60 lines of duplication.


7. autoTriggerProvider effect could double-fire in React StrictMode (dev)

AuthOptionsInner.tsx — The effect calls onProviderClickRef.current(autoTriggerProvider, false) on every render where autoTriggerProvider is set. In React 18 StrictMode (development only), effects run twice, meaning this fires onProviderClick twice → could trigger two OAuth redirects in dev. The ssr: false dynamic import limits blast radius to client-side only, but worth adding a guard.


Minor

8. mobileAppUrl uses direct URL instead of r.daily.dev redirect

constants.ts — All other external links in this file use https://r.daily.dev/... redirect URLs. The new mobileAppUrl = 'https://app.daily.dev' is inconsistent. If this is intentional (direct app store redirect), a comment would help.

9. OnboardingV2Styles rendered after feed section

OnboardingV2.tsx:1132<OnboardingV2Styles /> is placed after the feed renders. styled-jsx injects synchronously so FOUC is unlikely, but convention is to inject styles before the content they style.

10. usePersistentContext with undocumented 3rd argument

OnboardingV2.tsx:229-231:

const [signupContext, setSignupContext] = usePersistentContext<
  'github' | 'ai' | null
>(ONBOARDING_SIGNUP_CONTEXT_KEY, null, ['github', 'ai']);

The third argument appears to be an allowlist for hydration validation. Not documented — a comment would help readers understand why it's there.


- Add error handling to experience level mutation (.catch)
- Wrap OnboardingV2 in ErrorBoundary + Toast
- Replace inline GitHub SVGs with GitHubIcon component (filled.svg with currentColor)
- Add StrictMode guard for autoTriggerProvider (tracks provider name)
- Fix mobileAppUrl to use https://api.daily.dev/mobile redirect
- Move OnboardingV2Styles before content to prevent FOUC
- Remove secondary from existing GitHubIcon usage (was no-op)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@capJavert capJavert force-pushed the feat/onboarding-v2-ab-test branch from 5638446 to 9790db2 Compare April 8, 2026 14:48
Track all three setTimeout calls (outer + 600ms + 350ms) and clear
them all on unmount to prevent leaked timers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add scopes support to BetterAuth social auth flow:
- Add optional scopes param to getBetterAuthSocialRedirect chain
- Add socialProviderScopes prop to AuthOptionsProps
- Pass ['user:email', 'repo'] when signupContext is github
- Enables bragi to analyze private repos for better tag extraction

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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