From cd3ef6f003f9a9b358d3b964ee8f8ce9d0f641fe Mon Sep 17 00:00:00 2001 From: Roo Code Date: Thu, 11 Dec 2025 12:23:46 +0000 Subject: [PATCH] feat: add Intercom messenger integration to web-roo-code --- apps/web-roo-code/.env.example | 4 + .../src/components/providers/index.ts | 1 + .../providers/intercom-provider.tsx | 96 +++++++++++++++++++ .../src/components/providers/providers.tsx | 13 ++- .../src/lib/analytics/consent-manager.ts | 10 +- 5 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 apps/web-roo-code/src/components/providers/intercom-provider.tsx diff --git a/apps/web-roo-code/.env.example b/apps/web-roo-code/.env.example index c5e8726df1..72cc171f01 100644 --- a/apps/web-roo-code/.env.example +++ b/apps/web-roo-code/.env.example @@ -2,6 +2,10 @@ # Replace these values with your actual PostHog API key and host NEXT_PUBLIC_POSTHOG_KEY=your_posthog_api_key_here +# Intercom Messenger Configuration +# Replace this with your actual Intercom App ID +NEXT_PUBLIC_INTERCOM_APP_ID=your_intercom_app_id_here + # Basin Form Endpoint for Static Form Submissions # Replace this with your actual Basin form endpoint (e.g., https://usebasin.com/f/your-form-id) NEXT_PUBLIC_BASIN_ENDPOINT=https://usebasin.com/f/your-form-id-here diff --git a/apps/web-roo-code/src/components/providers/index.ts b/apps/web-roo-code/src/components/providers/index.ts index a02b0db6f8..55a69451f3 100644 --- a/apps/web-roo-code/src/components/providers/index.ts +++ b/apps/web-roo-code/src/components/providers/index.ts @@ -1 +1,2 @@ +export { IntercomProvider } from "./intercom-provider" export { Providers } from "./providers" diff --git a/apps/web-roo-code/src/components/providers/intercom-provider.tsx b/apps/web-roo-code/src/components/providers/intercom-provider.tsx new file mode 100644 index 0000000000..8ac18a52de --- /dev/null +++ b/apps/web-roo-code/src/components/providers/intercom-provider.tsx @@ -0,0 +1,96 @@ +"use client" + +import { useEffect } from "react" +import { hasConsent, onConsentChange } from "@/lib/analytics/consent-manager" + +// Intercom interface - it's a callable function with overloads +interface IntercomInstance { + (command: "boot", config: Record): void + (command: "shutdown"): void + (command: "update", config?: Record): void + (command: "show"): void + (command: "hide"): void + (command: "showMessages"): void + (command: "showNewMessage", message?: string): void + (command: "onHide", callback: () => void): void + (command: "onShow", callback: () => void): void + (command: "onUnreadCountChange", callback: (unreadCount: number) => void): void +} + +declare global { + interface Window { + Intercom?: IntercomInstance + intercomSettings?: Record + } +} + +export function IntercomProvider({ children }: { children: React.ReactNode }) { + const appId = process.env.NEXT_PUBLIC_INTERCOM_APP_ID + + useEffect(() => { + // Only initialize if Intercom App ID is configured + if (!appId) { + return + } + + // Check initial consent status + const consentGiven = hasConsent() + + if (consentGiven) { + loadIntercom(appId) + } + + // Listen for consent changes + const cleanup = onConsentChange((granted) => { + if (granted) { + loadIntercom(appId) + } else { + shutdownIntercom() + } + }) + + return () => { + cleanup() + shutdownIntercom() + } + }, [appId]) + + const loadIntercom = (appId: string) => { + if (typeof window === "undefined") return + + // Skip if already loaded + if (window.Intercom) { + window.Intercom("update") + return + } + + // Set up Intercom settings + window.intercomSettings = { + app_id: appId, + alignment: "right", + horizontal_padding: 20, + vertical_padding: 20, + } + + // Load Intercom script + const script = document.createElement("script") + script.async = true + script.src = `https://widget.intercom.io/widget/${appId}` + + script.onload = () => { + if (window.Intercom) { + window.Intercom("boot", window.intercomSettings || {}) + } + } + + document.head.appendChild(script) + } + + const shutdownIntercom = () => { + if (typeof window !== "undefined" && window.Intercom) { + window.Intercom("shutdown") + } + } + + return <>{children} +} diff --git a/apps/web-roo-code/src/components/providers/providers.tsx b/apps/web-roo-code/src/components/providers/providers.tsx index a0e532b5e4..64efa13383 100644 --- a/apps/web-roo-code/src/components/providers/providers.tsx +++ b/apps/web-roo-code/src/components/providers/providers.tsx @@ -4,6 +4,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { ThemeProvider } from "next-themes" import { GoogleTagManagerProvider } from "./google-tag-manager-provider" +import { IntercomProvider } from "./intercom-provider" import { PostHogProvider } from "./posthog-provider" const queryClient = new QueryClient() @@ -12,11 +13,13 @@ export const Providers = ({ children }: { children: React.ReactNode }) => { return ( - - - {children} - - + + + + {children} + + + ) diff --git a/apps/web-roo-code/src/lib/analytics/consent-manager.ts b/apps/web-roo-code/src/lib/analytics/consent-manager.ts index a99f6c19f2..ce2b09f890 100644 --- a/apps/web-roo-code/src/lib/analytics/consent-manager.ts +++ b/apps/web-roo-code/src/lib/analytics/consent-manager.ts @@ -21,10 +21,10 @@ export function hasConsent(): boolean { /** * Dispatch a consent change event */ -export function dispatchConsentEvent(consented: boolean): void { +export function dispatchConsentEvent(granted: boolean): void { if (typeof window !== "undefined") { const event = new CustomEvent(CONSENT_EVENT, { - detail: { consented }, + detail: { granted }, }) window.dispatchEvent(event) } @@ -33,14 +33,14 @@ export function dispatchConsentEvent(consented: boolean): void { /** * Listen for consent changes */ -export function onConsentChange(callback: (consented: boolean) => void): () => void { +export function onConsentChange(callback: (granted: boolean) => void): () => void { if (typeof window === "undefined") { return () => {} } const handler = (event: Event) => { - const customEvent = event as CustomEvent<{ consented: boolean }> - callback(customEvent.detail.consented) + const customEvent = event as CustomEvent<{ granted: boolean }> + callback(customEvent.detail.granted) } window.addEventListener(CONSENT_EVENT, handler)