import { currentUserState } from '@/auth/states/currentUserState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { supportChatState } from '@/client-config/states/supportChatState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { isNonEmptyString } from '@sniptt/guards'; import { useCallback, useEffect, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { type User, type WorkspaceMember } from '~/generated-metadata/graphql'; import { scheduleIdleCallback } from '~/utils/scheduleIdleCallback'; // Front chat is non-critical UI, so we load its ~2 s bundle during an idle // period rather than letting it compete with metadata loading and first // render during boot. The timeout caps the wait so the launcher (and any // unread-reply badge) still appears promptly, and it doubles as the plain // delay on browsers without requestIdleCallback (Safari/iOS). const FRONT_CHAT_IDLE_LOAD_TIMEOUT_MS = 2000; const insertScript = ({ src, innerHTML, onLoad, defer = false, }: { src?: string; innerHTML?: string; onLoad?: (...args: any[]) => void; defer?: boolean; }) => { const script = document.createElement('script'); if (isNonEmptyString(src)) script.src = src; if (isNonEmptyString(innerHTML)) script.innerHTML = innerHTML; if (isDefined(onLoad)) script.onload = onLoad; script.defer = defer; document.body.appendChild(script); }; export const useInstantiateSupportChat = () => { const currentUser = useAtomStateValue(currentUserState); const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState); const supportChat = useAtomStateValue(supportChatState); const [isFrontChatLoaded, setIsFrontChatLoaded] = useState(false); const configureFront = useCallback( ( chatId: string, currentUser: Pick, currentWorkspaceMember: Pick, ) => { const url = 'https://chat-assets.frontapp.com/v1/chat.bundle.js'; let script = document.querySelector(`script[src="${url}"]`); // This function only gets called when front chat is not loaded // If the script is already defined, but front chat is not loaded // then there was an error loading the script; reload the script if (isDefined(script)) { script.parentNode?.removeChild(script); script = null; } insertScript({ src: url, defer: true, onLoad: () => { window.FrontChat?.('init', { chatId, useDefaultLauncher: false, email: currentUser.email, name: currentWorkspaceMember.name.firstName + ' ' + currentWorkspaceMember.name.lastName, userHash: currentUser?.supportUserHash, }); setIsFrontChatLoaded(true); }, }); }, [], ); useEffect(() => { if ( supportChat?.supportDriver === 'FRONT' && isNonEmptyString(supportChat.supportFrontChatId) && isNonEmptyString(currentUser?.email) && isDefined(currentWorkspaceMember) && !isFrontChatLoaded ) { return scheduleIdleCallback( () => { configureFront( supportChat.supportFrontChatId as string, currentUser, currentWorkspaceMember, ); }, { timeout: FRONT_CHAT_IDLE_LOAD_TIMEOUT_MS }, ); } }, [ configureFront, currentUser, isFrontChatLoaded, supportChat?.supportDriver, supportChat.supportFrontChatId, currentWorkspaceMember, ]); return { isFrontChatLoaded }; };