import { ThreadWebWorker, release, retain } from '@quilted/threads'; import { RemoteReceiver } from '@remote-dom/core/receivers'; import { useEffect, useRef } from 'react'; import { type ConfirmationModalCaller } from 'twenty-shared/types'; import { type CommandConfirmationModalResult } from 'twenty-sdk'; import { type FrontComponentHostCommunicationApi } from '../../types/FrontComponentHostCommunicationApi'; import { type SdkClientUrls } from '../../types/HostToWorkerRenderContext'; import { type WorkerExports } from '../../types/WorkerExports'; import { createRemoteWorker } from '../worker/utils/createRemoteWorker'; // Must match COMMAND_MENU_ITEM_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME in twenty-front const COMMAND_MENU_ITEM_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME = 'command-menu-item-confirmation-modal-result'; type CommandMenuItemConfirmationModalResultBrowserEventDetail = { caller: ConfirmationModalCaller; confirmationResult: CommandConfirmationModalResult; }; type FrontComponentWorkerEffectProps = { componentUrl: string; applicationAccessToken?: string; apiUrl?: string; sdkClientUrls?: SdkClientUrls; frontComponentId: string; frontComponentHostCommunicationApi: FrontComponentHostCommunicationApi; setReceiver: React.Dispatch>; setThread: React.Dispatch< React.SetStateAction | null> >; setError: React.Dispatch>; }; export const FrontComponentWorkerEffect = ({ componentUrl, applicationAccessToken, apiUrl, sdkClientUrls, frontComponentId, frontComponentHostCommunicationApi, setReceiver, setThread, setError, }: FrontComponentWorkerEffectProps) => { const isInitializedRef = useRef(false); useEffect(() => { if (isInitializedRef.current) { return; } const newReceiver = new RemoteReceiver({ retain, release }); const worker = createRemoteWorker(); worker.onerror = (event: ErrorEvent) => { const workerError = event.error ?? new Error(event.message || 'Unknown worker error'); console.error('[FrontComponentRenderer] Worker error:', workerError); setError(workerError); }; const thread = new ThreadWebWorker< WorkerExports, FrontComponentHostCommunicationApi >(worker, { exports: frontComponentHostCommunicationApi, }); const handleCommandMenuItemConfirmationModalResultBrowserEvent = ( event: CustomEvent, ) => { const commandMenuItemConfirmationModalResultBrowserEventDetail = event.detail; const caller = commandMenuItemConfirmationModalResultBrowserEventDetail.caller; if ( caller.type !== 'frontComponent' || caller.frontComponentId !== frontComponentId ) { return; } thread.imports .onConfirmationModalResult( commandMenuItemConfirmationModalResultBrowserEventDetail.confirmationResult, ) .catch((error: Error) => { setError(error); }); }; window.addEventListener( COMMAND_MENU_ITEM_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME, handleCommandMenuItemConfirmationModalResultBrowserEvent as EventListener, ); setThread(thread); thread.imports .render(newReceiver.connection, { componentUrl, applicationAccessToken, apiUrl, sdkClientUrls, }) .catch((error: Error) => { setError(error); }); setReceiver(newReceiver); isInitializedRef.current = true; return () => { window.removeEventListener( COMMAND_MENU_ITEM_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME, handleCommandMenuItemConfirmationModalResultBrowserEvent as EventListener, ); setThread(null); worker.terminate(); isInitializedRef.current = false; }; }, [ componentUrl, applicationAccessToken, apiUrl, sdkClientUrls, frontComponentId, setError, setReceiver, setThread, frontComponentHostCommunicationApi, ]); return null; };