2
0
Files
bot/packages/js/src/components/Bot.tsx

134 lines
3.8 KiB
TypeScript
Raw Normal View History

import { LiteBadge } from './LiteBadge'
import { createSignal, onCleanup, onMount, Show } from 'solid-js'
import { getViewerUrl, isDefined, isNotEmpty } from 'utils'
import { getInitialChatReplyQuery } from '@/queries/getInitialChatReplyQuery'
import { ConversationContainer } from './ConversationContainer'
import css from '../assets/index.css'
import { InitialChatReply, StartParams } from 'models'
import { setIsMobile } from '@/utils/isMobileSignal'
import { BotContext } from '@/types'
import { injectHeadCode } from '@/utils/injectHeadCode'
export type BotProps = StartParams & {
initialChatReply?: InitialChatReply
apiHost?: string
}
export const Bot = (props: BotProps) => {
const [initialChatReply, setInitialChatReply] = createSignal<
InitialChatReply | undefined
>(props.initialChatReply)
onMount(() => {
if (!props.typebotId) return
const initialChatReplyValue = initialChatReply()
if (isDefined(initialChatReplyValue)) {
const customHeadCode =
initialChatReplyValue.typebot.settings.metadata.customHeadCode
if (customHeadCode) {
injectHeadCode(customHeadCode)
}
} else {
const urlParams = new URLSearchParams(location.search)
const prefilledVariables: { [key: string]: string } = {}
urlParams.forEach((value, key) => {
prefilledVariables[key] = value
})
getInitialChatReplyQuery({
typebotId: props.typebotId,
apiHost: props.apiHost,
isPreview: props.isPreview ?? false,
resultId: props.resultId,
prefilledVariables: {
...prefilledVariables,
...props.prefilledVariables,
},
}).then((initialChatReply) => {
setInitialChatReply(initialChatReply)
})
}
})
return (
<Show
when={isNotEmpty(props.apiHost ?? getViewerUrl())}
fallback={() => (
<p>process.env.NEXT_PUBLIC_VIEWER_URL is missing in env</p>
)}
>
<Show when={initialChatReply()} keyed>
{(initialChatReply) => (
<BotContent
initialChatReply={initialChatReply}
context={{
apiHost: props.apiHost,
isPreview: props.isPreview ?? false,
typebotId: props.typebotId as string,
resultId: initialChatReply.resultId,
}}
/>
)}
</Show>
</Show>
)
}
type BotContentProps = {
initialChatReply: InitialChatReply
context: BotContext
}
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'
}:wght@300;400;600&display=swap')`
font.rel = 'stylesheet'
document.head.appendChild(font)
}
onMount(() => {
injectCustomFont()
if (botContainer) {
resizeObserver.observe(botContainer)
}
})
onCleanup(() => {
if (botContainer) {
resizeObserver.unobserve(botContainer)
}
})
return (
<>
<style>{css}</style>
<div
ref={botContainer}
class="flex w-full h-full text-base overflow-hidden bg-cover flex-col items-center typebot-container"
>
<div class="flex w-full h-full justify-center">
<ConversationContainer
context={props.context}
initialChatReply={props.initialChatReply}
/>
</div>
<Show
when={
props.initialChatReply.typebot.settings.general.isBrandingEnabled
}
>
<LiteBadge botContainer={botContainer} />
</Show>
</div>
</>
)
}