2
0

feat(inputs): Add Set variable step

This commit is contained in:
Baptiste Arnaud
2022-01-14 07:49:24 +01:00
parent 13f72f5ff7
commit 4ccb7bca49
55 changed files with 1024 additions and 223 deletions

View File

@ -4,9 +4,19 @@ import { TransitionGroup, CSSTransition } from 'react-transition-group'
import { ChatStep } from './ChatStep'
import { AvatarSideContainer } from './AvatarSideContainer'
import { HostAvatarsContext } from '../../contexts/HostAvatarsContext'
import { ChoiceInputStep, Step } from 'models'
import { ChoiceInputStep, LogicStep, Step } from 'models'
import { useTypebot } from '../../contexts/TypebotContext'
import { isChoiceInput } from 'utils'
import {
isChoiceInput,
isInputStep,
isLogicStep,
isTextBubbleStep,
} from 'utils'
import {
evaluateExpression,
isMathFormula,
parseVariables,
} from 'services/variable'
type ChatBlockProps = {
stepIds: string[]
@ -14,15 +24,20 @@ type ChatBlockProps = {
}
export const ChatBlock = ({ stepIds, onBlockEnd }: ChatBlockProps) => {
const { typebot } = useTypebot()
const { typebot, updateVariableValue } = useTypebot()
const [displayedSteps, setDisplayedSteps] = useState<Step[]>([])
useEffect(() => {
setDisplayedSteps([typebot.steps.byId[stepIds[0]]])
displayNextStep()
}, [])
useEffect(() => {
autoScrollToBottom()
const currentStep = [...displayedSteps].pop()
if (currentStep && isLogicStep(currentStep)) {
executeLogic(currentStep)
displayNextStep()
}
}, [displayedSteps])
const autoScrollToBottom = () => {
@ -34,20 +49,37 @@ export const ChatBlock = ({ stepIds, onBlockEnd }: ChatBlockProps) => {
const displayNextStep = (answerContent?: string) => {
const currentStep = [...displayedSteps].pop()
if (!currentStep) throw new Error('currentStep should exist')
const isSingleChoiceStep =
isChoiceInput(currentStep) && !currentStep.options.isMultipleChoice
if (isSingleChoiceStep)
return onBlockEnd(getSingleChoiceTargetId(currentStep, answerContent))
if (
currentStep?.target?.blockId ||
displayedSteps.length === stepIds.length
)
return onBlockEnd(currentStep?.target?.blockId)
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, answerContent))
if (
currentStep?.target?.blockId ||
displayedSteps.length === stepIds.length
)
return onBlockEnd(currentStep?.target?.blockId)
}
const nextStep = typebot.steps.byId[stepIds[displayedSteps.length]]
if (nextStep) setDisplayedSteps([...displayedSteps, nextStep])
}
const executeLogic = (step: LogicStep) => {
if (!step.options?.variableId || !step.options.expressionToEvaluate) return
const expression = step.options.expressionToEvaluate
const evaluatedExpression = isMathFormula(expression)
? evaluateExpression(parseVariables(expression, typebot.variables))
: expression
updateVariableValue(step.options.variableId, evaluatedExpression)
}
const getSingleChoiceTargetId = (
currentStep: ChoiceInputStep,
answerContent?: string
@ -68,16 +100,18 @@ export const ChatBlock = ({ stepIds, onBlockEnd }: ChatBlockProps) => {
<AvatarSideContainer />
<div className="flex flex-col w-full">
<TransitionGroup>
{displayedSteps.map((step) => (
<CSSTransition
key={step.id}
classNames="bubble"
timeout={500}
unmountOnExit
>
<ChatStep step={step} onTransitionEnd={displayNextStep} />
</CSSTransition>
))}
{displayedSteps
.filter((step) => isInputStep(step) || isTextBubbleStep(step))
.map((step) => (
<CSSTransition
key={step.id}
classNames="bubble"
timeout={500}
unmountOnExit
>
<ChatStep step={step} onTransitionEnd={displayNextStep} />
</CSSTransition>
))}
</TransitionGroup>
</div>
</HostAvatarsContext>

View File

@ -1,9 +1,10 @@
import React, { useEffect, useRef, useState } from 'react'
import { useHostAvatars } from '../../../../contexts/HostAvatarsContext'
import { useTypebot } from '../../../../contexts/TypebotContext'
import { BubbleStepType, StepType, TextStep } from 'models'
import { computeTypingTimeout } from '../../../../services/chat'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { useHostAvatars } from 'contexts/HostAvatarsContext'
import { useTypebot } from 'contexts/TypebotContext'
import { BubbleStepType, TextStep } from 'models'
import { computeTypingTimeout } from 'services/chat'
import { TypingContent } from './TypingContent'
import { parseVariables } from 'services/variable'
type HostMessageBubbleProps = {
step: TextStep
@ -24,6 +25,11 @@ export const HostMessageBubble = ({
const messageContainer = useRef<HTMLDivElement | null>(null)
const [isTyping, setIsTyping] = useState(true)
const content = useMemo(
() => parseVariables(step.content.html, typebot.variables),
[typebot.variables]
)
useEffect(() => {
sendAvatarOffset()
const typingTimeout = computeTypingTimeout(
@ -72,7 +78,7 @@ export const HostMessageBubble = ({
(isTyping ? 'opacity-0 h-6' : 'opacity-100 h-full')
}
dangerouslySetInnerHTML={{
__html: step.content.html,
__html: content,
}}
/>
)}