Skip to content

Commit e26fb75

Browse files
authored
Add Intercom (#383)
1 parent 18329e4 commit e26fb75

File tree

4 files changed

+108
-3
lines changed

4 files changed

+108
-3
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
POSTHOG_API_KEY=your_posthog_api_key
2+
INTERCOM_APP_ID=your_intercom_app_id

docusaurus.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ const config: Config = {
4040
onBrokenLinks: 'warn',
4141
onBrokenMarkdownLinks: 'warn',
4242

43+
// Custom fields for client-side access
44+
customFields: {
45+
intercomAppId: process.env.INTERCOM_APP_ID,
46+
},
47+
4348
// Even if you don't use internationalization, you can use this field to set
4449
// useful metadata like html lang. For example, if your site is Chinese, you
4550
// may want to replace "en" with "zh-Hans".
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import React, { useEffect } from 'react';
2+
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
3+
import { hasConsent, onConsentChange } from '../../lib/analytics/consent-manager';
4+
5+
// Intercom interface - it's a callable function with overloads
6+
interface IntercomInstance {
7+
(command: 'boot', config: Record<string, unknown>): void;
8+
(command: 'shutdown'): void;
9+
(command: 'update', config?: Record<string, unknown>): void;
10+
(command: 'show'): void;
11+
(command: 'hide'): void;
12+
(command: 'showMessages'): void;
13+
(command: 'showNewMessage', message?: string): void;
14+
(command: 'onHide', callback: () => void): void;
15+
(command: 'onShow', callback: () => void): void;
16+
(command: 'onUnreadCountChange', callback: (unreadCount: number) => void): void;
17+
}
18+
19+
declare global {
20+
interface Window {
21+
Intercom?: IntercomInstance;
22+
intercomSettings?: Record<string, unknown>;
23+
}
24+
}
25+
26+
export function IntercomProvider({ children }: { children: React.ReactNode }) {
27+
const { siteConfig } = useDocusaurusContext();
28+
const appId = siteConfig.customFields?.intercomAppId as string | undefined;
29+
30+
useEffect(() => {
31+
// Only initialize if Intercom App ID is configured
32+
if (!appId) {
33+
return;
34+
}
35+
36+
// Check initial consent status
37+
const consentGiven = hasConsent();
38+
39+
if (consentGiven) {
40+
loadIntercom(appId);
41+
}
42+
43+
// Listen for consent changes
44+
const cleanup = onConsentChange((granted) => {
45+
if (granted) {
46+
loadIntercom(appId);
47+
} else {
48+
shutdownIntercom();
49+
}
50+
});
51+
52+
return () => {
53+
cleanup();
54+
shutdownIntercom();
55+
};
56+
}, []);
57+
58+
const loadIntercom = (appId: string) => {
59+
if (typeof window === 'undefined') return;
60+
61+
// Skip if already loaded
62+
if (window.Intercom) {
63+
window.Intercom('update');
64+
return;
65+
}
66+
67+
// Set up Intercom settings
68+
window.intercomSettings = {
69+
app_id: appId,
70+
alignment: 'right',
71+
horizontal_padding: 20,
72+
vertical_padding: 20,
73+
};
74+
75+
// Load Intercom script
76+
const script = document.createElement('script');
77+
script.async = true;
78+
script.src = `https://widget.intercom.io/widget/${appId}`;
79+
80+
script.onload = () => {
81+
if (window.Intercom) {
82+
window.Intercom('boot', window.intercomSettings || {});
83+
}
84+
};
85+
86+
document.head.appendChild(script);
87+
};
88+
89+
const shutdownIntercom = () => {
90+
if (typeof window !== 'undefined' && window.Intercom) {
91+
window.Intercom('shutdown');
92+
}
93+
};
94+
95+
return <>{children}</>;
96+
}

src/theme/Layout/index.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ import React from 'react';
22
import OriginalLayout from '@theme-original/Layout';
33
import { CookieConsent } from '../../components/CookieConsent';
44
import { PostHogProvider } from '../../components/PostHogProvider';
5+
import { IntercomProvider } from '../../components/IntercomProvider';
56
import { TeamsBanner } from '../../components/TeamsBanner';
67

78
export default function Layout(props) {
89
return (
910
<PostHogProvider>
10-
<TeamsBanner />
11-
<OriginalLayout {...props} />
12-
<CookieConsent />
11+
<IntercomProvider>
12+
<TeamsBanner />
13+
<OriginalLayout {...props} />
14+
<CookieConsent />
15+
</IntercomProvider>
1316
</PostHogProvider>
1417
);
1518
}

0 commit comments

Comments
 (0)