🚸 (bot) Update reply if we get new format from backend
This commit is contained in:
@@ -744,6 +744,9 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"url": {
|
"url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"isAutoplayEnabled": {
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -1986,6 +1989,7 @@
|
|||||||
"Custom",
|
"Custom",
|
||||||
"Empty",
|
"Empty",
|
||||||
"User ID",
|
"User ID",
|
||||||
|
"Now",
|
||||||
"Today",
|
"Today",
|
||||||
"Yesterday",
|
"Yesterday",
|
||||||
"Tomorrow",
|
"Tomorrow",
|
||||||
|
|||||||
@@ -312,6 +312,9 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"url": {
|
"url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"isAutoplayEnabled": {
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -1554,6 +1557,7 @@
|
|||||||
"Custom",
|
"Custom",
|
||||||
"Empty",
|
"Empty",
|
||||||
"User ID",
|
"User ID",
|
||||||
|
"Now",
|
||||||
"Today",
|
"Today",
|
||||||
"Yesterday",
|
"Yesterday",
|
||||||
"Tomorrow",
|
"Tomorrow",
|
||||||
@@ -4031,6 +4035,9 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"url": {
|
"url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"isAutoplayEnabled": {
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -6021,6 +6028,10 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"lastMessageNewFormat": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The sent message is validated and formatted on the backend. This is set only if the message differs from the formatted version."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
|||||||
@@ -79,8 +79,14 @@ export const sendMessage = publicProcedure
|
|||||||
clientSideActions,
|
clientSideActions,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const { messages, input, clientSideActions, newSessionState, logs } =
|
const {
|
||||||
await continueBotFlow(session.state)(message)
|
messages,
|
||||||
|
input,
|
||||||
|
clientSideActions,
|
||||||
|
newSessionState,
|
||||||
|
logs,
|
||||||
|
lastMessageNewFormat,
|
||||||
|
} = await continueBotFlow(session.state)(message)
|
||||||
|
|
||||||
const allLogs = clientLogs ? [...(logs ?? []), ...clientLogs] : logs
|
const allLogs = clientLogs ? [...(logs ?? []), ...clientLogs] : logs
|
||||||
|
|
||||||
@@ -101,6 +107,7 @@ export const sendMessage = publicProcedure
|
|||||||
clientSideActions,
|
clientSideActions,
|
||||||
dynamicTheme: parseDynamicThemeReply(newSessionState),
|
dynamicTheme: parseDynamicThemeReply(newSessionState),
|
||||||
logs,
|
logs,
|
||||||
|
lastMessageNewFormat,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export const continueBotFlow =
|
|||||||
message: 'Current block is not an input block',
|
message: 'Current block is not an input block',
|
||||||
})
|
})
|
||||||
|
|
||||||
let formattedReply = null
|
let formattedReply: string | undefined
|
||||||
|
|
||||||
if (isInputBlock(block)) {
|
if (isInputBlock(block)) {
|
||||||
if (reply && !isReplyValid(reply, block))
|
if (reply && !isReplyValid(reply, block))
|
||||||
@@ -116,25 +116,46 @@ export const continueBotFlow =
|
|||||||
const nextEdgeId = getOutgoingEdgeId(newSessionState)(block, formattedReply)
|
const nextEdgeId = getOutgoingEdgeId(newSessionState)(block, formattedReply)
|
||||||
|
|
||||||
if (groupHasMoreBlocks && !nextEdgeId) {
|
if (groupHasMoreBlocks && !nextEdgeId) {
|
||||||
return executeGroup(newSessionState)({
|
const chatReply = await executeGroup(newSessionState)({
|
||||||
...group,
|
...group,
|
||||||
blocks: group.blocks.slice(blockIndex + 1),
|
blocks: group.blocks.slice(blockIndex + 1),
|
||||||
})
|
})
|
||||||
|
return {
|
||||||
|
...chatReply,
|
||||||
|
lastMessageNewFormat:
|
||||||
|
formattedReply !== reply ? formattedReply : undefined,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nextEdgeId && state.linkedTypebots.queue.length === 0)
|
if (!nextEdgeId && state.linkedTypebots.queue.length === 0)
|
||||||
return { messages: [], newSessionState }
|
return {
|
||||||
|
messages: [],
|
||||||
|
newSessionState,
|
||||||
|
lastMessageNewFormat:
|
||||||
|
formattedReply !== reply ? formattedReply : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
const nextGroup = getNextGroup(newSessionState)(nextEdgeId)
|
const nextGroup = getNextGroup(newSessionState)(nextEdgeId)
|
||||||
|
|
||||||
if (!nextGroup) return { messages: [], newSessionState }
|
if (!nextGroup)
|
||||||
|
return {
|
||||||
|
messages: [],
|
||||||
|
newSessionState,
|
||||||
|
lastMessageNewFormat:
|
||||||
|
formattedReply !== reply ? formattedReply : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
return executeGroup(newSessionState)(nextGroup.group)
|
const chatReply = executeGroup(newSessionState)(nextGroup.group)
|
||||||
|
return {
|
||||||
|
...chatReply,
|
||||||
|
lastMessageNewFormat:
|
||||||
|
formattedReply !== reply ? formattedReply : undefined,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const processAndSaveAnswer =
|
const processAndSaveAnswer =
|
||||||
(state: SessionState, block: InputBlock, itemId?: string) =>
|
(state: SessionState, block: InputBlock, itemId?: string) =>
|
||||||
async (reply: string | null): Promise<SessionState> => {
|
async (reply: string | undefined): Promise<SessionState> => {
|
||||||
if (!reply) return state
|
if (!reply) return state
|
||||||
let newState = await saveAnswer(state, block, itemId)(reply)
|
let newState = await saveAnswer(state, block, itemId)(reply)
|
||||||
newState = saveVariableValueIfAny(newState, block)(reply)
|
newState = saveVariableValueIfAny(newState, block)(reply)
|
||||||
@@ -236,7 +257,7 @@ const getOutgoingEdgeId =
|
|||||||
({ typebot: { variables } }: Pick<SessionState, 'typebot'>) =>
|
({ typebot: { variables } }: Pick<SessionState, 'typebot'>) =>
|
||||||
(
|
(
|
||||||
block: InputBlock | SetVariableBlock | OpenAIBlock | WebhookBlock,
|
block: InputBlock | SetVariableBlock | OpenAIBlock | WebhookBlock,
|
||||||
reply: string | null
|
reply: string | undefined
|
||||||
) => {
|
) => {
|
||||||
if (
|
if (
|
||||||
block.type === InputBlockType.CHOICE &&
|
block.type === InputBlockType.CHOICE &&
|
||||||
@@ -264,8 +285,8 @@ const getOutgoingEdgeId =
|
|||||||
export const formatReply = (
|
export const formatReply = (
|
||||||
inputValue: string | undefined,
|
inputValue: string | undefined,
|
||||||
blockType: BlockType
|
blockType: BlockType
|
||||||
): string | null => {
|
): string | undefined => {
|
||||||
if (!inputValue) return null
|
if (!inputValue) return
|
||||||
switch (blockType) {
|
switch (blockType) {
|
||||||
case InputBlockType.PHONE:
|
case InputBlockType.PHONE:
|
||||||
return formatPhoneNumber(inputValue)
|
return formatPhoneNumber(inputValue)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/js",
|
"name": "@typebot.io/js",
|
||||||
"version": "0.1.15",
|
"version": "0.1.16",
|
||||||
"description": "Javascript library to display typebots on your website",
|
"description": "Javascript library to display typebots on your website",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
@@ -93,8 +93,6 @@ export const ChatChunk = (props: Props) => {
|
|||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
block={props.input}
|
block={props.input}
|
||||||
inputIndex={props.inputIndex}
|
inputIndex={props.inputIndex}
|
||||||
onSubmit={props.onSubmit}
|
|
||||||
onSkip={props.onSkip}
|
|
||||||
hasHostAvatar={props.theme.chat.hostAvatar?.isEnabled ?? false}
|
hasHostAvatar={props.theme.chat.hostAvatar?.isEnabled ?? false}
|
||||||
guestAvatar={props.theme.chat.guestAvatar}
|
guestAvatar={props.theme.chat.guestAvatar}
|
||||||
context={props.context}
|
context={props.context}
|
||||||
@@ -102,6 +100,8 @@ export const ChatChunk = (props: Props) => {
|
|||||||
props.settings.general.isInputPrefillEnabled ?? true
|
props.settings.general.isInputPrefillEnabled ?? true
|
||||||
}
|
}
|
||||||
hasError={props.hasError}
|
hasError={props.hasError}
|
||||||
|
onSubmit={props.onSubmit}
|
||||||
|
onSkip={props.onSkip}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Show when={props.streamingMessageId} keyed>
|
<Show when={props.streamingMessageId} keyed>
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ import { executeClientSideAction } from '@/utils/executeClientSideActions'
|
|||||||
import { LoadingChunk } from './LoadingChunk'
|
import { LoadingChunk } from './LoadingChunk'
|
||||||
import { PopupBlockedToast } from './PopupBlockedToast'
|
import { PopupBlockedToast } from './PopupBlockedToast'
|
||||||
import { setStreamingMessage } from '@/utils/streamingMessageSignal'
|
import { setStreamingMessage } from '@/utils/streamingMessageSignal'
|
||||||
|
import {
|
||||||
|
formattedMessages,
|
||||||
|
setFormattedMessages,
|
||||||
|
} from '@/utils/formattedMessagesSignal'
|
||||||
|
|
||||||
const parseDynamicTheme = (
|
const parseDynamicTheme = (
|
||||||
initialTheme: Theme,
|
initialTheme: Theme,
|
||||||
@@ -164,6 +168,15 @@ export const ConversationContainer = (props: Props) => {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
if (!data) return
|
if (!data) return
|
||||||
|
if (data.lastMessageNewFormat) {
|
||||||
|
setFormattedMessages([
|
||||||
|
...formattedMessages(),
|
||||||
|
{
|
||||||
|
inputId: [...chatChunks()].pop()?.input?.id ?? '',
|
||||||
|
formattedMessage: data.lastMessageNewFormat as string,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
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)
|
||||||
if (data.input?.id && props.onNewInputBlock) {
|
if (data.input?.id && props.onNewInputBlock) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { PhoneInput } from '@/features/blocks/inputs/phone'
|
|||||||
import { DateForm } from '@/features/blocks/inputs/date'
|
import { DateForm } from '@/features/blocks/inputs/date'
|
||||||
import { RatingForm } from '@/features/blocks/inputs/rating'
|
import { RatingForm } from '@/features/blocks/inputs/rating'
|
||||||
import { FileUploadForm } from '@/features/blocks/inputs/fileUpload'
|
import { FileUploadForm } from '@/features/blocks/inputs/fileUpload'
|
||||||
import { createSignal, Switch, Match } from 'solid-js'
|
import { createSignal, Switch, Match, createEffect } from 'solid-js'
|
||||||
import { isNotDefined } from '@typebot.io/lib'
|
import { isNotDefined } from '@typebot.io/lib'
|
||||||
import { isMobile } from '@/utils/isMobileSignal'
|
import { isMobile } from '@/utils/isMobileSignal'
|
||||||
import { PaymentForm } from '@/features/blocks/inputs/payment'
|
import { PaymentForm } from '@/features/blocks/inputs/payment'
|
||||||
@@ -33,6 +33,7 @@ import { MultipleChoicesForm } from '@/features/blocks/inputs/buttons/components
|
|||||||
import { Buttons } from '@/features/blocks/inputs/buttons/components/Buttons'
|
import { Buttons } from '@/features/blocks/inputs/buttons/components/Buttons'
|
||||||
import { SinglePictureChoice } from '@/features/blocks/inputs/pictureChoice/SinglePictureChoice'
|
import { SinglePictureChoice } from '@/features/blocks/inputs/pictureChoice/SinglePictureChoice'
|
||||||
import { MultiplePictureChoice } from '@/features/blocks/inputs/pictureChoice/MultiplePictureChoice'
|
import { MultiplePictureChoice } from '@/features/blocks/inputs/pictureChoice/MultiplePictureChoice'
|
||||||
|
import { formattedMessages } from '@/utils/formattedMessagesSignal'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
ref: HTMLDivElement | undefined
|
ref: HTMLDivElement | undefined
|
||||||
@@ -49,6 +50,7 @@ type Props = {
|
|||||||
|
|
||||||
export const InputChatBlock = (props: Props) => {
|
export const InputChatBlock = (props: Props) => {
|
||||||
const [answer, setAnswer] = createSignal<string>()
|
const [answer, setAnswer] = createSignal<string>()
|
||||||
|
const [formattedMessage, setFormattedMessage] = createSignal<string>()
|
||||||
|
|
||||||
const handleSubmit = async ({ label, value }: InputSubmitContent) => {
|
const handleSubmit = async ({ label, value }: InputSubmitContent) => {
|
||||||
setAnswer(label ?? value)
|
setAnswer(label ?? value)
|
||||||
@@ -60,11 +62,18 @@ export const InputChatBlock = (props: Props) => {
|
|||||||
props.onSkip()
|
props.onSkip()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const formattedMessage = formattedMessages().find(
|
||||||
|
(message) => message.inputId === props.block.id
|
||||||
|
)?.formattedMessage
|
||||||
|
if (formattedMessage) setFormattedMessage(formattedMessage)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={answer() && !props.hasError}>
|
<Match when={answer() && !props.hasError}>
|
||||||
<GuestBubble
|
<GuestBubble
|
||||||
message={answer() as string}
|
message={formattedMessage() ?? (answer() as string)}
|
||||||
showAvatar={props.guestAvatar?.isEnabled ?? false}
|
showAvatar={props.guestAvatar?.isEnabled ?? false}
|
||||||
avatarSrc={props.guestAvatar?.url && props.guestAvatar.url}
|
avatarSrc={props.guestAvatar?.url && props.guestAvatar.url}
|
||||||
/>
|
/>
|
||||||
|
|||||||
5
packages/embeds/js/src/utils/formattedMessagesSignal.ts
Normal file
5
packages/embeds/js/src/utils/formattedMessagesSignal.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { createSignal } from 'solid-js'
|
||||||
|
|
||||||
|
export const [formattedMessages, setFormattedMessages] = createSignal<
|
||||||
|
{ inputId: string; formattedMessage: string }[]
|
||||||
|
>([])
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/nextjs",
|
"name": "@typebot.io/nextjs",
|
||||||
"version": "0.1.15",
|
"version": "0.1.16",
|
||||||
"description": "Convenient library to display typebots on your Next.js website",
|
"description": "Convenient library to display typebots on your Next.js website",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/react",
|
"name": "@typebot.io/react",
|
||||||
"version": "0.1.15",
|
"version": "0.1.16",
|
||||||
"description": "Convenient library to display typebots on your Next.js website",
|
"description": "Convenient library to display typebots on your Next.js website",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|||||||
@@ -292,6 +292,12 @@ export const chatReplySchema = z.object({
|
|||||||
resultId: z.string().optional(),
|
resultId: z.string().optional(),
|
||||||
dynamicTheme: dynamicThemeSchema.optional(),
|
dynamicTheme: dynamicThemeSchema.optional(),
|
||||||
logs: z.array(replyLogSchema).optional(),
|
logs: z.array(replyLogSchema).optional(),
|
||||||
|
lastMessageNewFormat: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'The sent message is validated and formatted on the backend. This is set only if the message differs from the formatted version.'
|
||||||
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type ChatSession = z.infer<typeof chatSessionSchema>
|
export type ChatSession = z.infer<typeof chatSessionSchema>
|
||||||
|
|||||||
Reference in New Issue
Block a user