diff --git a/src/app/components/VideoCallContent.svelte b/src/app/components/VideoCallContent.svelte index 45f82737..46829f05 100644 --- a/src/app/components/VideoCallContent.svelte +++ b/src/app/components/VideoCallContent.svelte @@ -2,6 +2,9 @@ import cx from "classnames" import {Track} from "livekit-client" import {displayProfileByPubkey, loadProfile} from "@welshman/app" + import Pin from "@assets/icons/pin.svg?dataurl" + import Button from "@lib/components/Button.svelte" + import Icon from "@lib/components/Icon.svelte" import ProfileCircle from "@app/components/ProfileCircle.svelte" import VideoCallVideo from "@app/components/VideoCallVideo.svelte" import { @@ -9,6 +12,8 @@ currentVoiceRoom, videoCallContentActive, videoCallLayoutRevision, + videoPrimaryTileKey, + toggleVideoPrimaryTile, pubkeyFromLiveKitIdentity, } from "@app/voice" @@ -30,6 +35,8 @@ source: Track.Source.Camera | Track.Source.ScreenShare } + type TileLayout = "spotlight" | "default" | "strip" + const {variant, url, h, visible = true, class: className = ""}: Props = $props() const roomMatches = $derived($currentVoiceRoom?.url === url && $currentVoiceRoom?.h === h) @@ -102,6 +109,33 @@ return out }) + /** Identity + source only — LiveKit can change trackSid after publish, which broke spotlight + stale-key effect. */ + const tileKey = (t: Tile) => `${t.identity}\x1f${t.source}` + + const primaryTile = $derived.by(() => { + const k = $videoPrimaryTileKey + if (k === undefined) return undefined + return tiles.find(t => tileKey(t) === k) + }) + + const secondaryTiles = $derived.by(() => { + const p = primaryTile + if (p === undefined) return tiles + const pk = tileKey(p) + return tiles.filter(t => tileKey(t) !== pk) + }) + + const useSpotlightLayout = $derived(primaryTile !== undefined) + const useMultiGrid = $derived(!useSpotlightLayout && tiles.length > 2) + + $effect(() => { + const k = $videoPrimaryTileKey + if (k === undefined) return + if (!tiles.some(t => tileKey(t) === k)) { + videoPrimaryTileKey.set(undefined) + } + }) + $effect(() => { for (const t of tiles) { const pk = pubkeyFromLiveKitIdentity(t.identity) @@ -116,43 +150,90 @@ } const showTileGrid = $derived(tiles.length > 0) + + const spotlightHandlerFor = (key: string) => () => { + toggleVideoPrimaryTile(key) + } + + const panelChrome = $derived( + cx( + variant === "mobile" && + "cb ct cw z-compose bg-base-300/95 fixed inset-x-0 flex min-h-0 flex-col gap-2 overflow-hidden p-2 md:hidden", + variant === "desktop-split" && + "cb ct cw-split-video z-compose bg-base-300/95 fixed hidden min-h-0 flex-col gap-2 overflow-hidden p-2 md:flex", + variant === "desktop-full" && + "cb ct cw z-compose bg-base-300/95 fixed hidden min-h-0 flex-col gap-2 overflow-hidden p-2 md:flex", + className, + ), + ) -{#if showPanel && (showTileGrid || allowEmptyPanel)} +{#snippet videoTile(tile: Tile, layout: TileLayout)}