Skip to content

Commit e4907ad

Browse files
committed
posthog migration
1 parent 94e2cbc commit e4907ad

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+374
-587
lines changed

apps/dashboard/.env.example

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ NEXT_PUBLIC_DASHBOARD_UPLOAD_SERVER="https://storage.thirdweb-dev.com"
3535
# - not required to build (unless testing contract search)
3636
NEXT_PUBLIC_TYPESENSE_CONTRACT_API_KEY=
3737

38-
# posthog API key
39-
# - not required for prod/staging
40-
NEXT_PUBLIC_POSTHOG_API_KEY="ignored"
41-
4238
# Stripe Customer portal
4339
NEXT_PUBLIC_STRIPE_KEY=
4440

@@ -108,4 +104,8 @@ STRIPE_SECRET_KEY=""
108104

109105
# required for server wallet management
110106
NEXT_PUBLIC_THIRDWEB_VAULT_URL=""
111-
NEXT_PUBLIC_ENGINE_CLOUD_URL=""
107+
NEXT_PUBLIC_ENGINE_CLOUD_URL=""
108+
109+
# posthog setup
110+
NEXT_PUBLIC_POSTHOG_KEY=""
111+
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com

apps/dashboard/knip.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"@thirdweb-dev/service-utils",
1414
"@thirdweb-dev/vault-sdk",
1515
"@types/color",
16-
"fast-xml-parser"
16+
"fast-xml-parser",
17+
"posthog-node"
1718
]
1819
}

