web: Attempt at compliant, cookie-less anonymous tracking for the website (#8957)

This commit is contained in:
Bruno Bergher 2025-10-31 19:19:35 +00:00 committed by GitHub
parent 416fa5727e
commit eb68c65296
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 52 additions and 45 deletions

View file

@ -5,7 +5,7 @@ import ReactCookieConsent from "react-cookie-consent"
import { Cookie } from "lucide-react"
import { getDomain } from "tldts"
import { CONSENT_COOKIE_NAME } from "@roo-code/types"
import { dispatchConsentEvent } from "@/lib/analytics/consent-manager"
import { handleConsentAccept, handleConsentReject } from "@/lib/analytics/consent-manager"
/**
* GDPR-compliant cookie consent banner component
@ -23,11 +23,11 @@ export function CookieConsentWrapper() {
}, [])
const handleAccept = () => {
dispatchConsentEvent(true)
handleConsentAccept()
}
const handleDecline = () => {
dispatchConsentEvent(false)
handleConsentReject()
}
const extraCookieOptions = cookieDomain

View file

@ -3,8 +3,8 @@
import { usePathname, useSearchParams } from "next/navigation"
import posthog from "posthog-js"
import { PostHogProvider as OriginalPostHogProvider } from "posthog-js/react"
import { useEffect, Suspense, useState } from "react"
import { hasConsent, onConsentChange } from "@/lib/analytics/consent-manager"
import { useEffect, Suspense } from "react"
import { hasConsent } from "@/lib/analytics/consent-manager"
function PageViewTracker() {
const pathname = usePathname()
@ -28,11 +28,9 @@ function PageViewTracker() {
}
export function PostHogProvider({ children }: { children: React.ReactNode }) {
const [isInitialized, setIsInitialized] = useState(false)
useEffect(() => {
// Initialize PostHog only on the client side AND when consent is given
if (typeof window !== "undefined") {
// Initialize PostHog immediately on the client side
if (typeof window !== "undefined" && !posthog.__loaded) {
const posthogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY
const posthogHost = process.env.NEXT_PUBLIC_POSTHOG_HOST
@ -52,48 +50,32 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) {
)
}
const initializePosthog = () => {
if (!isInitialized) {
posthog.init(posthogKey, {
api_host: posthogHost || "https://us.i.posthog.com",
capture_pageview: false,
loaded: (posthogInstance) => {
if (process.env.NODE_ENV === "development") {
posthogInstance.debug()
}
},
respect_dnt: true, // Respect Do Not Track
})
setIsInitialized(true)
}
}
// Check if user has already consented to cookies
const userHasConsented = hasConsent()
// Check initial consent status
if (hasConsent()) {
initializePosthog()
}
// Listen for consent changes
const unsubscribe = onConsentChange((consented) => {
if (consented && !isInitialized) {
initializePosthog()
}
// Initialize PostHog with appropriate persistence based on consent
posthog.init(posthogKey, {
api_host: posthogHost || "https://us.i.posthog.com",
capture_pageview: false, // We handle pageview tracking manually
loaded: (posthogInstance) => {
if (process.env.NODE_ENV === "development") {
posthogInstance.debug()
}
},
save_referrer: true, // Save referrer information
save_campaign_params: true, // Save UTM parameters
respect_dnt: true, // Respect Do Not Track
persistence: userHasConsented ? "localStorage+cookie" : "memory", // Use localStorage if consented, otherwise memory-only
opt_out_capturing_by_default: false, // Start tracking immediately
})
return () => {
unsubscribe()
}
}
}, [isInitialized])
}, [])
// Only provide PostHog context if it's initialized
return (
<OriginalPostHogProvider client={posthog}>
{isInitialized && (
<Suspense fallback={null}>
<PageViewTracker />
</Suspense>
)}
<Suspense fallback={null}>
<PageViewTracker />
</Suspense>
{children}
</OriginalPostHogProvider>
)

View file

@ -5,6 +5,7 @@
import { getCookieConsentValue } from "react-cookie-consent"
import { CONSENT_COOKIE_NAME } from "@roo-code/types"
import posthog from "posthog-js"
export const CONSENT_EVENT = "cookieConsentChanged"
@ -45,3 +46,27 @@ export function onConsentChange(callback: (consented: boolean) => void): () => v
window.addEventListener(CONSENT_EVENT, handler)
return () => window.removeEventListener(CONSENT_EVENT, handler)
}
/**
* Handle user accepting cookies
* Opts PostHog back into cookie-based tracking
*/
export function handleConsentAccept(): void {
if (typeof window !== "undefined" && posthog.__loaded) {
// User accepted - ensure localStorage+cookie persistence is enabled
posthog.opt_in_capturing()
posthog.set_config({
persistence: "localStorage+cookie",
})
}
dispatchConsentEvent(true)
}
/**
* Handle user rejecting cookies
* Switches PostHog to cookieless (memory-only) mode
*/
export function handleConsentReject(): void {
// User rejected - stick to cookieless mode
dispatchConsentEvent(false)
}