2023-07-19 11:20:44 +02:00
|
|
|
import { BotContext, ChatChunk as ChatChunkType } from '@/types'
|
2023-03-07 17:11:24 +01:00
|
|
|
import { isMobile } from '@/utils/isMobileSignal'
|
2023-11-13 15:27:36 +01:00
|
|
|
import { ContinueChatResponse, Settings, Theme } from '@typebot.io/schemas'
|
2023-01-25 11:27:47 +01:00
|
|
|
import { createSignal, For, onMount, Show } from 'solid-js'
|
2022-12-22 17:02:34 +01:00
|
|
|
import { HostBubble } from '../bubbles/HostBubble'
|
|
|
|
import { InputChatBlock } from '../InputChatBlock'
|
|
|
|
import { AvatarSideContainer } from './AvatarSideContainer'
|
2023-07-19 11:20:44 +02:00
|
|
|
import { StreamingBubble } from '../bubbles/StreamingBubble'
|
2023-11-08 15:34:16 +01:00
|
|
|
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
|
|
|
|
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
2022-12-22 17:02:34 +01:00
|
|
|
|
2023-11-13 15:27:36 +01:00
|
|
|
type Props = Pick<ContinueChatResponse, 'messages' | 'input'> & {
|
2022-12-22 17:02:34 +01:00
|
|
|
theme: Theme
|
|
|
|
settings: Settings
|
2024-03-07 15:39:09 +01:00
|
|
|
index: number
|
2022-12-22 17:02:34 +01:00
|
|
|
context: BotContext
|
2023-04-03 18:14:32 +02:00
|
|
|
hasError: boolean
|
2023-04-14 12:11:42 +02:00
|
|
|
hideAvatar: boolean
|
2023-07-19 11:20:44 +02:00
|
|
|
streamingMessageId: ChatChunkType['streamingMessageId']
|
2024-03-07 15:39:09 +01:00
|
|
|
isTransitionDisabled?: boolean
|
2023-01-27 10:54:59 +01:00
|
|
|
onNewBubbleDisplayed: (blockId: string) => Promise<void>
|
2023-05-12 16:37:04 -04:00
|
|
|
onScrollToBottom: (top?: number) => void
|
2023-12-13 10:22:02 +01:00
|
|
|
onSubmit: (input?: string) => void
|
2022-12-22 17:02:34 +01:00
|
|
|
onSkip: () => void
|
2023-01-26 15:26:42 +01:00
|
|
|
onAllBubblesDisplayed: () => void
|
2022-12-22 17:02:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export const ChatChunk = (props: Props) => {
|
2023-05-26 18:31:13 +02:00
|
|
|
let inputRef: HTMLDivElement | undefined
|
2024-03-07 15:39:09 +01:00
|
|
|
const [displayedMessageIndex, setDisplayedMessageIndex] = createSignal(
|
|
|
|
props.isTransitionDisabled ? props.messages.length : 0
|
|
|
|
)
|
2023-11-15 14:10:41 +01:00
|
|
|
const [lastBubbleOffsetTop, setLastBubbleOffsetTop] = createSignal<number>()
|
2022-12-22 17:02:34 +01:00
|
|
|
|
2023-01-25 11:27:47 +01:00
|
|
|
onMount(() => {
|
2023-07-19 11:20:44 +02:00
|
|
|
if (props.streamingMessageId) return
|
2023-01-26 15:26:42 +01:00
|
|
|
if (props.messages.length === 0) {
|
|
|
|
props.onAllBubblesDisplayed()
|
|
|
|
}
|
2023-05-26 18:31:13 +02:00
|
|
|
props.onScrollToBottom(
|
|
|
|
inputRef?.offsetTop ? inputRef?.offsetTop - 50 : undefined
|
|
|
|
)
|
2023-01-25 11:27:47 +01:00
|
|
|
})
|
|
|
|
|
2023-05-12 16:37:04 -04:00
|
|
|
const displayNextMessage = async (bubbleOffsetTop?: number) => {
|
2024-01-24 12:03:41 +01:00
|
|
|
if (
|
|
|
|
(props.settings.typingEmulation?.delayBetweenBubbles ??
|
|
|
|
defaultSettings.typingEmulation.delayBetweenBubbles) > 0 &&
|
|
|
|
displayedMessageIndex() < props.messages.length - 1
|
|
|
|
) {
|
|
|
|
await new Promise((resolve) =>
|
|
|
|
setTimeout(
|
|
|
|
resolve,
|
|
|
|
(props.settings.typingEmulation?.delayBetweenBubbles ??
|
|
|
|
defaultSettings.typingEmulation.delayBetweenBubbles) * 1000
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
2023-01-27 10:54:59 +01:00
|
|
|
const lastBubbleBlockId = props.messages[displayedMessageIndex()].id
|
|
|
|
await props.onNewBubbleDisplayed(lastBubbleBlockId)
|
2022-12-22 17:02:34 +01:00
|
|
|
setDisplayedMessageIndex(
|
|
|
|
displayedMessageIndex() === props.messages.length
|
|
|
|
? displayedMessageIndex()
|
|
|
|
: displayedMessageIndex() + 1
|
|
|
|
)
|
2023-05-12 16:37:04 -04:00
|
|
|
props.onScrollToBottom(bubbleOffsetTop)
|
2023-01-26 15:26:42 +01:00
|
|
|
if (displayedMessageIndex() === props.messages.length) {
|
2023-11-15 14:10:41 +01:00
|
|
|
setLastBubbleOffsetTop(bubbleOffsetTop)
|
2023-01-26 15:26:42 +01:00
|
|
|
props.onAllBubblesDisplayed()
|
|
|
|
}
|
2022-12-22 17:02:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2023-04-26 15:59:22 +02:00
|
|
|
<div class="flex flex-col w-full min-w-0 gap-2">
|
2023-04-26 17:54:00 +02:00
|
|
|
<Show when={props.messages.length > 0}>
|
|
|
|
<div class={'flex' + (isMobile() ? ' gap-1' : ' gap-2')}>
|
|
|
|
<Show
|
|
|
|
when={
|
2023-11-08 15:34:16 +01:00
|
|
|
(props.theme.chat?.hostAvatar?.isEnabled ??
|
|
|
|
defaultTheme.chat.hostAvatar.isEnabled) &&
|
2023-04-26 17:54:00 +02:00
|
|
|
props.messages.length > 0
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<AvatarSideContainer
|
2023-11-08 15:34:16 +01:00
|
|
|
hostAvatarSrc={props.theme.chat?.hostAvatar?.url}
|
2023-04-26 17:54:00 +02:00
|
|
|
hideAvatar={props.hideAvatar}
|
2024-03-07 15:39:09 +01:00
|
|
|
isTransitionDisabled={props.isTransitionDisabled}
|
2023-04-26 17:54:00 +02:00
|
|
|
/>
|
|
|
|
</Show>
|
|
|
|
|
|
|
|
<div
|
|
|
|
class="flex flex-col flex-1 gap-2"
|
|
|
|
style={{
|
2023-11-08 15:34:16 +01:00
|
|
|
'max-width':
|
|
|
|
props.theme.chat?.guestAvatar?.isEnabled ??
|
|
|
|
defaultTheme.chat.guestAvatar.isEnabled
|
|
|
|
? isMobile()
|
|
|
|
? 'calc(100% - 32px - 32px)'
|
|
|
|
: 'calc(100% - 48px - 48px)'
|
|
|
|
: isMobile()
|
|
|
|
? 'calc(100% - 32px)'
|
|
|
|
: 'calc(100% - 48px)',
|
2023-04-26 17:54:00 +02:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
<For each={props.messages.slice(0, displayedMessageIndex() + 1)}>
|
2024-01-24 12:03:41 +01:00
|
|
|
{(message, idx) => (
|
2023-04-26 17:54:00 +02:00
|
|
|
<HostBubble
|
|
|
|
message={message}
|
|
|
|
typingEmulation={props.settings.typingEmulation}
|
2024-01-24 12:03:41 +01:00
|
|
|
isTypingSkipped={
|
|
|
|
(props.settings.typingEmulation?.isDisabledOnFirstMessage ??
|
|
|
|
defaultSettings.typingEmulation
|
|
|
|
.isDisabledOnFirstMessage) &&
|
2024-03-07 15:39:09 +01:00
|
|
|
props.index === 0 &&
|
2024-01-24 12:03:41 +01:00
|
|
|
idx() === 0
|
|
|
|
}
|
2024-03-07 15:39:09 +01:00
|
|
|
onTransitionEnd={
|
|
|
|
props.isTransitionDisabled ? undefined : displayNextMessage
|
|
|
|
}
|
2023-12-13 10:22:02 +01:00
|
|
|
onCompleted={props.onSubmit}
|
2023-04-26 17:54:00 +02:00
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</For>
|
|
|
|
</div>
|
2023-04-26 15:59:22 +02:00
|
|
|
</div>
|
2023-04-26 17:54:00 +02:00
|
|
|
</Show>
|
2023-04-26 15:59:22 +02:00
|
|
|
{props.input && displayedMessageIndex() === props.messages.length && (
|
|
|
|
<InputChatBlock
|
2023-05-26 18:31:13 +02:00
|
|
|
ref={inputRef}
|
2023-04-26 15:59:22 +02:00
|
|
|
block={props.input}
|
2024-03-07 15:39:09 +01:00
|
|
|
chunkIndex={props.index}
|
2023-11-08 15:34:16 +01:00
|
|
|
hasHostAvatar={
|
|
|
|
props.theme.chat?.hostAvatar?.isEnabled ??
|
|
|
|
defaultTheme.chat.hostAvatar.isEnabled
|
|
|
|
}
|
|
|
|
guestAvatar={props.theme.chat?.guestAvatar}
|
2023-04-26 15:59:22 +02:00
|
|
|
context={props.context}
|
|
|
|
isInputPrefillEnabled={
|
2023-11-08 15:34:16 +01:00
|
|
|
props.settings.general?.isInputPrefillEnabled ??
|
|
|
|
defaultSettings.general.isInputPrefillEnabled
|
2023-04-26 15:59:22 +02:00
|
|
|
}
|
|
|
|
hasError={props.hasError}
|
2023-11-15 14:10:41 +01:00
|
|
|
onTransitionEnd={() => props.onScrollToBottom(lastBubbleOffsetTop())}
|
2023-08-07 15:22:05 +02:00
|
|
|
onSubmit={props.onSubmit}
|
|
|
|
onSkip={props.onSkip}
|
2023-04-26 15:59:22 +02:00
|
|
|
/>
|
|
|
|
)}
|
2023-07-19 11:20:44 +02:00
|
|
|
<Show when={props.streamingMessageId} keyed>
|
|
|
|
{(streamingMessageId) => (
|
|
|
|
<div class={'flex' + (isMobile() ? ' gap-1' : ' gap-2')}>
|
2023-11-08 15:34:16 +01:00
|
|
|
<Show
|
|
|
|
when={
|
|
|
|
props.theme.chat?.hostAvatar?.isEnabled ??
|
|
|
|
defaultTheme.chat.hostAvatar.isEnabled
|
|
|
|
}
|
|
|
|
>
|
2023-07-19 11:20:44 +02:00
|
|
|
<AvatarSideContainer
|
2023-11-08 15:34:16 +01:00
|
|
|
hostAvatarSrc={props.theme.chat?.hostAvatar?.url}
|
2023-07-19 11:20:44 +02:00
|
|
|
hideAvatar={props.hideAvatar}
|
|
|
|
/>
|
|
|
|
</Show>
|
|
|
|
|
|
|
|
<div
|
|
|
|
class="flex flex-col flex-1 gap-2"
|
|
|
|
style={{
|
2023-11-08 15:34:16 +01:00
|
|
|
'max-width':
|
|
|
|
props.theme.chat?.hostAvatar?.isEnabled ??
|
|
|
|
defaultTheme.chat.hostAvatar.isEnabled
|
|
|
|
? isMobile()
|
|
|
|
? 'calc(100% - 32px - 32px)'
|
|
|
|
: 'calc(100% - 48px - 48px)'
|
|
|
|
: isMobile()
|
|
|
|
? 'calc(100% - 32px)'
|
|
|
|
: 'calc(100% - 48px)',
|
2023-07-19 11:20:44 +02:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
<StreamingBubble streamingMessageId={streamingMessageId} />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</Show>
|
2022-12-22 17:02:34 +01:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|