@ -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} />
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user