2
0

🧰 Aggregate utils & set up results collection in viewer

This commit is contained in:
Baptiste Arnaud
2021-12-29 10:22:26 +01:00
parent 447172d0cb
commit f088f694b9
42 changed files with 404 additions and 141 deletions

View File

@ -8,7 +8,7 @@ import { HostAvatarsContext } from '../../contexts/HostAvatarsContext'
type ChatBlockProps = {
block: Block
onBlockEnd: (nextBlockId: string) => void
onBlockEnd: (nextBlockId?: string) => void
}
export const ChatBlock = ({ block, onBlockEnd }: ChatBlockProps) => {
@ -31,7 +31,10 @@ export const ChatBlock = ({ block, onBlockEnd }: ChatBlockProps) => {
const displayNextStep = () => {
const currentStep = [...displayedSteps].pop()
if (currentStep?.target?.blockId)
if (
currentStep?.target?.blockId ||
displayedSteps.length === block.steps.length
)
return onBlockEnd(currentStep?.target?.blockId)
const nextStep = block.steps[displayedSteps.length]
if (nextStep) setDisplayedSteps([...displayedSteps, nextStep])

View File

@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react'
import { useAnswers } from '../../../contexts/AnswersContext'
import { useHostAvatars } from '../../../contexts/HostAvatarsContext'
import { Step } from '../../../models'
import { isTextInputStep, isTextStep } from '../../../services/utils'
@ -13,13 +14,21 @@ export const ChatStep = ({
step: Step
onTransitionEnd: () => void
}) => {
const { addAnswer } = useAnswers()
const handleInputSubmit = (content: string) => {
addAnswer({ stepId: step.id, blockId: step.blockId, content })
onTransitionEnd()
}
if (isTextStep(step))
return <HostMessageBubble step={step} onTransitionEnd={onTransitionEnd} />
if (isTextInputStep(step)) return <InputChatStep onSubmit={onTransitionEnd} />
if (isTextInputStep(step))
return <InputChatStep onSubmit={handleInputSubmit} />
return <span>No step</span>
}
const InputChatStep = ({ onSubmit }: { onSubmit: () => void }) => {
const InputChatStep = ({ onSubmit }: { onSubmit: (value: string) => void }) => {
const { addNewAvatarOffset } = useHostAvatars()
const [answer, setAnswer] = useState<string>()
@ -29,7 +38,7 @@ const InputChatStep = ({ onSubmit }: { onSubmit: () => void }) => {
const handleSubmit = (value: string) => {
setAnswer(value)
onSubmit()
onSubmit(value)
}
if (answer) {

View File

@ -1,28 +1,36 @@
import React, { useEffect, useRef, useState } from 'react'
import { PublicTypebot } from '..'
import { Answer, PublicTypebot } from '..'
import { Block } from '..'
import { ChatBlock } from './ChatBlock/ChatBlock'
import { useFrame } from 'react-frame-component'
import { setCssVariablesValue } from '../services/theme'
import { useAnswers } from '../contexts/AnswersContext'
import { deepEqual } from 'fast-equals'
type Props = {
typebot: PublicTypebot
onNewBlockVisible: (blockId: string) => void
onAnswersUpdate: (answers: Answer[]) => void
onCompleted: () => void
}
export const ConversationContainer = ({
typebot,
onNewBlockVisisble,
}: {
typebot: PublicTypebot
onNewBlockVisisble: (blockId: string) => void
}) => {
onNewBlockVisible,
onAnswersUpdate,
onCompleted,
}: Props) => {
const { document: frameDocument } = useFrame()
const [displayedBlocks, setDisplayedBlocks] = useState<Block[]>([])
const [isConversationEnded, setIsConversationEnded] = useState(false)
const [localAnswers, setLocalAnswers] = useState<Answer[]>([])
const { answers } = useAnswers()
const bottomAnchor = useRef<HTMLDivElement | null>(null)
const displayNextBlock = (blockId: string) => {
const displayNextBlock = (blockId?: string) => {
if (!blockId) return onCompleted()
const nextBlock = typebot.blocks.find((b) => b.id === blockId)
if (!nextBlock) return
onNewBlockVisisble(blockId)
if (!nextBlock) return onCompleted()
onNewBlockVisible(blockId)
setDisplayedBlocks([...displayedBlocks, nextBlock])
}
@ -35,6 +43,12 @@ export const ConversationContainer = ({
setCssVariablesValue(typebot.theme, frameDocument.body.style)
}, [typebot.theme, frameDocument])
useEffect(() => {
if (deepEqual(localAnswers, answers)) return
setLocalAnswers(answers)
onAnswersUpdate(answers)
}, [answers])
return (
<div
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"
@ -48,14 +62,7 @@ export const ConversationContainer = ({
/>
))}
{/* We use a block to simulate padding because it makes iOS scroll flicker */}
<div
className="w-full"
ref={bottomAnchor}
style={{
transition: isConversationEnded ? 'height 1s' : '',
height: isConversationEnded ? '5%' : '20%',
}}
/>
<div className="w-full" ref={bottomAnchor} />
</div>
)
}

View File

@ -1,19 +1,23 @@
import React, { useMemo } from 'react'
import { BackgroundType, PublicTypebot } from '../models'
import { Answer, BackgroundType, PublicTypebot } from '../models'
import { TypebotContext } from '../contexts/TypebotContext'
import Frame from 'react-frame-component'
//@ts-ignore
import style from '../assets/style.css'
import { ConversationContainer } from './ConversationContainer'
import { ResultContext } from '../contexts/ResultsContext'
import { AnswersContext } from '../contexts/AnswersContext'
export type TypebotViewerProps = {
typebot: PublicTypebot
onNewBlockVisisble?: (blockId: string) => void
onNewBlockVisible?: (blockId: string) => void
onAnswersUpdate?: (answers: Answer[]) => void
onCompleted?: () => void
}
export const TypebotViewer = ({
typebot,
onNewBlockVisisble,
onNewBlockVisible,
onAnswersUpdate,
onCompleted,
}: TypebotViewerProps) => {
const containerBgColor = useMemo(
() =>
@ -23,7 +27,13 @@ export const TypebotViewer = ({
[typebot.theme.general.background]
)
const handleNewBlockVisible = (blockId: string) => {
if (onNewBlockVisisble) onNewBlockVisisble(blockId)
if (onNewBlockVisible) onNewBlockVisible(blockId)
}
const handleAnswersUpdate = (answers: Answer[]) => {
if (onAnswersUpdate) onAnswersUpdate(answers)
}
const handleCompleted = () => {
if (onCompleted) onCompleted()
}
return (
@ -38,22 +48,24 @@ export const TypebotViewer = ({
}}
/>
<TypebotContext typebot={typebot}>
<ResultContext typebotId={typebot.id}>
<AnswersContext typebotId={typebot.id}>
<div
className="flex text-base overflow-hidden bg-cover h-screen w-screen flex-col items-center typebot-container"
style={{
// We set this as inline style to avoid color for SSR
// We set this as inline style to avoid color flash for SSR
backgroundColor: containerBgColor,
}}
>
<div className="flex w-full h-full justify-center">
<ConversationContainer
typebot={typebot}
onNewBlockVisisble={handleNewBlockVisible}
onNewBlockVisible={handleNewBlockVisible}
onAnswersUpdate={handleAnswersUpdate}
onCompleted={handleCompleted}
/>
</div>
</div>
</ResultContext>
</AnswersContext>
</TypebotContext>
</Frame>
)