2
0

🚸 (bot) Update reply if we get new format from backend

This commit is contained in:
Baptiste Arnaud
2023-08-07 15:22:05 +02:00
parent ec0a5be793
commit af1bee8756
12 changed files with 94 additions and 18 deletions

View File

@ -744,6 +744,9 @@
"properties": {
"url": {
"type": "string"
},
"isAutoplayEnabled": {
"type": "boolean"
}
},
"additionalProperties": false
@ -1986,6 +1989,7 @@
"Custom",
"Empty",
"User ID",
"Now",
"Today",
"Yesterday",
"Tomorrow",

View File

@ -312,6 +312,9 @@
"properties": {
"url": {
"type": "string"
},
"isAutoplayEnabled": {
"type": "boolean"
}
},
"additionalProperties": false
@ -1554,6 +1557,7 @@
"Custom",
"Empty",
"User ID",
"Now",
"Today",
"Yesterday",
"Tomorrow",
@ -4031,6 +4035,9 @@
"properties": {
"url": {
"type": "string"
},
"isAutoplayEnabled": {
"type": "boolean"
}
},
"additionalProperties": false
@ -6021,6 +6028,10 @@
],
"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": [

View File

@ -79,8 +79,14 @@ export const sendMessage = publicProcedure
clientSideActions,
}
} else {
const { messages, input, clientSideActions, newSessionState, logs } =
await continueBotFlow(session.state)(message)
const {
messages,
input,
clientSideActions,
newSessionState,
logs,
lastMessageNewFormat,
} = await continueBotFlow(session.state)(message)
const allLogs = clientLogs ? [...(logs ?? []), ...clientLogs] : logs
@ -101,6 +107,7 @@ export const sendMessage = publicProcedure
clientSideActions,
dynamicTheme: parseDynamicThemeReply(newSessionState),
logs,
lastMessageNewFormat,
}
}
}

View File

@ -85,7 +85,7 @@ export const continueBotFlow =
message: 'Current block is not an input block',
})
let formattedReply = null
let formattedReply: string | undefined
if (isInputBlock(block)) {
if (reply && !isReplyValid(reply, block))
@ -116,25 +116,46 @@ export const continueBotFlow =
const nextEdgeId = getOutgoingEdgeId(newSessionState)(block, formattedReply)
if (groupHasMoreBlocks && !nextEdgeId) {
return executeGroup(newSessionState)({
const chatReply = await executeGroup(newSessionState)({
...group,
blocks: group.blocks.slice(blockIndex + 1),
})
return {
...chatReply,
lastMessageNewFormat:
formattedReply !== reply ? formattedReply : undefined,
}
}
if (!nextEdgeId && state.linkedTypebots.queue.length === 0)
return { messages: [], newSessionState }
return {
messages: [],
newSessionState,
lastMessageNewFormat:
formattedReply !== reply ? formattedReply : undefined,
}
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 =
(state: SessionState, block: InputBlock, itemId?: string) =>
async (reply: string | null): Promise<SessionState> => {
async (reply: string | undefined): Promise<SessionState> => {
if (!reply) return state
let newState = await saveAnswer(state, block, itemId)(reply)
newState = saveVariableValueIfAny(newState, block)(reply)
@ -236,7 +257,7 @@ const getOutgoingEdgeId =
({ typebot: { variables } }: Pick<SessionState, 'typebot'>) =>
(
block: InputBlock | SetVariableBlock | OpenAIBlock | WebhookBlock,
reply: string | null
reply: string | undefined
) => {
if (
block.type === InputBlockType.CHOICE &&
@ -264,8 +285,8 @@ const getOutgoingEdgeId =
export const formatReply = (
inputValue: string | undefined,
blockType: BlockType
): string | null => {
if (!inputValue) return null
): string | undefined => {
if (!inputValue) return
switch (blockType) {
case InputBlockType.PHONE:
return formatPhoneNumber(inputValue)

View File

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

View File

@ -93,8 +93,6 @@ export const ChatChunk = (props: Props) => {
ref={inputRef}
block={props.input}
inputIndex={props.inputIndex}
onSubmit={props.onSubmit}
onSkip={props.onSkip}
hasHostAvatar={props.theme.chat.hostAvatar?.isEnabled ?? false}
guestAvatar={props.theme.chat.guestAvatar}
context={props.context}
@ -102,6 +100,8 @@ export const ChatChunk = (props: Props) => {
props.settings.general.isInputPrefillEnabled ?? true
}
hasError={props.hasError}
onSubmit={props.onSubmit}
onSkip={props.onSkip}
/>
)}
<Show when={props.streamingMessageId} keyed>

View File

@ -21,6 +21,10 @@ import { executeClientSideAction } from '@/utils/executeClientSideActions'
import { LoadingChunk } from './LoadingChunk'
import { PopupBlockedToast } from './PopupBlockedToast'
import { setStreamingMessage } from '@/utils/streamingMessageSignal'
import {
formattedMessages,
setFormattedMessages,
} from '@/utils/formattedMessagesSignal'
const parseDynamicTheme = (
initialTheme: Theme,
@ -164,6 +168,15 @@ export const ConversationContainer = (props: Props) => {
])
}
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.dynamicTheme) setDynamicTheme(data.dynamicTheme)
if (data.input?.id && props.onNewInputBlock) {

View File

@ -25,7 +25,7 @@ import { PhoneInput } from '@/features/blocks/inputs/phone'
import { DateForm } from '@/features/blocks/inputs/date'
import { RatingForm } from '@/features/blocks/inputs/rating'
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 { isMobile } from '@/utils/isMobileSignal'
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 { SinglePictureChoice } from '@/features/blocks/inputs/pictureChoice/SinglePictureChoice'
import { MultiplePictureChoice } from '@/features/blocks/inputs/pictureChoice/MultiplePictureChoice'
import { formattedMessages } from '@/utils/formattedMessagesSignal'
type Props = {
ref: HTMLDivElement | undefined
@ -49,6 +50,7 @@ type Props = {
export const InputChatBlock = (props: Props) => {
const [answer, setAnswer] = createSignal<string>()
const [formattedMessage, setFormattedMessage] = createSignal<string>()
const handleSubmit = async ({ label, value }: InputSubmitContent) => {
setAnswer(label ?? value)
@ -60,11 +62,18 @@ export const InputChatBlock = (props: Props) => {
props.onSkip()
}
createEffect(() => {
const formattedMessage = formattedMessages().find(
(message) => message.inputId === props.block.id
)?.formattedMessage
if (formattedMessage) setFormattedMessage(formattedMessage)
})
return (
<Switch>
<Match when={answer() && !props.hasError}>
<GuestBubble
message={answer() as string}
message={formattedMessage() ?? (answer() as string)}
showAvatar={props.guestAvatar?.isEnabled ?? false}
avatarSrc={props.guestAvatar?.url && props.guestAvatar.url}
/>

View File

@ -0,0 +1,5 @@
import { createSignal } from 'solid-js'
export const [formattedMessages, setFormattedMessages] = createSignal<
{ inputId: string; formattedMessage: string }[]
>([])

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/nextjs",
"version": "0.1.15",
"version": "0.1.16",
"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.1.15",
"version": "0.1.16",
"description": "Convenient library to display typebots on your Next.js website",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -292,6 +292,12 @@ export const chatReplySchema = z.object({
resultId: z.string().optional(),
dynamicTheme: dynamicThemeSchema.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>