rework video + text chat display controls
This commit is contained in:
+11
@@ -22,6 +22,17 @@
|
|||||||
@apply pl-sai pr-sai;
|
@apply pl-sai pr-sai;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* root */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
font-family: Lato;
|
||||||
|
--sait: var(--safe-area-inset-top, env(safe-area-inset-top));
|
||||||
|
--saib: var(--safe-area-inset-bottom, env(safe-area-inset-bottom));
|
||||||
|
--sail: var(--safe-area-inset-left, env(safe-area-inset-left));
|
||||||
|
--sair: var(--safe-area-inset-right, env(safe-area-inset-right));
|
||||||
|
--video-call-panel-bg: #181e24;
|
||||||
|
}
|
||||||
|
|
||||||
@utility py-sai {
|
@utility py-sai {
|
||||||
@apply pt-sai pb-sai;
|
@apply pt-sai pb-sai;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
||||||
import VideoCallVideo from "@app/components/VideoCallVideo.svelte"
|
import VideoCallVideo from "@app/components/VideoCallVideo.svelte"
|
||||||
|
import VoiceWidget from "@app/components/VoiceWidget.svelte"
|
||||||
import {
|
import {
|
||||||
currentVoiceSession,
|
currentVoiceSession,
|
||||||
currentVoiceRoom,
|
currentVoiceRoom,
|
||||||
videoCallContentActive,
|
|
||||||
videoCallLayoutRevision,
|
videoCallLayoutRevision,
|
||||||
videoPrimaryTileKey,
|
videoPrimaryTileKey,
|
||||||
toggleVideoPrimaryTile,
|
toggleVideoPrimaryTile,
|
||||||
@@ -41,13 +41,7 @@
|
|||||||
|
|
||||||
const roomMatches = $derived($currentVoiceRoom?.url === url && $currentVoiceRoom?.h === h)
|
const roomMatches = $derived($currentVoiceRoom?.url === url && $currentVoiceRoom?.h === h)
|
||||||
|
|
||||||
const allowEmptyPanel = $derived(variant === "desktop-split" || variant === "desktop-full")
|
const showPanel = $derived(visible && roomMatches)
|
||||||
|
|
||||||
const showPanel = $derived(
|
|
||||||
visible &&
|
|
||||||
roomMatches &&
|
|
||||||
(variant === "mobile" ? $videoCallContentActive : $videoCallContentActive || allowEmptyPanel),
|
|
||||||
)
|
|
||||||
|
|
||||||
const tiles = $derived.by(() => {
|
const tiles = $derived.by(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- re-run when remote video subscribes
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- re-run when remote video subscribes
|
||||||
@@ -158,11 +152,11 @@
|
|||||||
const panelChrome = $derived(
|
const panelChrome = $derived(
|
||||||
cx(
|
cx(
|
||||||
variant === "mobile" &&
|
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",
|
"cb top-[calc(var(--sait)+6rem)] cw z-compose bg-[var(--video-call-panel-bg)] fixed inset-x-0 flex min-h-0 flex-col gap-2 overflow-y-auto overflow-x-hidden px-2 pb-2 pt-1 md:hidden",
|
||||||
variant === "desktop-split" &&
|
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",
|
"cb ct cw-split-video z-compose bg-[var(--video-call-panel-bg)] fixed hidden min-h-0 flex-col gap-2 overflow-hidden p-2 md:flex",
|
||||||
variant === "desktop-full" &&
|
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",
|
"cb ct cw z-compose bg-[var(--video-call-panel-bg)] fixed hidden min-h-0 flex-col gap-2 overflow-hidden p-2 md:flex",
|
||||||
className,
|
className,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -208,43 +202,56 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if showPanel && (showTileGrid || allowEmptyPanel)}
|
{#snippet videoPanelBody()}
|
||||||
<div class={panelChrome}>
|
{#if showTileGrid}
|
||||||
{#if showTileGrid}
|
{#if useSpotlightLayout && primaryTile}
|
||||||
{#if useSpotlightLayout && primaryTile}
|
<div class="flex min-h-0 flex-1 flex-col gap-2 overflow-hidden">
|
||||||
<div class="flex min-h-0 flex-1 flex-col gap-2 overflow-hidden">
|
{@render videoTile(primaryTile, "spotlight")}
|
||||||
{@render videoTile(primaryTile, "spotlight")}
|
{#if secondaryTiles.length > 0}
|
||||||
{#if secondaryTiles.length > 0}
|
<div
|
||||||
<div
|
class="flex max-h-40 shrink-0 flex-row gap-2 overflow-x-auto overflow-y-hidden py-0.5">
|
||||||
class="flex max-h-40 shrink-0 flex-row gap-2 overflow-x-auto overflow-y-hidden py-0.5">
|
{#each secondaryTiles as tile (tileKey(tile))}
|
||||||
{#each secondaryTiles as tile (tileKey(tile))}
|
{@render videoTile(tile, "strip")}
|
||||||
{@render videoTile(tile, "strip")}
|
{/each}
|
||||||
{/each}
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{: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))}
|
|
||||||
{@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))}
|
|
||||||
{@render videoTile(tile, "default")}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<div
|
|
||||||
class="flex min-h-[12rem] flex-1 flex-col items-center justify-center gap-2 rounded-box bg-base-100/50 p-4 text-center text-sm opacity-80">
|
|
||||||
<p>No camera or screen share yet.</p>
|
|
||||||
<p class="text-xs">
|
|
||||||
Use the camera or screen share control in the voice widget to share video.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
{: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))}
|
||||||
|
{@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))}
|
||||||
|
{@render videoTile(tile, "default")}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
class="flex min-h-[12rem] flex-1 flex-col items-center justify-center gap-2 rounded-box bg-base-200/50 p-4 text-center text-sm opacity-80">
|
||||||
|
<p>No camera or screen share yet.</p>
|
||||||
|
<p class="text-xs">Use the camera or screen share control to share video.</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
{#if showPanel}
|
||||||
|
<div class={panelChrome}>
|
||||||
|
{#if variant === "mobile"}
|
||||||
|
<div class="flex min-h-0 flex-1 flex-col gap-2">
|
||||||
|
<div class="min-h-0 flex-1 overflow-hidden">
|
||||||
|
{@render videoPanelBody()}
|
||||||
|
</div>
|
||||||
|
<div class="shrink-0">
|
||||||
|
<VoiceWidget />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
{@render videoPanelBody()}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {readable} from "svelte/store"
|
import {readable} from "svelte/store"
|
||||||
import {fly} from "svelte/transition"
|
import {fly} from "svelte/transition"
|
||||||
|
import {browser} from "$app/environment"
|
||||||
import {goto} from "$app/navigation"
|
import {goto} from "$app/navigation"
|
||||||
import {page} from "$app/stores"
|
import {page} from "$app/stores"
|
||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
import Monitor from "@assets/icons/monitor.svg?dataurl"
|
import Monitor from "@assets/icons/monitor.svg?dataurl"
|
||||||
import PhoneRounded from "@assets/icons/phone-rounded.svg?dataurl"
|
import PhoneRounded from "@assets/icons/phone-rounded.svg?dataurl"
|
||||||
import PhoneCallingRounded from "@assets/icons/phone-calling-rounded.svg?dataurl"
|
import PhoneCallingRounded from "@assets/icons/phone-calling-rounded.svg?dataurl"
|
||||||
|
import ChatRound from "@assets/icons/chat-round.svg?dataurl"
|
||||||
import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
|
import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
|
||||||
import Settings from "@assets/icons/settings.svg?dataurl"
|
import Settings from "@assets/icons/settings.svg?dataurl"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
@@ -32,6 +34,8 @@
|
|||||||
currentVoiceSession,
|
currentVoiceSession,
|
||||||
currentVoiceRoom,
|
currentVoiceRoom,
|
||||||
voiceState,
|
voiceState,
|
||||||
|
voiceMobileRoomPanel,
|
||||||
|
voiceDesktopRoomPanel,
|
||||||
isLocalSpeaking,
|
isLocalSpeaking,
|
||||||
leaveVoiceRoom,
|
leaveVoiceRoom,
|
||||||
toggleMute,
|
toggleMute,
|
||||||
@@ -76,6 +80,45 @@
|
|||||||
pushModal(VoiceCallAudioSettingsDialog)
|
pushModal(VoiceCallAudioSettingsDialog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isMd = $state(
|
||||||
|
typeof window !== "undefined" && window.matchMedia("(min-width: 768px)").matches,
|
||||||
|
)
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (!browser) return
|
||||||
|
const mq = window.matchMedia("(min-width: 768px)")
|
||||||
|
const sync = () => {
|
||||||
|
isMd = mq.matches
|
||||||
|
}
|
||||||
|
sync()
|
||||||
|
mq.addEventListener("change", sync)
|
||||||
|
return () => mq.removeEventListener("change", sync)
|
||||||
|
})
|
||||||
|
|
||||||
|
const showVoiceLayoutToggle = $derived(
|
||||||
|
$voiceState === VoiceState.Connected &&
|
||||||
|
targetRoom !== undefined &&
|
||||||
|
getRoomType(targetRoom) === RoomType.Voice &&
|
||||||
|
typeof h === "string" &&
|
||||||
|
relay !== undefined &&
|
||||||
|
decodeRelay(relay) === targetRoom.url &&
|
||||||
|
h === targetRoom.h,
|
||||||
|
)
|
||||||
|
|
||||||
|
const layoutToggleActive = $derived(
|
||||||
|
showVoiceLayoutToggle &&
|
||||||
|
((!isMd && $voiceMobileRoomPanel === "chat") || (isMd && $voiceDesktopRoomPanel === "split")),
|
||||||
|
)
|
||||||
|
|
||||||
|
const onLayoutToggle = () => {
|
||||||
|
if (!showVoiceLayoutToggle) return
|
||||||
|
if (isMd) {
|
||||||
|
voiceDesktopRoomPanel.update(p => (p === "split" ? "chat" : "split"))
|
||||||
|
} else {
|
||||||
|
voiceMobileRoomPanel.update(p => (p === "chat" ? "video" : "chat"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const mediaToggleClass = "center tooltip tooltip-top btn btn-sm btn-square btn-ghost"
|
const mediaToggleClass = "center tooltip tooltip-top btn btn-sm btn-square btn-ghost"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -96,18 +139,28 @@
|
|||||||
out:fly={{y: 60, duration: 250}}
|
out:fly={{y: 60, duration: 250}}
|
||||||
class="flex flex-col gap-2 rounded-box bg-base-100 p-3">
|
class="flex flex-col gap-2 rounded-box bg-base-100 p-3">
|
||||||
<div class="flex flex-col gap-0.5">
|
<div class="flex flex-col gap-0.5">
|
||||||
{#if $voiceState === VoiceState.Joining}
|
<div class="flex items-center justify-between gap-2">
|
||||||
<span class="text-sm font-semibold text-warning">Joining...</span>
|
{#if $voiceState === VoiceState.Joining}
|
||||||
{:else if $voiceState === VoiceState.Connected}
|
<span class="text-sm font-semibold text-warning">Joining...</span>
|
||||||
<span class="text-sm font-semibold text-success">Voice Connected</span>
|
{:else if $voiceState === VoiceState.Connected}
|
||||||
{:else}
|
<span class="text-sm font-semibold text-success">Voice Connected</span>
|
||||||
<span class="text-sm font-semibold text-neutral-content">Disconnected</span>
|
{:else}
|
||||||
{/if}
|
<span class="text-sm font-semibold text-neutral-content">Disconnected</span>
|
||||||
|
{/if}
|
||||||
|
{#if showVoiceLayoutToggle}
|
||||||
|
<Button
|
||||||
|
data-tip="Toggle full-screen chat ↔ video (mobile) or split view (desktop)"
|
||||||
|
class={cx(mediaToggleClass, "shrink-0", layoutToggleActive && "text-primary")}
|
||||||
|
onclick={onLayoutToggle}>
|
||||||
|
<Icon icon={ChatRound} size={4} />
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
<span class="ellipsize text-xs opacity-70">
|
<span class="ellipsize text-xs opacity-70">
|
||||||
{roomName} / {spaceName}
|
{roomName} / {spaceName}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
{#if $voiceState === VoiceState.Joining}
|
{#if $voiceState === VoiceState.Joining}
|
||||||
<span class="loading loading-spinner loading-sm"></span>
|
<span class="loading loading-spinner loading-sm"></span>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
+13
-20
@@ -87,6 +87,17 @@ export const voiceState = writable<VoiceState>(VoiceState.Disconnected)
|
|||||||
|
|
||||||
export const currentVoiceRoom = writable<Room | undefined>(undefined)
|
export const currentVoiceRoom = writable<Room | undefined>(undefined)
|
||||||
|
|
||||||
|
/** Mobile room UI: full-screen chat vs video (see VoiceWidget layout toggle). */
|
||||||
|
export const voiceMobileRoomPanel = writable<"chat" | "video">("chat")
|
||||||
|
|
||||||
|
/** Desktop room UI: messages only, video only, or split (see VoiceWidget layout toggle). */
|
||||||
|
export const voiceDesktopRoomPanel = writable<"chat" | "video" | "split">("split")
|
||||||
|
|
||||||
|
const resetVoiceRoomPanels = () => {
|
||||||
|
voiceMobileRoomPanel.set("chat")
|
||||||
|
voiceDesktopRoomPanel.set("chat")
|
||||||
|
}
|
||||||
|
|
||||||
export const participantPubkeyMap = writable<Map<string, Pubkey>>(new Map())
|
export const participantPubkeyMap = writable<Map<string, Pubkey>>(new Map())
|
||||||
|
|
||||||
/** Bumps when remote video is subscribed/unsubscribed so layout/video UI can react. */
|
/** Bumps when remote video is subscribed/unsubscribed so layout/video UI can react. */
|
||||||
@@ -229,6 +240,7 @@ const onRoomDisconnected = (reason?: DisconnectReason) => {
|
|||||||
videoCallLayoutRevision.set(0)
|
videoCallLayoutRevision.set(0)
|
||||||
videoPrimaryTileKey.set(undefined)
|
videoPrimaryTileKey.set(undefined)
|
||||||
currentVoiceSession.set(undefined)
|
currentVoiceSession.set(undefined)
|
||||||
|
resetVoiceRoomPanels()
|
||||||
if (reason !== undefined && reason !== DisconnectReason.CLIENT_INITIATED) {
|
if (reason !== undefined && reason !== DisconnectReason.CLIENT_INITIATED) {
|
||||||
voiceState.set(VoiceState.Disconnected)
|
voiceState.set(VoiceState.Disconnected)
|
||||||
const message =
|
const message =
|
||||||
@@ -396,6 +408,7 @@ export const leaveVoiceRoom = async () => {
|
|||||||
videoCallLayoutRevision.set(0)
|
videoCallLayoutRevision.set(0)
|
||||||
videoPrimaryTileKey.set(undefined)
|
videoPrimaryTileKey.set(undefined)
|
||||||
currentVoiceSession.set(undefined)
|
currentVoiceSession.set(undefined)
|
||||||
|
resetVoiceRoomPanels()
|
||||||
session.room.disconnect()
|
session.room.disconnect()
|
||||||
speakingParticipants.set([])
|
speakingParticipants.set([])
|
||||||
participantPubkeyMap.set(new Map())
|
participantPubkeyMap.set(new Map())
|
||||||
@@ -429,26 +442,6 @@ export const toggleMute = async () => {
|
|||||||
|
|
||||||
const VISUAL_SOURCES = [Track.Source.Camera, Track.Source.ScreenShare] as const
|
const VISUAL_SOURCES = [Track.Source.Camera, Track.Source.ScreenShare] as const
|
||||||
|
|
||||||
const roomHasSubscribedRemoteVisual = (room: LiveKitRoom): boolean => {
|
|
||||||
for (const p of room.remoteParticipants.values()) {
|
|
||||||
for (const source of VISUAL_SOURCES) {
|
|
||||||
const pub = p.getTrackPublication(source)
|
|
||||||
if (pub?.isSubscribed && pub.track) return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** True when local camera/screen share is on or any subscribed remote camera/screen track. */
|
|
||||||
export const videoCallContentActive = derived(
|
|
||||||
[currentVoiceSession, voiceState, videoCallLayoutRevision],
|
|
||||||
([$session, $state, _rev]) => {
|
|
||||||
if ($state !== VoiceState.Connected || !$session) return false
|
|
||||||
if ($session.cameraOn || $session.screenShareOn) return true
|
|
||||||
return roomHasSubscribedRemoteVisual($session.room)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
const countLiveVisualFeeds = (session: VoiceSession): number => {
|
const countLiveVisualFeeds = (session: VoiceSession): number => {
|
||||||
const room = session.room
|
const room = session.room
|
||||||
let n = 0
|
let n = 0
|
||||||
|
|||||||
@@ -45,7 +45,14 @@
|
|||||||
} from "@app/core/state"
|
} from "@app/core/state"
|
||||||
import VoiceWidget from "@app/components/VoiceWidget.svelte"
|
import VoiceWidget from "@app/components/VoiceWidget.svelte"
|
||||||
import VideoCallContent from "@app/components/VideoCallContent.svelte"
|
import VideoCallContent from "@app/components/VideoCallContent.svelte"
|
||||||
import {VoiceState, currentVoiceRoom, videoTileCount, voiceState} from "@app/voice"
|
import {
|
||||||
|
VoiceState,
|
||||||
|
currentVoiceRoom,
|
||||||
|
videoTileCount,
|
||||||
|
voiceMobileRoomPanel,
|
||||||
|
voiceDesktopRoomPanel,
|
||||||
|
voiceState,
|
||||||
|
} from "@app/voice"
|
||||||
import {makeFeed} from "@app/core/requests"
|
import {makeFeed} from "@app/core/requests"
|
||||||
import {popKey} from "@lib/implicit"
|
import {popKey} from "@lib/implicit"
|
||||||
import {checked} from "@app/util/notifications"
|
import {checked} from "@app/util/notifications"
|
||||||
@@ -66,23 +73,20 @@
|
|||||||
$currentVoiceRoom?.h === h,
|
$currentVoiceRoom?.h === h,
|
||||||
)
|
)
|
||||||
|
|
||||||
let mobileRoomPanel = $state<"chat" | "video">("chat")
|
|
||||||
let voiceDesktopPanel = $state<"chat" | "video" | "split">("split")
|
|
||||||
|
|
||||||
const showMobileVideoPanel = $derived(
|
const showMobileVideoPanel = $derived(
|
||||||
isVoiceRoom && $voiceState === VoiceState.Connected && mobileRoomPanel === "video",
|
isVoiceRoom && $voiceState === VoiceState.Connected && $voiceMobileRoomPanel === "video",
|
||||||
)
|
)
|
||||||
|
|
||||||
const pageContentHiddenDesktopVideoOnly = $derived(
|
const pageContentHiddenDesktopVideoOnly = $derived(
|
||||||
voiceConnectedHere && voiceDesktopPanel === "video",
|
voiceConnectedHere && $voiceDesktopRoomPanel === "video",
|
||||||
)
|
)
|
||||||
|
|
||||||
let prevVideoTileCount = $state(0)
|
let prevVideoTileCount = $state(0)
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if ($voiceState !== VoiceState.Connected) {
|
if ($voiceState !== VoiceState.Connected) {
|
||||||
mobileRoomPanel = "chat"
|
voiceMobileRoomPanel.set("chat")
|
||||||
voiceDesktopPanel = "chat"
|
voiceDesktopRoomPanel.set("chat")
|
||||||
prevVideoTileCount = 0
|
prevVideoTileCount = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -96,8 +100,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (prevVideoTileCount === 0 && n >= 1) {
|
if (prevVideoTileCount === 0 && n >= 1) {
|
||||||
voiceDesktopPanel = "video"
|
voiceDesktopRoomPanel.set("video")
|
||||||
mobileRoomPanel = "video"
|
voiceMobileRoomPanel.set("video")
|
||||||
}
|
}
|
||||||
prevVideoTileCount = n
|
prevVideoTileCount = n
|
||||||
})
|
})
|
||||||
@@ -402,40 +406,6 @@
|
|||||||
<RoomName {url} {h} />
|
<RoomName {url} {h} />
|
||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet action()}
|
{#snippet action()}
|
||||||
{#if voiceConnectedHere}
|
|
||||||
<div class="flex gap-1 md:hidden">
|
|
||||||
<Button
|
|
||||||
class={cx("btn btn-sm", mobileRoomPanel === "chat" && "btn-primary")}
|
|
||||||
onclick={() => (mobileRoomPanel = "chat")}>
|
|
||||||
Chat
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
class={cx("btn btn-sm", mobileRoomPanel === "video" && "btn-primary")}
|
|
||||||
onclick={() => (mobileRoomPanel = "video")}>
|
|
||||||
Video
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div class="hidden flex-wrap gap-1 md:flex">
|
|
||||||
<Button
|
|
||||||
data-tip="Messages only"
|
|
||||||
class={cx("btn btn-sm", voiceDesktopPanel === "chat" && "btn-primary")}
|
|
||||||
onclick={() => (voiceDesktopPanel = "chat")}>
|
|
||||||
Chat
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
data-tip="Video only"
|
|
||||||
class={cx("btn btn-sm", voiceDesktopPanel === "video" && "btn-primary")}
|
|
||||||
onclick={() => (voiceDesktopPanel = "video")}>
|
|
||||||
Video
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
data-tip="Video and chat side by side"
|
|
||||||
class={cx("btn btn-sm", voiceDesktopPanel === "split" && "btn-primary")}
|
|
||||||
onclick={() => (voiceDesktopPanel = "split")}>
|
|
||||||
Video + Chat
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<SpaceSearch {url} {h} />
|
<SpaceSearch {url} {h} />
|
||||||
<Button class="btn btn-neutral btn-sm btn-square" onclick={showRoomDetail}>
|
<Button class="btn btn-neutral btn-sm btn-square" onclick={showRoomDetail}>
|
||||||
<Icon size={4} icon={InfoCircle} />
|
<Icon size={4} icon={InfoCircle} />
|
||||||
@@ -446,34 +416,34 @@
|
|||||||
<div
|
<div
|
||||||
class={cx(
|
class={cx(
|
||||||
"flex min-h-0 flex-1 flex-col",
|
"flex min-h-0 flex-1 flex-col",
|
||||||
voiceConnectedHere && voiceDesktopPanel === "split" && "md:flex-row",
|
voiceConnectedHere && $voiceDesktopRoomPanel === "split" && "md:flex-row",
|
||||||
)}>
|
)}>
|
||||||
{#if voiceConnectedHere}
|
{#if voiceConnectedHere}
|
||||||
<VideoCallContent
|
<VideoCallContent
|
||||||
variant="desktop-split"
|
variant="desktop-split"
|
||||||
{url}
|
{url}
|
||||||
{h}
|
{h}
|
||||||
visible={voiceDesktopPanel === "split"}
|
visible={$voiceDesktopRoomPanel === "split"}
|
||||||
class="hidden min-h-0 w-full min-w-0 flex-1 flex-col md:flex" />
|
class="hidden min-h-0 w-full min-w-0 flex-1 flex-col md:flex" />
|
||||||
<VideoCallContent
|
<VideoCallContent
|
||||||
variant="desktop-full"
|
variant="desktop-full"
|
||||||
{url}
|
{url}
|
||||||
{h}
|
{h}
|
||||||
visible={voiceDesktopPanel === "video"}
|
visible={$voiceDesktopRoomPanel === "video"}
|
||||||
class="hidden min-h-0 w-full min-w-0 flex-1 flex-col md:flex" />
|
class="hidden min-h-0 w-full min-w-0 flex-1 flex-col md:flex" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class={cx(
|
class={cx(
|
||||||
"flex min-h-0 min-w-0 flex-1 flex-col",
|
"flex min-h-0 min-w-0 flex-1 flex-col",
|
||||||
voiceConnectedHere && voiceDesktopPanel === "video" && "md:hidden",
|
voiceConnectedHere && $voiceDesktopRoomPanel === "video" && "md:hidden",
|
||||||
)}>
|
)}>
|
||||||
{#if isVoiceRoom && $voiceState === VoiceState.Connected}
|
{#if isVoiceRoom && $voiceState === VoiceState.Connected}
|
||||||
<VideoCallContent
|
<VideoCallContent
|
||||||
variant="mobile"
|
variant="mobile"
|
||||||
{url}
|
{url}
|
||||||
{h}
|
{h}
|
||||||
visible={mobileRoomPanel === "video"}
|
visible={$voiceMobileRoomPanel === "video"}
|
||||||
class="md:hidden" />
|
class="md:hidden" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user