2
0

(setVariable) Add client-side set variable execution

Closes #461
This commit is contained in:
Baptiste Arnaud
2023-04-14 12:11:42 +02:00
parent 397a33afc6
commit 03cc067418
17 changed files with 207 additions and 69 deletions

View File

@ -2,7 +2,7 @@ import { createSignal, onCleanup, onMount } from 'solid-js'
import { isMobile } from '@/utils/isMobileSignal'
import { Avatar } from '../avatars/Avatar'
type Props = { hostAvatarSrc?: string }
type Props = { hostAvatarSrc?: string; hideAvatar?: boolean }
export const AvatarSideContainer = (props: Props) => {
let avatarContainer: HTMLDivElement | undefined
@ -34,12 +34,13 @@ export const AvatarSideContainer = (props: Props) => {
>
<div
class={
'absolute mb-2 flex items-center top-0 ' +
(isMobile() ? 'w-6 h-6' : 'w-10 h-10')
'absolute mb-2 flex items-center top-0' +
(isMobile() ? ' w-6 h-6' : ' w-10 h-10') +
(props.hideAvatar ? ' opacity-0' : ' opacity-100')
}
style={{
top: `${top()}px`,
transition: 'top 350ms ease-out',
transition: 'top 350ms ease-out, opacity 250ms ease-out',
}}
>
<Avatar initialAvatarSrc={props.hostAvatarSrc} />

View File

@ -13,6 +13,7 @@ type Props = Pick<ChatReply, 'messages' | 'input'> & {
context: BotContext
isLoadingBubbleDisplayed: boolean
hasError: boolean
hideAvatar: boolean
onNewBubbleDisplayed: (blockId: string) => Promise<void>
onScrollToBottom: () => void
onSubmit: (input: string) => void
@ -56,6 +57,7 @@ export const ChatChunk = (props: Props) => {
>
<AvatarSideContainer
hostAvatarSrc={props.theme.chat.hostAvatar?.url}
hideAvatar={props.hideAvatar}
/>
</Show>
<div

View File

@ -70,7 +70,12 @@ export const ConversationContainer = (props: Props) => {
)
for (const action of actionsBeforeFirstBubble) {
const response = await executeClientSideAction(action)
if (response) setBlockedPopupUrl(response.blockedPopupUrl)
if (response && 'replyToSend' in response) {
sendMessage(response.replyToSend)
return
}
if (response && 'blockedPopupUrl' in response)
setBlockedPopupUrl(response.blockedPopupUrl)
}
}
})()
@ -122,7 +127,12 @@ export const ConversationContainer = (props: Props) => {
)
for (const action of actionsBeforeFirstBubble) {
const response = await executeClientSideAction(action)
if (response) setBlockedPopupUrl(response.blockedPopupUrl)
if (response && 'replyToSend' in response) {
sendMessage(response.replyToSend)
return
}
if (response && 'blockedPopupUrl' in response)
setBlockedPopupUrl(response.blockedPopupUrl)
}
}
setChatChunks((displayedChunks) => [
@ -159,7 +169,12 @@ export const ConversationContainer = (props: Props) => {
)
for (const action of actionsToExecute) {
const response = await executeClientSideAction(action)
if (response) setBlockedPopupUrl(response.blockedPopupUrl)
if (response && 'replyToSend' in response) {
sendMessage(response.replyToSend)
return
}
if (response && 'blockedPopupUrl' in response)
setBlockedPopupUrl(response.blockedPopupUrl)
}
}
}
@ -187,6 +202,7 @@ export const ConversationContainer = (props: Props) => {
onSkip={handleSkip}
context={props.context}
hasError={hasError() && index() === chatChunks().length - 1}
hideAvatar={!chatChunk.input && index() < chatChunks().length - 1}
/>
)}
</For>

View File

@ -0,0 +1,33 @@
import { isNotDefined } from '@typebot.io/lib'
import type { ScriptToExecute } from '@typebot.io/schemas'
export const executeSetVariable = async ({
content,
args,
}: ScriptToExecute): Promise<{ replyToSend: string | undefined }> => {
try {
const func = Function(
...args.map((arg) => arg.id),
content.includes('return ') ? content : `return ${content}`
)
const replyToSend = await func(...args.map((arg) => arg.value))
return {
replyToSend: safeStringify(replyToSend),
}
} catch (err) {
return {
replyToSend: safeStringify(content),
}
}
}
export const safeStringify = (val: unknown): string | undefined => {
if (isNotDefined(val)) return
if (typeof val === 'string') return val
try {
return JSON.stringify(val)
} catch {
console.warn('Failed to safely stringify variable value', val)
return
}
}

View File

@ -2,12 +2,15 @@ import { executeChatwoot } from '@/features/blocks/integrations/chatwoot'
import { executeGoogleAnalyticsBlock } from '@/features/blocks/integrations/googleAnalytics/utils/executeGoogleAnalytics'
import { executeRedirect } from '@/features/blocks/logic/redirect'
import { executeScript } from '@/features/blocks/logic/script/executeScript'
import { executeSetVariable } from '@/features/blocks/logic/setVariable/executeSetVariable'
import { executeWait } from '@/features/blocks/logic/wait/utils/executeWait'
import type { ChatReply } from '@typebot.io/schemas'
export const executeClientSideAction = async (
clientSideAction: NonNullable<ChatReply['clientSideActions']>[0]
): Promise<{ blockedPopupUrl: string } | void> => {
): Promise<
{ blockedPopupUrl: string } | { replyToSend: string | undefined } | void
> => {
if ('chatwoot' in clientSideAction) {
return executeChatwoot(clientSideAction.chatwoot)
}
@ -23,4 +26,7 @@ export const executeClientSideAction = async (
if ('wait' in clientSideAction) {
return executeWait(clientSideAction.wait)
}
if ('setVariable' in clientSideAction) {
return executeSetVariable(clientSideAction.setVariable.scriptToExecute)
}
}