diff --git a/src/routes/spaces/[relay]/[h]/+layout.svelte b/src/routes/spaces/[relay]/[h]/+layout.svelte
index efbb3754..bc9b64e1 100644
--- a/src/routes/spaces/[relay]/[h]/+layout.svelte
+++ b/src/routes/spaces/[relay]/[h]/+layout.svelte
@@ -1,7 +1,14 @@
-
{#key $page.url.searchParams.get("at")}
-
+ {@render children?.()}
{/key}
diff --git a/src/routes/spaces/[relay]/[h]/+page.svelte b/src/routes/spaces/[relay]/[h]/+page.svelte
index 48b6c327..a3648ef4 100644
--- a/src/routes/spaces/[relay]/[h]/+page.svelte
+++ b/src/routes/spaces/[relay]/[h]/+page.svelte
@@ -13,6 +13,7 @@
import ClockCircle from "@assets/icons/clock-circle.svg?dataurl"
import InfoCircle from "@assets/icons/info-circle.svg?dataurl"
import Login2 from "@assets/icons/login-3.svg?dataurl"
+ import cx from "classnames"
import {slide, fade, fly} from "@lib/transition"
import Button from "@lib/components/Button.svelte"
import Divider from "@lib/components/Divider.svelte"
@@ -43,7 +44,9 @@
userSettingsValues,
} from "@app/core/state"
import VoiceWidget from "@app/components/VoiceWidget.svelte"
- import {VoiceState, voiceState} from "@app/voice"
+ import VideoCallContent from "@app/components/VideoCallContent.svelte"
+ import {VoiceState, currentVoiceRoom, voiceState} from "@app/call/stores"
+ import {VideoCallLayout, videoCallLayout, videoTileCount} from "@app/call/video"
import {makeFeed} from "@app/core/requests"
import {popKey} from "@lib/implicit"
import {checked} from "@app/util/notifications"
@@ -56,6 +59,49 @@
const url = decodeRelay(relay)
const room = deriveRoom(url, h)
const isVoiceRoom = $derived(getRoomType($room) === RoomType.Voice)
+
+ const voiceConnectedHere = $derived(
+ isVoiceRoom &&
+ $voiceState === VoiceState.Connected &&
+ $currentVoiceRoom?.url === url &&
+ $currentVoiceRoom?.h === h,
+ )
+
+ const showMobileVideoPanel = $derived(
+ isVoiceRoom &&
+ $voiceState === VoiceState.Connected &&
+ $videoCallLayout === VideoCallLayout.Video,
+ )
+
+ const pageContentHiddenDesktopVideoOnly = $derived(
+ voiceConnectedHere && $videoCallLayout === VideoCallLayout.Video,
+ )
+
+ let prevVideoTileCount = $state(0)
+
+ $effect(() => {
+ if ($voiceState !== VoiceState.Connected) {
+ videoCallLayout.set(VideoCallLayout.Chat)
+ prevVideoTileCount = 0
+ return
+ }
+
+ const here = isVoiceRoom && $currentVoiceRoom?.url === url && $currentVoiceRoom?.h === h
+ const n = $videoTileCount
+
+ if (!here) {
+ prevVideoTileCount = 0
+ return
+ }
+
+ 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)
const membershipStatus = deriveUserRoomMembershipStatus(url, h)
const at = $derived(parseInt($page.url.searchParams.get("at")!))
@@ -364,127 +410,168 @@
{/snippet}
-
- {#if $room.isPrivate && $membershipStatus !== MembershipStatus.Granted}
-
-
-
You aren't currently a member of this room.
- {#if $membershipStatus === MembershipStatus.Pending}
-
- {:else}
-
- {/if}
-
-
- {:else}
- {#if loadingForward}
-
- Looking for messages...
-
- {/if}
- {#each elements as { type, id, value, showPubkey, addSpaceBelow } (id)}
- {#if type === "new-messages"}
-
- {:else if type === "date"}
- {value}
- {:else}
- {@const event = $state.snapshot(value as TrustedEvent)}
- {#if event.kind === ROOM_ADD_MEMBER}
-
- {:else}
-
-
-
- {/if}
- {/if}
- {/each}
-
- {#if loadingBackward}
- Looking for messages...
- {:else}
- End of message history
- {/if}
-
+
+ {#if voiceConnectedHere}
+
{/if}
-
-
-
-
- {#if $room.isPrivate && $membershipStatus !== MembershipStatus.Granted}
-
- {:else if $room.isRestricted && $membershipStatus !== MembershipStatus.Granted}
-
-
Only members are allowed to post to this room.
- {#if $membershipStatus === MembershipStatus.Pending}
-
- {:else}
-
- {/if}
-
- {:else}
-
- {#if parent}
-
- {/if}
- {#if share}
-
- {/if}
- {#if eventToEdit}
-
- {/if}
-
- {#key eventToEdit}
-
- {/key}
+
+ {#if isVoiceRoom && $voiceState === VoiceState.Connected}
+
{/if}
-
- {#if isVoiceRoom || $voiceState === VoiceState.Joining || $voiceState === VoiceState.Connected}
-
-
+
+
+ {#if $room.isPrivate && $membershipStatus !== MembershipStatus.Granted}
+
+
+
You aren't currently a member of this room.
+ {#if $membershipStatus === MembershipStatus.Pending}
+
+ {:else}
+
+ {/if}
+
+
+ {:else}
+ {#if loadingForward}
+
+ Looking for messages...
+
+ {/if}
+ {#each elements as { type, id, value, showPubkey, addSpaceBelow } (id)}
+ {#if type === "new-messages"}
+
+ {:else if type === "date"}
+ {value}
+ {:else}
+ {@const event = $state.snapshot(value as TrustedEvent)}
+ {#if event.kind === ROOM_ADD_MEMBER}
+
+ {:else}
+
+
+
+ {/if}
+ {/if}
+ {/each}
+
+ {#if loadingBackward}
+ Looking for messages...
+ {:else}
+ End of message history
+ {/if}
+
+ {/if}
+
+
+
+
+
+ {#if $room.isPrivate && $membershipStatus !== MembershipStatus.Granted}
+
+ {:else if $room.isRestricted && $membershipStatus !== MembershipStatus.Granted}
+
+
Only members are allowed to post to this room.
+ {#if $membershipStatus === MembershipStatus.Pending}
+
+ {:else}
+
+ {/if}
+
+ {:else}
+
+ {#if parent}
+
+ {/if}
+ {#if share}
+
+ {/if}
+ {#if eventToEdit}
+
+ {/if}
+
+ {#key eventToEdit}
+
+ {/key}
+ {/if}
+
+ {#if isVoiceRoom || $voiceState === VoiceState.Joining || $voiceState === VoiceState.Connected}
+
+
+
+ {/if}
- {/if}
+
{#if showScrollButton}