docs: improved analytics and tracking (#13671)
* docs: improved analytics and tracking * remove detailed feedback component * remove ignore build script for api reference * improvements * fix pathname
This commit is contained in:
@@ -8,7 +8,6 @@ import React, {
|
||||
useState,
|
||||
} from "react"
|
||||
import { Analytics, AnalyticsBrowser } from "@segment/analytics-next"
|
||||
import { PostHogProvider as PHProvider } from "posthog-js/react"
|
||||
import posthog from "posthog-js"
|
||||
|
||||
// @ts-expect-error Doesn't have a types package
|
||||
@@ -22,16 +21,22 @@ export type ExtraData = {
|
||||
export type AnalyticsContextType = {
|
||||
loaded: boolean
|
||||
analytics: Analytics | null
|
||||
track: (
|
||||
event: string,
|
||||
options?: Record<string, any>,
|
||||
callback?: () => void
|
||||
) => void
|
||||
track: ({
|
||||
event,
|
||||
instant,
|
||||
}: {
|
||||
event: TrackedEvent
|
||||
instant?: boolean
|
||||
}) => void
|
||||
}
|
||||
|
||||
type Trackers = "segment" | "posthog"
|
||||
|
||||
export type TrackedEvent = {
|
||||
event: string
|
||||
options?: Record<string, any>
|
||||
callback?: () => void
|
||||
tracker?: Trackers | Trackers[]
|
||||
}
|
||||
|
||||
const AnalyticsContext = createContext<AnalyticsContextType | null>(null)
|
||||
@@ -39,8 +44,6 @@ const AnalyticsContext = createContext<AnalyticsContextType | null>(null)
|
||||
export type AnalyticsProviderProps = {
|
||||
segmentWriteKey?: string
|
||||
reoDevKey?: string
|
||||
postHogKey?: string
|
||||
postHogApiHost?: string
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
@@ -50,8 +53,6 @@ export const AnalyticsProvider = ({
|
||||
segmentWriteKey = "temp",
|
||||
reoDevKey,
|
||||
children,
|
||||
postHogKey,
|
||||
postHogApiHost = "https://eu.i.posthog.com",
|
||||
}: AnalyticsProviderProps) => {
|
||||
// loaded is used to ensure that a connection has been made to segment
|
||||
// even if it failed. This is to ensure that the connection isn't
|
||||
@@ -85,21 +86,13 @@ export const AnalyticsProvider = ({
|
||||
}
|
||||
}, [loaded, segmentWriteKey])
|
||||
|
||||
const track = useCallback(
|
||||
async (
|
||||
event: string,
|
||||
options?: Record<string, any>,
|
||||
callback?: () => void
|
||||
) => {
|
||||
const trackWithSegment = useCallback(
|
||||
async ({ event, options }: TrackedEvent) => {
|
||||
if (analytics) {
|
||||
void analytics.track(
|
||||
event,
|
||||
{
|
||||
...options,
|
||||
uuid: analytics.user().anonymousId(),
|
||||
},
|
||||
callback
|
||||
)
|
||||
void analytics.track(event, {
|
||||
...options,
|
||||
uuid: analytics.user().anonymousId(),
|
||||
})
|
||||
} else {
|
||||
// push the event into the queue
|
||||
setQueue((prevQueue) => [
|
||||
@@ -107,45 +100,70 @@ export const AnalyticsProvider = ({
|
||||
{
|
||||
event,
|
||||
options,
|
||||
tracker: "segment",
|
||||
},
|
||||
])
|
||||
if (callback) {
|
||||
console.warn(
|
||||
"Segment is either not installed or not configured. Simulating success..."
|
||||
)
|
||||
callback()
|
||||
}
|
||||
console.warn(
|
||||
"Segment is either not installed or not configured. Simulating success..."
|
||||
)
|
||||
}
|
||||
},
|
||||
[analytics, loaded]
|
||||
)
|
||||
|
||||
const initPostHog = useCallback(() => {
|
||||
if (!postHogKey) {
|
||||
return
|
||||
}
|
||||
const trackWithPostHog = async ({ event, options }: TrackedEvent) => {
|
||||
posthog.capture(event, options)
|
||||
}
|
||||
|
||||
posthog.init(postHogKey, {
|
||||
api_host: postHogApiHost,
|
||||
person_profiles: "always",
|
||||
defaults: "2025-05-24",
|
||||
})
|
||||
}, [])
|
||||
const processEvent = useCallback(
|
||||
async (event: TrackedEvent) => {
|
||||
const trackers = Array.isArray(event.tracker)
|
||||
? event.tracker
|
||||
: [event.tracker]
|
||||
await Promise.all(
|
||||
trackers.map(async (tracker) => {
|
||||
switch (tracker) {
|
||||
case "posthog":
|
||||
return trackWithPostHog(event)
|
||||
case "segment":
|
||||
default:
|
||||
return trackWithSegment(event)
|
||||
}
|
||||
})
|
||||
)
|
||||
},
|
||||
[trackWithSegment, trackWithPostHog]
|
||||
)
|
||||
|
||||
const track = ({ event }: { event: TrackedEvent }) => {
|
||||
// Always queue events - this makes tracking non-blocking
|
||||
setQueue((prevQueue) => [...prevQueue, event])
|
||||
|
||||
// Process event callback immediately
|
||||
// This ensures that the callback is called even if the event is queued
|
||||
event.callback?.()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
initSegment()
|
||||
initPostHog()
|
||||
}, [initSegment])
|
||||
|
||||
useEffect(() => {
|
||||
if (analytics && queue.length) {
|
||||
// track stuff in queue
|
||||
queue.forEach(async (trackEvent) =>
|
||||
track(trackEvent.event, trackEvent.options)
|
||||
)
|
||||
// Process queue in background without blocking
|
||||
const currentQueue = [...queue]
|
||||
setQueue([])
|
||||
|
||||
// Process events asynchronously in batches to avoid overwhelming the system
|
||||
const batchSize = 5
|
||||
for (let i = 0; i < currentQueue.length; i += batchSize) {
|
||||
const batch = currentQueue.slice(i, i + batchSize)
|
||||
setTimeout(() => {
|
||||
batch.forEach(processEvent)
|
||||
}, i * 10) // Small delay between batches
|
||||
}
|
||||
}
|
||||
}, [analytics, queue])
|
||||
}, [analytics, queue, trackWithSegment, trackWithPostHog, processEvent])
|
||||
|
||||
useEffect(() => {
|
||||
if (!reoDevKey) {
|
||||
@@ -155,12 +173,12 @@ export const AnalyticsProvider = ({
|
||||
loadReoScript({
|
||||
clientID: reoDevKey,
|
||||
})
|
||||
.then((Reo: any) => {
|
||||
Reo.init({
|
||||
.then((Reo: unknown) => {
|
||||
;(Reo as { init: (config: { clientID: string }) => void }).init({
|
||||
clientID: reoDevKey,
|
||||
})
|
||||
})
|
||||
.catch((e: any) => {
|
||||
.catch((e: Error) => {
|
||||
console.error(`Could not connect to Reodotdev. Error: ${e}`)
|
||||
})
|
||||
}, [reoDevKey])
|
||||
@@ -173,11 +191,7 @@ export const AnalyticsProvider = ({
|
||||
loaded,
|
||||
}}
|
||||
>
|
||||
{postHogKey ? (
|
||||
<PHProvider client={posthog}>{children}</PHProvider>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
{children}
|
||||
</AnalyticsContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -71,9 +71,14 @@ export const LearningPathProvider: React.FC<LearningPathProviderProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
track(`learning_path_${path.name}`, {
|
||||
url: pathname,
|
||||
state: `start`,
|
||||
track({
|
||||
event: {
|
||||
event: `learning_path_${path.name}`,
|
||||
options: {
|
||||
url: pathname,
|
||||
state: `start`,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -86,14 +91,19 @@ export const LearningPathProvider: React.FC<LearningPathProviderProps> = ({
|
||||
const endPath = () => {
|
||||
const didFinish = currentStep === (path?.steps.length || 0) - 1
|
||||
const reachedIndex = currentStep === -1 ? 0 : currentStep
|
||||
track(`learning_path_${path?.name}`, {
|
||||
url: pathname,
|
||||
state: !didFinish ? `closed` : `end`,
|
||||
reachedStep:
|
||||
path?.steps[reachedIndex]?.title ||
|
||||
path?.steps[reachedIndex]?.description ||
|
||||
path?.steps[reachedIndex]?.descriptionJSX ||
|
||||
reachedIndex,
|
||||
track({
|
||||
event: {
|
||||
event: `learning_path_${path?.name}`,
|
||||
options: {
|
||||
url: pathname,
|
||||
state: !didFinish ? `closed` : `end`,
|
||||
reachedStep:
|
||||
path?.steps[reachedIndex]?.title ||
|
||||
path?.steps[reachedIndex]?.description ||
|
||||
path?.steps[reachedIndex]?.descriptionJSX ||
|
||||
reachedIndex,
|
||||
},
|
||||
},
|
||||
})
|
||||
setPath(null)
|
||||
setCurrentStep(-1)
|
||||
|
||||
Reference in New Issue
Block a user