zebra-spaces: drop screen-resume flow — two flows for the same result is worse than one
The 'resume screen share' button needed the same user tap as just clicking 'share screen' again, so it added complexity without buying any UX. Camera silent-resume stays — that's a meaningfully different flow (zero clicks if browser remembers the permission).
This commit is contained in:
parent
abcbdc3568
commit
623c0a0b0c
1 changed files with 10 additions and 47 deletions
|
|
@ -579,10 +579,6 @@ try {
|
|||
|
||||
<section id="sec-screen-share" class="hidden">
|
||||
<h2>share</h2>
|
||||
<div class="row hidden" id="row-resume-screen">
|
||||
<button id="btn-resume-screen" class="invert">resume screen share</button>
|
||||
<span class="note">browser needs your tap to recapture the screen — security requirement.</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button id="btn-screen-share" class="invert">share screen</button>
|
||||
<button id="btn-screen-stop" class="hidden">stop sharing</button>
|
||||
|
|
@ -663,22 +659,11 @@ const THEME_KEY = 'zebra-theme-v1';
|
|||
* hard refresh auto-rejoins. Different tabs can sit in different
|
||||
* spaces because sessionStorage doesn't bleed across tabs. */
|
||||
const ACTIVE_CALL_KEY = 'zebra-spaces-active-call-v1';
|
||||
/* what publishes were live before the refresh. Camera can attempt
|
||||
* silent resume (browser usually remembers getUserMedia permission
|
||||
* per-origin); screen CANNOT silently restart — getDisplayMedia
|
||||
* requires a fresh user gesture every time (security boundary, by
|
||||
* design). We surface a 'resume screen' button instead. */
|
||||
const ACTIVE_CAM_KEY = 'zebra-spaces-active-cam-v1';
|
||||
const ACTIVE_SCREEN_KEY = 'zebra-spaces-active-screen-v1';
|
||||
|
||||
function showResumeScreenPrompt(){
|
||||
const r = document.getElementById('row-resume-screen');
|
||||
if (r) r.classList.remove('hidden');
|
||||
}
|
||||
function hideResumeScreenPrompt(){
|
||||
const r = document.getElementById('row-resume-screen');
|
||||
if (r) r.classList.add('hidden');
|
||||
}
|
||||
/* camera resume only — screen-share recovery isn't possible silently
|
||||
* (getDisplayMedia needs a fresh gesture) and offering a separate
|
||||
* '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';
|
||||
|
||||
/* theme toggle. The class is already applied pre-paint by the head
|
||||
* script, so this just wires the click + keeps the button label in
|
||||
|
|
@ -1146,8 +1131,6 @@ async function sfuPublishScreen(){
|
|||
renderScreenTile(myKeys.pubHex, stream, { local: true });
|
||||
$('btn-screen-share').classList.add('hidden');
|
||||
$('btn-screen-stop').classList.remove('hidden');
|
||||
try { sessionStorage.setItem(ACTIVE_SCREEN_KEY, '1'); } catch(_){}
|
||||
hideResumeScreenPrompt();
|
||||
}
|
||||
async function sfuUnpublishScreen(){
|
||||
if (!sfuScreenPC && !sfuScreenStream) return;
|
||||
|
|
@ -1161,8 +1144,6 @@ async function sfuUnpublishScreen(){
|
|||
$('btn-screen-share').classList.remove('hidden');
|
||||
$('btn-screen-stop').classList.add('hidden');
|
||||
logLine('', 'screen share stopped');
|
||||
try { sessionStorage.removeItem(ACTIVE_SCREEN_KEY); } catch(_){}
|
||||
hideResumeScreenPrompt();
|
||||
}
|
||||
|
||||
/* ----- camera publish (kind=camera) ----- */
|
||||
|
|
@ -1624,13 +1605,11 @@ async function handleSignal(raw){
|
|||
* role is listener (was speaker before but room demoted us on
|
||||
* rejoin), drop the flags so we don't endlessly prompt. */
|
||||
if (canSpeak(myRole)){
|
||||
/* camera: silent attempt — getUserMedia normally remembers
|
||||
/* camera silent attempt — getUserMedia normally remembers
|
||||
* per-origin permission, so the browser won't prompt. If it
|
||||
* does prompt + the user denies, the flag self-clears via
|
||||
* sfuPublishCamera's error path the next time it tries. */
|
||||
let wantCam = false, wantScreen = false;
|
||||
* does prompt + the user denies, the flag self-clears. */
|
||||
let wantCam = false;
|
||||
try { wantCam = sessionStorage.getItem(ACTIVE_CAM_KEY) === '1'; } catch(_){}
|
||||
try { wantScreen = sessionStorage.getItem(ACTIVE_SCREEN_KEY) === '1'; } catch(_){}
|
||||
if (wantCam){
|
||||
setTimeout(() => {
|
||||
sfuPublishCamera().catch(e => {
|
||||
|
|
@ -1639,14 +1618,8 @@ async function handleSignal(raw){
|
|||
});
|
||||
}, 250);
|
||||
}
|
||||
if (wantScreen){
|
||||
/* screen needs a user gesture per browser security — surface the
|
||||
* resume button instead of trying to call getDisplayMedia silently */
|
||||
showResumeScreenPrompt();
|
||||
}
|
||||
} else {
|
||||
try { sessionStorage.removeItem(ACTIVE_CAM_KEY); } catch(_){}
|
||||
try { sessionStorage.removeItem(ACTIVE_SCREEN_KEY); } catch(_){}
|
||||
}
|
||||
renderShareUrl();
|
||||
onRoleEntered();
|
||||
|
|
@ -2070,12 +2043,6 @@ function renderRoom(){
|
|||
$('btn-raise').addEventListener('click', () => { send({ type:'raise-hand' }); });
|
||||
$('btn-lower').addEventListener('click', () => { send({ type:'lower-hand' }); });
|
||||
$('btn-screen-share').addEventListener('click', () => { sfuPublishScreen().catch(e => logLine('err','screen share: '+e.message)); });
|
||||
$('btn-resume-screen').addEventListener('click', () => {
|
||||
/* user gesture — getDisplayMedia can fire now. On cancel we leave the
|
||||
* prompt visible so they can try again; on success the publish path
|
||||
* hides it via hideResumeScreenPrompt(). */
|
||||
sfuPublishScreen().catch(e => logLine('err','screen resume: '+e.message));
|
||||
});
|
||||
$('btn-screen-stop').addEventListener('click', () => { sfuUnpublishScreen(); });
|
||||
$('btn-camera-share').addEventListener('click', () => { sfuPublishCamera().catch(e => logLine('err','camera share: '+e.message)); });
|
||||
$('btn-camera-stop').addEventListener('click', () => { sfuUnpublishCamera(); });
|
||||
|
|
@ -2279,8 +2246,6 @@ function handleBlocked(source){
|
|||
/* booted = clear auto-rejoin so a refresh doesn't immediately retry */
|
||||
try { sessionStorage.removeItem(ACTIVE_CALL_KEY); } catch(_){}
|
||||
try { sessionStorage.removeItem(ACTIVE_CAM_KEY); } catch(_){}
|
||||
try { sessionStorage.removeItem(ACTIVE_SCREEN_KEY); } catch(_){}
|
||||
hideResumeScreenPrompt();
|
||||
showNotice('You are blocked from this space.', 'warn');
|
||||
logLine('err','blocked ('+source+') — stopping reconnects');
|
||||
setStatus('blocked from this space','err');
|
||||
|
|
@ -2410,8 +2375,6 @@ $('btn-leave').addEventListener('click', async () => {
|
|||
/* explicit leave wipes the auto-rejoin state — user said 'out' */
|
||||
try { sessionStorage.removeItem(ACTIVE_CALL_KEY); } catch(_){}
|
||||
try { sessionStorage.removeItem(ACTIVE_CAM_KEY); } catch(_){}
|
||||
try { sessionStorage.removeItem(ACTIVE_SCREEN_KEY); } catch(_){}
|
||||
hideResumeScreenPrompt();
|
||||
if (sigReconnect){ clearTimeout(sigReconnect); sigReconnect = null; }
|
||||
for (const u of [...peers.keys()]) tearPeer(u);
|
||||
if (ws){ try { ws.close(); } catch(_){} ws = null; }
|
||||
|
|
@ -2443,8 +2406,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">b89aba673e7e569e95639e8cfd0c67fb</span><br>
|
||||
sha256 <span class="stamp-sha">f0aa1f22d67a6ebab6d37be08792d993dd100b9ea67962887136470ccbd6e159</span><br>
|
||||
md5 <span class="stamp-md5">bcedfb60e6bcfaedb68a6e765e07147a</span><br>
|
||||
sha256 <span class="stamp-sha">e05988eba4d8be995628c3875f7e3e1daca1f5fed54e571517ead8fcecbb288b</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