'use client' import { useStore } from '@nanostores/react' import { type CSSProperties, lazy, Suspense, useState } from 'react' import { PrettyLink } from '@/lib/external-link' import { $embedAllowed, $embedMode } from '@/store/embed-consent' import { EmbedFacade } from './embed-consent' import { EMBED_MAX_H } from './embed-size' import { EmbedFail } from './fail' import type { EmbedDescriptor } from './providers/types' import { RichBoundary } from './rich-boundary' const FrameEmbedRenderer = lazy(() => import('./frame-embed')) const SocialEmbedRenderer = lazy(() => import('./social-embed')) const SpotifyEmbedRenderer = lazy(() => import('./spotify-embed')) const YouTubeEmbedRenderer = lazy(() => import('./youtube-embed')) function intrinsicHeight(descriptor: EmbedDescriptor): number { if (descriptor.aspectRatio) { return Math.round((descriptor.maxWidth ?? 640) / descriptor.aspectRatio) } return descriptor.height ?? 320 } function LazyRenderer({ descriptor }: { descriptor: EmbedDescriptor }) { // X and Instagram load their official blockquote script in-document. The tweet // check also narrows the union to FrameEmbed for the iframe renderers below. if (descriptor.renderer === 'tweet' || descriptor.provider === 'instagram') { return } if (descriptor.provider === 'youtube') { return } if (descriptor.provider === 'spotify') { return } return } export function UrlEmbed({ descriptor }: { descriptor: EmbedDescriptor }) { const mode = useStore($embedMode) const allowed = useStore($embedAllowed) const [loaded, setLoaded] = useState(false) // Privacy gate: don't reach out to the provider until consented. `off` keeps // it a plain link; otherwise the placeholder shows until "Load" (this embed) // or "Always allow" / global `always` permits the fetch. if (mode === 'off') { return } const consented = mode === 'always' || loaded || allowed.includes(descriptor.provider) const aspect = descriptor.aspectRatio // Ratio embeds cap WIDTH off the ratio so height tops out at the cap while // scaling. Non-ratio embeds own their own height (measured / fixed). const style: CSSProperties = { containIntrinsicSize: `auto ${intrinsicHeight(descriptor)}px`, contentVisibility: 'auto', ...(aspect ? { width: `min(${descriptor.maxWidth ?? 640}px, 100%, calc(${EMBED_MAX_H} * ${aspect}))` } : { width: descriptor.maxWidth ? `min(${descriptor.maxWidth}px, 100%)` : '100%' }) } return ( } resetKey={descriptor.id}> {consented ? ( ) : ( setLoaded(true)} /> )} ) }