zebra-spaces: mute state survives hard refresh
sessionStorage key MUTE_STATE_KEY persists '1' (muted) / '0' (live) so the page reconnects in the same mute state the user last clicked. No hot-mic on refresh — if you left muted, you come back muted. Restored from ensureMicAndUI() after getMic() succeeds; applyMuteState() syncs the button label, the mic track enable flag, the user's own room row, and broadcasts mic-state to peers if we came back muted. Cleared only on explicit leave; survives role demote → re-promote so reclaiming the mic keeps the user's previously-chosen state.
This commit is contained in:
parent
8631c106d2
commit
40cc4a5598
1 changed files with 29 additions and 9 deletions
|
|
@ -1236,6 +1236,13 @@ const ACTIVE_CALL_KEY = 'zebra-spaces-active-call-v1';
|
|||
* 'resume screen' button is the same friction as clicking 'share
|
||||
* screen' again, so we just don't track screen state across reload. */
|
||||
const ACTIVE_CAM_KEY = 'zebra-spaces-active-cam-v1';
|
||||
/* mute state for the current tab — '1' means muted. Survives a hard
|
||||
* refresh via sessionStorage so the page reconnects in the same state
|
||||
* the user last set. Cleared on explicit leave. NOT cleared on role
|
||||
* demotion (mic dropped), because the next promotion should respect
|
||||
* the user's previously-chosen mute state instead of silently
|
||||
* un-muting them. */
|
||||
const MUTE_STATE_KEY = 'zebra-spaces-mute-v1';
|
||||
|
||||
/* theme toggle. The class is already applied pre-paint by the head
|
||||
* script, so this just wires the click + keeps the button label in
|
||||
|
|
@ -3052,6 +3059,11 @@ async function ensureMicAndUI(){
|
|||
try {
|
||||
await getMic(); await refreshMicList();
|
||||
$('btn-mute').disabled = false;
|
||||
/* restore the user's last mute choice. Hard refresh keeps
|
||||
* sessionStorage so reconnecting in muted state is preserved
|
||||
* without anyone hearing a hot-mic moment. */
|
||||
applyMuteState();
|
||||
if (muted) sendMicState();
|
||||
if (myUUID) startMeter(myUUID, micStream);
|
||||
} catch(e){ logLine('err','mic blocked: '+e.message); }
|
||||
updateRoleUI();
|
||||
|
|
@ -3503,16 +3515,23 @@ $('btn-decline-mic').addEventListener('click', () => {
|
|||
* mute applies to all live senders (we may have many).
|
||||
* ================================================================== */
|
||||
let muted = false;
|
||||
try { muted = sessionStorage.getItem(MUTE_STATE_KEY) === '1'; } catch(_){}
|
||||
function applyMuteState(){
|
||||
/* keep button UI, mic-track enable, room row, and peer broadcast in
|
||||
* sync. Used by both the click handler and the post-mic-acquire
|
||||
* restore path (so a hard refresh comes back muted if that's how the
|
||||
* user left it). Safe to call without a mic — guards each side. */
|
||||
if (micStream){ micStream.getAudioTracks().forEach(t=>t.enabled=!muted); }
|
||||
$('btn-mute').textContent = muted ? 'unmute' : 'mute';
|
||||
$('btn-mute').className = muted ? 'invert' : '';
|
||||
const mm = members.get(myUUID);
|
||||
if (mm){ mm.muted = muted; renderRoom(); }
|
||||
}
|
||||
$('btn-mute').addEventListener('click', () => {
|
||||
if (!micStream) return;
|
||||
muted = !muted;
|
||||
micStream.getAudioTracks().forEach(t=>t.enabled=!muted);
|
||||
$('btn-mute').textContent = muted ? 'unmute' : 'mute';
|
||||
$('btn-mute').className = muted ? 'invert' : '';
|
||||
/* flip our own mic icon — mic-state broadcasts inform OTHER peers, but
|
||||
* without this our own row never updates locally */
|
||||
const mm = members.get(myUUID);
|
||||
if (mm){ mm.muted = muted; renderRoom(); }
|
||||
try { sessionStorage.setItem(MUTE_STATE_KEY, muted ? '1' : '0'); } catch(_){}
|
||||
applyMuteState();
|
||||
sendMicState();
|
||||
});
|
||||
$('mic-select').addEventListener('change', async (e) => {
|
||||
|
|
@ -3649,6 +3668,7 @@ $('btn-leave').addEventListener('click', async () => {
|
|||
$('btn-leave').disabled = true;
|
||||
$('btn-mute').disabled = true;
|
||||
$('btn-mute').textContent = 'mute'; $('btn-mute').className=''; muted = false;
|
||||
try { sessionStorage.removeItem(MUTE_STATE_KEY); } catch(_){}
|
||||
logLine('', 'left the space');
|
||||
});
|
||||
|
||||
|
|
@ -3658,8 +3678,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
|
|||
|
||||
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-02</span><br>
|
||||
md5 <span class="stamp-md5">b3db2fd79fc51b75927a92257dee6295</span><br>
|
||||
sha256 <span class="stamp-sha">08421e83202e6042104b1b06b7f5075eab58c844f5b8109d9a00f5d295cd1cde</span><br>
|
||||
md5 <span class="stamp-md5">60f9f219a6148fe06d689b2b8fa0affe</span><br>
|
||||
sha256 <span class="stamp-sha">a6e49160225e5cf39525e12417491de5531c4b5388075bef3faed15e3799ad59</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue