feat: parse youtube, simplecast and custom iframe embeds on server-side
This commit is contained in:
parent
77e4369176
commit
bd0406eac5
|
@ -4,7 +4,6 @@ import {
|
||||||
extractIdFromFirstTag,
|
extractIdFromFirstTag,
|
||||||
extractInnerHtml,
|
extractInnerHtml,
|
||||||
} from '@/utils/html.utils'
|
} from '@/utils/html.utils'
|
||||||
import { convertToIframe } from '@/utils/string.utils'
|
|
||||||
import { HeadingElementsRef } from '@/utils/ui.utils'
|
import { HeadingElementsRef } from '@/utils/ui.utils'
|
||||||
import { Typography } from '@acid-info/lsd-react'
|
import { Typography } from '@acid-info/lsd-react'
|
||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
|
@ -40,31 +39,18 @@ export const RenderArticleBlock = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case 'p': {
|
case 'p': {
|
||||||
const isIframeRegex = /<iframe[^>]*>(?:<\/iframe>|[^]*?<\/iframe>)/
|
const isIframe = block.embed && block.labels.includes('embed')
|
||||||
const isIframe = isIframeRegex.test(block.text)
|
|
||||||
|
|
||||||
const isYoutubeRegex =
|
return block.embed && isIframe ? (
|
||||||
/^(https?\:\/\/)?((www\.)?youtube\.com|youtu\.?be)\/.+$/
|
block.labels.includes('youtube_embed') ? (
|
||||||
|
<ReactPlayer url={block.embed.src} />
|
||||||
const isYoutube = isYoutubeRegex.test(block.text)
|
) : (
|
||||||
const youtubeLink = block.text.match(isYoutubeRegex) ?? []
|
|
||||||
|
|
||||||
const isSimplecastRegex =
|
|
||||||
/^https?:\/\/([a-zA-Z0-9-]+\.)*simplecast\.com\/[^?\s]+(\?[\s\S]*)?$/
|
|
||||||
|
|
||||||
const isSimplecast = isSimplecastRegex.test(block.text)
|
|
||||||
const simplecastLink = block.text.match(isSimplecastRegex) ?? []
|
|
||||||
|
|
||||||
return isIframe ? (
|
|
||||||
<IframeContainer dangerouslySetInnerHTML={{ __html: block.text }} />
|
|
||||||
) : isYoutube ? (
|
|
||||||
<ReactPlayer url={youtubeLink[0]} />
|
|
||||||
) : isSimplecast ? (
|
|
||||||
<IframeContainer
|
<IframeContainer
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: convertToIframe(simplecastLink[0] ?? ''),
|
__html: block.embed.html,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<Paragraph
|
<Paragraph
|
||||||
variant="body1"
|
variant="body1"
|
||||||
|
|
|
@ -17,7 +17,12 @@ const ArticleBlocks = ({ data }: Props) => {
|
||||||
const headingElementsRef = useIntersectionObserver(setTocId)
|
const headingElementsRef = useIntersectionObserver(setTocId)
|
||||||
|
|
||||||
const blocks = useMemo(
|
const blocks = useMemo(
|
||||||
() => data.content.filter((b) => b.labels.length === 0),
|
() =>
|
||||||
|
data.content.filter(
|
||||||
|
(b) =>
|
||||||
|
b.labels.length === 0 ||
|
||||||
|
b.labels.includes(LPE.Post.ContentBlockLabels.Embed),
|
||||||
|
),
|
||||||
[data.content],
|
[data.content],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ const ArticlePage = ({ data, errors, why }: ArticleProps) => {
|
||||||
|
|
||||||
if (!data) return null
|
if (!data) return null
|
||||||
if (errors) return <div>{errors}</div>
|
if (errors) return <div>{errors}</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SEO
|
<SEO
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { LPE } from '../../../types/lpe.types'
|
import { LPE } from '../../../types/lpe.types'
|
||||||
|
import { convertToIframe } from '../../../utils/string.utils'
|
||||||
import { UnbodyResGoogleDocData, UnbodyResTextBlockData } from '../unbody.types'
|
import { UnbodyResGoogleDocData, UnbodyResTextBlockData } from '../unbody.types'
|
||||||
import { UnbodyDataTypeConfig } from './types'
|
import { UnbodyDataTypeConfig } from './types'
|
||||||
|
|
||||||
|
@ -15,6 +16,55 @@ export const TextBlockDataType: UnbodyDataTypeConfig<
|
||||||
isMatch: (helpers, data, original, root) => data.__typename === 'TextBlock',
|
isMatch: (helpers, data, original, root) => data.__typename === 'TextBlock',
|
||||||
|
|
||||||
transform: (helpers, data, original, root) => {
|
transform: (helpers, data, original, root) => {
|
||||||
|
const { text = '', html = '' } = data
|
||||||
|
const labels: LPE.Post.ContentBlockLabel[] = []
|
||||||
|
let embed: LPE.Post.TextBlockEmbed | null = null
|
||||||
|
|
||||||
|
if (text.length > 0 || html.length > 0) {
|
||||||
|
const isLink =
|
||||||
|
/^<p[^>]*><span[^>]*><a[^>]*href="(https):\/\/[^ "]+"[^>]*>.*<\/a><\/span><\/p>$/.test(
|
||||||
|
html,
|
||||||
|
)
|
||||||
|
const isIframe = /<iframe[^>]*>(?:<\/iframe>|[^]*?<\/iframe>)/.test(text)
|
||||||
|
|
||||||
|
if (isLink) {
|
||||||
|
const youtube = html.match(
|
||||||
|
/(https?\:\/\/)?((www\.)?youtube\.com|youtu\.?be)\/[^ "]+/gi,
|
||||||
|
)
|
||||||
|
|
||||||
|
const simplecast = html.match(
|
||||||
|
/(https?\:\/\/)?((player\.)?simplecast\.com)\/[^ "]+/gi,
|
||||||
|
)
|
||||||
|
|
||||||
|
const label = youtube?.[0]
|
||||||
|
? LPE.Post.ContentBlockLabels.YoutubeEmbed
|
||||||
|
: LPE.Post.ContentBlockLabels.SimplecastEmbed
|
||||||
|
const src = youtube?.[0] || simplecast?.[0]
|
||||||
|
if (src && src.length > 0) {
|
||||||
|
labels.push(label)
|
||||||
|
labels.push(LPE.Post.ContentBlockLabels.Embed)
|
||||||
|
|
||||||
|
embed = {
|
||||||
|
src,
|
||||||
|
html: convertToIframe(src),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIframe) {
|
||||||
|
const src = text.match(/(?<=src=").*?(?=[\?"])/g)?.[0]
|
||||||
|
|
||||||
|
if (src) {
|
||||||
|
labels.push(LPE.Post.ContentBlockLabels.Embed)
|
||||||
|
|
||||||
|
embed = {
|
||||||
|
html: text,
|
||||||
|
src,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: data?._additional?.id || `${data.order}`,
|
id: data?._additional?.id || `${data.order}`,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
@ -24,7 +74,8 @@ export const TextBlockDataType: UnbodyDataTypeConfig<
|
||||||
footnotes: data.footnotesObj,
|
footnotes: data.footnotesObj,
|
||||||
order: data.order,
|
order: data.order,
|
||||||
tagName: data.tagName,
|
tagName: data.tagName,
|
||||||
labels: [],
|
labels,
|
||||||
|
...(embed ? { embed } : {}),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,9 @@ export namespace LPE {
|
||||||
Footnote: 'footnote',
|
Footnote: 'footnote',
|
||||||
Paragraph: 'paragraph',
|
Paragraph: 'paragraph',
|
||||||
CoverImage: 'cover_image',
|
CoverImage: 'cover_image',
|
||||||
|
Embed: 'embed',
|
||||||
|
YoutubeEmbed: 'youtube_embed',
|
||||||
|
SimplecastEmbed: 'simplecast_embed',
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ContentBlockLabel = DictValues<typeof ContentBlockLabels>
|
export type ContentBlockLabel = DictValues<typeof ContentBlockLabels>
|
||||||
|
@ -72,6 +75,11 @@ export namespace LPE {
|
||||||
document?: D
|
document?: D
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TextBlockEmbed = {
|
||||||
|
src: string
|
||||||
|
html: string
|
||||||
|
}
|
||||||
|
|
||||||
export type TextBlock<D = any> = ContentBlockCommon<D> & {
|
export type TextBlock<D = any> = ContentBlockCommon<D> & {
|
||||||
text: string
|
text: string
|
||||||
html: string
|
html: string
|
||||||
|
@ -79,6 +87,7 @@ export namespace LPE {
|
||||||
classNames: string[]
|
classNames: string[]
|
||||||
type: Extract<ContentBlockType, 'text'>
|
type: Extract<ContentBlockType, 'text'>
|
||||||
footnotes: Post.Footnotes
|
footnotes: Post.Footnotes
|
||||||
|
embed?: TextBlockEmbed
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ImageBlock<D = any> = ContentBlockCommon<D> &
|
export type ImageBlock<D = any> = ContentBlockCommon<D> &
|
||||||
|
|
Loading…
Reference in New Issue