✨ (settings) Add delay between bubbles option and typing disabling on first message
This commit is contained in:
@@ -93,10 +93,12 @@ export const resumeWhatsAppFlow = async ({
|
||||
visitedEdges,
|
||||
} = resumeResponse
|
||||
|
||||
const isFirstChatChunk = (!session || isSessionExpired) ?? false
|
||||
await sendChatReplyToWhatsApp({
|
||||
to: receivedMessage.from,
|
||||
messages,
|
||||
input,
|
||||
isFirstChatChunk,
|
||||
typingEmulation: newSessionState.typingEmulation,
|
||||
clientSideActions,
|
||||
credentials,
|
||||
|
||||
@@ -16,12 +16,14 @@ import { isNotDefined } from '@typebot.io/lib/utils'
|
||||
import { computeTypingDuration } from '../computeTypingDuration'
|
||||
import { continueBotFlow } from '../continueBotFlow'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
|
||||
// Media can take some time to be delivered. This make sure we don't send a message before the media is delivered.
|
||||
const messageAfterMediaTimeout = 5000
|
||||
|
||||
type Props = {
|
||||
to: string
|
||||
isFirstChatChunk: boolean
|
||||
typingEmulation: SessionState['typingEmulation']
|
||||
credentials: WhatsAppCredentials['data']
|
||||
state: SessionState
|
||||
@@ -30,6 +32,7 @@ type Props = {
|
||||
export const sendChatReplyToWhatsApp = async ({
|
||||
to,
|
||||
typingEmulation,
|
||||
isFirstChatChunk,
|
||||
messages,
|
||||
input,
|
||||
clientSideActions,
|
||||
@@ -57,6 +60,7 @@ export const sendChatReplyToWhatsApp = async ({
|
||||
to,
|
||||
messages,
|
||||
input,
|
||||
isFirstChatChunk: false,
|
||||
typingEmulation: newSessionState.typingEmulation,
|
||||
clientSideActions,
|
||||
credentials,
|
||||
@@ -64,19 +68,40 @@ export const sendChatReplyToWhatsApp = async ({
|
||||
})
|
||||
}
|
||||
|
||||
let i = -1
|
||||
for (const message of messagesBeforeInput) {
|
||||
i += 1
|
||||
if (
|
||||
i > 0 &&
|
||||
(typingEmulation?.delayBetweenBubbles ??
|
||||
defaultSettings.typingEmulation.delayBetweenBubbles) > 0
|
||||
) {
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(
|
||||
resolve,
|
||||
(typingEmulation?.delayBetweenBubbles ??
|
||||
defaultSettings.typingEmulation.delayBetweenBubbles) * 1000
|
||||
)
|
||||
)
|
||||
}
|
||||
const whatsAppMessage = convertMessageToWhatsAppMessage(message)
|
||||
if (isNotDefined(whatsAppMessage)) continue
|
||||
const lastSentMessageIsMedia = ['audio', 'video', 'image'].includes(
|
||||
sentMessages.at(-1)?.type ?? ''
|
||||
)
|
||||
|
||||
const typingDuration = lastSentMessageIsMedia
|
||||
? messageAfterMediaTimeout
|
||||
: isFirstChatChunk &&
|
||||
i === 0 &&
|
||||
(typingEmulation?.isDisabledOnFirstMessage ??
|
||||
defaultSettings.typingEmulation.isDisabledOnFirstMessage)
|
||||
? 0
|
||||
: getTypingDuration({
|
||||
message: whatsAppMessage,
|
||||
typingEmulation,
|
||||
})
|
||||
if (typingDuration)
|
||||
if ((typingDuration ?? 0) > 0)
|
||||
await new Promise((resolve) => setTimeout(resolve, typingDuration))
|
||||
try {
|
||||
await sendWhatsAppMessage({
|
||||
@@ -101,6 +126,7 @@ export const sendChatReplyToWhatsApp = async ({
|
||||
to,
|
||||
messages,
|
||||
input,
|
||||
isFirstChatChunk: false,
|
||||
typingEmulation: newSessionState.typingEmulation,
|
||||
clientSideActions,
|
||||
credentials,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/js",
|
||||
"version": "0.2.35",
|
||||
"version": "0.2.36",
|
||||
"description": "Javascript library to display typebots on your website",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -40,6 +40,20 @@ export const ChatChunk = (props: Props) => {
|
||||
})
|
||||
|
||||
const displayNextMessage = async (bubbleOffsetTop?: number) => {
|
||||
if (
|
||||
(props.settings.typingEmulation?.delayBetweenBubbles ??
|
||||
defaultSettings.typingEmulation.delayBetweenBubbles) > 0 &&
|
||||
displayedMessageIndex() < props.messages.length - 1
|
||||
) {
|
||||
// eslint-disable-next-line solid/reactivity
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(
|
||||
resolve,
|
||||
(props.settings.typingEmulation?.delayBetweenBubbles ??
|
||||
defaultSettings.typingEmulation.delayBetweenBubbles) * 1000
|
||||
)
|
||||
)
|
||||
}
|
||||
const lastBubbleBlockId = props.messages[displayedMessageIndex()].id
|
||||
await props.onNewBubbleDisplayed(lastBubbleBlockId)
|
||||
setDisplayedMessageIndex(
|
||||
@@ -86,10 +100,17 @@ export const ChatChunk = (props: Props) => {
|
||||
}}
|
||||
>
|
||||
<For each={props.messages.slice(0, displayedMessageIndex() + 1)}>
|
||||
{(message) => (
|
||||
{(message, idx) => (
|
||||
<HostBubble
|
||||
message={message}
|
||||
typingEmulation={props.settings.typingEmulation}
|
||||
isTypingSkipped={
|
||||
(props.settings.typingEmulation?.isDisabledOnFirstMessage ??
|
||||
defaultSettings.typingEmulation
|
||||
.isDisabledOnFirstMessage) &&
|
||||
props.inputIndex === 0 &&
|
||||
idx() === 0
|
||||
}
|
||||
onTransitionEnd={displayNextMessage}
|
||||
onCompleted={props.onSubmit}
|
||||
/>
|
||||
|
||||
@@ -20,6 +20,7 @@ import { Match, Switch } from 'solid-js'
|
||||
type Props = {
|
||||
message: ChatMessage
|
||||
typingEmulation: Settings['typingEmulation']
|
||||
isTypingSkipped: boolean
|
||||
onTransitionEnd: (offsetTop?: number) => void
|
||||
onCompleted: (reply?: string) => void
|
||||
}
|
||||
@@ -38,6 +39,7 @@ export const HostBubble = (props: Props) => {
|
||||
<Match when={props.message.type === BubbleBlockType.TEXT}>
|
||||
<TextBubble
|
||||
content={props.message.content as TextBubbleBlock['content']}
|
||||
isTypingSkipped={props.isTypingSkipped}
|
||||
typingEmulation={props.typingEmulation}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
/>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { computeTypingDuration } from '@typebot.io/bot-engine/computeTypingDurat
|
||||
type Props = {
|
||||
content: TextBubbleBlock['content']
|
||||
typingEmulation: Settings['typingEmulation']
|
||||
isTypingSkipped: boolean
|
||||
onTransitionEnd: (offsetTop?: number) => void
|
||||
}
|
||||
|
||||
@@ -35,7 +36,7 @@ export const TextBubble = (props: Props) => {
|
||||
? computePlainText(props.content.richText)
|
||||
: ''
|
||||
const typingDuration =
|
||||
props.typingEmulation?.enabled === false
|
||||
props.typingEmulation?.enabled === false || props.isTypingSkipped
|
||||
? 0
|
||||
: computeTypingDuration({
|
||||
bubbleContent: plainText,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/nextjs",
|
||||
"version": "0.2.35",
|
||||
"version": "0.2.36",
|
||||
"description": "Convenient library to display typebots on your Next.js website",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/react",
|
||||
"version": "0.2.35",
|
||||
"version": "0.2.36",
|
||||
"description": "Convenient library to display typebots on your React app",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -10,7 +10,13 @@ export const defaultSettings = {
|
||||
isBrandingEnabled: false,
|
||||
isTypingEmulationEnabled: true,
|
||||
},
|
||||
typingEmulation: { enabled: true, speed: 300, maxDelay: 1.5 },
|
||||
typingEmulation: {
|
||||
enabled: true,
|
||||
speed: 400,
|
||||
maxDelay: 3,
|
||||
delayBetweenBubbles: 0,
|
||||
isDisabledOnFirstMessage: true,
|
||||
},
|
||||
metadata: {
|
||||
description:
|
||||
'Build beautiful conversational forms and embed them directly in your applications without a line of code. Triple your response rate and collect answers that has more value compared to a traditional form.',
|
||||
|
||||
@@ -20,6 +20,8 @@ const typingEmulation = z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
speed: z.number().optional(),
|
||||
maxDelay: z.number().optional(),
|
||||
delayBetweenBubbles: z.number().min(0).max(5).optional(),
|
||||
isDisabledOnFirstMessage: z.boolean().optional(),
|
||||
})
|
||||
|
||||
const metadataSchema = z.object({
|
||||
|
||||
Reference in New Issue
Block a user