Introduce bot v2 in builder (#328)

Also, the new engine is the default for updated typebots for viewer

Closes #211
This commit is contained in:
Baptiste Arnaud
2023-02-21 15:25:14 +01:00
committed by GitHub
parent 527dc8a5b1
commit debdac12ff
208 changed files with 4462 additions and 5236 deletions

View File

@@ -1,64 +1,49 @@
import {
chakra,
Modal,
ModalBody,
ModalContent,
ModalOverlay,
useColorModeValue,
useDisclosure,
} from '@chakra-ui/react'
import { TypebotViewer } from 'bot-engine'
import { chakra, useColorModeValue } from '@chakra-ui/react'
import { Popup } from '@typebot.io/react'
import { useUser } from '@/features/account'
import { AnswerInput, Typebot } from 'models'
import { Typebot } from 'models'
import React, { useEffect, useRef, useState } from 'react'
import { getViewerUrl, sendRequest } from 'utils'
import { sendRequest } from 'utils'
import confetti from 'canvas-confetti'
import { useToast } from '@/hooks/useToast'
import { parseTypebotToPublicTypebot } from '@/features/publish'
import { useRouter } from 'next/router'
type Props = { totalTypebots: number }
export const OnboardingModal = ({ totalTypebots }: Props) => {
const { push } = useRouter()
const botPath = useColorModeValue(
'/bots/onboarding.json',
'/bots/onboarding-dark.json'
)
const { user, updateUser } = useUser()
const { isOpen, onOpen, onClose } = useDisclosure()
const [typebot, setTypebot] = useState<Typebot>()
const confettiCanvaContainer = useRef<HTMLCanvasElement | null>(null)
const confettiCanon = useRef<confetti.CreateTypes>()
const [chosenCategories, setChosenCategories] = useState<string[]>([])
const [openedOnce, setOpenedOnce] = useState(false)
const { showToast } = useToast()
useEffect(() => {
fetchTemplate()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const isNewUser =
user &&
new Date(user?.createdAt as unknown as string).toDateString() ===
new Date().toDateString() &&
totalTypebots === 0
useEffect(() => {
if (openedOnce) return
const isNewUser =
user &&
new Date(user?.createdAt as unknown as string).toDateString() ===
new Date().toDateString() &&
totalTypebots === 0
if (isNewUser) {
onOpen()
setOpenedOnce(true)
const fetchTemplate = async () => {
const { data, error } = await sendRequest(botPath)
if (error)
return showToast({ title: error.name, description: error.message })
setTypebot(data as Typebot)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [user])
fetchTemplate()
}, [botPath, showToast])
useEffect(() => {
initConfettis()
return () => {
window.removeEventListener('message', handleIncomingMessage)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [confettiCanvaContainer.current])
}, [])
const initConfettis = () => {
if (!confettiCanvaContainer.current || confettiCanon.current) return
@@ -66,48 +51,41 @@ export const OnboardingModal = ({ totalTypebots }: Props) => {
resize: true,
useWorker: true,
})
window.addEventListener('message', handleIncomingMessage)
}
const handleIncomingMessage = (message: MessageEvent) => {
if (message.data.from === 'typebot') {
if (message.data.action === 'shootConfettis' && confettiCanon.current)
shootConfettis(confettiCanon.current)
}
const handleBotEnd = () => {
setTimeout(() => {
push('/typebots/create', { query: { isFirstBot: true } })
}, 2000)
}
const fetchTemplate = async () => {
const { data, error } = await sendRequest(botPath)
if (error)
return showToast({ title: error.name, description: error.message })
setTypebot(data as Typebot)
}
const handleNewAnswer = async (answer: AnswerInput) => {
const isName = answer.variableId === 'cl126f4hf000i2e6d8zvzc3t1'
const isCompany = answer.variableId === 'cl126jqww000w2e6dq9yv4ifq'
const isCategories = answer.variableId === 'cl126mo3t001b2e6dvyi16bkd'
const isOtherCategories = answer.variableId === 'cl126q38p001q2e6d0hj23f6b'
if (isName) updateUser({ name: answer.content })
if (isCompany) updateUser({ company: answer.content })
const handleNewAnswer = async (answer: {
message: string
blockId: string
}) => {
const isName = answer.blockId === 'cl126820m000g2e6dfleq78bt'
const isCompany = answer.blockId === 'cl126jioz000v2e6dwrk1f2cb'
const isCategories = answer.blockId === 'cl126lb8v00142e6duv5qe08l'
const isOtherCategories = answer.blockId === 'cl126pv7n001o2e6dajltc4qz'
const answeredAllQuestions =
isOtherCategories || (isCategories && !answer.message.includes('Other'))
if (answeredAllQuestions && confettiCanon.current)
shootConfettis(confettiCanon.current)
if (isName) updateUser({ name: answer.message })
if (isCompany) updateUser({ company: answer.message })
if (isCategories) {
const onboardingCategories = answer.content.split(', ')
const onboardingCategories = answer.message.split(', ')
updateUser({ onboardingCategories })
setChosenCategories(onboardingCategories)
}
if (isOtherCategories)
updateUser({
onboardingCategories: [...chosenCategories, answer.content],
onboardingCategories: [...chosenCategories, answer.message],
})
}
return (
<Modal
size="3xl"
isOpen={isOpen}
onClose={onClose}
blockScrollOnMount={false}
>
<>
<chakra.canvas
ref={confettiCanvaContainer}
pos="fixed"
@@ -118,23 +96,18 @@ export const OnboardingModal = ({ totalTypebots }: Props) => {
zIndex={9999}
pointerEvents="none"
/>
<ModalOverlay />
<ModalContent h="85vh">
<ModalBody p="10">
{typebot && (
<TypebotViewer
apiHost={getViewerUrl()}
typebot={parseTypebotToPublicTypebot(typebot)}
predefinedVariables={{
Name: user?.name?.split(' ')[0] ?? undefined,
}}
onNewAnswer={handleNewAnswer}
style={{ borderRadius: '0.25rem' }}
/>
)}
</ModalBody>
</ModalContent>
</Modal>
{typebot && (
<Popup
typebot={typebot}
prefilledVariables={{
Name: user?.name?.split(' ')[0] ?? undefined,
}}
defaultOpen={isNewUser}
onAnswer={handleNewAnswer}
onEnd={handleBotEnd}
/>
)}
</>
)
}