Skip to content

Commit c0c106f

Browse files
committed
2 parents f8d0c46 + b3df566 commit c0c106f

30 files changed

Lines changed: 945 additions & 234 deletions

‎apps/web/app/(navigation)/page.tsx‎

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
"use client"
22

33
import { useOnboardingStorage } from "@hooks/use-onboarding-storage"
4+
import { useOrgOnboarding } from "@hooks/use-org-onboarding"
45
import { useAuth } from "@lib/auth-context"
56
import { ChevronsDown, LoaderIcon } from "lucide-react"
67
import { useRouter } from "next/navigation"
7-
import { useEffect } from "react"
8+
import { useEffect, useMemo } from "react"
89
import { InstallPrompt } from "@/components/install-prompt"
910
import { ChromeExtensionButton } from "@/components/chrome-extension-button"
1011
import { ChatInput } from "@/components/chat-input"
@@ -14,11 +15,37 @@ import { useFeatureFlagEnabled } from "posthog-js/react"
1415

1516
export default function Page() {
1617
const { user, session } = useAuth()
17-
const { shouldShowOnboarding, isLoading: onboardingLoading } =
18-
useOnboardingStorage()
1918
const router = useRouter()
2019
const flagEnabled = useFeatureFlagEnabled("nova-alpha-access")
2120

21+
// TODO: remove this flow after the feature flag is removed
22+
// Old app: localStorage-backed onboarding
23+
const {
24+
shouldShowOnboarding: shouldShowOldOnboarding,
25+
isLoading: oldOnboardingLoading,
26+
} = useOnboardingStorage()
27+
28+
// New app: DB-backed onboarding (org.metadata.isOnboarded)
29+
const {
30+
shouldShowOnboarding: shouldShowNewOnboarding,
31+
isLoading: newOnboardingLoading,
32+
} = useOrgOnboarding()
33+
34+
// Select the appropriate onboarding state based on feature flag
35+
const isOnboardingLoading = useMemo(() => {
36+
if (flagEnabled) {
37+
return newOnboardingLoading
38+
}
39+
return oldOnboardingLoading
40+
}, [flagEnabled, newOnboardingLoading, oldOnboardingLoading])
41+
42+
const shouldShowOnboarding = useMemo(() => {
43+
if (flagEnabled) {
44+
return shouldShowNewOnboarding()
45+
}
46+
return shouldShowOldOnboarding()
47+
}, [flagEnabled, shouldShowNewOnboarding, shouldShowOldOnboarding])
48+
2249
useEffect(() => {
2350
const url = new URL(window.location.href)
2451
const authenticateChromeExtension = url.searchParams.get(
@@ -46,16 +73,16 @@ export default function Page() {
4673
}, [user, session])
4774

4875
useEffect(() => {
49-
if (user && !onboardingLoading && shouldShowOnboarding()) {
76+
if (user && !isOnboardingLoading && shouldShowOnboarding) {
5077
if (flagEnabled) {
5178
router.push("/new/onboarding?step=input&flow=welcome")
5279
} else {
5380
router.push("/onboarding")
5481
}
5582
}
56-
}, [user, shouldShowOnboarding, onboardingLoading, router, flagEnabled])
83+
}, [user, shouldShowOnboarding, isOnboardingLoading, router, flagEnabled])
5784

58-
if (!user || onboardingLoading) {
85+
if (!user || isOnboardingLoading) {
5986
return (
6087
<div className="min-h-screen flex items-center justify-center bg-[#0f1419]">
6188
<div className="flex flex-col items-center gap-4">
@@ -66,7 +93,7 @@ export default function Page() {
6693
)
6794
}
6895

69-
if (shouldShowOnboarding()) {
96+
if (shouldShowOnboarding) {
7097
return null
7198
}
7299

‎apps/web/app/api/onboarding/research/route.ts‎

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,22 @@ interface ResearchRequest {
77
email?: string
88
}
99

10-
// prompt to get user context from X/Twitter profile
11-
function finalPrompt(xUrl: string, userContext: string) {
10+
function extractHandle(url: string): string {
11+
const cleaned = url
12+
.toLowerCase()
13+
.replace("https://x.com/", "")
14+
.replace("https://twitter.com/", "")
15+
.replace("http://x.com/", "")
16+
.replace("http://twitter.com/", "")
17+
.replace("@", "")
18+
19+
return (cleaned.split("/")[0] ?? cleaned).split("?")[0] ?? cleaned
20+
}
21+
22+
function finalPrompt(handle: string, userContext: string) {
1223
return `You are researching a user based on their X/Twitter profile to help personalize their experience.
1324
14-
X/Twitter Profile URL: ${xUrl}${userContext}
25+
X Handle: @${handle}${userContext}
1526
1627
Please analyze this X/Twitter profile and provide a comprehensive but concise summary of the user. Include:
1728
- Professional background and current role (if available)
@@ -29,18 +40,12 @@ export async function POST(req: Request) {
2940

3041
if (!xUrl?.trim()) {
3142
return Response.json(
32-
{ error: "X/Twitter URL is required" },
43+
{ error: "X/Twitter URL or handle is required" },
3344
{ status: 400 },
3445
)
3546
}
3647

37-
const lowerUrl = xUrl.toLowerCase()
38-
if (!lowerUrl.includes("x.com") && !lowerUrl.includes("twitter.com")) {
39-
return Response.json(
40-
{ error: "URL must be an X/Twitter profile link" },
41-
{ status: 400 },
42-
)
43-
}
48+
const handle = extractHandle(xUrl)
4449

4550
const contextParts: string[] = []
4651
if (name) contextParts.push(`Name: ${name}`)
@@ -51,29 +56,13 @@ export async function POST(req: Request) {
5156
: ""
5257

5358
const { text } = await generateText({
54-
model: xai("grok-4-1-fast-reasoning"),
55-
prompt: finalPrompt(xUrl, userContext),
56-
providerOptions: {
57-
xai: {
58-
searchParameters: {
59-
mode: "on",
60-
sources: [
61-
{
62-
type: "web",
63-
safeSearch: true,
64-
},
65-
{
66-
type: "x",
67-
includedXHandles: [
68-
lowerUrl
69-
.replace("https://x.com/", "")
70-
.replace("https://twitter.com/", ""),
71-
],
72-
postFavoriteCount: 10,
73-
},
74-
],
75-
},
76-
},
59+
model: xai.responses("grok-4-fast"),
60+
prompt: finalPrompt(handle, userContext),
61+
tools: {
62+
web_search: xai.tools.webSearch(),
63+
x_search: xai.tools.xSearch({
64+
allowedXHandles: [handle],
65+
}),
7766
},
7867
})
7968

‎apps/web/app/new/layout.tsx‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useEffect } from "react"
44
import { useFeatureFlagEnabled } from "posthog-js/react"
55
import { useRouter } from "next/navigation"
6+
import { MobileBanner } from "@/components/new/mobile-banner"
67

78
export default function NewLayout({ children }: { children: React.ReactNode }) {
89
const router = useRouter()
@@ -18,5 +19,10 @@ export default function NewLayout({ children }: { children: React.ReactNode }) {
1819
return null
1920
}
2021

21-
return <>{children}</>
22+
return (
23+
<>
24+
<MobileBanner />
25+
{children}
26+
</>
27+
)
2228
}

‎apps/web/app/new/onboarding/setup/layout.tsx‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
"use client"
22

3-
import { createContext, useContext, useCallback, type ReactNode } from "react"
3+
import {
4+
createContext,
5+
useContext,
6+
useCallback,
7+
useEffect,
8+
useRef,
9+
type ReactNode,
10+
} from "react"
411
import { useRouter, useSearchParams } from "next/navigation"
512
import { useOnboardingContext, type MemoryFormData } from "../layout"
13+
import { analytics } from "@/lib/analytics"
614

715
export const SETUP_STEPS = ["relatable", "integrations"] as const
816
export type SetupStep = (typeof SETUP_STEPS)[number]
@@ -34,9 +42,11 @@ export default function SetupLayout({ children }: { children: ReactNode }) {
3442
const currentStep: SetupStep = SETUP_STEPS.includes(stepParam as SetupStep)
3543
? (stepParam as SetupStep)
3644
: "relatable"
45+
const hasTrackedInitialStep = useRef(false)
3746

3847
const goToStep = useCallback(
3948
(step: SetupStep) => {
49+
analytics.onboardingStepViewed({ step, trigger: "user" })
4050
router.push(`/new/onboarding/setup?step=${step}`)
4151
},
4252
[router],
@@ -54,6 +64,13 @@ export default function SetupLayout({ children }: { children: ReactNode }) {
5464
router.push("/new")
5565
}, [router, resetOnboarding])
5666

67+
useEffect(() => {
68+
if (!hasTrackedInitialStep.current) {
69+
analytics.onboardingStepViewed({ step: currentStep, trigger: "user" })
70+
hasTrackedInitialStep.current = true
71+
}
72+
}, [currentStep])
73+
5774
const contextValue: SetupContextValue = {
5875
memoryFormData,
5976
currentStep,

‎apps/web/app/new/onboarding/welcome/layout.tsx‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from "react"
1212
import { useRouter, useSearchParams } from "next/navigation"
1313
import { useOnboardingContext, type MemoryFormData } from "../layout"
14+
import { analytics } from "@/lib/analytics"
1415

1516
export const WELCOME_STEPS = [
1617
"input",
@@ -61,6 +62,7 @@ export default function WelcomeLayout({ children }: { children: ReactNode }) {
6162
const [isSubmitting, setIsSubmitting] = useState(false)
6263
const [showWelcomeContent, setShowWelcomeContent] = useState(false)
6364
const isMountedRef = useRef(true)
65+
const hasTrackedInitialStep = useRef(false)
6466

6567
useEffect(() => {
6668
isMountedRef.current = true
@@ -89,6 +91,7 @@ export default function WelcomeLayout({ children }: { children: ReactNode }) {
8991
timers.push(
9092
setTimeout(() => {
9193
if (isMountedRef.current) {
94+
analytics.onboardingStepViewed({ step: "welcome", trigger: "auto" })
9295
router.replace("/new/onboarding/welcome?step=welcome")
9396
}
9497
}, 2000),
@@ -97,6 +100,10 @@ export default function WelcomeLayout({ children }: { children: ReactNode }) {
97100
timers.push(
98101
setTimeout(() => {
99102
if (isMountedRef.current) {
103+
analytics.onboardingStepViewed({
104+
step: "username",
105+
trigger: "auto",
106+
})
100107
router.replace("/new/onboarding/welcome?step=username")
101108
}
102109
}, 2000),
@@ -108,8 +115,19 @@ export default function WelcomeLayout({ children }: { children: ReactNode }) {
108115
}
109116
}, [currentStep, router])
110117

118+
useEffect(() => {
119+
if (!hasTrackedInitialStep.current) {
120+
analytics.onboardingStepViewed({
121+
step: currentStep,
122+
trigger: "user",
123+
})
124+
hasTrackedInitialStep.current = true
125+
}
126+
}, [currentStep])
127+
111128
const goToStep = useCallback(
112129
(step: WelcomeStep) => {
130+
analytics.onboardingStepViewed({ step, trigger: "user" })
113131
router.push(`/new/onboarding/welcome?step=${step}`)
114132
},
115133
[router],

‎apps/web/app/new/onboarding/welcome/page.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from "./layout"
2020
import { gapVariants, orbVariants } from "@/lib/variants"
2121
import { authClient } from "@lib/auth"
22+
import { analytics } from "@/lib/analytics"
2223

2324
function UserSupermemory({ name }: { name: string }) {
2425
return (
@@ -88,6 +89,7 @@ export default function WelcomePage() {
8889
console.error("Failed to update displayUsername:", error)
8990
}
9091

92+
analytics.onboardingNameSubmitted({ name_length: name.trim().length })
9193
goToStep("greeting")
9294
setIsSubmitting(false)
9395
}

0 commit comments

Comments
 (0)