2
0

feat(steps): Add Embed bubble

This commit is contained in:
Baptiste Arnaud
2022-03-23 12:01:35 +01:00
parent c01ffa3f0b
commit 953b95d254
15 changed files with 296 additions and 16 deletions

View File

@ -197,9 +197,12 @@ const ChatChunks = ({
const avatarSideContainerRef = useRef<any>()
useEffect(() => {
avatarSideContainerRef.current?.refreshTopOffset()
refreshTopOffset()
})
const refreshTopOffset = () =>
avatarSideContainerRef.current?.refreshTopOffset()
return (
<>
<div className="flex">
@ -209,18 +212,26 @@ const ChatChunks = ({
hostAvatarSrc={hostAvatar.src}
/>
)}
<TransitionGroup>
{bubbles.map((step) => (
<CSSTransition
key={step.id}
classNames="bubble"
timeout={500}
unmountOnExit
>
<HostBubble step={step} onTransitionEnd={onDisplayNextStep} />
</CSSTransition>
))}
</TransitionGroup>
<div className="flex-1">
<TransitionGroup>
{bubbles.map((step) => (
<CSSTransition
key={step.id}
classNames="bubble"
timeout={500}
unmountOnExit
>
<HostBubble
step={step}
onTransitionEnd={() => {
onDisplayNextStep()
refreshTopOffset()
}}
/>
</CSSTransition>
))}
</TransitionGroup>
</div>
</div>
<CSSTransition
classNames="bubble"

View File

@ -0,0 +1,68 @@
import React, { useEffect, useRef, useState } from 'react'
import { EmbedBubbleStep } from 'models'
import { TypingContent } from './TypingContent'
type Props = {
step: EmbedBubbleStep
onTransitionEnd: () => void
}
export const showAnimationDuration = 400
export const EmbedBubble = ({ step, onTransitionEnd }: Props) => {
const messageContainer = useRef<HTMLDivElement | null>(null)
const [isTyping, setIsTyping] = useState(true)
useEffect(() => {
showContentAfterMediaLoad()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const showContentAfterMediaLoad = () => {
setTimeout(() => {
setIsTyping(false)
onTypingEnd()
}, 1000)
}
const onTypingEnd = () => {
setIsTyping(false)
setTimeout(() => {
onTransitionEnd()
}, showAnimationDuration)
}
return (
<div className="flex flex-col w-full" ref={messageContainer}>
<div className="flex mb-2 w-full lg:w-11/12 items-center">
<div
className={
'flex relative z-10 items-start typebot-host-bubble w-full'
}
>
<div
className="flex items-center absolute px-4 py-2 rounded-lg bubble-typing z-10 "
style={{
width: isTyping ? '4rem' : '100%',
height: isTyping ? '2rem' : '100%',
}}
>
{isTyping ? <TypingContent /> : <></>}
</div>
<iframe
id="embed-bubble-content"
src={step.content.url}
className={
'w-full z-20 p-4 content-opacity ' +
(isTyping ? 'opacity-0' : 'opacity-100')
}
style={{
height: isTyping ? '2rem' : step.content.height,
borderRadius: '15px',
}}
/>
</div>
</div>
</div>
)
}

View File

@ -1,5 +1,6 @@
import { BubbleStep, BubbleStepType } from 'models'
import React from 'react'
import { EmbedBubble } from './EmbedBubble'
import { ImageBubble } from './ImageBubble'
import { TextBubble } from './TextBubble'
import { VideoBubble } from './VideoBubble'
@ -17,5 +18,7 @@ export const HostBubble = ({ step, onTransitionEnd }: Props) => {
return <ImageBubble step={step} onTransitionEnd={onTransitionEnd} />
case BubbleStepType.VIDEO:
return <VideoBubble step={step} onTransitionEnd={onTransitionEnd} />
case BubbleStepType.EMBED:
return <EmbedBubble step={step} onTransitionEnd={onTransitionEnd} />
}
}

View File

@ -1,17 +1,23 @@
import { StepBase } from '.'
export type BubbleStep = TextBubbleStep | ImageBubbleStep | VideoBubbleStep
export type BubbleStep =
| TextBubbleStep
| ImageBubbleStep
| VideoBubbleStep
| EmbedBubbleStep
export enum BubbleStepType {
TEXT = 'text',
IMAGE = 'image',
VIDEO = 'video',
EMBED = 'embed',
}
export type BubbleStepContent =
| TextBubbleContent
| ImageBubbleContent
| VideoBubbleContent
| EmbedBubbleContent
export type TextBubbleStep = StepBase & {
type: BubbleStepType.TEXT
@ -28,6 +34,11 @@ export type VideoBubbleStep = StepBase & {
content: VideoBubbleContent
}
export type EmbedBubbleStep = StepBase & {
type: BubbleStepType.EMBED
content: EmbedBubbleContent
}
export type TextBubbleContent = {
html: string
richText: unknown[]
@ -38,6 +49,11 @@ export type ImageBubbleContent = {
url?: string
}
export type EmbedBubbleContent = {
url?: string
height: number
}
export enum VideoBubbleContentType {
URL = 'url',
YOUTUBE = 'youtube',
@ -59,3 +75,5 @@ export const defaultTextBubbleContent: TextBubbleContent = {
export const defaultImageBubbleContent: ImageBubbleContent = {}
export const defaultVideoBubbleContent: VideoBubbleContent = {}
export const defaultEmbedBubbleContent: EmbedBubbleContent = { height: 400 }

View File

@ -149,3 +149,11 @@ export const omit: Omit = (obj, ...keys) => {
}
return ret
}
export const sanitizeUrl = (url: string): string =>
url.startsWith('http') ||
url.startsWith('mailto:') ||
url.startsWith('tel:') ||
url.startsWith('sms:')
? url
: `https://${url}`