2
0
Files
bot/packages/bot-engine/src/components/ConversationContainer.tsx

157 lines
4.9 KiB
TypeScript
Raw Normal View History

import React, { useEffect, useRef, useState } from 'react'
2022-06-11 07:27:38 +02:00
import { ChatGroup } from './ChatGroup'
2021-12-23 09:37:42 +01:00
import { useFrame } from 'react-frame-component'
import { setCssVariablesValue } from '../services/theme'
import { useAnswers } from '../contexts/AnswersContext'
2022-06-11 07:27:38 +02:00
import { Group, Edge, PublicTypebot, Theme, VariableWithValue } from 'models'
import { byId, isDefined, isNotDefined } from 'utils'
import { animateScroll as scroll } from 'react-scroll'
2022-03-09 15:12:00 +01:00
import { LinkedTypebot, useTypebot } from 'contexts/TypebotContext'
import { ChatContext } from 'contexts/ChatContext'
type Props = {
2022-02-10 10:25:38 +01:00
theme: Theme
2022-03-23 09:56:39 +01:00
predefinedVariables?: { [key: string]: string | undefined }
2022-06-11 07:27:38 +02:00
startGroupId?: string
onNewGroupVisible: (edge: Edge) => void
onCompleted: () => void
}
export const ConversationContainer = ({
2022-02-10 10:25:38 +01:00
theme,
2022-03-23 09:56:39 +01:00
predefinedVariables,
2022-06-11 07:27:38 +02:00
startGroupId,
onNewGroupVisible,
onCompleted,
}: Props) => {
const {
typebot,
updateVariableValue,
linkedBotQueue,
popEdgeIdFromLinkedTypebotQueue,
} = useTypebot()
2021-12-23 09:37:42 +01:00
const { document: frameDocument } = useFrame()
2022-06-11 07:27:38 +02:00
const [displayedGroups, setDisplayedGroups] = useState<
{ group: Group; startBlockIndex: number }[]
2022-01-15 17:30:20 +01:00
>([])
const { updateVariables } = useAnswers()
const bottomAnchor = useRef<HTMLDivElement | null>(null)
const scrollableContainer = useRef<HTMLDivElement | null>(null)
2022-06-11 07:27:38 +02:00
const displayNextGroup = ({
edgeId,
updatedTypebot,
2022-06-11 07:27:38 +02:00
groupId,
}: {
edgeId?: string
2022-06-11 07:27:38 +02:00
groupId?: string
2022-03-09 15:12:00 +01:00
updatedTypebot?: PublicTypebot | LinkedTypebot
}) => {
2022-03-09 15:12:00 +01:00
const currentTypebot = updatedTypebot ?? typebot
2022-06-11 07:27:38 +02:00
if (groupId) {
const nextGroup = currentTypebot.groups.find(byId(groupId))
if (!nextGroup) return
onNewGroupVisible({
id: 'edgeId',
2022-06-11 07:27:38 +02:00
from: { groupId: 'block', blockId: 'block' },
to: { groupId },
})
2022-06-11 07:27:38 +02:00
return setDisplayedGroups([
...displayedGroups,
{ group: nextGroup, startBlockIndex: 0 },
])
}
2022-03-09 15:12:00 +01:00
const nextEdge = currentTypebot.edges.find(byId(edgeId))
if (!nextEdge) {
if (linkedBotQueue.length > 0) {
const nextEdgeId = linkedBotQueue[0].edgeId
popEdgeIdFromLinkedTypebotQueue()
2022-06-11 07:27:38 +02:00
displayNextGroup({ edgeId: nextEdgeId })
}
return onCompleted()
}
2022-06-11 07:27:38 +02:00
const nextGroup = currentTypebot.groups.find(byId(nextEdge.to.groupId))
if (!nextGroup) return onCompleted()
const startBlockIndex = nextEdge.to.blockId
? nextGroup.blocks.findIndex(byId(nextEdge.to.blockId))
: 0
2022-06-11 07:27:38 +02:00
onNewGroupVisible(nextEdge)
setDisplayedGroups([
...displayedGroups,
{ group: nextGroup, startBlockIndex },
])
}
useEffect(() => {
if (
isDefined(predefinedVariables) &&
Object.keys(predefinedVariables).length > 0
) {
const prefilledVariables = injectPredefinedVariables(predefinedVariables)
updateVariables(prefilledVariables)
}
2022-06-11 07:27:38 +02:00
displayNextGroup({
edgeId: startGroupId
? undefined
2022-06-11 07:27:38 +02:00
: typebot.groups[0].blocks[0].outgoingEdgeId,
groupId: startGroupId,
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [predefinedVariables])
const injectPredefinedVariables = (predefinedVariables: {
2022-03-23 09:56:39 +01:00
[key: string]: string | undefined
}) => {
const prefilledVariables: VariableWithValue[] = []
Object.keys(predefinedVariables).forEach((key) => {
const matchingVariable = typebot.variables.find(
(v) => v.name.toLowerCase() === key.toLowerCase()
)
if (!predefinedVariables || isNotDefined(matchingVariable)) return
2022-03-23 09:56:39 +01:00
const value = predefinedVariables[key]
if (!value) return
updateVariableValue(matchingVariable?.id, value)
prefilledVariables.push({ ...matchingVariable, value })
})
return prefilledVariables
}
2021-12-23 09:37:42 +01:00
useEffect(() => {
if (!frameDocument) return
2022-02-10 10:25:38 +01:00
setCssVariablesValue(theme, frameDocument.body.style)
}, [theme, frameDocument])
2021-12-23 09:37:42 +01:00
const autoScrollToBottom = () => {
if (!scrollableContainer.current) return
setTimeout(() => {
scroll.scrollToBottom({
duration: 500,
container: scrollableContainer.current,
})
}, 1)
}
return (
<div
ref={scrollableContainer}
className="overflow-y-scroll w-full lg:w-3/4 min-h-full rounded lg:px-5 px-3 pt-10 relative scrollable-container typebot-chat-view"
>
<ChatContext onScroll={autoScrollToBottom}>
2022-06-11 07:27:38 +02:00
{displayedGroups.map((displayedGroup, idx) => (
<ChatGroup
key={displayedGroup.group.id + idx}
blocks={displayedGroup.group.blocks}
startBlockIndex={displayedGroup.startBlockIndex}
onGroupEnd={displayNextGroup}
groupTitle={displayedGroup.group.title}
keepShowingHostAvatar={idx === displayedGroups.length - 1}
/>
))}
</ChatContext>
{/* We use a block to simulate padding because it makes iOS scroll flicker */}
<div className="w-full h-32" ref={bottomAnchor} />
</div>
)
}