Skip to content

Commit b6fa012

Browse files
committed
refactor: remove unstable posthog reference, make survey fetching deterministic after ph init
1 parent 4aa4f1f commit b6fa012

File tree

3 files changed

+96
-75
lines changed

3 files changed

+96
-75
lines changed

src/features/client-providers.tsx

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import { Toaster } from '@/ui/primitives/sonner'
44
import { ToastProvider } from '@/ui/primitives/toast'
55
import { TooltipProvider } from '@/ui/primitives/tooltip'
66
import { ThemeProvider } from 'next-themes'
7-
import posthog from 'posthog-js'
8-
import { PostHogProvider as PHProvider } from 'posthog-js/react'
9-
import { useEffect } from 'react'
7+
import { PostHogProvider } from './posthog-provider'
108

119
interface ClientProvidersProps {
1210
children: React.ReactNode
@@ -29,30 +27,3 @@ export default function ClientProviders({ children }: ClientProvidersProps) {
2927
</PostHogProvider>
3028
)
3129
}
32-
33-
export function PostHogProvider({ children }: { children: React.ReactNode }) {
34-
useEffect(() => {
35-
if (!process.env.NEXT_PUBLIC_POSTHOG_KEY) {
36-
return
37-
}
38-
39-
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
40-
// Note that PostHog will automatically capture page views and common events
41-
//
42-
// This setup utilizes Next.js rewrites to act as a reverse proxy, to improve
43-
// reliability of client-side tracking & make requests less likely to be intercepted by tracking blockers.
44-
// https://posthog.com/docs/libraries/next-js#configuring-a-reverse-proxy-to-posthog
45-
api_host: '/ph-proxy',
46-
ui_host: 'https://us.posthog.com',
47-
advanced_enable_surveys: true,
48-
disable_session_recording: process.env.NODE_ENV !== 'production',
49-
advanced_disable_toolbar_metrics: true,
50-
opt_in_site_apps: true,
51-
loaded: (posthog) => {
52-
if (process.env.NODE_ENV === 'development') posthog.debug()
53-
},
54-
})
55-
}, [])
56-
57-
return <PHProvider client={posthog}>{children}</PHProvider>
58-
}

src/features/dashboard/navbar/dashboard-survey-popover.tsx

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,28 @@
11
'use client'
22

3+
import { useAppPostHogProvider } from '@/features/posthog-provider'
34
import { l } from '@/lib/clients/logger/logger'
4-
import { useToast } from '@/lib/hooks/use-toast'
55
import { Popover, PopoverContent } from '@/ui/primitives/popover'
66
import { SurveyContent } from '@/ui/survey'
77
import { PopoverTrigger } from '@radix-ui/react-popover'
8-
import { Survey } from 'posthog-js'
98
import { usePostHog } from 'posthog-js/react'
109
import { useCallback, useState } from 'react'
11-
import useSWR from 'swr'
10+
import { toast } from 'sonner'
1211

1312
interface DashboardSurveyPopoverProps {
1413
trigger: React.ReactNode
1514
}
1615

