Clean up VideoCallContent

This commit is contained in:
mplorentz
2026-04-08 09:46:33 -04:00
parent 8d10d1700c
commit 3f64db0f0d
3 changed files with 36 additions and 47 deletions
+24 -27
View File
@@ -12,7 +12,6 @@
currentVoiceSession,
currentVoiceRoom,
VideoCallLayout,
videoCallLayoutRevision,
videoPrimaryTileKey,
toggleVideoPrimaryTile,
pubkeyFromLiveKitIdentity,
@@ -42,29 +41,27 @@
$currentVoiceRoom?.url === url && $currentVoiceRoom?.h === h,
)
const showPanel = $derived(
const showVideoContent = $derived(
isViewingCurrentCallRoom &&
(mobile
? layout === VideoCallLayout.Video
: layout === VideoCallLayout.Split || layout === VideoCallLayout.Video),
)
const tiles = $derived.by(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- re-run when remote video subscribes
$videoCallLayoutRevision
const videoTiles = $derived.by(() => {
const session = $currentVoiceSession
if (!session || $currentVoiceRoom?.url !== url || $currentVoiceRoom?.h !== h) {
return []
}
const room = session.room
const out: VideoTile[] = []
const lp = room.localParticipant
const videoTiles: VideoTile[] = []
const user = room.localParticipant
if (session.cameraOn) {
const localPub = lp.getTrackPublication(Track.Source.Camera)
out.push({
identity: lp.identity,
const localPub = user.getTrackPublication(Track.Source.Camera)
videoTiles.push({
identity: user.identity,
isLocal: true,
trackSid: localPub?.trackSid ?? "local-camera",
attachable: localPub?.track,
@@ -73,9 +70,9 @@
}
if (session.screenShareOn) {
const localPub = lp.getTrackPublication(Track.Source.ScreenShare)
out.push({
identity: lp.identity,
const localPub = user.getTrackPublication(Track.Source.ScreenShare)
videoTiles.push({
identity: user.identity,
isLocal: true,
trackSid: localPub?.trackSid ?? "local-screen",
attachable: localPub?.track,
@@ -86,7 +83,7 @@
for (const rp of room.remoteParticipants.values()) {
const camPub = rp.getTrackPublication(Track.Source.Camera)
if (camPub?.isSubscribed && camPub.track) {
out.push({
videoTiles.push({
identity: rp.identity,
isLocal: false,
trackSid: camPub.trackSid,
@@ -96,7 +93,7 @@
}
const screenPub = rp.getTrackPublication(Track.Source.ScreenShare)
if (screenPub?.isSubscribed && screenPub.track) {
out.push({
videoTiles.push({
identity: rp.identity,
isLocal: false,
trackSid: screenPub.trackSid,
@@ -106,7 +103,7 @@
}
}
return out
return videoTiles
})
/** Identity + source only — LiveKit can change trackSid after publish, which broke spotlight + stale-key effect. */
@@ -115,29 +112,29 @@
const primaryTile = $derived.by(() => {
const k = $videoPrimaryTileKey
if (k === undefined) return undefined
return tiles.find(t => tileKey(t) === k)
return videoTiles.find(t => tileKey(t) === k)
})
const secondaryTiles = $derived.by(() => {
const p = primaryTile
if (p === undefined) return tiles
if (p === undefined) return videoTiles
const pk = tileKey(p)
return tiles.filter(t => tileKey(t) !== pk)
return videoTiles.filter(t => tileKey(t) !== pk)
})
const useSpotlightLayout = $derived(primaryTile !== undefined)
const useMultiGrid = $derived(!useSpotlightLayout && tiles.length > 2)
const useMultiGrid = $derived(!useSpotlightLayout && videoTiles.length > 2)
$effect(() => {
const k = $videoPrimaryTileKey
if (k === undefined) return
if (!tiles.some(t => tileKey(t) === k)) {
if (!videoTiles.some(t => tileKey(t) === k)) {
videoPrimaryTileKey.set(undefined)
}
})
$effect(() => {
for (const t of tiles) {
for (const t of videoTiles) {
const pk = pubkeyFromLiveKitIdentity(t.identity)
if (pk) loadProfile(pk)
}
@@ -149,7 +146,7 @@
return source === Track.Source.ScreenShare ? `${name} · screen` : name
}
const showTileGrid = $derived(tiles.length > 0)
const showTileGrid = $derived(videoTiles.length > 0)
const spotlightHandlerFor = (key: string) => () => {
toggleVideoPrimaryTile(key)
@@ -190,7 +187,7 @@
class="pointer-events-none absolute bottom-1 left-1 max-w-[calc(100%-0.5rem)] truncate rounded bg-base-100/80 px-1.5 py-0.5 text-xs">
{labelFor(tile.identity, tile.source)}{tile.isLocal ? " (you)" : ""}
</span>
{#if tiles.length > 1}
{#if videoTiles.length > 1}
{@const pinned = $videoPrimaryTileKey === tileKey(tile)}
<Button
data-tip={pinned ? "Exit spotlight" : "Spotlight"}
@@ -223,13 +220,13 @@
{:else if useMultiGrid}
<div
class="grid min-h-0 flex-1 grid-cols-1 content-start gap-2 overflow-y-auto sm:grid-cols-2">
{#each tiles as tile (tileKey(tile))}
{#each videoTiles as tile (tileKey(tile))}
{@render videoTile(tile, "default")}
{/each}
</div>
{:else}
<div class="flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto">
{#each tiles as tile (tileKey(tile))}
{#each videoTiles as tile (tileKey(tile))}
{@render videoTile(tile, "default")}
{/each}
</div>
@@ -243,7 +240,7 @@
{/if}
{/snippet}
{#if showPanel}
{#if showVideoContent}
<div class={panelChrome}>
{#if mobile}
<div class="flex min-h-0 flex-1 flex-col gap-2">
+9 -20
View File
@@ -102,9 +102,6 @@ const resetVideoCallLayout = () => {
export const participantPubkeyMap = writable<Map<string, Pubkey>>(new Map())
/** Bumps when remote video is subscribed/unsubscribed so layout/video UI can react. */
export const videoCallLayoutRevision = writable(0)
/** Spotlight tile id — must match VideoCallContent `tileKey` (identity + source, not trackSid). */
export const videoPrimaryTileKey = writable<string | undefined>(undefined)
@@ -112,7 +109,9 @@ export const toggleVideoPrimaryTile = (key: string) => {
videoPrimaryTileKey.update(k => (k === key ? undefined : key))
}
const bumpVideoCallLayoutRevision = () => videoCallLayoutRevision.update(n => n + 1)
const triggerVideoTileCount = () => {
currentVoiceSession.update(s => (s ? {...s} : s))
}
const addParticipant = (identity: string) => {
participantPubkeyMap.update(m => {
@@ -239,7 +238,6 @@ const setUpMicrophone = async (
}
const onRoomDisconnected = (reason?: DisconnectReason) => {
videoCallLayoutRevision.set(0)
videoPrimaryTileKey.set(undefined)
currentVoiceSession.set(undefined)
resetVideoCallLayout()
@@ -262,14 +260,14 @@ const onTrackSubscribed = (track: Track) => {
document.body.appendChild(element)
element.play().catch(() => {})
} else if (track.kind === Track.Kind.Video) {
bumpVideoCallLayoutRevision()
triggerVideoTileCount()
}
}
const onTrackUnsubscribed = (track: Track) => {
track.detach().forEach(el => el.remove())
if (track.kind === Track.Kind.Video) {
bumpVideoCallLayoutRevision()
triggerVideoTileCount()
}
}
@@ -300,7 +298,6 @@ const onLocalTrackUnpublished = (
if (!session || participant.identity !== session.room.localParticipant.identity) return
if (!session.screenShareOn) return
currentVoiceSession.set({...session, screenShareOn: false})
bumpVideoCallLayoutRevision()
}
let joinAbortController: AbortController | undefined
@@ -407,7 +404,6 @@ export const leaveVoiceRoom = async () => {
}
voiceState.set(VoiceState.Disconnected)
videoCallLayoutRevision.set(0)
videoPrimaryTileKey.set(undefined)
currentVoiceSession.set(undefined)
resetVideoCallLayout()
@@ -465,13 +461,10 @@ const countLiveVisualFeeds = (session: VoiceSession): number => {
return n
}
export const videoTileCount = derived(
[currentVoiceSession, voiceState, videoCallLayoutRevision],
([$session, $state, _rev]) => {
if ($state !== VoiceState.Connected || !$session) return 0
return countLiveVisualFeeds($session)
},
)
export const videoTileCount = derived([currentVoiceSession, voiceState], ([$session, $state]) => {
if ($state !== VoiceState.Connected || !$session) return 0
return countLiveVisualFeeds($session)
})
export const toggleCamera = async () => {
const session = get(currentVoiceSession)
@@ -481,14 +474,12 @@ export const toggleCamera = async () => {
if (!cameraOn) {
session.room.localParticipant.setCameraEnabled(false)
currentVoiceSession.set({...session, cameraOn})
bumpVideoCallLayoutRevision()
return
}
try {
await session.room.localParticipant.setCameraEnabled(true)
currentVoiceSession.set({...session, cameraOn})
bumpVideoCallLayoutRevision()
} catch (e) {
pushToast({theme: "error", message: "Could not access camera"})
}
@@ -502,14 +493,12 @@ export const toggleScreenShare = async () => {
if (!screenShareOn) {
session.room.localParticipant.setScreenShareEnabled(false)
currentVoiceSession.set({...session, screenShareOn})
bumpVideoCallLayoutRevision()
return
}
try {
await session.room.localParticipant.setScreenShareEnabled(true)
currentVoiceSession.set({...session, screenShareOn})
bumpVideoCallLayoutRevision()
} catch (e) {
pushToast({theme: "error", message: "Could not start screen sharing"})
}
@@ -103,6 +103,9 @@
if (prevVideoTileCount === 0 && n >= 1) {
videoCallLayout.set(VideoCallLayout.Video)
}
if (prevVideoTileCount >= 1 && n === 0 && $videoCallLayout === VideoCallLayout.Split) {
videoCallLayout.set(VideoCallLayout.Chat)
}
prevVideoTileCount = n
})
const shouldProtect = canEnforceNip70(url)