@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/js",
|
||||
"version": "0.2.15",
|
||||
"version": "0.2.16",
|
||||
"description": "Javascript library to display typebots on your website",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { LiteBadge } from './LiteBadge'
|
||||
import { createEffect, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||
import { isNotDefined, isNotEmpty } from '@typebot.io/lib'
|
||||
import { getInitialChatReplyQuery } from '@/queries/getInitialChatReplyQuery'
|
||||
import { startChatQuery } from '@/queries/startChatQuery'
|
||||
import { ConversationContainer } from './ConversationContainer'
|
||||
import { setIsMobile } from '@/utils/isMobileSignal'
|
||||
import { BotContext, InitialChatReply, OutgoingLog } from '@/types'
|
||||
@@ -12,7 +12,8 @@ import {
|
||||
} from '@/utils/storage'
|
||||
import { setCssVariablesValue } from '@/utils/setCssVariablesValue'
|
||||
import immutableCss from '../assets/immutable.css'
|
||||
import { InputBlock, StartElementId } from '@typebot.io/schemas'
|
||||
import { InputBlock } from '@typebot.io/schemas'
|
||||
import { StartFrom } from '@typebot.io/schemas'
|
||||
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||
|
||||
export type BotProps = {
|
||||
@@ -27,7 +28,8 @@ export type BotProps = {
|
||||
onInit?: () => void
|
||||
onEnd?: () => void
|
||||
onNewLogs?: (logs: OutgoingLog[]) => void
|
||||
} & StartElementId
|
||||
startFrom?: StartFrom
|
||||
}
|
||||
|
||||
export const Bot = (props: BotProps & { class?: string }) => {
|
||||
const [initialChatReply, setInitialChatReply] = createSignal<
|
||||
@@ -47,11 +49,13 @@ export const Bot = (props: BotProps & { class?: string }) => {
|
||||
})
|
||||
const typebotIdFromProps =
|
||||
typeof props.typebot === 'string' ? props.typebot : undefined
|
||||
const { data, error } = await getInitialChatReplyQuery({
|
||||
const isPreview =
|
||||
typeof props.typebot !== 'string' || (props.isPreview ?? false)
|
||||
const { data, error } = await startChatQuery({
|
||||
stripeRedirectStatus: urlParams.get('redirect_status') ?? undefined,
|
||||
typebot: props.typebot,
|
||||
apiHost: props.apiHost,
|
||||
isPreview: props.isPreview ?? false,
|
||||
isPreview,
|
||||
resultId: isNotEmpty(props.resultId)
|
||||
? props.resultId
|
||||
: getExistingResultIdFromStorage(typebotIdFromProps),
|
||||
@@ -59,14 +63,10 @@ export const Bot = (props: BotProps & { class?: string }) => {
|
||||
...prefilledVariables,
|
||||
...props.prefilledVariables,
|
||||
},
|
||||
...('startGroupId' in props
|
||||
? { startGroupId: props.startGroupId }
|
||||
: 'startEventId' in props
|
||||
? { startEventId: props.startEventId }
|
||||
: {}),
|
||||
startFrom: props.startFrom,
|
||||
})
|
||||
if (error && 'code' in error && typeof error.code === 'string') {
|
||||
if (typeof props.typebot !== 'string' || (props.isPreview ?? false)) {
|
||||
if (isPreview) {
|
||||
return setError(
|
||||
new Error('An error occurred while loading the bot.', {
|
||||
cause: error.message,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BotContext, ChatChunk as ChatChunkType } from '@/types'
|
||||
import { isMobile } from '@/utils/isMobileSignal'
|
||||
import { ChatReply, Settings, Theme } from '@typebot.io/schemas'
|
||||
import { ContinueChatResponse, Settings, Theme } from '@typebot.io/schemas'
|
||||
import { createSignal, For, onMount, Show } from 'solid-js'
|
||||
import { HostBubble } from '../bubbles/HostBubble'
|
||||
import { InputChatBlock } from '../InputChatBlock'
|
||||
@@ -9,7 +9,7 @@ import { StreamingBubble } from '../bubbles/StreamingBubble'
|
||||
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
|
||||
type Props = Pick<ChatReply, 'messages' | 'input'> & {
|
||||
type Props = Pick<ContinueChatResponse, 'messages' | 'input'> & {
|
||||
theme: Theme
|
||||
settings: Settings
|
||||
inputIndex: number
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
ChatReply,
|
||||
ContinueChatResponse,
|
||||
InputBlock,
|
||||
SendMessageInput,
|
||||
Theme,
|
||||
ChatLog,
|
||||
} from '@typebot.io/schemas'
|
||||
import {
|
||||
createEffect,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
onMount,
|
||||
Show,
|
||||
} from 'solid-js'
|
||||
import { sendMessageQuery } from '@/queries/sendMessageQuery'
|
||||
import { continueChatQuery } from '@/queries/continueChatQuery'
|
||||
import { ChatChunk } from './ChatChunk'
|
||||
import {
|
||||
BotContext,
|
||||
@@ -30,10 +30,11 @@ import {
|
||||
setFormattedMessages,
|
||||
} from '@/utils/formattedMessagesSignal'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
import { saveClientLogsQuery } from '@/queries/saveClientLogsQuery'
|
||||
|
||||
const parseDynamicTheme = (
|
||||
initialTheme: Theme,
|
||||
dynamicTheme: ChatReply['dynamicTheme']
|
||||
dynamicTheme: ContinueChatResponse['dynamicTheme']
|
||||
): Theme => ({
|
||||
...initialTheme,
|
||||
chat: {
|
||||
@@ -74,7 +75,7 @@ export const ConversationContainer = (props: Props) => {
|
||||
},
|
||||
])
|
||||
const [dynamicTheme, setDynamicTheme] = createSignal<
|
||||
ChatReply['dynamicTheme']
|
||||
ContinueChatResponse['dynamicTheme']
|
||||
>(props.initialChatReply.dynamicTheme)
|
||||
const [theme, setTheme] = createSignal(props.initialChatReply.typebot.theme)
|
||||
const [isSending, setIsSending] = createSignal(false)
|
||||
@@ -136,9 +137,16 @@ export const ConversationContainer = (props: Props) => {
|
||||
|
||||
const sendMessage = async (
|
||||
message: string | undefined,
|
||||
clientLogs?: SendMessageInput['clientLogs']
|
||||
clientLogs?: ChatLog[]
|
||||
) => {
|
||||
if (clientLogs) props.onNewLogs?.(clientLogs)
|
||||
if (clientLogs) {
|
||||
props.onNewLogs?.(clientLogs)
|
||||
await saveClientLogsQuery({
|
||||
apiHost: props.context.apiHost,
|
||||
sessionId: props.initialChatReply.sessionId,
|
||||
clientLogs,
|
||||
})
|
||||
}
|
||||
setHasError(false)
|
||||
const currentInputBlock = [...chatChunks()].pop()?.input
|
||||
if (currentInputBlock?.id && props.onAnswer && message)
|
||||
@@ -153,11 +161,10 @@ export const ConversationContainer = (props: Props) => {
|
||||
const longRequest = setTimeout(() => {
|
||||
setIsSending(true)
|
||||
}, 1000)
|
||||
const { data, error } = await sendMessageQuery({
|
||||
const { data, error } = await continueChatQuery({
|
||||
apiHost: props.context.apiHost,
|
||||
sessionId: props.initialChatReply.sessionId,
|
||||
message,
|
||||
clientLogs,
|
||||
})
|
||||
clearTimeout(longRequest)
|
||||
setIsSending(false)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type {
|
||||
ChatReply,
|
||||
ContinueChatResponse,
|
||||
ChoiceInputBlock,
|
||||
EmailInputBlock,
|
||||
FileInputBlock,
|
||||
@@ -39,7 +39,7 @@ import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constan
|
||||
|
||||
type Props = {
|
||||
ref: HTMLDivElement | undefined
|
||||
block: NonNullable<ChatReply['input']>
|
||||
block: NonNullable<ContinueChatResponse['input']>
|
||||
hasHostAvatar: boolean
|
||||
guestAvatar?: NonNullable<Theme['chat']>['guestAvatar']
|
||||
inputIndex: number
|
||||
@@ -113,7 +113,7 @@ export const InputChatBlock = (props: Props) => {
|
||||
|
||||
const Input = (props: {
|
||||
context: BotContext
|
||||
block: NonNullable<ChatReply['input']>
|
||||
block: NonNullable<ContinueChatResponse['input']>
|
||||
inputIndex: number
|
||||
isInputPrefillEnabled: boolean
|
||||
onSubmit: (answer: InputSubmitContent) => void
|
||||
@@ -252,11 +252,11 @@ const Input = (props: {
|
||||
}
|
||||
|
||||
const isButtonsBlock = (
|
||||
block: ChatReply['input']
|
||||
block: ContinueChatResponse['input']
|
||||
): ChoiceInputBlock | undefined =>
|
||||
block?.type === InputBlockType.CHOICE ? block : undefined
|
||||
|
||||
const isPictureChoiceBlock = (
|
||||
block: ChatReply['input']
|
||||
block: ContinueChatResponse['input']
|
||||
): PictureChoiceBlock | undefined =>
|
||||
block?.type === InputBlockType.PICTURE_CHOICE ? block : undefined
|
||||
|
||||
@@ -10,7 +10,7 @@ export const defaultBotProps: BotProps = {
|
||||
onInit: undefined,
|
||||
onNewLogs: undefined,
|
||||
isPreview: undefined,
|
||||
startGroupId: undefined,
|
||||
startFrom: undefined,
|
||||
prefilledVariables: undefined,
|
||||
apiHost: undefined,
|
||||
resultId: undefined,
|
||||
|
||||
22
packages/embeds/js/src/queries/continueChatQuery.ts
Normal file
22
packages/embeds/js/src/queries/continueChatQuery.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { guessApiHost } from '@/utils/guessApiHost'
|
||||
import { isNotEmpty, sendRequest } from '@typebot.io/lib'
|
||||
import { ContinueChatResponse } from '@typebot.io/schemas'
|
||||
|
||||
export const continueChatQuery = ({
|
||||
apiHost,
|
||||
message,
|
||||
sessionId,
|
||||
}: {
|
||||
apiHost?: string
|
||||
message: string | undefined
|
||||
sessionId: string
|
||||
}) =>
|
||||
sendRequest<ContinueChatResponse>({
|
||||
method: 'POST',
|
||||
url: `${
|
||||
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
||||
}/api/v1/sessions/${sessionId}/continueChat`,
|
||||
body: {
|
||||
message,
|
||||
},
|
||||
})
|
||||
@@ -1,74 +0,0 @@
|
||||
import { BotContext, InitialChatReply } from '@/types'
|
||||
import { guessApiHost } from '@/utils/guessApiHost'
|
||||
import type {
|
||||
SendMessageInput,
|
||||
StartElementId,
|
||||
StartParams,
|
||||
} from '@typebot.io/schemas'
|
||||
import { isNotDefined, isNotEmpty, sendRequest } from '@typebot.io/lib'
|
||||
import {
|
||||
getPaymentInProgressInStorage,
|
||||
removePaymentInProgressFromStorage,
|
||||
} from '@/features/blocks/inputs/payment/helpers/paymentInProgressStorage'
|
||||
|
||||
export async function getInitialChatReplyQuery({
|
||||
typebot,
|
||||
isPreview,
|
||||
apiHost,
|
||||
prefilledVariables,
|
||||
resultId,
|
||||
stripeRedirectStatus,
|
||||
...props
|
||||
}: StartParams & {
|
||||
stripeRedirectStatus?: string
|
||||
apiHost?: string
|
||||
} & StartElementId) {
|
||||
if (isNotDefined(typebot))
|
||||
throw new Error('Typebot ID is required to get initial messages')
|
||||
|
||||
const paymentInProgressStateStr = getPaymentInProgressInStorage() ?? undefined
|
||||
const paymentInProgressState = paymentInProgressStateStr
|
||||
? (JSON.parse(paymentInProgressStateStr) as {
|
||||
sessionId: string
|
||||
typebot: BotContext['typebot']
|
||||
})
|
||||
: undefined
|
||||
if (paymentInProgressState) removePaymentInProgressFromStorage()
|
||||
const { data, error } = await sendRequest<InitialChatReply>({
|
||||
method: 'POST',
|
||||
url: `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v2/sendMessage`,
|
||||
body: {
|
||||
startParams: paymentInProgressState
|
||||
? undefined
|
||||
: {
|
||||
isPreview,
|
||||
typebot,
|
||||
prefilledVariables,
|
||||
resultId,
|
||||
isStreamEnabled: true,
|
||||
startGroupId:
|
||||
'startGroupId' in props ? props.startGroupId : undefined,
|
||||
startEventId:
|
||||
'startEventId' in props ? props.startEventId : undefined,
|
||||
},
|
||||
sessionId: paymentInProgressState?.sessionId,
|
||||
message: paymentInProgressState
|
||||
? stripeRedirectStatus === 'failed'
|
||||
? 'fail'
|
||||
: 'Success'
|
||||
: undefined,
|
||||
} satisfies SendMessageInput,
|
||||
})
|
||||
|
||||
return {
|
||||
data: data
|
||||
? {
|
||||
...data,
|
||||
...(paymentInProgressState
|
||||
? { typebot: paymentInProgressState.typebot }
|
||||
: {}),
|
||||
}
|
||||
: undefined,
|
||||
error,
|
||||
}
|
||||
}
|
||||
22
packages/embeds/js/src/queries/saveClientLogsQuery.ts
Normal file
22
packages/embeds/js/src/queries/saveClientLogsQuery.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { guessApiHost } from '@/utils/guessApiHost'
|
||||
import type { ChatLog } from '@typebot.io/schemas'
|
||||
import { isNotEmpty, sendRequest } from '@typebot.io/lib'
|
||||
|
||||
export const saveClientLogsQuery = ({
|
||||
apiHost,
|
||||
sessionId,
|
||||
clientLogs,
|
||||
}: {
|
||||
apiHost?: string
|
||||
sessionId: string
|
||||
clientLogs: ChatLog[]
|
||||
}) =>
|
||||
sendRequest({
|
||||
method: 'POST',
|
||||
url: `${
|
||||
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
||||
}/api/v1/sessions/${sessionId}/clientLogs`,
|
||||
body: {
|
||||
clientLogs,
|
||||
},
|
||||
})
|
||||
@@ -1,13 +0,0 @@
|
||||
import { guessApiHost } from '@/utils/guessApiHost'
|
||||
import type { ChatReply, SendMessageInput } from '@typebot.io/schemas'
|
||||
import { isNotEmpty, sendRequest } from '@typebot.io/lib'
|
||||
|
||||
export const sendMessageQuery = ({
|
||||
apiHost,
|
||||
...body
|
||||
}: SendMessageInput & { apiHost?: string }) =>
|
||||
sendRequest<ChatReply>({
|
||||
method: 'POST',
|
||||
url: `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v2/sendMessage`,
|
||||
body,
|
||||
})
|
||||
104
packages/embeds/js/src/queries/startChatQuery.ts
Normal file
104
packages/embeds/js/src/queries/startChatQuery.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { BotContext, InitialChatReply } from '@/types'
|
||||
import { guessApiHost } from '@/utils/guessApiHost'
|
||||
import { isNotDefined, isNotEmpty, sendRequest } from '@typebot.io/lib'
|
||||
import {
|
||||
getPaymentInProgressInStorage,
|
||||
removePaymentInProgressFromStorage,
|
||||
} from '@/features/blocks/inputs/payment/helpers/paymentInProgressStorage'
|
||||
import {
|
||||
StartChatInput,
|
||||
StartFrom,
|
||||
StartPreviewChatInput,
|
||||
} from '@typebot.io/schemas'
|
||||
|
||||
export async function startChatQuery({
|
||||
typebot,
|
||||
isPreview,
|
||||
apiHost,
|
||||
prefilledVariables,
|
||||
resultId,
|
||||
stripeRedirectStatus,
|
||||
startFrom,
|
||||
}: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
typebot: string | any
|
||||
stripeRedirectStatus?: string
|
||||
apiHost?: string
|
||||
startFrom?: StartFrom
|
||||
isPreview: boolean
|
||||
prefilledVariables?: Record<string, unknown>
|
||||
resultId?: string
|
||||
}) {
|
||||
if (isNotDefined(typebot))
|
||||
throw new Error('Typebot ID is required to get initial messages')
|
||||
|
||||
const paymentInProgressStateStr = getPaymentInProgressInStorage() ?? undefined
|
||||
const paymentInProgressState = paymentInProgressStateStr
|
||||
? (JSON.parse(paymentInProgressStateStr) as {
|
||||
sessionId: string
|
||||
typebot: BotContext['typebot']
|
||||
})
|
||||
: undefined
|
||||
if (paymentInProgressState) {
|
||||
removePaymentInProgressFromStorage()
|
||||
const { data, error } = await sendRequest<InitialChatReply>({
|
||||
method: 'POST',
|
||||
url: `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v1/sessions/${
|
||||
paymentInProgressState.sessionId
|
||||
}/continueChat`,
|
||||
body: {
|
||||
message: paymentInProgressState
|
||||
? stripeRedirectStatus === 'failed'
|
||||
? 'fail'
|
||||
: 'Success'
|
||||
: undefined,
|
||||
},
|
||||
})
|
||||
return {
|
||||
data: data
|
||||
? {
|
||||
...data,
|
||||
...(paymentInProgressState
|
||||
? { typebot: paymentInProgressState.typebot }
|
||||
: {}),
|
||||
}
|
||||
: undefined,
|
||||
error,
|
||||
}
|
||||
}
|
||||
const typebotId = typeof typebot === 'string' ? typebot : typebot.id
|
||||
if (isPreview) {
|
||||
const { data, error } = await sendRequest<InitialChatReply>({
|
||||
method: 'POST',
|
||||
url: `${
|
||||
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
||||
}/api/v1/typebots/${typebotId}/preview/startChat`,
|
||||
body: {
|
||||
isStreamEnabled: true,
|
||||
startFrom,
|
||||
typebot,
|
||||
} satisfies Omit<StartPreviewChatInput, 'typebotId'>,
|
||||
})
|
||||
return {
|
||||
data,
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
const { data, error } = await sendRequest<InitialChatReply>({
|
||||
method: 'POST',
|
||||
url: `${
|
||||
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
||||
}/api/v1/typebots/${typebotId}/startChat`,
|
||||
body: {
|
||||
isStreamEnabled: true,
|
||||
prefilledVariables,
|
||||
resultId,
|
||||
} satisfies Omit<StartChatInput, 'publicId'>,
|
||||
})
|
||||
|
||||
return {
|
||||
data,
|
||||
error,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ChatReply } from '@typebot.io/schemas'
|
||||
import { ContinueChatResponse, StartChatResponse } from '@typebot.io/schemas'
|
||||
|
||||
export type InputSubmitContent = {
|
||||
label?: string
|
||||
@@ -13,9 +13,9 @@ export type BotContext = {
|
||||
sessionId: string
|
||||
}
|
||||
|
||||
export type InitialChatReply = ChatReply & {
|
||||
typebot: NonNullable<ChatReply['typebot']>
|
||||
sessionId: NonNullable<ChatReply['sessionId']>
|
||||
export type InitialChatReply = StartChatResponse & {
|
||||
typebot: NonNullable<StartChatResponse['typebot']>
|
||||
sessionId: NonNullable<StartChatResponse['sessionId']>
|
||||
}
|
||||
|
||||
export type OutgoingLog = {
|
||||
@@ -30,7 +30,7 @@ export type ClientSideActionContext = {
|
||||
}
|
||||
|
||||
export type ChatChunk = Pick<
|
||||
ChatReply,
|
||||
ContinueChatResponse,
|
||||
'messages' | 'input' | 'clientSideActions'
|
||||
> & {
|
||||
streamingMessageId?: string
|
||||
|
||||
@@ -8,11 +8,11 @@ import { executeWait } from '@/features/blocks/logic/wait/utils/executeWait'
|
||||
import { executeWebhook } from '@/features/blocks/integrations/webhook/executeWebhook'
|
||||
import { executePixel } from '@/features/blocks/integrations/pixel/executePixel'
|
||||
import { ClientSideActionContext } from '@/types'
|
||||
import type { ChatReply, ReplyLog } from '@typebot.io/schemas'
|
||||
import type { ContinueChatResponse, ChatLog } from '@typebot.io/schemas'
|
||||
import { injectStartProps } from './injectStartProps'
|
||||
|
||||
type Props = {
|
||||
clientSideAction: NonNullable<ChatReply['clientSideActions']>[0]
|
||||
clientSideAction: NonNullable<ContinueChatResponse['clientSideActions']>[0]
|
||||
context: ClientSideActionContext
|
||||
onMessageStream?: (props: { id: string; message: string }) => void
|
||||
}
|
||||
@@ -23,7 +23,7 @@ export const executeClientSideAction = async ({
|
||||
onMessageStream,
|
||||
}: Props): Promise<
|
||||
| { blockedPopupUrl: string }
|
||||
| { replyToSend: string | undefined; logs?: ReplyLog[] }
|
||||
| { replyToSend: string | undefined; logs?: ChatLog[] }
|
||||
| void
|
||||
> => {
|
||||
if ('chatwoot' in clientSideAction) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/nextjs",
|
||||
"version": "0.2.15",
|
||||
"version": "0.2.16",
|
||||
"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.15",
|
||||
"version": "0.2.16",
|
||||
"description": "Convenient library to display typebots on your React app",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
Reference in New Issue
Block a user