2
0

(embed) Option to add a wait event for the embed bubble

Closes #1590
This commit is contained in:
Baptiste Arnaud
2024-06-19 15:27:45 +02:00
parent 4ab1803d39
commit 918836d6cf
20 changed files with 200 additions and 46 deletions

View File

@ -1,11 +1,12 @@
import { TRPCError } from '@trpc/server'
import { isDefined, isNotDefined } from '@typebot.io/lib/utils'
import { isDefined, isNotDefined, isNotEmpty } from '@typebot.io/lib/utils'
import { getSession } from '../queries/getSession'
import { continueBotFlow } from '../continueBotFlow'
import { filterPotentiallySensitiveLogs } from '../logs/filterPotentiallySensitiveLogs'
import { parseDynamicTheme } from '../parseDynamicTheme'
import { saveStateToDatabase } from '../saveStateToDatabase'
import { computeCurrentProgress } from '../computeCurrentProgress'
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
type Props = {
origin: string | undefined
@ -77,8 +78,11 @@ export const continueChat = async ({
clientSideActions,
visitedEdges,
setVariableHistory,
hasCustomEmbedBubble: messages.some(
(message) => message.type === 'custom-embed'
hasEmbedBubbleWithWaitEvent: messages.some(
(message) =>
message.type === 'custom-embed' ||
(message.type === BubbleBlockType.EMBED &&
message.content.waitForEvent?.isEnabled)
),
})

View File

@ -1,8 +1,10 @@
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
import { computeCurrentProgress } from '../computeCurrentProgress'
import { filterPotentiallySensitiveLogs } from '../logs/filterPotentiallySensitiveLogs'
import { restartSession } from '../queries/restartSession'
import { saveStateToDatabase } from '../saveStateToDatabase'
import { startSession } from '../startSession'
import { isNotEmpty } from '@typebot.io/lib'
type Props = {
origin: string | undefined
@ -74,8 +76,11 @@ export const startChat = async ({
clientSideActions,
visitedEdges,
setVariableHistory,
hasCustomEmbedBubble: messages.some(
(message) => message.type === 'custom-embed'
hasEmbedBubbleWithWaitEvent: messages.some(
(message) =>
message.type === 'custom-embed' ||
(message.type === BubbleBlockType.EMBED &&
message.content.waitForEvent?.isEnabled)
),
})

View File

@ -3,6 +3,7 @@ import { restartSession } from '../queries/restartSession'
import { saveStateToDatabase } from '../saveStateToDatabase'
import { startSession } from '../startSession'
import { computeCurrentProgress } from '../computeCurrentProgress'
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
type Props = {
message?: string
@ -69,8 +70,11 @@ export const startChatPreview = async ({
clientSideActions,
visitedEdges,
setVariableHistory,
hasCustomEmbedBubble: messages.some(
(message) => message.type === 'custom-embed'
hasEmbedBubbleWithWaitEvent: messages.some(
(message) =>
message.type === 'custom-embed' ||
(message.type === BubbleBlockType.EMBED &&
message.content.waitForEvent?.isEnabled)
),
initialSessionId: sessionId,
})

View File

@ -145,6 +145,13 @@ export const continueBotFlow = async (
}
}
}
} else if (
block.type === BubbleBlockType.EMBED &&
block.content?.waitForEvent?.saveDataInVariableId
) {
variableToUpdate = state.typebotsQueue[0].typebot.variables.find(
(v) => v.id === block.content?.waitForEvent?.saveDataInVariableId
)
}
if (variableToUpdate) {

View File

@ -6,7 +6,7 @@ import {
SessionState,
SetVariableHistoryItem,
} from '@typebot.io/schemas'
import { isNotEmpty } from '@typebot.io/lib'
import { isEmpty, isNotEmpty } from '@typebot.io/lib'
import {
isBubbleBlock,
isInputBlock,
@ -32,6 +32,7 @@ import {
BubbleBlockWithDefinedContent,
parseBubbleBlock,
} from './parseBubbleBlock'
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
type ContextProps = {
version: 1 | 2
@ -95,14 +96,30 @@ export const executeGroup = async (
if (isBubbleBlock(block)) {
if (!block.content || (firstBubbleWasStreamed && index === 0)) continue
messages.push(
parseBubbleBlock(block as BubbleBlockWithDefinedContent, {
version,
variables: newSessionState.typebotsQueue[0].typebot.variables,
typebotVersion: newSessionState.typebotsQueue[0].typebot.version,
textBubbleContentFormat,
})
)
const message = parseBubbleBlock(block as BubbleBlockWithDefinedContent, {
version,
variables: newSessionState.typebotsQueue[0].typebot.variables,
typebotVersion: newSessionState.typebotsQueue[0].typebot.version,
textBubbleContentFormat,
})
messages.push(message)
if (
message.type === BubbleBlockType.EMBED &&
message.content.waitForEvent?.isEnabled
) {
return {
messages,
newSessionState: {
...newSessionState,
currentBlockId: block.id,
},
clientSideActions,
logs,
visitedEdges,
setVariableHistory,
}
}
lastBubbleBlockId = block.id
continue
}

View File

@ -17,7 +17,7 @@ type Props = {
clientSideActions: ContinueChatResponse['clientSideActions']
visitedEdges: VisitedEdge[]
setVariableHistory: SetVariableHistoryItem[]
hasCustomEmbedBubble?: boolean
hasEmbedBubbleWithWaitEvent?: boolean
initialSessionId?: string
}
@ -28,7 +28,7 @@ export const saveStateToDatabase = async ({
clientSideActions,
visitedEdges,
setVariableHistory,
hasCustomEmbedBubble,
hasEmbedBubbleWithWaitEvent,
initialSessionId,
}: Props) => {
const containsSetVariableClientSideAction = clientSideActions?.some(
@ -36,7 +36,9 @@ export const saveStateToDatabase = async ({
)
const isCompleted = Boolean(
!input && !containsSetVariableClientSideAction && !hasCustomEmbedBubble
!input &&
!containsSetVariableClientSideAction &&
!hasEmbedBubbleWithWaitEvent
)
const queries: Prisma.PrismaPromise<any>[] = []

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/js",
"version": "0.2.88",
"version": "0.2.89",
"description": "Javascript library to display typebots on your website",
"type": "module",
"main": "dist/index.js",

View File

@ -51,6 +51,7 @@ export const HostBubble = (props: Props) => (
<EmbedBubble
content={props.message.content as EmbedBubbleBlock['content']}
onTransitionEnd={props.onTransitionEnd}
onCompleted={props.onCompleted}
/>
</Match>
<Match when={props.message.type === 'custom-embed'}>

View File

@ -4,10 +4,12 @@ import { createSignal, onCleanup, onMount } from 'solid-js'
import { clsx } from 'clsx'
import { EmbedBubbleBlock } from '@typebot.io/schemas'
import { defaultEmbedBubbleContent } from '@typebot.io/schemas/features/blocks/bubbles/embed/constants'
import { isNotEmpty } from '@typebot.io/lib/utils'
type Props = {
content: EmbedBubbleBlock['content']
onTransitionEnd?: (ref?: HTMLDivElement) => void
onCompleted?: (data?: string) => void
}
let typingTimeout: NodeJS.Timeout
@ -20,9 +22,29 @@ export const EmbedBubble = (props: Props) => {
props.onTransitionEnd ? true : false
)
const handleMessage = (
event: MessageEvent<{ name?: string; data?: string }>
) => {
if (
props.content?.waitForEvent?.isEnabled &&
isNotEmpty(event.data.name) &&
event.data.name === props.content?.waitForEvent.name
) {
props.onCompleted?.(
props.content.waitForEvent.saveDataInVariableId && event.data.data
? event.data.data
: undefined
)
window.removeEventListener('message', handleMessage)
}
}
onMount(() => {
typingTimeout = setTimeout(() => {
setIsTyping(false)
if (props.content?.waitForEvent?.isEnabled) {
window.addEventListener('message', handleMessage)
}
setTimeout(() => {
props.onTransitionEnd?.(ref)
}, showAnimationDuration)
@ -31,6 +53,7 @@ export const EmbedBubble = (props: Props) => {
onCleanup(() => {
if (typingTimeout) clearTimeout(typingTimeout)
window.removeEventListener('message', handleMessage)
})
return (

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/nextjs",
"version": "0.2.88",
"version": "0.2.89",
"description": "Convenient library to display typebots on your Next.js website",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/react",
"version": "0.2.88",
"version": "0.2.89",
"description": "Convenient library to display typebots on your React app",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -6,6 +6,13 @@ import { BubbleBlockType } from '../constants'
export const embedBubbleContentSchema = z.object({
url: z.string().optional(),
height: z.number().or(variableStringSchema).optional(),
waitForEvent: z
.object({
isEnabled: z.boolean().optional(),
name: z.string().optional(),
saveDataInVariableId: z.string().optional(),
})
.optional(),
})
export const embedBubbleBlockSchema = blockBaseSchema.merge(

View File

@ -16,7 +16,6 @@ import {
import { logSchema } from '../result'
import { settingsSchema, themeSchema } from '../typebot'
import {
textBubbleContentSchema,
imageBubbleContentSchema,
videoBubbleContentSchema,
audioBubbleContentSchema,