Add more video supports (#1023)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
	- Introduced a new layout option for the TextInput component.
- Added support for GUMLET and TIKTOK video content types in
VideoBubbleContent.
- Enhanced VideoUploadContent to handle new properties like aspectRatio
and maxWidth.
- Updated VideoBubble to include aspect-ratio and max-width styles based
on content properties.
- **Refactor**
- Changed the extension used for internationalization (i18n) in the VS
Code environment.
	- Modified how environment variables are accessed in tolgee.tsx.
- Updated parseVideoUrl function to include a new property
videoSizeSuggestion.
- **Chores**
- Updated the tolgeeEnv object in env.ts and added a new optional
parameter to the getRuntimeVariable function.
- Expanded video handling capabilities by introducing new video content
types and associated regular expressions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Closes #978 #936 #926
This commit is contained in:
Baptiste Arnaud
2023-11-10 11:17:14 +01:00
committed by GitHub
parent df578417aa
commit dd4de582a9
20 changed files with 306 additions and 143 deletions

View File

@@ -69,9 +69,11 @@ export const parseBubbleBlock = (
const parsedContent = block.content
? deepParseVariables(variables)(block.content)
: undefined
return {
...block,
content: {
...parsedContent,
...(parsedContent?.url ? parseVideoUrl(parsedContent.url) : {}),
height:
typeof parsedContent?.height === 'string'

View File

@@ -4,9 +4,14 @@ import { createSignal, Match, onCleanup, onMount, Switch } from 'solid-js'
import { clsx } from 'clsx'
import {
defaultVideoBubbleContent,
embedBaseUrls,
embeddableVideoTypes,
VideoBubbleContentType,
} from '@typebot.io/schemas/features/blocks/bubbles/video/constants'
import { VideoBubbleBlock } from '@typebot.io/schemas'
import {
EmbeddableVideoBubbleContentType,
VideoBubbleBlock,
} from '@typebot.io/schemas'
type Props = {
content: VideoBubbleBlock['content']
@@ -23,8 +28,8 @@ export const VideoBubble = (props: Props) => {
onMount(() => {
const typingDuration =
props.content?.type &&
[VideoBubbleContentType.VIMEO, VideoBubbleContentType.YOUTUBE].includes(
props.content?.type
embeddableVideoTypes.includes(
props.content?.type as EmbeddableVideoBubbleContentType
)
? 2000
: 100
@@ -50,6 +55,8 @@ export const VideoBubble = (props: Props) => {
style={{
width: isTyping() ? '64px' : '100%',
height: isTyping() ? '32px' : '100%',
'max-width':
props.content?.maxWidth ?? defaultVideoBubbleContent.maxWidth,
}}
>
{isTyping() && <TypingBubble />}
@@ -71,16 +78,19 @@ export const VideoBubble = (props: Props) => {
}
style={{
height: isTyping() ? (isMobile() ? '32px' : '36px') : 'auto',
'aspect-ratio': props.content?.aspectRatio,
'max-width':
props.content?.maxWidth ??
defaultVideoBubbleContent.maxWidth,
}}
/>
</Match>
<Match
when={
props.content?.type &&
[
VideoBubbleContentType.VIMEO,
VideoBubbleContentType.YOUTUBE,
].includes(props.content.type)
embeddableVideoTypes.includes(
props.content.type as EmbeddableVideoBubbleContentType
)
}
>
<div
@@ -93,17 +103,23 @@ export const VideoBubble = (props: Props) => {
? isMobile()
? '32px'
: '36px'
: `${
: !props.content?.aspectRatio
? `${
props.content?.height ??
defaultVideoBubbleContent.height
}px`,
}px`
: undefined,
'aspect-ratio': props.content?.aspectRatio,
'max-width':
props.content?.maxWidth ??
defaultVideoBubbleContent.maxWidth,
}}
>
<iframe
src={`${
props.content?.type === VideoBubbleContentType.VIMEO
? 'https://player.vimeo.com/video'
: 'https://www.youtube.com/embed'
embedBaseUrls[
props.content?.type as EmbeddableVideoBubbleContentType
]
}/${props.content?.id}`}
class={'w-full h-full'}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"

7
packages/env/env.ts vendored
View File

@@ -279,15 +279,16 @@ const tolgeeEnv = {
NEXT_PUBLIC_TOLGEE_API_URL: z
.string()
.url()
.default('https://tolgee.server.baptistearno.com"')
.optional(),
.optional()
.default('https://tolgee.server.baptistearno.com"'),
},
runtimeEnv: {
NEXT_PUBLIC_TOLGEE_API_KEY: getRuntimeVariable(
'NEXT_PUBLIC_TOLGEE_API_KEY'
),
NEXT_PUBLIC_TOLGEE_API_URL: getRuntimeVariable(
'NEXT_PUBLIC_TOLGEE_API_URL'
'NEXT_PUBLIC_TOLGEE_API_URL',
'https://tolgee.server.baptistearno.com'
),
},
}

View File

@@ -2,9 +2,9 @@ declare const window: {
__ENV?: any
}
export const getRuntimeVariable = (key: string) => {
export const getRuntimeVariable = (key: string, defaultValue?: string) => {
if (typeof window !== 'undefined')
return window.__ENV ? window.__ENV[key] : undefined
return window.__ENV ? window.__ENV[key] ?? defaultValue : undefined
if (typeof process === 'undefined') return undefined
return process.env[key]
return process.env[key] ?? defaultValue
}

View File

@@ -1,21 +1,74 @@
import { VideoBubbleContentType } from '@typebot.io/schemas/features/blocks/bubbles/video/constants'
const vimeoRegex = /vimeo\.com\/(\d+)/
const youtubeRegex =
/youtube\.com\/(watch\?v=|shorts\/)([\w-]+)|youtu\.be\/([\w-]+)/
import { VideoBubbleBlock } from '@typebot.io/schemas'
import {
VideoBubbleContentType,
gumletRegex,
horizontalVideoSuggestionSize,
tiktokRegex,
verticalVideoSuggestionSize,
vimeoRegex,
youtubeRegex,
} from '@typebot.io/schemas/features/blocks/bubbles/video/constants'
export const parseVideoUrl = (
url: string
): { type: VideoBubbleContentType; url: string; id?: string } => {
if (vimeoRegex.test(url)) {
const id = url.match(vimeoRegex)?.at(1)
if (!id) return { type: VideoBubbleContentType.URL, url }
return { type: VideoBubbleContentType.VIMEO, url, id }
}
): {
type: VideoBubbleContentType
url: string
id?: string
videoSizeSuggestion?: Pick<
NonNullable<VideoBubbleBlock['content']>,
'aspectRatio' | 'maxWidth'
>
} => {
if (youtubeRegex.test(url)) {
const id = url.match(youtubeRegex)?.at(2) ?? url.match(youtubeRegex)?.at(3)
if (!id) return { type: VideoBubbleContentType.URL, url }
return { type: VideoBubbleContentType.YOUTUBE, url, id }
const match = url.match(youtubeRegex)
const id = match?.at(2) ?? match?.at(3)
const parsedUrl = match?.at(0) ?? url
if (!id) return { type: VideoBubbleContentType.URL, url: parsedUrl }
return {
type: VideoBubbleContentType.YOUTUBE,
url: parsedUrl,
id,
videoSizeSuggestion: url.includes('shorts')
? verticalVideoSuggestionSize
: horizontalVideoSuggestionSize,
}
}
if (vimeoRegex.test(url)) {
const match = url.match(vimeoRegex)
const id = match?.at(1)
const parsedUrl = match?.at(0) ?? url
if (!id) return { type: VideoBubbleContentType.URL, url: parsedUrl }
return {
type: VideoBubbleContentType.VIMEO,
url: parsedUrl,
id,
videoSizeSuggestion: horizontalVideoSuggestionSize,
}
}
if (tiktokRegex.test(url)) {
const match = url.match(tiktokRegex)
const id = url.match(tiktokRegex)?.at(1)
const parsedUrl = match?.at(0) ?? url
if (!id) return { type: VideoBubbleContentType.URL, url: parsedUrl }
return {
type: VideoBubbleContentType.TIKTOK,
url: parsedUrl,
id,
videoSizeSuggestion: verticalVideoSuggestionSize,
}
}
if (gumletRegex.test(url)) {
const match = url.match(gumletRegex)
const id = match?.at(1)
const parsedUrl = match?.at(0) ?? url
if (!id) return { type: VideoBubbleContentType.URL, url: parsedUrl }
return {
type: VideoBubbleContentType.GUMLET,
url: parsedUrl,
id,
videoSizeSuggestion: horizontalVideoSuggestionSize,
}
}
return { type: VideoBubbleContentType.URL, url }
}

View File

@@ -2,8 +2,49 @@ export enum VideoBubbleContentType {
URL = 'url',
YOUTUBE = 'youtube',
VIMEO = 'vimeo',
TIKTOK = 'tiktok',
GUMLET = 'gumlet',
}
export const embeddableVideoTypes = [
VideoBubbleContentType.YOUTUBE,
VideoBubbleContentType.VIMEO,
VideoBubbleContentType.TIKTOK,
VideoBubbleContentType.GUMLET,
] as const
export const defaultVideoBubbleContent = {
height: 400,
aspectRatio: '16/9',
maxWidth: '100%',
} as const
export const horizontalVideoSuggestionSize = {
aspectRatio: '16/9',
maxWidth: '100%',
}
export const verticalVideoSuggestionSize = {
aspectRatio: '9/16',
maxWidth: '400px',
}
const youtubeBaseUrl = 'https://www.youtube.com/embed'
export const youtubeRegex =
/youtube\.com\/(watch\?v=|shorts\/)([\w-]+)|youtu\.be\/([\w-]+)/
const vimeoBaseUrl = 'https://player.vimeo.com/video'
export const vimeoRegex = /vimeo\.com\/(\d+)/
const tiktokBaseUrl = 'https://www.tiktok.com/embed/v2'
export const tiktokRegex = /tiktok\.com\/@[\w-]+\/video\/(\d+)/
const gumletBaseUrl = 'https://play.gumlet.io/embed'
export const gumletRegex = /gumlet\.com\/watch\/(\w+)/
export const embedBaseUrls = {
[VideoBubbleContentType.VIMEO]: vimeoBaseUrl,
[VideoBubbleContentType.YOUTUBE]: youtubeBaseUrl,
[VideoBubbleContentType.TIKTOK]: tiktokBaseUrl,
[VideoBubbleContentType.GUMLET]: gumletBaseUrl,
} as const

View File

@@ -9,6 +9,8 @@ export const videoBubbleContentSchema = z.object({
id: z.string().optional(),
type: z.nativeEnum(VideoBubbleContentType).optional(),
height: z.number().or(variableStringSchema).optional(),
aspectRatio: z.string().optional(),
maxWidth: z.string().optional(),
})
export const videoBubbleBlockSchema = blockBaseSchema.merge(
@@ -19,3 +21,7 @@ export const videoBubbleBlockSchema = blockBaseSchema.merge(
)
export type VideoBubbleBlock = z.infer<typeof videoBubbleBlockSchema>
export type EmbeddableVideoBubbleContentType = Exclude<
VideoBubbleContentType,
VideoBubbleContentType.URL
>