@@ -12,21 +12,22 @@ import {
|
||||
} from '@/utils/storage'
|
||||
import { setCssVariablesValue } from '@/utils/setCssVariablesValue'
|
||||
import immutableCss from '../assets/immutable.css'
|
||||
import { InputBlock, StartElementId } from '@typebot.io/schemas'
|
||||
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||
|
||||
export type BotProps = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
typebot: string | any
|
||||
isPreview?: boolean
|
||||
resultId?: string
|
||||
startGroupId?: string
|
||||
prefilledVariables?: Record<string, unknown>
|
||||
apiHost?: string
|
||||
onNewInputBlock?: (ids: { id: string; groupId: string }) => void
|
||||
onNewInputBlock?: (inputBlock: InputBlock) => void
|
||||
onAnswer?: (answer: { message: string; blockId: string }) => void
|
||||
onInit?: () => void
|
||||
onEnd?: () => void
|
||||
onNewLogs?: (logs: OutgoingLog[]) => void
|
||||
}
|
||||
} & StartElementId
|
||||
|
||||
export const Bot = (props: BotProps & { class?: string }) => {
|
||||
const [initialChatReply, setInitialChatReply] = createSignal<
|
||||
@@ -54,11 +55,15 @@ export const Bot = (props: BotProps & { class?: string }) => {
|
||||
resultId: isNotEmpty(props.resultId)
|
||||
? props.resultId
|
||||
: getExistingResultIdFromStorage(typebotIdFromProps),
|
||||
startGroupId: props.startGroupId,
|
||||
prefilledVariables: {
|
||||
...prefilledVariables,
|
||||
...props.prefilledVariables,
|
||||
},
|
||||
...('startGroupId' in props
|
||||
? { startGroupId: props.startGroupId }
|
||||
: 'startEventId' in props
|
||||
? { startEventId: props.startEventId }
|
||||
: {}),
|
||||
})
|
||||
if (error && 'code' in error && typeof error.code === 'string') {
|
||||
if (typeof props.typebot !== 'string' || (props.isPreview ?? false)) {
|
||||
@@ -80,7 +85,7 @@ export const Bot = (props: BotProps & { class?: string }) => {
|
||||
}
|
||||
|
||||
if (data.resultId && typebotIdFromProps)
|
||||
setResultInStorage(data.typebot.settings.general.rememberUser?.storage)(
|
||||
setResultInStorage(data.typebot.settings.general?.rememberUser?.storage)(
|
||||
typebotIdFromProps,
|
||||
data.resultId
|
||||
)
|
||||
@@ -88,10 +93,7 @@ export const Bot = (props: BotProps & { class?: string }) => {
|
||||
setCustomCss(data.typebot.theme.customCss ?? '')
|
||||
|
||||
if (data.input?.id && props.onNewInputBlock)
|
||||
props.onNewInputBlock({
|
||||
id: data.input.id,
|
||||
groupId: data.input.groupId,
|
||||
})
|
||||
props.onNewInputBlock(data.input)
|
||||
if (data.logs) props.onNewLogs?.(data.logs)
|
||||
}
|
||||
|
||||
@@ -157,7 +159,7 @@ type BotContentProps = {
|
||||
initialChatReply: InitialChatReply
|
||||
context: BotContext
|
||||
class?: string
|
||||
onNewInputBlock?: (block: { id: string; groupId: string }) => void
|
||||
onNewInputBlock?: (inputBlock: InputBlock) => void
|
||||
onAnswer?: (answer: { message: string; blockId: string }) => void
|
||||
onEnd?: () => void
|
||||
onNewLogs?: (logs: OutgoingLog[]) => void
|
||||
@@ -176,13 +178,15 @@ const BotContent = (props: BotContentProps) => {
|
||||
existingFont
|
||||
?.getAttribute('href')
|
||||
?.includes(
|
||||
props.initialChatReply.typebot?.theme?.general?.font ?? 'Open Sans'
|
||||
props.initialChatReply.typebot?.theme?.general?.font ??
|
||||
defaultTheme.general.font
|
||||
)
|
||||
)
|
||||
return
|
||||
const font = document.createElement('link')
|
||||
font.href = `https://fonts.bunny.net/css2?family=${
|
||||
props.initialChatReply.typebot?.theme?.general?.font ?? 'Open Sans'
|
||||
props.initialChatReply.typebot?.theme?.general?.font ??
|
||||
defaultTheme.general.font
|
||||
}:ital,wght@0,300;0,400;0,600;1,300;1,400;1,600&display=swap');')`
|
||||
font.rel = 'stylesheet'
|
||||
font.id = 'bot-font'
|
||||
@@ -224,7 +228,9 @@ const BotContent = (props: BotContentProps) => {
|
||||
/>
|
||||
</div>
|
||||
<Show
|
||||
when={props.initialChatReply.typebot.settings.general.isBrandingEnabled}
|
||||
when={
|
||||
props.initialChatReply.typebot.settings.general?.isBrandingEnabled
|
||||
}
|
||||
>
|
||||
<LiteBadge botContainer={botContainer} />
|
||||
</Show>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { BotContext, ChatChunk as ChatChunkType } from '@/types'
|
||||
import { isMobile } from '@/utils/isMobileSignal'
|
||||
import type { ChatReply, Settings, Theme } from '@typebot.io/schemas'
|
||||
import { ChatReply, Settings, Theme } from '@typebot.io/schemas'
|
||||
import { createSignal, For, onMount, Show } from 'solid-js'
|
||||
import { HostBubble } from '../bubbles/HostBubble'
|
||||
import { InputChatBlock } from '../InputChatBlock'
|
||||
import { AvatarSideContainer } from './AvatarSideContainer'
|
||||
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'> & {
|
||||
theme: Theme
|
||||
@@ -56,12 +58,13 @@ export const ChatChunk = (props: Props) => {
|
||||
<div class={'flex' + (isMobile() ? ' gap-1' : ' gap-2')}>
|
||||
<Show
|
||||
when={
|
||||
props.theme.chat.hostAvatar?.isEnabled &&
|
||||
(props.theme.chat?.hostAvatar?.isEnabled ??
|
||||
defaultTheme.chat.hostAvatar.isEnabled) &&
|
||||
props.messages.length > 0
|
||||
}
|
||||
>
|
||||
<AvatarSideContainer
|
||||
hostAvatarSrc={props.theme.chat.hostAvatar?.url}
|
||||
hostAvatarSrc={props.theme.chat?.hostAvatar?.url}
|
||||
hideAvatar={props.hideAvatar}
|
||||
/>
|
||||
</Show>
|
||||
@@ -69,13 +72,15 @@ export const ChatChunk = (props: Props) => {
|
||||
<div
|
||||
class="flex flex-col flex-1 gap-2"
|
||||
style={{
|
||||
'max-width': props.theme.chat.guestAvatar?.isEnabled
|
||||
? isMobile()
|
||||
? 'calc(100% - 32px - 32px)'
|
||||
: 'calc(100% - 48px - 48px)'
|
||||
: isMobile()
|
||||
? 'calc(100% - 32px)'
|
||||
: 'calc(100% - 48px)',
|
||||
'max-width':
|
||||
props.theme.chat?.guestAvatar?.isEnabled ??
|
||||
defaultTheme.chat.guestAvatar.isEnabled
|
||||
? isMobile()
|
||||
? 'calc(100% - 32px - 32px)'
|
||||
: 'calc(100% - 48px - 48px)'
|
||||
: isMobile()
|
||||
? 'calc(100% - 32px)'
|
||||
: 'calc(100% - 48px)',
|
||||
}}
|
||||
>
|
||||
<For each={props.messages.slice(0, displayedMessageIndex() + 1)}>
|
||||
@@ -95,11 +100,15 @@ export const ChatChunk = (props: Props) => {
|
||||
ref={inputRef}
|
||||
block={props.input}
|
||||
inputIndex={props.inputIndex}
|
||||
hasHostAvatar={props.theme.chat.hostAvatar?.isEnabled ?? false}
|
||||
guestAvatar={props.theme.chat.guestAvatar}
|
||||
hasHostAvatar={
|
||||
props.theme.chat?.hostAvatar?.isEnabled ??
|
||||
defaultTheme.chat.hostAvatar.isEnabled
|
||||
}
|
||||
guestAvatar={props.theme.chat?.guestAvatar}
|
||||
context={props.context}
|
||||
isInputPrefillEnabled={
|
||||
props.settings.general.isInputPrefillEnabled ?? true
|
||||
props.settings.general?.isInputPrefillEnabled ??
|
||||
defaultSettings.general.isInputPrefillEnabled
|
||||
}
|
||||
hasError={props.hasError}
|
||||
onSubmit={props.onSubmit}
|
||||
@@ -109,9 +118,14 @@ export const ChatChunk = (props: Props) => {
|
||||
<Show when={props.streamingMessageId} keyed>
|
||||
{(streamingMessageId) => (
|
||||
<div class={'flex' + (isMobile() ? ' gap-1' : ' gap-2')}>
|
||||
<Show when={props.theme.chat.hostAvatar?.isEnabled}>
|
||||
<Show
|
||||
when={
|
||||
props.theme.chat?.hostAvatar?.isEnabled ??
|
||||
defaultTheme.chat.hostAvatar.isEnabled
|
||||
}
|
||||
>
|
||||
<AvatarSideContainer
|
||||
hostAvatarSrc={props.theme.chat.hostAvatar?.url}
|
||||
hostAvatarSrc={props.theme.chat?.hostAvatar?.url}
|
||||
hideAvatar={props.hideAvatar}
|
||||
/>
|
||||
</Show>
|
||||
@@ -119,13 +133,15 @@ export const ChatChunk = (props: Props) => {
|
||||
<div
|
||||
class="flex flex-col flex-1 gap-2"
|
||||
style={{
|
||||
'max-width': props.theme.chat.guestAvatar?.isEnabled
|
||||
? isMobile()
|
||||
? 'calc(100% - 32px - 32px)'
|
||||
: 'calc(100% - 48px - 48px)'
|
||||
: isMobile()
|
||||
? 'calc(100% - 32px)'
|
||||
: 'calc(100% - 48px)',
|
||||
'max-width':
|
||||
props.theme.chat?.hostAvatar?.isEnabled ??
|
||||
defaultTheme.chat.hostAvatar.isEnabled
|
||||
? isMobile()
|
||||
? 'calc(100% - 32px - 32px)'
|
||||
: 'calc(100% - 48px - 48px)'
|
||||
: isMobile()
|
||||
? 'calc(100% - 32px)'
|
||||
: 'calc(100% - 48px)',
|
||||
}}
|
||||
>
|
||||
<StreamingBubble streamingMessageId={streamingMessageId} />
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { ChatReply, SendMessageInput, Theme } from '@typebot.io/schemas'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/enums'
|
||||
import {
|
||||
ChatReply,
|
||||
InputBlock,
|
||||
SendMessageInput,
|
||||
Theme,
|
||||
} from '@typebot.io/schemas'
|
||||
import {
|
||||
createEffect,
|
||||
createSignal,
|
||||
@@ -25,6 +29,7 @@ import {
|
||||
formattedMessages,
|
||||
setFormattedMessages,
|
||||
} from '@/utils/formattedMessagesSignal'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
|
||||
const parseDynamicTheme = (
|
||||
initialTheme: Theme,
|
||||
@@ -34,26 +39,26 @@ const parseDynamicTheme = (
|
||||
chat: {
|
||||
...initialTheme.chat,
|
||||
hostAvatar:
|
||||
initialTheme.chat.hostAvatar && dynamicTheme?.hostAvatarUrl
|
||||
initialTheme.chat?.hostAvatar && dynamicTheme?.hostAvatarUrl
|
||||
? {
|
||||
...initialTheme.chat.hostAvatar,
|
||||
url: dynamicTheme.hostAvatarUrl,
|
||||
}
|
||||
: initialTheme.chat.hostAvatar,
|
||||
: initialTheme.chat?.hostAvatar,
|
||||
guestAvatar:
|
||||
initialTheme.chat.guestAvatar && dynamicTheme?.guestAvatarUrl
|
||||
initialTheme.chat?.guestAvatar && dynamicTheme?.guestAvatarUrl
|
||||
? {
|
||||
...initialTheme.chat.guestAvatar,
|
||||
url: dynamicTheme?.guestAvatarUrl,
|
||||
}
|
||||
: initialTheme.chat.guestAvatar,
|
||||
: initialTheme.chat?.guestAvatar,
|
||||
},
|
||||
})
|
||||
|
||||
type Props = {
|
||||
initialChatReply: InitialChatReply
|
||||
context: BotContext
|
||||
onNewInputBlock?: (ids: { id: string; groupId: string }) => void
|
||||
onNewInputBlock?: (inputBlock: InputBlock) => void
|
||||
onAnswer?: (answer: { message: string; blockId: string }) => void
|
||||
onEnd?: () => void
|
||||
onNewLogs?: (logs: OutgoingLog[]) => void
|
||||
@@ -178,11 +183,8 @@ export const ConversationContainer = (props: Props) => {
|
||||
}
|
||||
if (data.logs) props.onNewLogs?.(data.logs)
|
||||
if (data.dynamicTheme) setDynamicTheme(data.dynamicTheme)
|
||||
if (data.input?.id && props.onNewInputBlock) {
|
||||
props.onNewInputBlock({
|
||||
id: data.input.id,
|
||||
groupId: data.input.groupId,
|
||||
})
|
||||
if (data.input && props.onNewInputBlock) {
|
||||
props.onNewInputBlock(data.input)
|
||||
}
|
||||
if (data.clientSideActions) {
|
||||
const actionsBeforeFirstBubble = data.clientSideActions.filter((action) =>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Theme } from '@typebot.io/schemas'
|
||||
import { Show } from 'solid-js'
|
||||
import { LoadingBubble } from '../bubbles/LoadingBubble'
|
||||
import { AvatarSideContainer } from './AvatarSideContainer'
|
||||
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||
|
||||
type Props = {
|
||||
theme: Theme
|
||||
@@ -11,9 +12,14 @@ export const LoadingChunk = (props: Props) => (
|
||||
<div class="flex w-full">
|
||||
<div class="flex flex-col w-full min-w-0">
|
||||
<div class="flex gap-2">
|
||||
<Show when={props.theme.chat.hostAvatar?.isEnabled}>
|
||||
<Show
|
||||
when={
|
||||
props.theme.chat?.hostAvatar?.isEnabled ??
|
||||
defaultTheme.chat.hostAvatar.isEnabled
|
||||
}
|
||||
>
|
||||
<AvatarSideContainer
|
||||
hostAvatarSrc={props.theme.chat.hostAvatar?.url}
|
||||
hostAvatarSrc={props.theme.chat?.hostAvatar?.url}
|
||||
/>
|
||||
</Show>
|
||||
<LoadingBubble />
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import type {
|
||||
ChatReply,
|
||||
ChoiceInputBlock,
|
||||
DateInputOptions,
|
||||
EmailInputBlock,
|
||||
FileInputBlock,
|
||||
NumberInputBlock,
|
||||
PaymentInputOptions,
|
||||
PhoneNumberInputBlock,
|
||||
RatingInputBlock,
|
||||
RuntimeOptions,
|
||||
@@ -13,8 +11,9 @@ import type {
|
||||
Theme,
|
||||
UrlInputBlock,
|
||||
PictureChoiceBlock,
|
||||
PaymentInputBlock,
|
||||
DateInputBlock,
|
||||
} from '@typebot.io/schemas'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/enums'
|
||||
import { GuestBubble } from './bubbles/GuestBubble'
|
||||
import { BotContext, InputSubmitContent } from '@/types'
|
||||
import { TextInput } from '@/features/blocks/inputs/textInput'
|
||||
@@ -34,12 +33,15 @@ 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'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
import { defaultPaymentInputOptions } from '@typebot.io/schemas/features/blocks/inputs/payment/constants'
|
||||
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||
|
||||
type Props = {
|
||||
ref: HTMLDivElement | undefined
|
||||
block: NonNullable<ChatReply['input']>
|
||||
hasHostAvatar: boolean
|
||||
guestAvatar?: Theme['chat']['guestAvatar']
|
||||
guestAvatar?: NonNullable<Theme['chat']>['guestAvatar']
|
||||
inputIndex: number
|
||||
context: BotContext
|
||||
isInputPrefillEnabled: boolean
|
||||
@@ -74,7 +76,10 @@ export const InputChatBlock = (props: Props) => {
|
||||
<Match when={answer() && !props.hasError}>
|
||||
<GuestBubble
|
||||
message={formattedMessage() ?? (answer() as string)}
|
||||
showAvatar={props.guestAvatar?.isEnabled ?? false}
|
||||
showAvatar={
|
||||
props.guestAvatar?.isEnabled ??
|
||||
defaultTheme.chat.guestAvatar.isEnabled
|
||||
}
|
||||
avatarSrc={props.guestAvatar?.url && props.guestAvatar.url}
|
||||
/>
|
||||
</Match>
|
||||
@@ -122,8 +127,8 @@ const Input = (props: {
|
||||
const submitPaymentSuccess = () =>
|
||||
props.onSubmit({
|
||||
value:
|
||||
(props.block.options as PaymentInputOptions).labels.success ??
|
||||
'Success',
|
||||
(props.block.options as PaymentInputBlock['options'])?.labels
|
||||
?.success ?? defaultPaymentInputOptions.labels.success,
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -158,9 +163,9 @@ const Input = (props: {
|
||||
</Match>
|
||||
<Match when={props.block.type === InputBlockType.PHONE}>
|
||||
<PhoneInput
|
||||
labels={(props.block as PhoneNumberInputBlock).options.labels}
|
||||
labels={(props.block as PhoneNumberInputBlock).options?.labels}
|
||||
defaultCountryCode={
|
||||
(props.block as PhoneNumberInputBlock).options.defaultCountryCode
|
||||
(props.block as PhoneNumberInputBlock).options?.defaultCountryCode
|
||||
}
|
||||
defaultValue={getPrefilledValue()}
|
||||
onSubmit={onSubmit}
|
||||
@@ -168,7 +173,7 @@ const Input = (props: {
|
||||
</Match>
|
||||
<Match when={props.block.type === InputBlockType.DATE}>
|
||||
<DateForm
|
||||
options={props.block.options as DateInputOptions}
|
||||
options={props.block.options as DateInputBlock['options']}
|
||||
defaultValue={getPrefilledValue()}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
@@ -176,7 +181,7 @@ const Input = (props: {
|
||||
<Match when={isButtonsBlock(props.block)} keyed>
|
||||
{(block) => (
|
||||
<Switch>
|
||||
<Match when={!block.options.isMultipleChoice}>
|
||||
<Match when={!block.options?.isMultipleChoice}>
|
||||
<Buttons
|
||||
inputIndex={props.inputIndex}
|
||||
defaultItems={block.items}
|
||||
@@ -184,7 +189,7 @@ const Input = (props: {
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={block.options.isMultipleChoice}>
|
||||
<Match when={block.options?.isMultipleChoice}>
|
||||
<MultipleChoicesForm
|
||||
inputIndex={props.inputIndex}
|
||||
defaultItems={block.items}
|
||||
@@ -198,14 +203,14 @@ const Input = (props: {
|
||||
<Match when={isPictureChoiceBlock(props.block)} keyed>
|
||||
{(block) => (
|
||||
<Switch>
|
||||
<Match when={!block.options.isMultipleChoice}>
|
||||
<Match when={!block.options?.isMultipleChoice}>
|
||||
<SinglePictureChoice
|
||||
defaultItems={block.items}
|
||||
options={block.options}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={block.options.isMultipleChoice}>
|
||||
<Match when={block.options?.isMultipleChoice}>
|
||||
<MultiplePictureChoice
|
||||
defaultItems={block.items}
|
||||
options={block.options}
|
||||
@@ -237,7 +242,7 @@ const Input = (props: {
|
||||
{
|
||||
...props.block.options,
|
||||
...props.block.runtimeOptions,
|
||||
} as PaymentInputOptions & RuntimeOptions
|
||||
} as PaymentInputBlock['options'] & RuntimeOptions
|
||||
}
|
||||
onSuccess={submitPaymentSuccess}
|
||||
/>
|
||||
|
||||
@@ -4,20 +4,20 @@ import { ImageBubble } from '@/features/blocks/bubbles/image'
|
||||
import { TextBubble } from '@/features/blocks/bubbles/textBubble'
|
||||
import { VideoBubble } from '@/features/blocks/bubbles/video'
|
||||
import type {
|
||||
AudioBubbleContent,
|
||||
AudioBubbleBlock,
|
||||
ChatMessage,
|
||||
EmbedBubbleContent,
|
||||
ImageBubbleContent,
|
||||
TextBubbleContent,
|
||||
TypingEmulation,
|
||||
VideoBubbleContent,
|
||||
EmbedBubbleBlock,
|
||||
ImageBubbleBlock,
|
||||
Settings,
|
||||
TextBubbleBlock,
|
||||
VideoBubbleBlock,
|
||||
} from '@typebot.io/schemas'
|
||||
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/enums'
|
||||
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
|
||||
import { Match, Switch } from 'solid-js'
|
||||
|
||||
type Props = {
|
||||
message: ChatMessage
|
||||
typingEmulation: TypingEmulation
|
||||
typingEmulation: Settings['typingEmulation']
|
||||
onTransitionEnd: (offsetTop?: number) => void
|
||||
}
|
||||
|
||||
@@ -30,32 +30,32 @@ export const HostBubble = (props: Props) => {
|
||||
<Switch>
|
||||
<Match when={props.message.type === BubbleBlockType.TEXT}>
|
||||
<TextBubble
|
||||
content={props.message.content as TextBubbleContent}
|
||||
content={props.message.content as TextBubbleBlock['content']}
|
||||
typingEmulation={props.typingEmulation}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={props.message.type === BubbleBlockType.IMAGE}>
|
||||
<ImageBubble
|
||||
content={props.message.content as ImageBubbleContent}
|
||||
content={props.message.content as ImageBubbleBlock['content']}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={props.message.type === BubbleBlockType.VIDEO}>
|
||||
<VideoBubble
|
||||
content={props.message.content as VideoBubbleContent}
|
||||
content={props.message.content as VideoBubbleBlock['content']}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={props.message.type === BubbleBlockType.EMBED}>
|
||||
<EmbedBubble
|
||||
content={props.message.content as EmbedBubbleContent}
|
||||
content={props.message.content as EmbedBubbleBlock['content']}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={props.message.type === BubbleBlockType.AUDIO}>
|
||||
<AudioBubble
|
||||
content={props.message.content as AudioBubbleContent}
|
||||
content={props.message.content as AudioBubbleBlock['content']}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
/>
|
||||
</Match>
|
||||
|
||||
Reference in New Issue
Block a user