2
0

💄 (embed) Improve avatar alignment and audio auto load

This commit is contained in:
Baptiste Arnaud
2023-07-31 18:22:50 +02:00
parent e34b939786
commit 14c3d95b8a
9 changed files with 101 additions and 78 deletions

View File

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

View File

@@ -76,10 +76,10 @@
@keyframes chatBubbles { @keyframes chatBubbles {
0% { 0% {
transform: translateY(0); transform: translateY(2.5);
} }
50% { 50% {
transform: translateY(-5px); transform: translateY(-2.5px);
} }
100% { 100% {
transform: translateY(0); transform: translateY(0);

View File

@@ -1,4 +1,5 @@
import { TypingBubble } from '@/components' import { TypingBubble } from '@/components'
import { isMobile } from '@/utils/isMobileSignal'
import type { AudioBubbleContent } from '@typebot.io/schemas' import type { AudioBubbleContent } from '@typebot.io/schemas'
import { createSignal, onCleanup, onMount } from 'solid-js' import { createSignal, onCleanup, onMount } from 'solid-js'
@@ -8,7 +9,7 @@ type Props = {
} }
const showAnimationDuration = 400 const showAnimationDuration = 400
const typingDuration = 500 const typingDuration = 100
let typingTimeout: NodeJS.Timeout let typingTimeout: NodeJS.Timeout
@@ -18,7 +19,8 @@ export const AudioBubble = (props: Props) => {
let audioElement: HTMLAudioElement | undefined let audioElement: HTMLAudioElement | undefined
const [isTyping, setIsTyping] = createSignal(true) const [isTyping, setIsTyping] = createSignal(true)
const endTyping = () => { onMount(() => {
typingTimeout = setTimeout(() => {
if (isPlayed) return if (isPlayed) return
isPlayed = true isPlayed = true
setIsTyping(false) setIsTyping(false)
@@ -26,21 +28,7 @@ export const AudioBubble = (props: Props) => {
() => props.onTransitionEnd(ref?.offsetTop), () => props.onTransitionEnd(ref?.offsetTop),
showAnimationDuration showAnimationDuration
) )
} }, typingDuration)
onMount(() => {
typingTimeout = setTimeout(endTyping, typingDuration)
audioElement?.addEventListener(
'canplay',
() => {
clearTimeout(typingTimeout)
audioElement
?.play()
.catch((e) => console.warn("Couldn't autoplay audio", e))
endTyping()
},
{ once: true }
)
}) })
onCleanup(() => { onCleanup(() => {
@@ -63,11 +51,14 @@ export const AudioBubble = (props: Props) => {
<audio <audio
ref={audioElement} ref={audioElement}
src={props.url} src={props.url}
autoplay
class={ class={
'z-10 text-fade-in m-2 ' + 'z-10 text-fade-in ' +
(isTyping() ? 'opacity-0' : 'opacity-100') (isTyping() ? 'opacity-0' : 'opacity-100 m-2')
} }
style={{ height: isTyping() ? '32px' : 'revert' }} style={{
height: isTyping() ? (isMobile() ? '32px' : '36px') : 'revert',
}}
controls controls
/> />
</div> </div>

View File

@@ -1,6 +1,8 @@
import { TypingBubble } from '@/components' import { TypingBubble } from '@/components'
import { isMobile } from '@/utils/isMobileSignal'
import type { EmbedBubbleContent } from '@typebot.io/schemas' import type { EmbedBubbleContent } from '@typebot.io/schemas'
import { createSignal, onCleanup, onMount } from 'solid-js' import { createSignal, onCleanup, onMount } from 'solid-js'
import { clsx } from 'clsx'
type Props = { type Props = {
content: EmbedBubbleContent content: EmbedBubbleContent
@@ -43,19 +45,27 @@ export const EmbedBubble = (props: Props) => {
> >
{isTyping() && <TypingBubble />} {isTyping() && <TypingBubble />}
</div> </div>
<div
class={clsx(
'p-4 z-20 text-fade-in w-full',
isTyping() ? 'opacity-0' : 'opacity-100 p-4'
)}
style={{
height: isTyping()
? isMobile()
? '32px'
: '36px'
: `${props.content.height}px`,
}}
>
<iframe <iframe
id="embed-bubble-content" id="embed-bubble-content"
src={props.content.url} src={props.content.url}
class={ class={'w-full h-full '}
'w-full z-20 p-4 text-fade-in ' +
(isTyping() ? 'opacity-0' : 'opacity-100')
}
style={{
height: isTyping() ? '32px' : `${props.content.height}px`,
}}
/> />
</div> </div>
</div> </div>
</div> </div>
</div>
) )
} }

View File

@@ -1,6 +1,8 @@
import { TypingBubble } from '@/components' import { TypingBubble } from '@/components'
import type { ImageBubbleContent } from '@typebot.io/schemas' import type { ImageBubbleContent } from '@typebot.io/schemas'
import { createSignal, onCleanup, onMount } from 'solid-js' import { createSignal, onCleanup, onMount } from 'solid-js'
import { clsx } from 'clsx'
import { isMobile } from '@/utils/isMobileSignal'
type Props = { type Props = {
content: ImageBubbleContent content: ImageBubbleContent
@@ -73,12 +75,20 @@ export const ImageBubble = (props: Props) => {
<a <a
href={props.content.clickLink.url} href={props.content.clickLink.url}
target="_blank" target="_blank"
class="p-4 z-10" class={clsx('z-10', isTyping() ? 'h-8' : 'p-4')}
> >
{Image} {Image}
</a> </a>
) : ( ) : (
<figure class="p-4 z-10">{Image}</figure> <figure
class={clsx(
'z-10',
!isTyping() && 'p-4',
isTyping() ? (isMobile() ? 'h-8' : 'h-9') : ''
)}
>
{Image}
</figure>
)} )}
</div> </div>
</div> </div>

View File

@@ -4,6 +4,8 @@ import { For, createSignal, onCleanup, onMount } from 'solid-js'
import { computeTypingDuration } from '../helpers/computeTypingDuration' import { computeTypingDuration } from '../helpers/computeTypingDuration'
import { PlateBlock } from './plate/PlateBlock' import { PlateBlock } from './plate/PlateBlock'
import { computePlainText } from '../helpers/convertRichTextToPlainText' import { computePlainText } from '../helpers/convertRichTextToPlainText'
import { clsx } from 'clsx'
import { isMobile } from '@/utils/isMobileSignal'
type Props = { type Props = {
content: TextBubbleContent content: TextBubbleContent
@@ -65,10 +67,13 @@ export const TextBubble = (props: Props) => {
{isTyping() && <TypingBubble />} {isTyping() && <TypingBubble />}
</div> </div>
<div <div
class={ class={clsx(
'overflow-hidden text-fade-in mx-4 my-2 whitespace-pre-wrap slate-html-container relative text-ellipsis ' + 'overflow-hidden text-fade-in mx-4 my-2 whitespace-pre-wrap slate-html-container relative text-ellipsis',
(isTyping() ? 'opacity-0 h-6' : 'opacity-100 h-full') isTyping() ? 'opacity-0' : 'opacity-100'
} )}
style={{
height: isTyping() ? (isMobile() ? '16px' : '20px') : '100%',
}}
> >
<For each={props.content.richText}> <For each={props.content.richText}>
{(element) => <PlateBlock element={element} />} {(element) => <PlateBlock element={element} />}

View File

@@ -1,7 +1,9 @@
import { TypingBubble } from '@/components' import { TypingBubble } from '@/components'
import { isMobile } from '@/utils/isMobileSignal'
import type { VideoBubbleContent } from '@typebot.io/schemas' import type { VideoBubbleContent } from '@typebot.io/schemas'
import { VideoBubbleContentType } from '@typebot.io/schemas/features/blocks/bubbles/video/enums' import { VideoBubbleContentType } from '@typebot.io/schemas/features/blocks/bubbles/video/enums'
import { createSignal, Match, onCleanup, onMount, Switch } from 'solid-js' import { createSignal, Match, onCleanup, onMount, Switch } from 'solid-js'
import { clsx } from 'clsx'
type Props = { type Props = {
content: VideoBubbleContent content: VideoBubbleContent
@@ -13,24 +15,23 @@ let typingTimeout: NodeJS.Timeout
export const VideoBubble = (props: Props) => { export const VideoBubble = (props: Props) => {
let ref: HTMLDivElement | undefined let ref: HTMLDivElement | undefined
let videoElement: HTMLVideoElement | undefined
const [isTyping, setIsTyping] = createSignal(true) const [isTyping, setIsTyping] = createSignal(true)
const onTypingEnd = () => { onMount(() => {
const videoElement = ref?.querySelector('video') const typingDuration =
if (videoElement) props.content?.type &&
videoElement [VideoBubbleContentType.VIMEO, VideoBubbleContentType.YOUTUBE].includes(
.play() props.content?.type
.catch((e) => console.warn('Could not autoplay the video:', e)) )
? 2000
: 100
typingTimeout = setTimeout(() => {
if (!isTyping()) return if (!isTyping()) return
setIsTyping(false) setIsTyping(false)
setTimeout(() => { setTimeout(() => {
props.onTransitionEnd(ref?.offsetTop) props.onTransitionEnd(ref?.offsetTop)
}, showAnimationDuration) }, showAnimationDuration)
} }, typingDuration)
onMount(() => {
typingTimeout = setTimeout(onTypingEnd, 2000)
}) })
onCleanup(() => { onCleanup(() => {
@@ -40,7 +41,7 @@ export const VideoBubble = (props: Props) => {
return ( return (
<div class="flex flex-col animate-fade-in" ref={ref}> <div class="flex flex-col animate-fade-in" ref={ref}>
<div class="flex w-full items-center"> <div class="flex w-full items-center">
<div class={'flex relative z-10 items-start typebot-host-bubble'}> <div class="flex relative z-10 items-start typebot-host-bubble overflow-hidden">
<div <div
class="flex items-center absolute px-4 py-2 bubble-typing z-10 " class="flex items-center absolute px-4 py-2 bubble-typing z-10 "
style={{ style={{
@@ -58,7 +59,7 @@ export const VideoBubble = (props: Props) => {
} }
> >
<video <video
ref={videoElement} autoplay
src={props.content.url} src={props.content.url}
controls controls
class={ class={
@@ -66,7 +67,7 @@ export const VideoBubble = (props: Props) => {
(isTyping() ? 'opacity-0' : 'opacity-100') (isTyping() ? 'opacity-0' : 'opacity-100')
} }
style={{ style={{
height: isTyping() ? '32px' : 'auto', height: isTyping() ? (isMobile() ? '32px' : '36px') : 'auto',
}} }}
/> />
</Match> </Match>
@@ -78,6 +79,15 @@ export const VideoBubble = (props: Props) => {
VideoBubbleContentType.YOUTUBE, VideoBubbleContentType.YOUTUBE,
].includes(props.content.type) ].includes(props.content.type)
} }
>
<div
class={clsx(
'p-4 z-10 text-fade-in w-full',
isTyping() ? 'opacity-0' : 'opacity-100 p-4'
)}
style={{
height: isTyping() ? (isMobile() ? '32px' : '36px') : '200px',
}}
> >
<iframe <iframe
src={`${ src={`${
@@ -85,14 +95,11 @@ export const VideoBubble = (props: Props) => {
? 'https://player.vimeo.com/video' ? 'https://player.vimeo.com/video'
: 'https://www.youtube.com/embed' : 'https://www.youtube.com/embed'
}/${props.content.id}`} }/${props.content.id}`}
class={ class={'w-full h-full'}
'w-full p-4 text-fade-in z-10 ' +
(isTyping() ? 'opacity-0' : 'opacity-100')
}
height={isTyping() ? '32px' : '200px'}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen allowfullscreen
/> />
</div>
</Match> </Match>
</Switch> </Switch>
</div> </div>

View File

@@ -1,6 +1,6 @@
{ {
"name": "@typebot.io/nextjs", "name": "@typebot.io/nextjs",
"version": "0.1.12", "version": "0.1.13",
"description": "Convenient library to display typebots on your Next.js website", "description": "Convenient library to display typebots on your Next.js website",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@typebot.io/react", "name": "@typebot.io/react",
"version": "0.1.12", "version": "0.1.13",
"description": "Convenient library to display typebots on your Next.js website", "description": "Convenient library to display typebots on your Next.js website",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",