From 9ba5562801bd2315dbe54030f88763d2200a103f Mon Sep 17 00:00:00 2001 From: dleffel Date: Tue, 4 Nov 2025 20:18:52 -0800 Subject: [PATCH 1/2] chore: simplify Google Analytics to standard implementation - Remove Consent Mode v2 implementation - Remove URL passthrough and cookieless tracking features - Switch to Google's standard out-of-the-box gtag.js pattern - Maintain consent-based loading (scripts only load after user consent) Rationale: The standard OoTB implementation is more stable and complies with consent requirements. The cookieless tracking wasn't working properly, and the effort to get it working isn't worth the added complexity. --- .../providers/google-analytics-provider.tsx | 134 ++++-------------- 1 file changed, 24 insertions(+), 110 deletions(-) diff --git a/apps/web-roo-code/src/components/providers/google-analytics-provider.tsx b/apps/web-roo-code/src/components/providers/google-analytics-provider.tsx index 3d274db5090..075da972aa8 100644 --- a/apps/web-roo-code/src/components/providers/google-analytics-provider.tsx +++ b/apps/web-roo-code/src/components/providers/google-analytics-provider.tsx @@ -8,146 +8,60 @@ import { hasConsent, onConsentChange } from "@/lib/analytics/consent-manager" const GTM_ID = "AW-17391954825" /** - * Google Analytics Provider with Consent Mode v2 - * Implements cookieless pings and advanced consent management + * Google Analytics Provider + * Implements Google's standard gtag.js loading pattern */ export function GoogleAnalyticsProvider({ children }: { children: React.ReactNode }) { const [shouldLoad, setShouldLoad] = useState(false) useEffect(() => { - // Initialize consent defaults BEFORE loading gtag.js (required for Consent Mode v2) - initializeConsentDefaults() - // Check initial consent status if (hasConsent()) { setShouldLoad(true) - updateConsentGranted() } // Listen for consent changes const unsubscribe = onConsentChange((consented) => { - if (consented) { - if (!shouldLoad) { - setShouldLoad(true) - } - updateConsentGranted() - } else { - updateConsentDenied() + if (consented && !shouldLoad) { + setShouldLoad(true) } }) return unsubscribe - // eslint-disable-next-line react-hooks/exhaustive-deps -- shouldLoad intentionally omitted to prevent re-initialization loop - }, []) + }, [shouldLoad]) - const initializeConsentDefaults = () => { - // Set up consent defaults before gtag loads (Consent Mode v2 requirement) + useEffect(() => { + // Initialize dataLayer as early as possible (Google's recommended pattern) if (typeof window !== "undefined") { window.dataLayer = window.dataLayer || [] - window.gtag = function (...args: GtagArgs) { - window.dataLayer.push(args) - } - - // Set default consent state to 'denied' with cookieless pings enabled - window.gtag("consent", "default", { - ad_storage: "denied", - ad_user_data: "denied", - ad_personalization: "denied", - analytics_storage: "denied", - functionality_storage: "denied", - personalization_storage: "denied", - security_storage: "granted", // Always granted for security - wait_for_update: 500, // Wait 500ms for consent before sending data - }) - - // Enable cookieless pings for Google Ads - window.gtag("set", "url_passthrough", true) - } - } - - const updateConsentGranted = () => { - // User accepted cookies - update consent to granted - if (typeof window !== "undefined" && window.gtag) { - window.gtag("consent", "update", { - ad_storage: "granted", - ad_user_data: "granted", - ad_personalization: "granted", - analytics_storage: "granted", - functionality_storage: "granted", - personalization_storage: "granted", - }) - } - } - - const updateConsentDenied = () => { - // User declined cookies - keep consent denied (cookieless pings still work) - if (typeof window !== "undefined" && window.gtag) { - window.gtag("consent", "update", { - ad_storage: "denied", - ad_user_data: "denied", - ad_personalization: "denied", - analytics_storage: "denied", - functionality_storage: "denied", - personalization_storage: "denied", - }) } - } - - // Always render scripts (Consent Mode v2 needs gtag loaded even without consent) - // Cookieless pings will work with denied consent + }, []) return ( <> - {/* Google tag (gtag.js) - Loads immediately for Consent Mode v2 */} - + + )} {children} ) } -// Type definitions for Google Analytics with Consent Mode v2 -type ConsentState = "granted" | "denied" - -interface ConsentParams { - ad_storage?: ConsentState - ad_user_data?: ConsentState - ad_personalization?: ConsentState - analytics_storage?: ConsentState - functionality_storage?: ConsentState - personalization_storage?: ConsentState - security_storage?: ConsentState - wait_for_update?: number -} - -type GtagArgs = - | ["js", Date] - | ["config", string, GtagConfig?] - | ["event", string, GtagEventParameters?] - | ["consent", "default" | "update", ConsentParams] - | ["set", string, unknown] - -interface GtagConfig { - [key: string]: unknown -} - -interface GtagEventParameters { - [key: string]: unknown -} - // Declare global types for TypeScript declare global { interface Window { - dataLayer: GtagArgs[] - gtag: (...args: GtagArgs) => void + dataLayer: unknown[] + gtag: (...args: unknown[]) => void } } From ab22b2d642c84d9359eb038707ccbeedf5635c8d Mon Sep 17 00:00:00 2001 From: Roo Code Date: Wed, 5 Nov 2025 05:05:04 +0000 Subject: [PATCH 2/2] Fix: remove shouldLoad from useEffect deps and simplify consent callback --- .../components/providers/google-analytics-provider.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/web-roo-code/src/components/providers/google-analytics-provider.tsx b/apps/web-roo-code/src/components/providers/google-analytics-provider.tsx index 075da972aa8..7c0a7edfc62 100644 --- a/apps/web-roo-code/src/components/providers/google-analytics-provider.tsx +++ b/apps/web-roo-code/src/components/providers/google-analytics-provider.tsx @@ -22,19 +22,12 @@ export function GoogleAnalyticsProvider({ children }: { children: React.ReactNod // Listen for consent changes const unsubscribe = onConsentChange((consented) => { - if (consented && !shouldLoad) { + if (consented) { setShouldLoad(true) } }) return unsubscribe - }, [shouldLoad]) - - useEffect(() => { - // Initialize dataLayer as early as possible (Google's recommended pattern) - if (typeof window !== "undefined") { - window.dataLayer = window.dataLayer || [] - } }, []) return (