2
0
Files
bot/packages/bot-engine/src/components/ChatBlock/ChatBlock.tsx
2022-01-22 18:24:57 +01:00

125 lines
3.6 KiB
TypeScript

import React, { useEffect, useState } from 'react'
import { animateScroll as scroll } from 'react-scroll'
import { TransitionGroup, CSSTransition } from 'react-transition-group'
import { ChatStep } from './ChatStep'
import { AvatarSideContainer } from './AvatarSideContainer'
import { HostAvatarsContext } from '../../contexts/HostAvatarsContext'
import { Step } from 'models'
import { useTypebot } from '../../contexts/TypebotContext'
import {
isBubbleStep,
isChoiceInput,
isInputStep,
isIntegrationStep,
isLogicStep,
} from 'utils'
import { executeLogic } from 'services/logic'
import { getSingleChoiceTargetId } from 'services/inputs'
import { executeIntegration } from 'services/integration'
type ChatBlockProps = {
stepIds: string[]
startStepId?: string
onBlockEnd: (edgeId?: string) => void
}
export const ChatBlock = ({
stepIds,
startStepId,
onBlockEnd,
}: ChatBlockProps) => {
const { typebot, updateVariableValue } = useTypebot()
const [displayedSteps, setDisplayedSteps] = useState<Step[]>([])
useEffect(() => {
const nextStep =
typebot.steps.byId[startStepId ?? stepIds[displayedSteps.length]]
if (nextStep) setDisplayedSteps([...displayedSteps, nextStep])
}, [])
useEffect(() => {
autoScrollToBottom()
onNewStepDisplayed()
}, [displayedSteps])
const onNewStepDisplayed = async () => {
const currentStep = [...displayedSteps].pop()
if (!currentStep) return
if (isLogicStep(currentStep)) {
const nextEdgeId = executeLogic(
currentStep,
typebot.variables,
updateVariableValue
)
nextEdgeId ? onBlockEnd(nextEdgeId) : displayNextStep()
}
if (isIntegrationStep(currentStep)) {
const nextEdgeId = await executeIntegration(
typebot.typebotId,
currentStep,
typebot.variables,
updateVariableValue
)
nextEdgeId ? onBlockEnd(nextEdgeId) : displayNextStep()
}
}
const autoScrollToBottom = () => {
scroll.scrollToBottom({
duration: 500,
containerId: 'scrollable-container',
})
}
const displayNextStep = (answerContent?: string) => {
const currentStep = [...displayedSteps].pop()
if (currentStep) {
if (
isInputStep(currentStep) &&
currentStep.options?.variableId &&
answerContent
) {
updateVariableValue(currentStep.options.variableId, answerContent)
}
const isSingleChoiceStep =
isChoiceInput(currentStep) && !currentStep.options.isMultipleChoice
if (isSingleChoiceStep)
return onBlockEnd(
getSingleChoiceTargetId(
currentStep,
typebot.choiceItems,
answerContent
)
)
if (currentStep?.edgeId || displayedSteps.length === stepIds.length)
return onBlockEnd(currentStep.edgeId)
}
const nextStep = typebot.steps.byId[stepIds[displayedSteps.length]]
if (nextStep) setDisplayedSteps([...displayedSteps, nextStep])
}
return (
<div className="flex">
<HostAvatarsContext>
<AvatarSideContainer />
<div className="flex flex-col w-full">
<TransitionGroup>
{displayedSteps
.filter((step) => isInputStep(step) || isBubbleStep(step))
.map((step) => (
<CSSTransition
key={step.id}
classNames="bubble"
timeout={500}
unmountOnExit
>
<ChatStep step={step} onTransitionEnd={displayNextStep} />
</CSSTransition>
))}
</TransitionGroup>
</div>
</HostAvatarsContext>
</div>
)
}