-
Notifications
You must be signed in to change notification settings - Fork 732
chore: add intercom (IN-1028) #3945
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
base: main
Are you sure you want to change the base?
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 | ||||
|---|---|---|---|---|---|---|
|
|
@@ -9,6 +9,7 @@ import { watch } from 'vue'; | |||||
| import config from '@/config'; | ||||||
| import { setRumUser } from '@/utils/datadog/rum'; | ||||||
| import useSessionTracking from '@/shared/modules/monitoring/useSessionTracking'; | ||||||
| import { boot as bootIntercom, shutdown as shutdownIntercom } from '@/utils/intercom'; | ||||||
|
|
||||||
| export default { | ||||||
| init() { | ||||||
|
|
@@ -36,6 +37,19 @@ export default { | |||||
| if (user) { | ||||||
| setRumUser(user); | ||||||
| lfxHeader.authuser = user; | ||||||
|
|
||||||
| const intercomJwt = user[config.intercom.auth0IntercomClaim]; | ||||||
| const userId = user[config.intercom.auth0UsernameClaim]; | ||||||
| if (userId && intercomJwt) { | ||||||
| bootIntercom({ | ||||||
| user_id: userId, | ||||||
| name: user.name, | ||||||
| email: user.email, | ||||||
| intercom_user_jwt: intercomJwt, | ||||||
| }).catch((error: any) => { | ||||||
| console.error('Intercom: Boot failed', error); | ||||||
|
||||||
| console.error('Intercom: Boot failed', error); | |
| console.error('Intercom: Boot failed:', (error && error.message) ? error.message : String(error)); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,167 @@ | ||||||||||||||||||||
| /* eslint-disable @typescript-eslint/no-unused-vars */ | ||||||||||||||||||||
| /* eslint-disable no-use-before-define */ | ||||||||||||||||||||
| import config from '@/config'; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| declare global { | ||||||||||||||||||||
| interface Window { | ||||||||||||||||||||
| Intercom?: any; | ||||||||||||||||||||
| intercomSettings?: any; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| let isLoaded = false; | ||||||||||||||||||||
| let isBooted = false; | ||||||||||||||||||||
| let isLoading = false; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| export interface IntercomBootOptions { | ||||||||||||||||||||
| user_id: string; | ||||||||||||||||||||
| name?: string; | ||||||||||||||||||||
| email?: string; | ||||||||||||||||||||
| intercom_user_jwt?: string; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const loadScript = (): void => { | ||||||||||||||||||||
| if (isLoaded || isLoading || typeof window === 'undefined') { | ||||||||||||||||||||
| return; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| isLoading = true; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Create stub so queued commands work before script loads | ||||||||||||||||||||
| const w = window as any; | ||||||||||||||||||||
| const ic = w.Intercom; | ||||||||||||||||||||
| if (typeof ic === 'function') { | ||||||||||||||||||||
| ic('reattach_activator'); | ||||||||||||||||||||
| ic('update', w.intercomSettings); | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| const i: any = (...args: any[]) => { i.c(args); }; | ||||||||||||||||||||
| i.q = []; | ||||||||||||||||||||
| i.c = (args: any) => { i.q.push(args); }; | ||||||||||||||||||||
| w.Intercom = i; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Pre-set app settings | ||||||||||||||||||||
| window.intercomSettings = { | ||||||||||||||||||||
| api_base: config.intercom.apiBase, | ||||||||||||||||||||
| app_id: config.intercom.appId, | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const script = document.createElement('script'); | ||||||||||||||||||||
| script.type = 'text/javascript'; | ||||||||||||||||||||
| script.async = true; | ||||||||||||||||||||
| script.src = `https://widget.intercom.io/widget/${config.intercom.appId}`; | ||||||||||||||||||||
| script.onload = () => { | ||||||||||||||||||||
| isLoaded = true; | ||||||||||||||||||||
| isLoading = false; | ||||||||||||||||||||
| }; | ||||||||||||||||||||
| script.onerror = (error) => { | ||||||||||||||||||||
| isLoading = false; | ||||||||||||||||||||
| console.error('Intercom: Failed to load script', error); | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
Comment on lines
+56
to
+59
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| const firstScript = document.getElementsByTagName('script')[0]; | ||||||||||||||||||||
| if (firstScript?.parentNode) { | ||||||||||||||||||||
| firstScript.parentNode.insertBefore(script, firstScript); | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| (document.head || document.body).appendChild(script); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| export const boot = (options: IntercomBootOptions): Promise<void> => new Promise((resolve, reject) => { | ||||||||||||||||||||
| if (typeof window === 'undefined') { | ||||||||||||||||||||
| reject(new Error('Window is undefined')); | ||||||||||||||||||||
| return; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (!config.intercom.appId) { | ||||||||||||||||||||
| console.info('Intercom: Disabled (no appId configured)'); | ||||||||||||||||||||
| reject(new Error('No Intercom app ID configured')); | ||||||||||||||||||||
| return; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
Comment on lines
+75
to
+79
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| if (isBooted) { | ||||||||||||||||||||
| const { intercom_user_jwt: _jwt, ...updateOptions } = options; | ||||||||||||||||||||
| update(updateOptions); | ||||||||||||||||||||
| resolve(); | ||||||||||||||||||||
| return; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (!isLoaded && !isLoading) { | ||||||||||||||||||||
| loadScript(); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Set JWT in intercomSettings before boot — required for identity verification | ||||||||||||||||||||
| if (options.intercom_user_jwt) { | ||||||||||||||||||||
| window.intercomSettings = window.intercomSettings || {}; | ||||||||||||||||||||
| window.intercomSettings.intercom_user_jwt = options.intercom_user_jwt; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
Comment on lines
+92
to
+96
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| const checkLoaded = setInterval(() => { | ||||||||||||||||||||
| if (isLoaded && window.Intercom) { | ||||||||||||||||||||
| clearInterval(checkLoaded); | ||||||||||||||||||||
| clearTimeout(timeoutHandle); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (isBooted) { | ||||||||||||||||||||
| const { intercom_user_jwt: _jwt, ...updateOptions } = options; | ||||||||||||||||||||
| update(updateOptions); | ||||||||||||||||||||
| resolve(); | ||||||||||||||||||||
| return; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| isBooted = true; | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| const { intercom_user_jwt: _jwt, ...bootOptions } = options; | ||||||||||||||||||||
| window.Intercom('boot', { | ||||||||||||||||||||
| api_base: config.intercom.apiBase, | ||||||||||||||||||||
| app_id: config.intercom.appId, | ||||||||||||||||||||
| ...bootOptions, | ||||||||||||||||||||
|
Comment on lines
+112
to
+116
|
||||||||||||||||||||
| const { intercom_user_jwt: _jwt, ...bootOptions } = options; | |
| window.Intercom('boot', { | |
| api_base: config.intercom.apiBase, | |
| app_id: config.intercom.appId, | |
| ...bootOptions, | |
| window.Intercom('boot', { | |
| api_base: config.intercom.apiBase, | |
| app_id: config.intercom.appId, | |
| ...options, |
Copilot
AI
Mar 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the script fails quickly (onerror), boot() will still poll for up to 10s before rejecting, which delays auth lifecycle completion and adds avoidable background work. Consider making loadScript() return a shared Promise (cached across calls) that resolves on onload and rejects on onerror, and have boot() await that instead of polling; this will fail fast, simplify control flow, and avoid repeated intervals/timeouts for concurrent boots.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.env.dist.localis typically a template; committing a concrete app id can cause accidental usage against the wrong Intercom workspace in local/dev setups. Consider leaving this blank or using an obvious placeholder value (with a short comment) to encourage explicit configuration per environment.