diff --git a/src/lib/components/party/Party.svelte b/src/lib/components/party/Party.svelte index 63dd6af5..8395b8c0 100644 --- a/src/lib/components/party/Party.svelte +++ b/src/lib/components/party/Party.svelte @@ -52,7 +52,7 @@ import Button from '$lib/components/ui/Button.svelte' import Icon from '$lib/components/Icon.svelte' import DescriptionRenderer from '$lib/components/DescriptionRenderer.svelte' - import { openDescriptionSidebar } from '$lib/features/description/openDescriptionSidebar.svelte' + import { openDescriptionPane } from '$lib/features/description/openDescriptionPane.svelte' import { openPartyEditSidebar, type PartyEditValues @@ -407,9 +407,10 @@ let deleting = $state(false) function openDescriptionPanel() { - openDescriptionSidebar({ + openDescriptionPane({ title: party.name || '(untitled party)', description: party.description, + videoUrl: party.videoUrl, canEdit: canEdit(), partyId: party.id, partyShortcode: party.shortcode, diff --git a/src/lib/components/sidebar/DescriptionPane.svelte b/src/lib/components/sidebar/DescriptionPane.svelte index f90efc4c..d13eb3bf 100644 --- a/src/lib/components/sidebar/DescriptionPane.svelte +++ b/src/lib/components/sidebar/DescriptionPane.svelte @@ -7,13 +7,24 @@ interface Props { description?: string + videoUrl?: string canEdit?: boolean partyId?: string partyShortcode?: string onSave?: (description: string) => Promise } - let { description, canEdit = false, partyId, partyShortcode, onSave }: Props = $props() + let { description, videoUrl, canEdit = false, partyId, partyShortcode, onSave }: Props = $props() + + /** Extract YouTube video ID from various URL formats */ + function extractVideoId(url?: string): string | null { + if (!url) return null + // Match youtube.com/watch?v=ID, youtu.be/ID, youtube.com/embed/ID + const match = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/) + return match?.[1] ?? null + } + + const videoId = $derived(extractVideoId(videoUrl)) const paneStack = usePaneStack() @@ -43,9 +54,20 @@ }) -
+
+ {#if videoId} +
+ +
+ {/if} {#if description}
@@ -65,13 +87,25 @@ @use '$src/themes/typography' as *; @use '$src/themes/effects' as *; - .description-sidebar { + .description-pane { display: flex; flex-direction: column; height: 100%; color: var(--text-primary); } + .video-embed { + margin-bottom: $unit-2x; + border-radius: $unit; + overflow: hidden; + + iframe { + width: 100%; + aspect-ratio: 16 / 9; + display: block; + } + } + .content-section { flex: 1; overflow-y: auto; diff --git a/src/lib/features/description/openDescriptionPane.svelte.ts b/src/lib/features/description/openDescriptionPane.svelte.ts index 70f1d9bf..307eec84 100644 --- a/src/lib/features/description/openDescriptionPane.svelte.ts +++ b/src/lib/features/description/openDescriptionPane.svelte.ts @@ -1,20 +1,22 @@ import { sidebar } from '$lib/stores/sidebar.svelte' -import DescriptionSidebar from '$lib/components/sidebar/DescriptionSidebar.svelte' +import DescriptionPane from '$lib/components/sidebar/DescriptionPane.svelte' -interface DescriptionSidebarOptions { +interface DescriptionPaneOptions { title?: string | undefined description?: string | undefined + videoUrl?: string | undefined canEdit?: boolean | undefined partyId?: string | undefined partyShortcode?: string | undefined onSave?: ((description: string) => Promise) | undefined } -export function openDescriptionSidebar(options: DescriptionSidebarOptions) { - const { title, description, canEdit, partyId, partyShortcode, onSave } = options +export function openDescriptionPane(options: DescriptionPaneOptions) { + const { title, description, videoUrl, canEdit, partyId, partyShortcode, onSave } = options - sidebar.openWithComponent(title ?? '', DescriptionSidebar, { + sidebar.openWithComponent(title ?? '', DescriptionPane, { description, + videoUrl, canEdit, partyId, partyShortcode, @@ -22,6 +24,6 @@ export function openDescriptionSidebar(options: DescriptionSidebarOptions) { }) } -export function closeDescriptionSidebar() { +export function closeDescriptionPane() { sidebar.close() }