apps/dashboard/next.config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,18 @@ const baseNextConfig: NextConfig = {
146146
},
147147
async rewrites() {
148148
return [
149+
{
150+
source: "/_ph/static/:path*",
151+
destination: "https://us-assets.i.posthog.com/static/:path*",
152+
},
153+
{
154+
source: "/_ph/:path*",
155+
destination: "https://us.i.posthog.com/:path*",
156+
},
157+
{
158+
source: "/_ph/decide",
159+
destination: "https://us.i.posthog.com/decide",
160+
},
149161
{
150162
source: "/thirdweb.eth",
151163
destination: "/deployer.thirdweb.eth",
@@ -173,6 +185,8 @@ const baseNextConfig: NextConfig = {
173185
]),
174186
];
175187
},
188+
// This is required to support PostHog trailing slash API requests
189+
skipTrailingSlashRedirect: true,
176190
images: {
177191
dangerouslyAllowSVG: true,
178192
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",

apps/dashboard/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
"date-fns": "4.1.0",
6464
"fast-xml-parser": "^5.2.5",
6565
"fetch-event-stream": "0.1.5",
66-
"flat": "^6.0.1",
6766
"framer-motion": "12.17.0",
6867
"fuse.js": "7.1.0",
6968
"input-otp": "^1.4.1",
@@ -78,7 +77,8 @@
7877
"p-limit": "^6.2.0",
7978
"papaparse": "^5.5.3",
8079
"pluralize": "^8.0.0",
81-
"posthog-js": "1.67.1",
80+
"posthog-js": "1.252.0",
81+
"posthog-node": "5.1.0",
8282
"prettier": "3.5.3",
8383
"qrcode": "^1.5.3",
8484
"react": "19.1.0",
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/* eslint-disable no-restricted-syntax */
2+
"use client";
3+
4+
import { PostHog } from "posthog-js";
5+
import { useEffect } from "react";
6+
import { DEFAULT_POSTHOG_SETTINGS } from "./shared";
7+
8+
const posthog = new PostHog();
9+
let initialized = false;
10+
11+
function init(accountId?: string) {
12+
const posthogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY;
13+
if (!posthogKey) {
14+
console.warn(
15+
"[DASHBOARD_ANALYTICS] NEXT_PUBLIC_POSTHOG_KEY is not set, cannt initialize analytics",
16+
);
17+
return;
18+
}
19+
20+
if (!initialized) {
21+
posthog.init(
22+
posthogKey,
23+
{
24+
...DEFAULT_POSTHOG_SETTINGS,
25+
loaded: (instance) => {
26+
// identify the user if we have an account id
27+
if (accountId) {
28+
instance.identify(accountId);
29+
}
30+
},
31+
},
32+
"dashboard",
33+
);
34+
initialized = true;
35+
}
36+
}
37+
38+
function identifyUser(userId: string) {
39+
if (!initialized) {
40+
console.warn(
41+
"[DASHBOARD_ANALYTICS] is not initialized, cannot identify user",
42+
);
43+
return;
44+
}
45+
posthog.identify(userId);
46+
}
47+
48+
function identifyTeam(teamId: string) {
49+
if (!initialized) {
50+
console.warn(
51+
"[DASHBOARD_ANALYTICS] is not initialized, cannot identify team",
52+
);
53+
return;
54+
}
55+
posthog.group("team", teamId);
56+
}
57+
58+
export function reset() {
59+
if (!initialized) {
60+
console.warn("[DASHBOARD_ANALYTICS] is not initialized, cannot reset");
61+
return;
62+
}
63+
posthog.reset();
64+
}
65+
66+
export function DashboardAnalyticsInitializer(props: { accountId?: string }) {
67+
useEffect(() => {
68+
init();
69+
}, []);
70+
71+
useEffect(() => {
72+
if (props.accountId) {
73+
identifyUser(props.accountId);
74+
}
75+
}, [props.accountId]);
76+
return null;
77+
}
78+
79+
export function TeamIdentifier(props: { teamId: string }) {
80+
useEffect(() => {
81+
identifyTeam(props.teamId);
82+
}, [props.teamId]);
83+
return null;
84+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const DEFAULT_POSTHOG_SETTINGS = {
2+
api_host: "/_ph",
3+
ui_host: "https://us.posthog.com",
4+
capture_pageview: "history_change",
5+
capture_pageleave: "if_capture_pageview",
6+
// disable exception capture (for now)
7+
capture_exceptions: false,
8+
// specifically disable autocapture (does not affect pageview capture)
9+
autocapture: false,
10+
debug: process.env.NODE_ENV === "development",
11+
} as const;

apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"use client";
22

3+
import * as analytics from "@/analytics/dashboard.client";
34
import { Button } from "@/components/ui/button";
45
import { useStore } from "@/lib/reactive";
56
import { getSDKTheme } from "app/(app)/components/sdk-component-theme";
67
import { LazyConfigureNetworkModal } from "components/configure-networks/LazyConfigureNetworkModal";
78
import { CustomChainRenderer } from "components/selects/CustomChainRenderer";
89
import { mapV4ChainToV5Chain } from "contexts/map-chains";
9-
import { useTrack } from "hooks/analytics/useTrack";
1010
import { useAllChainsData } from "hooks/chains/allChains";
1111
import { useTheme } from "next-themes";
1212
import Image from "next/image";
@@ -156,6 +156,7 @@ export const CustomConnectWallet = (props: {
156156
onDisconnect={async () => {
157157
try {
158158
await doLogout();
159+
analytics.reset();
159160
} catch (err) {
160161
console.error("Failed to log out", err);
161162
}
@@ -259,18 +260,16 @@ function ConnectWalletWelcomeScreen(props: {
259260
</div>
260261
</div>
261262

262-
<TrackedAnchorLink
263+
<Link
263264
className="text-center font-semibold opacity-70 hover:no-underline hover:opacity-100"
264265
target="_blank"
265-
category="custom-connect-wallet"
266-
label="new-to-wallets"
267266
href="https://blog.thirdweb.com/web3-wallet/"
268267
style={{
269268
color: fontColor,
270269
}}
271270
>
272271
New to Wallets?
273-
</TrackedAnchorLink>
272+
</Link>
274273
</div>
275274
);
276275
}
@@ -303,36 +302,3 @@ export function useCustomConnectModal() {
303302
[connect, theme],
304303
);
305304
}
306-
307-
/**
308-
* A link component extends the `Link` component and adds tracking.
309-
*/
310-
function TrackedAnchorLink(props: {
311-
category: string;
312-
label?: string;
313-
trackingProps?: Record<string, string>;
314-
href: string;
315-
target?: string;
316-
children: React.ReactNode;
317-
className?: string;
318-
style?: React.CSSProperties;
319-
}) {
320-
const trackEvent = useTrack();
321-
const { category, label, trackingProps } = props;
322-
323-
const onClick = useCallback(() => {
324-
trackEvent({ category, action: "click", label, ...trackingProps });
325-
}, [trackEvent, category, label, trackingProps]);
326-
327-
return (
328-
<Link
329-
onClick={onClick}
330-
target={props.target}
331-
href={props.href}
332-
className={props.className}
333-
style={props.style}
334-
>
335-
{props.children}
336-
</Link>
337-
);
338-
}

apps/dashboard/src/app/(app)/account/components/AccountHeader.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import { createTeam } from "@/actions/createTeam";
4+
import * as analytics from "@/analytics/dashboard.client";
45
import type { Project } from "@/api/projects";
56
import type { Team } from "@/api/team";
67
import { useDashboardRouter } from "@/lib/DashboardRouter";
@@ -35,6 +36,7 @@ export function AccountHeader(props: {
3536
const logout = useCallback(async () => {
3637
try {
3738
await doLogout();
39+
analytics.reset();
3840
if (wallet) {
3941
disconnect(wallet);
4042
}

apps/dashboard/src/app/(app)/account/settings/AccountSettingsPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { confirmEmailWithOTP } from "@/actions/confirmEmail";
33
import { apiServerProxy } from "@/actions/proxies";
44
import { updateAccount } from "@/actions/updateAccount";
5+
import * as analytics from "@/analytics/dashboard.client";
56
import { useDashboardRouter } from "@/lib/DashboardRouter";
67
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
78
import type { ThirdwebClient } from "thirdweb";
@@ -46,6 +47,7 @@ export function AccountSettingsPage(props: {
4647
}}
4748
onAccountDeleted={async () => {
4849
await doLogout();
50+
analytics.reset();
4951
if (activeWallet) {
5052
disconnect(activeWallet);
5153
}

apps/dashboard/src/app/(app)/layout.tsx

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import "../../global.css";
22
import { DashboardRouterTopProgressBar } from "@/lib/DashboardRouter";
33
import { cn } from "@/lib/utils";
4-
import { PHProvider } from "lib/posthog/Posthog";
5-
import { PosthogHeadSetup } from "lib/posthog/PosthogHeadSetup";
6-
import { PostHogPageView } from "lib/posthog/PosthogPageView";
4+
5+
import { DashboardAnalyticsInitializer } from "@/analytics/dashboard.client";
76
import type { Metadata } from "next";
87
import PlausibleProvider from "next-plausible";
98
import { Inter } from "next/font/google";
109
import NextTopLoader from "nextjs-toploader";
10+
import { getRawAccount } from "./account/settings/getAccount";
1111
import { AppRouterProviders } from "./providers";
1212

1313
const fontSans = Inter({
@@ -48,11 +48,12 @@ export const metadata: Metadata = {
4848
},
4949
};
5050

51-
export default function RootLayout({
51+
export default async function RootLayout({
5252
children,
5353
}: {
5454
children: React.ReactNode;
5555
}) {
56+
const account = await getRawAccount();
5657
return (
5758
<html lang="en" suppressHydrationWarning>
5859
<head>
@@ -61,26 +62,20 @@ export default function RootLayout({
6162
customDomain="https://pl.thirdweb.com"
6263
selfHosted
6364
/>
64-
<PosthogHeadSetup />
6565
</head>
66-
<PHProvider disable_session_recording={true}>
67-
<PostHogPageView />
68-
<body
69-
className={cn(
70-
"bg-background font-sans antialiased",
71-
fontSans.variable,
72-
)}
73-
>
74-
<AppRouterProviders>{children}</AppRouterProviders>
75-
<DashboardRouterTopProgressBar />
76-
<NextTopLoader
77-
color="hsl(var(--foreground))"
78-
height={3}
79-
shadow={false}
80-
showSpinner={false}
81-
/>
82-
</body>
83-
</PHProvider>
66+
<body
67+
className={cn("bg-background font-sans antialiased", fontSans.variable)}
68+
>
69+
<AppRouterProviders>{children}</AppRouterProviders>
70+
<DashboardRouterTopProgressBar />
71+
<DashboardAnalyticsInitializer accountId={account?.id} />
72+
<NextTopLoader
73+
color="hsl(var(--foreground))"
74+
height={3}
75+
shadow={false}
76+
showSpinner={false}
77+
/>
78+
</body>
8479
</html>
8580
);
8681
}

0 commit comments

Comments
 (0)