-
Notifications
You must be signed in to change notification settings - Fork 561
posthog migration #7340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
posthog migration #7340
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -110,6 +110,11 @@ module.exports = { | |
message: | ||
'This is likely a mistake. If you really want to import this - postfix the imported name with Icon. Example - "LinkIcon"', | ||
}, | ||
{ | ||
name: "posthog-js", | ||
message: | ||
'Import "posthog-js" directly only within the analytics helpers ("src/@/analytics/*"). Use the exported helpers from "@/analytics/track" elsewhere.', | ||
}, | ||
], | ||
}, | ||
], | ||
|
@@ -139,6 +144,13 @@ module.exports = { | |
"no-restricted-imports": ["off"], | ||
}, | ||
}, | ||
// allow direct PostHog imports inside analytics helpers | ||
{ | ||
files: "src/@/analytics/**/*", | ||
rules: { | ||
"no-restricted-imports": ["off"], | ||
}, | ||
}, | ||
Comment on lines
+147
to
+153
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Don’t disable all import restrictions in analytics helpers Turning Consider a narrower override that keeps the rest of the rule active: {
files: "src/@/analytics/**/*",
rules: {
- "no-restricted-imports": ["off"],
+ "no-restricted-imports": [
+ "error",
+ {
+ paths: [
+ // Copy the existing paths **except** the posthog-js entry,
+ // so the other restrictions remain enforced.
+ ],
+ },
+ ],
},
}, This preserves the architectural discipline for all other libraries while still allowing direct
🤖 Prompt for AI Agents
|
||
// enable rule specifically for TypeScript files | ||
{ | ||
files: ["*.ts", "*.tsx"], | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import posthog from "posthog-js"; | ||
|
||
const NEXT_PUBLIC_POSTHOG_KEY = process.env.NEXT_PUBLIC_POSTHOG_KEY; | ||
|
||
if (NEXT_PUBLIC_POSTHOG_KEY) { | ||
posthog.init(NEXT_PUBLIC_POSTHOG_KEY, { | ||
api_host: "/_ph", | ||
ui_host: "https://us.posthog.com", | ||
capture_pageview: "history_change", | ||
jnsdls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
capture_pageleave: "if_capture_pageview", | ||
// disable exception capture (for now) | ||
capture_exceptions: false, | ||
// specifically disable autocapture (does not affect pageview capture) | ||
autocapture: false, | ||
debug: process.env.NODE_ENV === "development", | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
"use client"; | ||
|
||
import posthog from "posthog-js"; | ||
import { useEffect } from "react"; | ||
import type { Account } from "../../@3rdweb-sdk/react/hooks/useApi"; | ||
|
||
const warnedMessages = new Set<string>(); | ||
function warnOnce(message: string) { | ||
if (warnedMessages.has(message)) { | ||
return; | ||
} | ||
warnedMessages.add(message); | ||
console.warn(message); | ||
} | ||
|
||
export function AccountIdentifier(props: { | ||
account: Pick<Account, "id" | "email">; | ||
}) { | ||
// eslint-disable-next-line no-restricted-syntax | ||
useEffect(() => { | ||
if (!posthog.__loaded) { | ||
warnOnce( | ||
"[DASHBOARD_ANALYTICS] is not initialized, cannot identify user", | ||
); | ||
return; | ||
} | ||
posthog.identify(props.account.id, { | ||
...(props.account.email ? { email: props.account.email } : {}), | ||
}); | ||
}, [props.account.id, props.account.email]); | ||
return null; | ||
} | ||
|
||
export function TeamIdentifier(props: { | ||
teamId: string; | ||
}) { | ||
// eslint-disable-next-line no-restricted-syntax | ||
useEffect(() => { | ||
if (!posthog.__loaded) { | ||
warnOnce( | ||
"[DASHBOARD_ANALYTICS] is not initialized, cannot identify team", | ||
); | ||
return; | ||
} | ||
posthog.group("team", props.teamId); | ||
}, [props.teamId]); | ||
return null; | ||
} | ||
|
||
export function reset() { | ||
if (!posthog.__loaded) { | ||
warnOnce("[DASHBOARD_ANALYTICS] is not initialized, cannot reset"); | ||
return; | ||
} | ||
posthog.reset(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/** | ||
* Central entry point for analytics tracking helpers. | ||
* | ||
* This file re-exports individual event *categories* located under | ||
* `./track/*`. Each category file in turn exposes small helper functions that | ||
* call PostHog with type-safe payloads. | ||
* | ||
* Example – reporting a contract deployment: | ||
* ```ts | ||
* import { reportContractDeployed } from "@/analytics/track"; | ||
* | ||
* reportContractDeployed({ | ||
* address: "0x…", | ||
* chainId: 1, | ||
* }); | ||
* ``` | ||
* | ||
* Adding a new event *category*: | ||
* 1. Create a new file under `./track/<category>.ts`. | ||
* 2. Inside that file, follow the pattern shown in `contract.ts` to define | ||
* Zod schemas + `reportWhatever` helpers. | ||
* 3. Add a star-export below so the helpers are surfaced at the package root: | ||
* ```ts | ||
* export * from "./track/<category>"; | ||
* ``` | ||
* 4. Consumers can now `import { reportMyEvent } from "@/analytics/track";`. | ||
*/ | ||
|
||
export * from "./track/contract"; | ||
export * from "./track/token"; | ||
export * from "./track/nft"; | ||
export * from "./track/onboarding"; | ||
export * from "./track/marketplace"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
# Analytics Tracking Helpers | ||
|
||
This folder contains **type-safe wrappers** around our analytics provider (PostHog). | ||
Instead of calling `posthog.capture` directly, feature code should import the | ||
pre-defined helpers from here. This guarantees that: | ||
|
||
1. Event names are consistent. | ||
2. Payloads adhere to a strict schema (validated at runtime via Zod and typed at | ||
compile time). | ||
|
||
--- | ||
|
||
## Quick start | ||
|
||
```ts | ||
import { reportContractDeployed } from "@/analytics/track"; | ||
|
||
// Contract deployment example | ||
reportContractDeployed({ | ||
address: "0x…", | ||
chainId: 1, | ||
}); | ||
``` | ||
|
||
> **Note** Ensure that PostHog is initialised *before* you emit events. Our | ||
> bootstrapping code does this automatically during app start-up. | ||
|
||
--- | ||
|
||
## Project structure | ||
|
||
``` | ||
track/ # ← you are here | ||
├─ __internal.ts # low-level wrapper around posthog.capture (do NOT use) | ||
├─ contract.ts # "contract" event category helpers | ||
├─ README.md # this file | ||
└─ … # future categories live here | ||
``` | ||
|
||
* `__internal.ts` exposes `__internal__reportEvent` which performs the actual | ||
PostHog call and safeguards against the SDK not being ready. | ||
* Every **category file** (such as `contract.ts`) groups together related events. | ||
Each event is represented by a `report<Something>` function. | ||
* `track.ts` sits one directory up and **re-exports all helper functions** so | ||
consumers can import the ones they need directly: | ||
|
||
```ts | ||
import { reportContractDeployed /*, reportOtherEvent */ } from "@/analytics/track"; | ||
``` | ||
|
||
--- | ||
|
||
## Adding a **new event** to an existing category | ||
|
||
1. Open the relevant category file (e.g. `contract.ts`). | ||
2. Define a new Zod schema describing the payload: | ||
|
||
```ts | ||
const ContractUpgradeSchema = z.object({ | ||
address: z.string(), | ||
oldVersion: z.string(), | ||
newVersion: z.string(), | ||
}); | ||
``` | ||
|
||
3. Export a reporting helper that forwards the validated payload: | ||
|
||
```ts | ||
export function reportContractUpgraded( | ||
payload: z.infer<typeof ContractUpgradeSchema>, | ||
) { | ||
__internal__reportEvent("contract upgraded", payload); | ||
} | ||
``` | ||
|
||
That's it – consumers can now call `track.contract.reportContractUpgraded(...)`. | ||
|
||
--- | ||
|
||
## Adding a **new category** | ||
|
||
1. Create a new file `track/<category>.ts`. | ||
2. Follow the same pattern as in `contract.ts` (define schemas + export helper | ||
functions). | ||
3. Open `track.ts` (one directory up) and add a star-export so the helpers are | ||
surfaced at the package root: | ||
|
||
```ts | ||
export * from "./track/<category>"; | ||
``` | ||
|
||
Your new helpers can now be imported directly: | ||
|
||
```ts | ||
import { reportMyNewEvent } from "@/analytics/track"; | ||
``` | ||
|
||
--- | ||
|
||
## Conventions & best practices | ||
|
||
* **Lowercase, space-separated event names** – keep them human-readable. | ||
* **Small, focused payloads** – only include properties that are useful for | ||
analytics. | ||
* **Avoid calling PostHog directly** – always go through `__internal__reportEvent` | ||
so we keep a single choke-point. |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,23 @@ | ||||||||||||||||||||||||
import posthog, { type Properties } from "posthog-js"; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
/** | ||||||||||||||||||||||||
* Low-level wrapper around `posthog.capture` with a safety-check that waits | ||||||||||||||||||||||||
* until PostHog has been initialised. | ||||||||||||||||||||||||
* | ||||||||||||||||||||||||
* **⚠️ INTERNAL USE ONLY** – Do not call this directly from feature code. | ||||||||||||||||||||||||
* Instead, create a domain-specific helper (see `contract.ts`) so we keep a | ||||||||||||||||||||||||
* single source of truth for event names & payload contracts. | ||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
export function __internal__reportEvent( | ||||||||||||||||||||||||
eventName: string, | ||||||||||||||||||||||||
properties?: Properties | null, | ||||||||||||||||||||||||
) { | ||||||||||||||||||||||||
if (!posthog.__loaded) { | ||||||||||||||||||||||||
console.warn( | ||||||||||||||||||||||||
"[DASHBOARD_ANALYTICS] is not initialized, cannot track event", | ||||||||||||||||||||||||
); | ||||||||||||||||||||||||
return; | ||||||||||||||||||||||||
Comment on lines
+16
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion
- if (!posthog.__loaded) {
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
+ if (!(posthog as unknown as { __loaded?: boolean }).__loaded) { 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
posthog.capture(eventName, properties); | ||||||||||||||||||||||||
} |
Uh oh!
There was an error while loading. Please reload this page.