diff --git a/desktop/index.html b/desktop/index.html index a183fab..9d11436 100644 --- a/desktop/index.html +++ b/desktop/index.html @@ -43,12 +43,16 @@ - +
+
diff --git a/desktop/src/main.ts b/desktop/src/main.ts index f227a5d..d2c8e7c 100644 --- a/desktop/src/main.ts +++ b/desktop/src/main.ts @@ -62,6 +62,7 @@ const lobbyFp = document.getElementById("lobby-fp")!; const lobbyUserList = document.getElementById("lobby-user-list")!; const lobbyUserCount = document.getElementById("lobby-user-count")!; const joinVoiceBtn = document.getElementById("join-voice-btn")!; +const joinVideoBtn = document.getElementById("join-video-btn")!; const incomingBanner = document.getElementById("incoming-call-banner")!; const incomingCallerName = document.getElementById("incoming-caller-name")!; const incomingIdenticon = document.getElementById("incoming-identicon")!; @@ -396,10 +397,40 @@ joinVoiceBtn.addEventListener("click", async () => { } }); +joinVideoBtn.addEventListener("click", async () => { + if (inVoice || connectPending) return; + const relay = getRelay(); + const s = loadSettings(); + if (!relay) { showToast("No relay configured"); return; } + connectPending = true; + const origText = joinVideoBtn.textContent; + joinVideoBtn.textContent = "Connecting…"; + (joinVideoBtn as HTMLButtonElement).disabled = true; + try { + await connectWithTimeout({ + relay: relay.address, + room: s.room || "general", + alias: s.alias || "", + osAec: s.osAec, + quality: s.quality || "auto", + }); + enterVoice(false); + startCamera(); + } catch (e: any) { + console.error("connect failed:", e); + showToast(`Join failed: ${errorMessage(e)}`); + } finally { + connectPending = false; + joinVideoBtn.textContent = origText; + (joinVideoBtn as HTMLButtonElement).disabled = false; + } +}); + function enterVoice(isDirect: boolean) { inVoice = true; const s = loadSettings(); joinVoiceBtn.classList.add("hidden"); + joinVideoBtn.classList.add("hidden"); voiceDrawer.classList.remove("hidden"); vdRoom.textContent = isDirect && directCallPeer ? (directCallPeer.alias || directCallPeer.fingerprint.substring(0, 16)) @@ -429,6 +460,7 @@ function leaveVoice() { pendingCallId = null; voiceDrawer.classList.add("hidden"); joinVoiceBtn.classList.remove("hidden"); + joinVideoBtn.classList.remove("hidden"); vdLevelBar.style.width = "0%"; if (statusInterval) { clearInterval(statusInterval); statusInterval = null; } stopCamera(); diff --git a/desktop/src/style.css b/desktop/src/style.css index 95d82c5..8834f6d 100644 --- a/desktop/src/style.css +++ b/desktop/src/style.css @@ -204,6 +204,16 @@ body { padding: 12px 0; display: flex; justify-content: center; + gap: 12px; +} + +.fab-video { + background: #3b82f6; + box-shadow: 0 4px 16px rgba(59, 130, 246, 0.3); +} + +.fab-video:hover { + box-shadow: 0 6px 20px rgba(59, 130, 246, 0.4); } .fab {