import { LiteBadge } from './LiteBadge' import { createEffect, createSignal, onCleanup, onMount, Show } from 'solid-js' import { injectCustomHeadCode, isNotEmpty } from 'utils' import { getInitialChatReplyQuery } from '@/queries/getInitialChatReplyQuery' import { ConversationContainer } from './ConversationContainer' import { setIsMobile } from '@/utils/isMobileSignal' import { BotContext, InitialChatReply, OutgoingLog } from '@/types' import { ErrorMessage } from './ErrorMessage' import { getExistingResultIdFromSession, setResultInSession, } from '@/utils/sessionStorage' import { setCssVariablesValue } from '@/utils/setCssVariablesValue' export type BotProps = { typebot: string | any isPreview?: boolean resultId?: string startGroupId?: string prefilledVariables?: Record apiHost?: string onNewInputBlock?: (ids: { id: string; groupId: string }) => void onAnswer?: (answer: { message: string; blockId: string }) => void onInit?: () => void onEnd?: () => void onNewLogs?: (logs: OutgoingLog[]) => void } export const Bot = (props: BotProps & { class?: string }) => { const [initialChatReply, setInitialChatReply] = createSignal< InitialChatReply | undefined >() const [customCss, setCustomCss] = createSignal('') const [isInitialized, setIsInitialized] = createSignal(false) const [error, setError] = createSignal() const initializeBot = async () => { setIsInitialized(true) const urlParams = new URLSearchParams(location.search) props.onInit?.() const prefilledVariables: { [key: string]: string } = {} urlParams.forEach((value, key) => { prefilledVariables[key] = value }) const { data, error } = await getInitialChatReplyQuery({ typebot: props.typebot, apiHost: props.apiHost, isPreview: props.isPreview ?? false, resultId: isNotEmpty(props.resultId) ? props.resultId : getExistingResultIdFromSession(), startGroupId: props.startGroupId, prefilledVariables: { ...prefilledVariables, ...props.prefilledVariables, }, }) if (error && 'code' in error && typeof error.code === 'string') { if (['BAD_REQUEST', 'FORBIDDEN'].includes(error.code)) setError(new Error('This bot is now closed.')) if (error.code === 'NOT_FOUND') setError(new Error("The bot you're looking for doesn't exist.")) return } if (!data) return setError(new Error("Error! Couldn't initiate the chat.")) if (data.resultId) setResultInSession(data.resultId) setInitialChatReply(data) setCustomCss(data.typebot.theme.customCss ?? '') if (data.input?.id && props.onNewInputBlock) props.onNewInputBlock({ id: data.input.id, groupId: data.input.groupId, }) if (data.logs) props.onNewLogs?.(data.logs) const customHeadCode = data.typebot.settings.metadata.customHeadCode if (customHeadCode) injectCustomHeadCode(customHeadCode) } createEffect(() => { if (!props.typebot || isInitialized()) return initializeBot().then() }) createEffect(() => { if (typeof props.typebot === 'string') return setCustomCss(props.typebot.theme.customCss ?? '') }) onCleanup(() => { setIsInitialized(false) }) return ( <> {(error) => } {(initialChatReply) => ( )} ) } type BotContentProps = { initialChatReply: InitialChatReply context: BotContext class?: string onNewInputBlock?: (block: { id: string; groupId: string }) => void onAnswer?: (answer: { message: string; blockId: string }) => void onEnd?: () => void onNewLogs?: (logs: OutgoingLog[]) => void } const BotContent = (props: BotContentProps) => { let botContainer: HTMLDivElement | undefined const resizeObserver = new ResizeObserver((entries) => { return setIsMobile(entries[0].target.clientWidth < 400) }) const injectCustomFont = () => { const font = document.createElement('link') font.href = `https://fonts.googleapis.com/css2?family=${ props.initialChatReply.typebot?.theme?.general?.font ?? 'Open Sans' }:ital,wght@0,300;0,400;0,600;1,300;1,400;1,600&display=swap');')` font.rel = 'stylesheet' document.head.appendChild(font) } onMount(() => { injectCustomFont() if (!botContainer) return resizeObserver.observe(botContainer) }) createEffect(() => { if (!botContainer) return setCssVariablesValue(props.initialChatReply.typebot.theme, botContainer) }) onCleanup(() => { if (!botContainer) return resizeObserver.unobserve(botContainer) }) return (
) }