From 236661b573bf281a9989d463b033b878ff89e7c6 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Wed, 3 Jun 2026 12:33:35 -0400 Subject: [PATCH] zebra-spaces: ask room to re-announce mic-state on welcome + new peer-joined Pairs with signal 33dcabf. Fixes the 'host refreshed and now sees everyone as unmuted' UX gap. Two paths: - On welcome (any rejoin): send mic-state-req; every speaker in the room responds with their current mic-state. - On peer-joined for anyone else: if I'm a speaker with a live mic, re-broadcast my own state so the new arrival sees it without having to ping. Both are bounded: only speakers respond, and the message is the same E2E-encrypted blob used for normal mic toggles, so no extra privacy surface beyond what already exists. --- web/zebra-spaces.html | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index dcf8661..dc6dbde 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -3012,12 +3012,29 @@ async function handleSignal(raw){ renderShareUrl(); onRoleEntered(); renderRoom(); + /* Ask the room to re-broadcast their current mic-state so our + * member list shows the right mute icons. Without this, mm.muted + * defaults to undefined and the UI reports every peer as + * 'open' until they happen to toggle. Fires for hiccup-takeover + * too (since the welcome path runs on every reconnect). */ + try { send({ type: 'mic-state-req' }); } catch(_){} break; case 'state': roomEpoch = m.epoch; applyState(m.state); flushSfuStreams(); renderRoom(); break; + case 'mic-state-req': + /* Another peer just (re)joined and asked the room to re-announce. + * Only speakers with a live mic respond — listeners have no + * meaningful mic-state to share. */ + if (canSpeak(myRole) && micStream) sendMicState(); + break; case 'peer-joined': members.set(m.uuid, { uuid:m.uuid, pubkey:m.pubkey, handle:m.handle, role:m.role, joined_at: Date.now()/1000 }); { const pub = pubHexFromMsg(m, 'pubkey'); logLine('', idTag(m.uuid, pub)+' joined as '+m.role); } + /* re-announce our mic state so the new arrival's UI reflects + * reality. Server-side mic-state-req covers welcome timing; this + * covers the standard 'a new peer joined while we were already + * here' case. */ + if (canSpeak(myRole) && micStream) sendMicState(); /* if the original host rejoined during their grace window, the room * is rescued — drop the "space closing" status from our top bar */ if (m.role === 'host'){ @@ -3915,8 +3932,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');