zebra-report: deploy tap-to-resume + kick-clears-auto-rejoin

This commit is contained in:
russell@unturf.com 2026-06-04 11:27:39 -04:00
parent e5bb1366fc
commit b8fd50c37b
No known key found for this signature in database

View file

@ -1667,9 +1667,13 @@ function attachSfuTrack(uuid, stream){
(t0 ? ' tr0={en='+t0.enabled+' mu='+t0.muted+' rs='+t0.readyState+'}' : ''));
try {
const p = a.play();
if (p && p.catch) p.catch(e => logLine('err','rtc autoplay '+uuid.slice(0,4)+': '+e.message));
if (p && p.catch) p.catch(e => {
logLine('err','rtc autoplay '+uuid.slice(0,4)+': '+e.message);
flagAudioBlocked('rtc autoplay '+uuid.slice(0,4));
});
} catch(e){
logLine('err','rtc play threw '+uuid.slice(0,4)+': '+e.message);
flagAudioBlocked('rtc play threw '+uuid.slice(0,4));
}
stopMeter(uuid); startMeter(uuid, stream);
logLine('', 'sfu: receiving '+((members.get(uuid)||{}).handle || uuid));
@ -3800,6 +3804,13 @@ async function handleSignal(raw){
* down our SFU + mesh PCs too so we actually stop hearing /
* broadcasting — closing the WS alone leaves the WebRTC paths up. */
wantConnected = false;
/* Clear the active-call key so a hard-refresh / bfcache restore
* doesn't sneak the kicked user right back in via auto-rejoin.
* The confirm() text says "they can rejoin" — but that means
* MANUALLY (type the code, click enter), not automatically on
* any tab event. Fox 2026-06-04: "we have auto-join on the
* phone that is kicked just rejoins". */
try { sessionStorage.removeItem(ACTIVE_CALL_KEY); } catch(_){}
const noticeVerb = m.action === 'kick' ? 'kicked from'
: m.action === 'ban' ? 'banned from'
: 'removed from';
@ -4269,19 +4280,49 @@ let selfListenerMode = false;
function activateListenerAudio(){
/* Fire .play() on every existing <audio> element inside the click
* gesture. The user just tapped — Firefox Android grants media
* permission for the duration of this synchronous handler. */
* permission for the duration of this synchronous handler. Also
* extends to streamAudio (DJ HTTP pulls) so a single tap recovers
* both WebRTC and HTTP paths. */
let played = 0, failed = 0;
for (const [uuid, a] of remoteAudio){
const tryPlay = (a, label, uuid) => {
try { a.muted = false; } catch(_){}
try {
const p = a.play();
if (p && p.then){
p.then(()=>played++).catch(e => { failed++; logLine('err','rtc play '+uuid.slice(0,8)+' paused='+a.paused+' rs='+a.readyState+' srcObj='+!!a.srcObject+' err='+e.name+': '+e.message); });
p.then(()=>played++).catch(e => { failed++; logLine('err',label+' play '+uuid.slice(0,8)+' paused='+a.paused+' rs='+a.readyState+' err='+e.name+': '+e.message); });
} else played++;
} catch(e){ failed++; logLine('err','rtc play threw '+uuid.slice(0,8)+': '+e.message); }
logLine('', 'rtc '+uuid.slice(0,8)+' paused='+a.paused+' muted='+a.muted+' rs='+a.readyState+' srcObj='+!!a.srcObject+' vol='+a.volume);
}
logLine('', 'activateListenerAudio played='+played+' failed='+failed+' remoteAudio='+remoteAudio.size);
} catch(e){ failed++; logLine('err',label+' play threw '+uuid.slice(0,8)+': '+e.message); }
};
for (const [uuid, a] of remoteAudio){ tryPlay(a, 'rtc', uuid); }
for (const [uuid, a] of streamAudio){ tryPlay(a, 'stream', uuid); }
logLine('', 'activateListenerAudio played='+played+' failed='+failed+' rtc='+remoteAudio.size+' stream='+streamAudio.size);
}
/* Auto-recovery hook for Firefox Android autoplay block: when
* attachSfuTrack's play() rejects, set audioPlaybackBlocked=true and
* surface a single notice. The next click anywhere on the page calls
* activateListenerAudio() inside the gesture, which re-tries play()
* on every paused element. One tap, all audio unblocks.
*
* Scoped — the document-click listener is armed only while the flag
* is true. Cleared the moment any audio resumes. Doesn't add noise
* to working sessions. */
let audioPlaybackBlocked = false;
function flagAudioBlocked(why){
if (audioPlaybackBlocked) return;
audioPlaybackBlocked = true;
logLine('err', 'audio blocked: '+why+' — tap anywhere to resume');
showNotice('Audio paused by browser. Tap anywhere to resume.', 'warn');
const handler = () => {
document.removeEventListener('click', handler, true);
document.removeEventListener('touchstart', handler, true);
activateListenerAudio();
/* the play() promises resolve async, but we can clear the flag
* optimistically — if any of them re-reject we'll re-flag. */
audioPlaybackBlocked = false;
hideNotice();
};
document.addEventListener('click', handler, true);
document.addEventListener('touchstart', handler, true);
}
function applyAudioMute(){
/* legacy hook still called by attachSfuTrack / startStream; for the
@ -5212,8 +5253,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> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-04</span><br>
md5 <span class="stamp-md5">ac81473f271b2df615e659a510a042b0</span><br>
sha256 <span class="stamp-sha">85e48d687bf5e4aae15997e165a08769ad99a93213436b97b695feada96b25e6</span><br>
md5 <span class="stamp-md5">6e89267a0fe4c92fb46b50f0315a4223</span><br>
sha256 <span class="stamp-sha">e28bb2a3b1a846b590515b0221d9cdb1e5e50e4b4c6a884c68891e7607a613c6</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>