🚸 (engine) Improve engine v2 client loading and timings
Client actions are triggered after the correct bubble block. If the send message request is longer than 1s we show a loading chunk Closes #276
This commit is contained in:
@@ -3231,7 +3231,19 @@
|
|||||||
"messages": {
|
"messages": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"anyOf": [
|
"allOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
@@ -3239,36 +3251,65 @@
|
|||||||
{
|
{
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "object",
|
"anyOf": [
|
||||||
"properties": {
|
{
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"text"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"content": {
|
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"html": {
|
"type": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"text"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"plainText": {
|
"content": {
|
||||||
"type": "string"
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"html": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"plainText": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"html",
|
||||||
|
"plainText"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"html",
|
"type",
|
||||||
"plainText"
|
"content"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"content"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
"required": [
|
|
||||||
"type",
|
|
||||||
"content"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -3276,7 +3317,7 @@
|
|||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"image"
|
"video"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
@@ -3284,6 +3325,17 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"url": {
|
"url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"url",
|
||||||
|
"youtube",
|
||||||
|
"vimeo"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -3303,7 +3355,7 @@
|
|||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"video"
|
"audio"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
@@ -3311,17 +3363,6 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"url": {
|
"url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"url",
|
|
||||||
"youtube",
|
|
||||||
"vimeo"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -3341,7 +3382,7 @@
|
|||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"audio"
|
"embed"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
@@ -3349,8 +3390,14 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"url": {
|
"url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"type": "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"required": [
|
||||||
|
"height"
|
||||||
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3361,37 +3408,6 @@
|
|||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"embed"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"content": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"url": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"type": "number"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"height"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"type",
|
|
||||||
"content"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -4461,7 +4477,16 @@
|
|||||||
"clientSideActions": {
|
"clientSideActions": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"anyOf": [
|
"allOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"lastBubbleBlockId": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
@@ -4469,88 +4494,163 @@
|
|||||||
{
|
{
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "object",
|
"anyOf": [
|
||||||
"properties": {
|
{
|
||||||
"codeToExecute": {
|
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"content": {
|
"codeToExecute": {
|
||||||
"type": "string"
|
"type": "object",
|
||||||
},
|
"properties": {
|
||||||
"args": {
|
"content": {
|
||||||
"type": "array",
|
"type": "string"
|
||||||
"items": {
|
},
|
||||||
"type": "object",
|
"args": {
|
||||||
"properties": {
|
"type": "array",
|
||||||
"id": {
|
"items": {
|
||||||
"type": "string"
|
"type": "object",
|
||||||
},
|
"properties": {
|
||||||
"value": {
|
"id": {
|
||||||
"anyOf": [
|
"type": "string"
|
||||||
{
|
|
||||||
"not": {}
|
|
||||||
},
|
},
|
||||||
{
|
"value": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"not": {}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "string"
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"nullable": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id"
|
||||||
],
|
],
|
||||||
"nullable": true
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"required": [
|
},
|
||||||
"id"
|
"required": [
|
||||||
],
|
"content",
|
||||||
"additionalProperties": false
|
"args"
|
||||||
}
|
],
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"content",
|
"codeToExecute"
|
||||||
"args"
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"redirect": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"isNewTab": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"isNewTab"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"redirect"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
"required": [
|
|
||||||
"codeToExecute"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"redirect": {
|
"chatwoot": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"url": {
|
"codeToExecute": {
|
||||||
"type": "string"
|
"type": "object",
|
||||||
},
|
"properties": {
|
||||||
"isNewTab": {
|
"content": {
|
||||||
"type": "boolean"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"args": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"not": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"content",
|
||||||
|
"args"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"isNewTab"
|
"codeToExecute"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"redirect"
|
"chatwoot"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
@@ -4559,71 +4659,30 @@
|
|||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"chatwoot": {
|
"googleAnalytics": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"codeToExecute": {
|
"trackingId": {
|
||||||
"type": "object",
|
"type": "string"
|
||||||
"properties": {
|
},
|
||||||
"content": {
|
"category": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"args": {
|
"action": {
|
||||||
"type": "array",
|
"type": "string"
|
||||||
"items": {
|
},
|
||||||
"type": "object",
|
"label": {
|
||||||
"properties": {
|
"type": "string"
|
||||||
"id": {
|
},
|
||||||
"type": "string"
|
"value": {
|
||||||
},
|
"type": "number"
|
||||||
"value": {
|
|
||||||
"anyOf": [
|
|
||||||
{
|
|
||||||
"not": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"anyOf": [
|
|
||||||
{
|
|
||||||
"anyOf": [
|
|
||||||
{
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "number"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"content",
|
|
||||||
"args"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
|
||||||
"codeToExecute"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"chatwoot"
|
"googleAnalytics"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
@@ -4632,55 +4691,25 @@
|
|||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"googleAnalytics": {
|
"wait": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"trackingId": {
|
"secondsToWaitFor": {
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"category": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"label": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"required": [
|
||||||
|
"secondsToWaitFor"
|
||||||
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"googleAnalytics"
|
"wait"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"wait": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"secondsToWaitFor": {
|
|
||||||
"type": "number"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"secondsToWaitFor"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"wait"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,13 +51,15 @@ if (window.$chatwoot) {
|
|||||||
|
|
||||||
export const executeChatwootBlock = (
|
export const executeChatwootBlock = (
|
||||||
{ typebot: { variables }, isPreview }: SessionState,
|
{ typebot: { variables }, isPreview }: SessionState,
|
||||||
block: ChatwootBlock
|
block: ChatwootBlock,
|
||||||
|
lastBubbleBlockId?: string
|
||||||
): ExecuteIntegrationResponse => {
|
): ExecuteIntegrationResponse => {
|
||||||
const chatwootCode = parseChatwootOpenCode(block.options)
|
const chatwootCode = parseChatwootOpenCode(block.options)
|
||||||
return {
|
return {
|
||||||
outgoingEdgeId: block.outgoingEdgeId,
|
outgoingEdgeId: block.outgoingEdgeId,
|
||||||
clientSideActions: [
|
clientSideActions: [
|
||||||
{
|
{
|
||||||
|
lastBubbleBlockId,
|
||||||
chatwoot: {
|
chatwoot: {
|
||||||
codeToExecute: {
|
codeToExecute: {
|
||||||
content: parseVariables(variables, { fieldToParse: 'id' })(
|
content: parseVariables(variables, { fieldToParse: 'id' })(
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import { GoogleAnalyticsBlock, SessionState } from 'models'
|
|||||||
|
|
||||||
export const executeGoogleAnalyticsBlock = (
|
export const executeGoogleAnalyticsBlock = (
|
||||||
{ typebot: { variables } }: SessionState,
|
{ typebot: { variables } }: SessionState,
|
||||||
block: GoogleAnalyticsBlock
|
block: GoogleAnalyticsBlock,
|
||||||
|
lastBubbleBlockId?: string
|
||||||
): ExecuteIntegrationResponse => ({
|
): ExecuteIntegrationResponse => ({
|
||||||
outgoingEdgeId: block.outgoingEdgeId,
|
outgoingEdgeId: block.outgoingEdgeId,
|
||||||
clientSideActions: [
|
clientSideActions: [
|
||||||
{
|
{
|
||||||
googleAnalytics: deepParseVariable(variables)(block.options),
|
googleAnalytics: deepParseVariable(variables)(block.options),
|
||||||
|
lastBubbleBlockId,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import { CodeBlock, SessionState } from 'models'
|
|||||||
|
|
||||||
export const executeCode = (
|
export const executeCode = (
|
||||||
{ typebot: { variables } }: SessionState,
|
{ typebot: { variables } }: SessionState,
|
||||||
block: CodeBlock
|
block: CodeBlock,
|
||||||
|
lastBubbleBlockId?: string
|
||||||
): ExecuteLogicResponse => {
|
): ExecuteLogicResponse => {
|
||||||
if (!block.options.content) return { outgoingEdgeId: block.outgoingEdgeId }
|
if (!block.options.content) return { outgoingEdgeId: block.outgoingEdgeId }
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ export const executeCode = (
|
|||||||
content,
|
content,
|
||||||
args,
|
args,
|
||||||
},
|
},
|
||||||
|
lastBubbleBlockId,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,15 @@ import { sanitizeUrl } from 'utils'
|
|||||||
|
|
||||||
export const executeRedirect = (
|
export const executeRedirect = (
|
||||||
{ typebot: { variables } }: SessionState,
|
{ typebot: { variables } }: SessionState,
|
||||||
block: RedirectBlock
|
block: RedirectBlock,
|
||||||
|
lastBubbleBlockId?: string
|
||||||
): ExecuteLogicResponse => {
|
): ExecuteLogicResponse => {
|
||||||
if (!block.options?.url) return { outgoingEdgeId: block.outgoingEdgeId }
|
if (!block.options?.url) return { outgoingEdgeId: block.outgoingEdgeId }
|
||||||
const formattedUrl = sanitizeUrl(parseVariables(variables)(block.options.url))
|
const formattedUrl = sanitizeUrl(parseVariables(variables)(block.options.url))
|
||||||
return {
|
return {
|
||||||
clientSideActions: [
|
clientSideActions: [
|
||||||
{
|
{
|
||||||
|
lastBubbleBlockId,
|
||||||
redirect: { url: formattedUrl, isNewTab: block.options.isNewTab },
|
redirect: { url: formattedUrl, isNewTab: block.options.isNewTab },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -4,19 +4,29 @@ import { SessionState, WaitBlock } from 'models'
|
|||||||
|
|
||||||
export const executeWait = async (
|
export const executeWait = async (
|
||||||
{ typebot: { variables } }: SessionState,
|
{ typebot: { variables } }: SessionState,
|
||||||
block: WaitBlock
|
block: WaitBlock,
|
||||||
|
lastBubbleBlockId?: string
|
||||||
): Promise<ExecuteLogicResponse> => {
|
): Promise<ExecuteLogicResponse> => {
|
||||||
if (!block.options.secondsToWaitFor)
|
if (!block.options.secondsToWaitFor)
|
||||||
return { outgoingEdgeId: block.outgoingEdgeId }
|
return { outgoingEdgeId: block.outgoingEdgeId }
|
||||||
const parsedSecondsToWaitFor = parseVariables(variables)(
|
const parsedSecondsToWaitFor = safeParseInt(
|
||||||
block.options.secondsToWaitFor
|
parseVariables(variables)(block.options.secondsToWaitFor)
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
outgoingEdgeId: block.outgoingEdgeId,
|
outgoingEdgeId: block.outgoingEdgeId,
|
||||||
// @ts-expect-error isNaN can be used with strings
|
clientSideActions: parsedSecondsToWaitFor
|
||||||
clientSideActions: isNaN(parsedSecondsToWaitFor)
|
? [
|
||||||
? undefined
|
{
|
||||||
: [{ wait: { secondsToWaitFor: parsedSecondsToWaitFor } }],
|
wait: { secondsToWaitFor: parsedSecondsToWaitFor },
|
||||||
|
lastBubbleBlockId,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const safeParseInt = (value: string) => {
|
||||||
|
const parsedValue = parseInt(value)
|
||||||
|
return isNaN(parsedValue) ? undefined : parsedValue
|
||||||
|
}
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ const parseRetryMessage = (
|
|||||||
return {
|
return {
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
|
id: block.id,
|
||||||
type: BubbleBlockType.TEXT,
|
type: BubbleBlockType.TEXT,
|
||||||
content: {
|
content: {
|
||||||
plainText: retryMessage,
|
plainText: retryMessage,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export const executeGroup =
|
|||||||
currentReply?.clientSideActions
|
currentReply?.clientSideActions
|
||||||
let logs: ChatReply['logs'] = currentReply?.logs
|
let logs: ChatReply['logs'] = currentReply?.logs
|
||||||
let nextEdgeId = null
|
let nextEdgeId = null
|
||||||
|
let lastBubbleBlockId: string | undefined
|
||||||
|
|
||||||
let newSessionState = state
|
let newSessionState = state
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ export const executeGroup =
|
|||||||
messages.push(
|
messages.push(
|
||||||
deepParseVariable(newSessionState.typebot.variables)(block)
|
deepParseVariable(newSessionState.typebot.variables)(block)
|
||||||
)
|
)
|
||||||
|
lastBubbleBlockId = block.id
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,9 +65,9 @@ export const executeGroup =
|
|||||||
logs,
|
logs,
|
||||||
}
|
}
|
||||||
const executionResponse = isLogicBlock(block)
|
const executionResponse = isLogicBlock(block)
|
||||||
? await executeLogic(newSessionState)(block)
|
? await executeLogic(newSessionState, lastBubbleBlockId)(block)
|
||||||
: isIntegrationBlock(block)
|
: isIntegrationBlock(block)
|
||||||
? await executeIntegration(newSessionState)(block)
|
? await executeIntegration(newSessionState, lastBubbleBlockId)(block)
|
||||||
: null
|
: null
|
||||||
|
|
||||||
if (!executionResponse) continue
|
if (!executionResponse) continue
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import { IntegrationBlock, IntegrationBlockType, SessionState } from 'models'
|
|||||||
import { ExecuteIntegrationResponse } from '../../types'
|
import { ExecuteIntegrationResponse } from '../../types'
|
||||||
|
|
||||||
export const executeIntegration =
|
export const executeIntegration =
|
||||||
(state: SessionState) =>
|
(state: SessionState, lastBubbleBlockId?: string) =>
|
||||||
async (block: IntegrationBlock): Promise<ExecuteIntegrationResponse> => {
|
async (block: IntegrationBlock): Promise<ExecuteIntegrationResponse> => {
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
case IntegrationBlockType.GOOGLE_SHEETS:
|
case IntegrationBlockType.GOOGLE_SHEETS:
|
||||||
return executeGoogleSheetBlock(state, block)
|
return executeGoogleSheetBlock(state, block)
|
||||||
case IntegrationBlockType.CHATWOOT:
|
case IntegrationBlockType.CHATWOOT:
|
||||||
return executeChatwootBlock(state, block)
|
return executeChatwootBlock(state, block, lastBubbleBlockId)
|
||||||
case IntegrationBlockType.GOOGLE_ANALYTICS:
|
case IntegrationBlockType.GOOGLE_ANALYTICS:
|
||||||
return executeGoogleAnalyticsBlock(state, block)
|
return executeGoogleAnalyticsBlock(state, block, lastBubbleBlockId)
|
||||||
case IntegrationBlockType.EMAIL:
|
case IntegrationBlockType.EMAIL:
|
||||||
return executeSendEmailBlock(state, block)
|
return executeSendEmailBlock(state, block)
|
||||||
case IntegrationBlockType.WEBHOOK:
|
case IntegrationBlockType.WEBHOOK:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { LogicBlock, LogicBlockType, SessionState } from 'models'
|
|||||||
import { ExecuteLogicResponse } from '../../types'
|
import { ExecuteLogicResponse } from '../../types'
|
||||||
|
|
||||||
export const executeLogic =
|
export const executeLogic =
|
||||||
(state: SessionState) =>
|
(state: SessionState, lastBubbleBlockId?: string) =>
|
||||||
async (block: LogicBlock): Promise<ExecuteLogicResponse> => {
|
async (block: LogicBlock): Promise<ExecuteLogicResponse> => {
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
case LogicBlockType.SET_VARIABLE:
|
case LogicBlockType.SET_VARIABLE:
|
||||||
@@ -16,12 +16,12 @@ export const executeLogic =
|
|||||||
case LogicBlockType.CONDITION:
|
case LogicBlockType.CONDITION:
|
||||||
return executeCondition(state, block)
|
return executeCondition(state, block)
|
||||||
case LogicBlockType.REDIRECT:
|
case LogicBlockType.REDIRECT:
|
||||||
return executeRedirect(state, block)
|
return executeRedirect(state, block, lastBubbleBlockId)
|
||||||
case LogicBlockType.CODE:
|
case LogicBlockType.CODE:
|
||||||
return executeCode(state, block)
|
return executeCode(state, block, lastBubbleBlockId)
|
||||||
case LogicBlockType.TYPEBOT_LINK:
|
case LogicBlockType.TYPEBOT_LINK:
|
||||||
return executeTypebotLink(state, block)
|
return executeTypebotLink(state, block)
|
||||||
case LogicBlockType.WAIT:
|
case LogicBlockType.WAIT:
|
||||||
return executeWait(state, block)
|
return executeWait(state, block, lastBubbleBlockId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ type Props = Pick<ChatReply, 'messages' | 'input'> & {
|
|||||||
settings: Settings
|
settings: Settings
|
||||||
inputIndex: number
|
inputIndex: number
|
||||||
context: BotContext
|
context: BotContext
|
||||||
|
isLoadingBubbleDisplayed: boolean
|
||||||
|
onNewBubbleDisplayed: (blockId: string) => Promise<void>
|
||||||
onScrollToBottom: () => void
|
onScrollToBottom: () => void
|
||||||
onSubmit: (input: string) => void
|
onSubmit: (input: string) => void
|
||||||
onSkip: () => void
|
onSkip: () => void
|
||||||
@@ -26,7 +28,9 @@ export const ChatChunk = (props: Props) => {
|
|||||||
props.onScrollToBottom()
|
props.onScrollToBottom()
|
||||||
})
|
})
|
||||||
|
|
||||||
const displayNextMessage = () => {
|
const displayNextMessage = async () => {
|
||||||
|
const lastBubbleBlockId = props.messages[displayedMessageIndex()].id
|
||||||
|
await props.onNewBubbleDisplayed(lastBubbleBlockId)
|
||||||
setDisplayedMessageIndex(
|
setDisplayedMessageIndex(
|
||||||
displayedMessageIndex() === props.messages.length
|
displayedMessageIndex() === props.messages.length
|
||||||
? displayedMessageIndex()
|
? displayedMessageIndex()
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import type { ChatReply, Theme } from 'models'
|
import type { ChatReply, Theme } from 'models'
|
||||||
import { createEffect, createSignal, For } from 'solid-js'
|
import { createEffect, createSignal, For, Show } from 'solid-js'
|
||||||
import { sendMessageQuery } from '@/queries/sendMessageQuery'
|
import { sendMessageQuery } from '@/queries/sendMessageQuery'
|
||||||
import { ChatChunk } from './ChatChunk'
|
import { ChatChunk } from './ChatChunk'
|
||||||
import { BotContext, InitialChatReply } from '@/types'
|
import { BotContext, InitialChatReply } from '@/types'
|
||||||
import { isNotDefined } from 'utils'
|
import { isNotDefined } from 'utils'
|
||||||
import { executeClientSideAction } from '@/utils/executeClientSideActions'
|
import { executeClientSideAction } from '@/utils/executeClientSideActions'
|
||||||
|
import { LoadingChunk } from './LoadingChunk'
|
||||||
|
|
||||||
const parseDynamicTheme = (
|
const parseDynamicTheme = (
|
||||||
initialTheme: Theme,
|
initialTheme: Theme,
|
||||||
@@ -55,6 +56,7 @@ export const ConversationContainer = (props: Props) => {
|
|||||||
ChatReply['dynamicTheme']
|
ChatReply['dynamicTheme']
|
||||||
>(props.initialChatReply.dynamicTheme)
|
>(props.initialChatReply.dynamicTheme)
|
||||||
const [theme, setTheme] = createSignal(props.initialChatReply.typebot.theme)
|
const [theme, setTheme] = createSignal(props.initialChatReply.typebot.theme)
|
||||||
|
const [isSending, setIsSending] = createSignal(false)
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
setTheme(
|
setTheme(
|
||||||
@@ -66,11 +68,16 @@ export const ConversationContainer = (props: Props) => {
|
|||||||
const currentBlockId = chatChunks().at(-1)?.input?.id
|
const currentBlockId = chatChunks().at(-1)?.input?.id
|
||||||
if (currentBlockId && props.onAnswer)
|
if (currentBlockId && props.onAnswer)
|
||||||
props.onAnswer({ message, blockId: currentBlockId })
|
props.onAnswer({ message, blockId: currentBlockId })
|
||||||
|
const longRequest = setTimeout(() => {
|
||||||
|
setIsSending(true)
|
||||||
|
}, 1000)
|
||||||
const data = await sendMessageQuery({
|
const data = await sendMessageQuery({
|
||||||
apiHost: props.context.apiHost,
|
apiHost: props.context.apiHost,
|
||||||
sessionId: props.initialChatReply.sessionId,
|
sessionId: props.initialChatReply.sessionId,
|
||||||
message,
|
message,
|
||||||
})
|
})
|
||||||
|
clearTimeout(longRequest)
|
||||||
|
setIsSending(false)
|
||||||
if (!data) return
|
if (!data) return
|
||||||
if (data.logs) props.onNewLogs?.(data.logs)
|
if (data.logs) props.onNewLogs?.(data.logs)
|
||||||
if (data.dynamicTheme) setDynamicTheme(data.dynamicTheme)
|
if (data.dynamicTheme) setDynamicTheme(data.dynamicTheme)
|
||||||
@@ -101,7 +108,10 @@ export const ConversationContainer = (props: Props) => {
|
|||||||
const lastChunk = chatChunks().at(-1)
|
const lastChunk = chatChunks().at(-1)
|
||||||
if (!lastChunk) return
|
if (!lastChunk) return
|
||||||
if (lastChunk.clientSideActions) {
|
if (lastChunk.clientSideActions) {
|
||||||
for (const action of lastChunk.clientSideActions) {
|
const actionsToExecute = lastChunk.clientSideActions.filter((action) =>
|
||||||
|
isNotDefined(action.lastBubbleBlockId)
|
||||||
|
)
|
||||||
|
for (const action of actionsToExecute) {
|
||||||
await executeClientSideAction(action)
|
await executeClientSideAction(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,6 +120,19 @@ export const ConversationContainer = (props: Props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleNewBubbleDisplayed = async (blockId: string) => {
|
||||||
|
const lastChunk = chatChunks().at(-1)
|
||||||
|
if (!lastChunk) return
|
||||||
|
if (lastChunk.clientSideActions) {
|
||||||
|
const actionsToExecute = lastChunk.clientSideActions.filter(
|
||||||
|
(action) => action.lastBubbleBlockId === blockId
|
||||||
|
)
|
||||||
|
for (const action of actionsToExecute) {
|
||||||
|
await executeClientSideAction(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={chatContainer}
|
ref={chatContainer}
|
||||||
@@ -123,6 +146,8 @@ export const ConversationContainer = (props: Props) => {
|
|||||||
input={chatChunk.input}
|
input={chatChunk.input}
|
||||||
theme={theme()}
|
theme={theme()}
|
||||||
settings={props.initialChatReply.typebot.settings}
|
settings={props.initialChatReply.typebot.settings}
|
||||||
|
isLoadingBubbleDisplayed={isSending()}
|
||||||
|
onNewBubbleDisplayed={handleNewBubbleDisplayed}
|
||||||
onAllBubblesDisplayed={handleAllBubblesDisplayed}
|
onAllBubblesDisplayed={handleAllBubblesDisplayed}
|
||||||
onSubmit={sendMessage}
|
onSubmit={sendMessage}
|
||||||
onScrollToBottom={autoScrollToBottom}
|
onScrollToBottom={autoScrollToBottom}
|
||||||
@@ -133,6 +158,9 @@ export const ConversationContainer = (props: Props) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
<Show when={isSending()}>
|
||||||
|
<LoadingChunk theme={theme()} />
|
||||||
|
</Show>
|
||||||
<BottomSpacer ref={bottomSpacer} />
|
<BottomSpacer ref={bottomSpacer} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { Theme } from 'models'
|
||||||
|
import { Show } from 'solid-js'
|
||||||
|
import { LoadingBubble } from '../bubbles/LoadingBubble'
|
||||||
|
import { AvatarSideContainer } from './AvatarSideContainer'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
theme: Theme
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LoadingChunk = (props: Props) => (
|
||||||
|
<div class="flex w-full">
|
||||||
|
<div class="flex flex-col w-full min-w-0">
|
||||||
|
<div class="flex">
|
||||||
|
<Show when={props.theme.chat.hostAvatar?.isEnabled}>
|
||||||
|
<AvatarSideContainer
|
||||||
|
hostAvatarSrc={props.theme.chat.hostAvatar?.url}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
<div
|
||||||
|
class="flex-1"
|
||||||
|
style={{
|
||||||
|
'margin-right': props.theme.chat.guestAvatar?.isEnabled
|
||||||
|
? '50px'
|
||||||
|
: '0.5rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LoadingBubble />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
25
packages/js/src/components/bubbles/LoadingBubble.tsx
Normal file
25
packages/js/src/components/bubbles/LoadingBubble.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { TypingBubble } from '@/components'
|
||||||
|
|
||||||
|
export const LoadingBubble = () => (
|
||||||
|
<div class="flex flex-col animate-fade-in">
|
||||||
|
<div class="flex mb-2 w-full items-center">
|
||||||
|
<div class={'flex relative items-start typebot-host-bubble'}>
|
||||||
|
<div
|
||||||
|
class="flex items-center absolute px-4 py-2 rounded-lg bubble-typing "
|
||||||
|
style={{
|
||||||
|
width: '4rem',
|
||||||
|
height: '2rem',
|
||||||
|
}}
|
||||||
|
data-testid="host-bubble"
|
||||||
|
>
|
||||||
|
<TypingBubble />
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class={
|
||||||
|
'overflow-hidden text-fade-in mx-4 my-2 whitespace-pre-wrap slate-html-container relative opacity-0 h-6 text-ellipsis'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
@@ -53,11 +53,8 @@ export const TextBubble = (props: Props) => {
|
|||||||
{isTyping() && <TypingBubble />}
|
{isTyping() && <TypingBubble />}
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
style={{
|
|
||||||
'text-overflow': 'ellipsis',
|
|
||||||
}}
|
|
||||||
class={
|
class={
|
||||||
'overflow-hidden text-fade-in mx-4 my-2 whitespace-pre-wrap slate-html-container relative ' +
|
'overflow-hidden text-fade-in mx-4 my-2 whitespace-pre-wrap slate-html-container relative text-ellipsis ' +
|
||||||
(isTyping() ? 'opacity-0 h-6' : 'opacity-100 h-full')
|
(isTyping() ? 'opacity-0 h-6' : 'opacity-100 h-full')
|
||||||
}
|
}
|
||||||
innerHTML={props.content.html}
|
innerHTML={props.content.html}
|
||||||
|
|||||||
@@ -87,11 +87,15 @@ const embedMessageSchema = z.object({
|
|||||||
content: embedBubbleContentSchema,
|
content: embedBubbleContentSchema,
|
||||||
})
|
})
|
||||||
|
|
||||||
const chatMessageSchema = textMessageSchema
|
const chatMessageSchema = z
|
||||||
.or(imageMessageSchema)
|
.object({ id: z.string() })
|
||||||
.or(videoMessageSchema)
|
.and(
|
||||||
.or(audioMessageSchema)
|
textMessageSchema
|
||||||
.or(embedMessageSchema)
|
.or(imageMessageSchema)
|
||||||
|
.or(videoMessageSchema)
|
||||||
|
.or(audioMessageSchema)
|
||||||
|
.or(embedMessageSchema)
|
||||||
|
)
|
||||||
|
|
||||||
const codeToExecuteSchema = z.object({
|
const codeToExecuteSchema = z.object({
|
||||||
content: z.string(),
|
content: z.string(),
|
||||||
@@ -167,29 +171,35 @@ const replyLogSchema = logSchema
|
|||||||
|
|
||||||
const clientSideActionSchema = z
|
const clientSideActionSchema = z
|
||||||
.object({
|
.object({
|
||||||
codeToExecute: codeToExecuteSchema,
|
lastBubbleBlockId: z.string().optional(),
|
||||||
})
|
})
|
||||||
.or(
|
.and(
|
||||||
z.object({
|
z
|
||||||
redirect: redirectOptionsSchema,
|
.object({
|
||||||
})
|
codeToExecute: codeToExecuteSchema,
|
||||||
)
|
})
|
||||||
.or(
|
.or(
|
||||||
z.object({
|
z.object({
|
||||||
chatwoot: z.object({ codeToExecute: codeToExecuteSchema }),
|
redirect: redirectOptionsSchema,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.or(
|
.or(
|
||||||
z.object({
|
z.object({
|
||||||
googleAnalytics: googleAnalyticsOptionsSchema,
|
chatwoot: z.object({ codeToExecute: codeToExecuteSchema }),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.or(
|
.or(
|
||||||
z.object({
|
z.object({
|
||||||
wait: z.object({
|
googleAnalytics: googleAnalyticsOptionsSchema,
|
||||||
secondsToWaitFor: z.number(),
|
})
|
||||||
}),
|
)
|
||||||
})
|
.or(
|
||||||
|
z.object({
|
||||||
|
wait: z.object({
|
||||||
|
secondsToWaitFor: z.number(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
export const chatReplySchema = z.object({
|
export const chatReplySchema = z.object({
|
||||||
|
|||||||
Reference in New Issue
Block a user