fix: PostHog event tracking (#17653)
* posthog provider fix * posthog fixes w/ trpc changes * Update yarn.lock --------- Co-authored-by: Keith Williams <keithwillcode@gmail.com>
This commit is contained in:
@@ -2,17 +2,25 @@
|
||||
|
||||
import posthog from "posthog-js";
|
||||
import { PostHogProvider } from "posthog-js/react";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
function Provider({ children }: { children: React.ReactNode }) {
|
||||
const initializeOnce = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!process.env.NEXT_PUBLIC_POSTHOG_KEY) return;
|
||||
if (!process.env.NEXT_PUBLIC_POSTHOG_KEY || initializeOnce.current) return;
|
||||
|
||||
initializeOnce.current = true;
|
||||
|
||||
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
|
||||
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || "https://us.i.posthog.com",
|
||||
autocapture: false,
|
||||
persistence: "memory",
|
||||
request_batching: false,
|
||||
// Enable debug mode in development
|
||||
request_batching: true,
|
||||
capture_pageview: false,
|
||||
capture_pageleave: false,
|
||||
disable_session_recording: true,
|
||||
advanced_disable_decide: true,
|
||||
loaded: (posthog) => {
|
||||
if (process.env.NODE_ENV === "development") posthog.debug();
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { noop } from "lodash";
|
||||
import type { PostHog } from "posthog-js";
|
||||
import { usePostHog as usePostHogLib } from "posthog-js/react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { z } from "zod";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
@@ -8,26 +9,31 @@ import { WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import { useHasTeamPlan, useHasPaidPlan } from "@calcom/lib/hooks/useHasPaidPlan";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
export const isPostHogEnabled = z.string().min(1).safeParse(process.env.NEXT_PUBLIC_POSTHOG_KEY).success;
|
||||
|
||||
type PostHogHook = {
|
||||
// Define the return type for our hook
|
||||
type PostHogMethods = {
|
||||
capture: (event: string, properties?: Record<string, any>) => void;
|
||||
identify: (distinctId: string, properties?: Record<string, any>) => void;
|
||||
identify: (distinctId?: string, properties?: Record<string, any>) => void;
|
||||
reset: () => void;
|
||||
};
|
||||
|
||||
const usePostHogHook: () => PostHogHook = isPostHogEnabled
|
||||
// Internal hook type that includes the methods we need
|
||||
type InternalPostHogHook = Pick<PostHog, "identify" | "capture" | "reset" | "register" | "get_distinct_id">;
|
||||
|
||||
const usePostHogHook: () => InternalPostHogHook = isPostHogEnabled
|
||||
? usePostHogLib
|
||||
: () => ({
|
||||
identify: noop,
|
||||
capture: noop,
|
||||
reset: noop,
|
||||
identify: () => undefined,
|
||||
capture: () => undefined,
|
||||
reset: () => undefined,
|
||||
register: () => undefined,
|
||||
get_distinct_id: () => "",
|
||||
});
|
||||
|
||||
const usePostHog = () => {
|
||||
const usePostHog = (): PostHogMethods => {
|
||||
const posthog = usePostHogHook();
|
||||
const { data } = trpc.viewer.me.useQuery();
|
||||
const { data: userData } = trpc.viewer.me.useQuery();
|
||||
const { data: statsData } = trpc.viewer.myStats.useQuery(undefined, {
|
||||
trpc: {
|
||||
context: {
|
||||
@@ -37,37 +43,67 @@ const usePostHog = () => {
|
||||
});
|
||||
const { hasPaidPlan } = useHasPaidPlan();
|
||||
const { hasTeamPlan } = useHasTeamPlan();
|
||||
const lastPropertiesString = useRef<string | null>(null);
|
||||
|
||||
const identify = async () => {
|
||||
if (!data || !statsData) return;
|
||||
useEffect(() => {
|
||||
if (!userData || !statsData) return;
|
||||
|
||||
posthog.identify(String(data.id), {
|
||||
distinctId: String(data.id),
|
||||
...(data && data?.name && { name: data.name }),
|
||||
...(data && data?.email && { email: data.email }),
|
||||
created_at: String(dayjs(data?.createdDate).unix()),
|
||||
//keys should be snake cased
|
||||
user_name: data?.username,
|
||||
link: `${WEBSITE_URL}/${data?.username}`,
|
||||
identity_provider: data?.identityProvider,
|
||||
timezone: data?.timeZone,
|
||||
locale: data?.locale,
|
||||
const properties = {
|
||||
name: userData.name,
|
||||
email: userData.email,
|
||||
created_at: String(dayjs(userData.createdDate).unix()),
|
||||
user_name: userData.username,
|
||||
link: `${WEBSITE_URL}/${userData.username}`,
|
||||
identity_provider: userData.identityProvider,
|
||||
timezone: userData.timeZone,
|
||||
locale: userData.locale,
|
||||
has_paid_plan: hasPaidPlan,
|
||||
has_team_plan: hasTeamPlan,
|
||||
metadata: data?.metadata,
|
||||
completed_onboarding: data?.completedOnboarding,
|
||||
completed_onboarding: userData.completedOnboarding,
|
||||
has_orgs_plan: !!userData.organizationId,
|
||||
organization: userData.organization?.slug,
|
||||
is_premium: userData.isPremium,
|
||||
sum_of_bookings: statsData?.sumOfBookings,
|
||||
sum_of_calendars: statsData?.sumOfCalendars,
|
||||
sum_of_teams: statsData?.sumOfTeams,
|
||||
has_orgs_plan: !!data?.organizationId,
|
||||
organization: data?.organization?.slug,
|
||||
sum_of_event_types: statsData?.sumOfEventTypes,
|
||||
sum_of_team_event_types: statsData?.sumOfTeamEventTypes,
|
||||
is_premium: data?.isPremium,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return { identify, capture: posthog.capture, reset: posthog.reset };
|
||||
// Clean properties
|
||||
const cleanProperties = Object.fromEntries(Object.entries(properties).filter(([_, v]) => v != null));
|
||||
|
||||
// Create a string representation of the properties for comparison
|
||||
const propertiesString = JSON.stringify(cleanProperties);
|
||||
|
||||
// Only set properties if they've changed
|
||||
if (propertiesString !== lastPropertiesString.current) {
|
||||
lastPropertiesString.current = propertiesString;
|
||||
// Use register instead of identify to avoid duplicate calls
|
||||
posthog.register(cleanProperties);
|
||||
// Only identify once
|
||||
if (!posthog.get_distinct_id()) {
|
||||
posthog.identify(String(userData.id));
|
||||
}
|
||||
}
|
||||
}, [userData, statsData, hasPaidPlan, hasTeamPlan, posthog]);
|
||||
|
||||
return {
|
||||
capture: (event: string, properties?: Record<string, any>) => {
|
||||
if (posthog.get_distinct_id()) {
|
||||
posthog.capture(event, properties);
|
||||
}
|
||||
},
|
||||
identify: (distinctId?: string, properties?: Record<string, any>) => {
|
||||
if (distinctId) {
|
||||
posthog.identify(distinctId, properties);
|
||||
}
|
||||
},
|
||||
reset: () => {
|
||||
lastPropertiesString.current = null;
|
||||
posthog.reset();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default usePostHog;
|
||||
|
||||
Reference in New Issue
Block a user