1716
function DashboardSurveyPopover({ trigger }: DashboardSurveyPopoverProps) {
1817
const posthog = usePostHog()
19-
const { toast } = useToast()
2018
const [isOpen, setIsOpen] = useState(false)
2119
const [wasSubmitted, setWasSubmitted] = useState(false)
2220

23-
const { data: survey, isLoading } = useSWR<Survey | undefined>(
24-
['dashboard-feedback-survey', posthog.__loaded],
25-
() => {
26-
return new Promise<Survey | undefined>((resolve) => {
27-
posthog.getSurveys((surveys) => {
28-
for (const survey of surveys) {
29-
if (
30-
survey.id ===
31-
process.env.NEXT_PUBLIC_POSTHOG_DASHBOARD_FEEDBACK_SURVEY_ID
32-
) {
33-
resolve(survey)
34-
return
35-
}
36-
}
37-
resolve(undefined)
38-
})
39-
})
40-
},
41-
{
42-
revalidateOnFocus: false,
43-
revalidateOnReconnect: false,
44-
revalidateIfStale: false,
45-
dedupingInterval: Infinity,
46-
shouldRetryOnError: false,
47-
}
48-
)
21+
const { dashboardFeedbackSurvey, isInitialized } = useAppPostHogProvider()
4922

5023
const handleSubmit = useCallback(
5124
(responses: Record<number, string>) => {
52-
if (!survey) return
25+
if (!dashboardFeedbackSurvey) return
5326

5427
const responseData = Object.entries(responses).reduce(
5528
(acc, [index, response]) => ({
@@ -60,31 +33,34 @@ function DashboardSurveyPopover({ trigger }: DashboardSurveyPopoverProps) {
6033
)
6134

6235
posthog.capture('survey sent', {
63-
$survey_id: survey.id,
36+
$survey_id: dashboardFeedbackSurvey.id,
6437
...responseData,
6538
})
6639

6740
setWasSubmitted(true)
6841

69-
toast({
70-
title: 'Thank you!',
71-
description: 'Your feedback has been recorded.',
72-
})
42+
toast.success('Thank you! Your feedback has been recorded.')
7343

7444
// reset states
7545
setIsOpen(false)
7646
setTimeout(() => {
7747
setWasSubmitted(false)
7848
}, 100)
7949
},
80-
[survey, posthog, toast]
50+
[dashboardFeedbackSurvey, posthog]
8151
)
8252

53+
// we will optimistically render the button on first render.
54+
// if we can't resolve the survey on the client side, we hide the button.
55+
if (!dashboardFeedbackSurvey && isInitialized) {
56+
return null
57+
}
58+
8359
return (
8460
<Popover
8561
open={isOpen}
8662
onOpenChange={(open) => {
87-
if (!survey) {
63+
if (!dashboardFeedbackSurvey) {
8864
l.error(
8965
{
9066
key: 'dashboard_survey_popover:survey_not_found',
@@ -98,14 +74,14 @@ function DashboardSurveyPopover({ trigger }: DashboardSurveyPopoverProps) {
9874
return
9975
}
10076

101-
if (!open && !wasSubmitted && survey) {
77+
if (!open && !wasSubmitted && dashboardFeedbackSurvey) {
10278
posthog.capture('survey dismissed', {
103-
$survey_id: survey.id,
79+
$survey_id: dashboardFeedbackSurvey.id,
10480
})
10581
}
106-
if (open && survey) {
82+
if (open && dashboardFeedbackSurvey) {
10783
posthog.capture('survey shown', {
108-
$survey_id: survey.id,
84+
$survey_id: dashboardFeedbackSurvey.id,
10985
})
11086
}
11187
setIsOpen(open)
@@ -118,10 +94,10 @@ function DashboardSurveyPopover({ trigger }: DashboardSurveyPopoverProps) {
11894
collisionPadding={20}
11995
sideOffset={25}
12096
>
121-
{survey && (
97+
{dashboardFeedbackSurvey && (
12298
<SurveyContent
123-
survey={survey}
124-
isLoading={isLoading}
99+
survey={dashboardFeedbackSurvey}
100+
isLoading={!isInitialized}
125101
onSubmit={handleSubmit}
126102
/>
127103
)}

src/features/posthog-provider.tsx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import posthog, { Survey } from 'posthog-js'
2+
import { PostHogProvider as PHProvider } from 'posthog-js/react'
3+
import { useEffect, useState } from 'react'
4+
5+
import { createContext, useContext } from 'react'
6+
7+
interface AppPostHogContextValue {
8+
isInitialized: boolean
9+
dashboardFeedbackSurvey: Survey | null
10+
}
11+
12+
const AppPostHogContext = createContext<AppPostHogContextValue | undefined>(
13+
undefined
14+
)
15+
16+
export function useAppPostHogProvider() {
17+
const ctx = useContext(AppPostHogContext)
18+
19+
if (!ctx) {
20+
throw new Error(
21+
'useAppPostHogProvider must be used within a PostHogProvider'
22+
)
23+
}
24+
25+
return ctx
26+
}
27+
28+
export function PostHogProvider({ children }: { children: React.ReactNode }) {
29+
const [dashboardFeedbackSurvey, setDashboardFeedbackSurvey] =
30+
useState<Survey | null>(null)
31+
const [isInitialized, setIsInitialized] = useState(false)
32+
33+
useEffect(() => {
34+
if (!process.env.NEXT_PUBLIC_POSTHOG_KEY) {
35+
return
36+
}
37+
38+
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
39+
// Note that PostHog will automatically capture page views and common events
40+
//
41+
// This setup utilizes Next.js rewrites to act as a reverse proxy, to improve
42+
// reliability of client-side tracking & make requests less likely to be intercepted by tracking blockers.
43+
// https://posthog.com/docs/libraries/next-js#configuring-a-reverse-proxy-to-posthog
44+
api_host: '/ph-proxy',
45+
ui_host: 'https://us.posthog.com',
46+
advanced_enable_surveys: true,
47+
disable_session_recording: process.env.NODE_ENV !== 'production',
48+
advanced_disable_toolbar_metrics: true,
49+
opt_in_site_apps: true,
50+
loaded: (posthog) => {
51+
if (process.env.NODE_ENV === 'development') posthog.debug()
52+
},
53+
})
54+
55+
posthog.getSurveys((surveys) => {
56+
for (const survey of surveys) {
57+
switch (survey.id) {
58+
case process.env.NEXT_PUBLIC_POSTHOG_DASHBOARD_FEEDBACK_SURVEY_ID:
59+
setDashboardFeedbackSurvey(survey)
60+
break
61+
}
62+
}
63+
setIsInitialized(true)
64+
})
65+
}, [])
66+
67+
return (
68+
<AppPostHogContext.Provider
69+
value={{ dashboardFeedbackSurvey, isInitialized }}
70+
>
71+
<PHProvider client={posthog}>{children}</PHProvider>
72+
</AppPostHogContext.Provider>
73+
)
74+
}

0 commit comments

Comments
 (0)