⚡ 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:
@@ -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'
|
||||
|
||||
@@ -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
7
packages/env/env.ts
vendored
@@ -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'
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
6
packages/env/getRuntimeVariable.ts
vendored
6
packages/env/getRuntimeVariable.ts
vendored
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user