From f971908d804ffd74067193c01f6e23d3fbcad26d Mon Sep 17 00:00:00 2001 From: sravan Date: Fri, 7 Aug 2026 11:14:41 +0530 Subject: [PATCH] Fix: hide native video while a meeting menu/panel is open (z-order) Native video draws on top of the WebView, so the More/audio menu, participant panel, meeting chat, modals and the image lightbox all opened BEHIND the shared screen. Now bzNativeSyncTiles clears the native tiles whenever any of those overlays is present (.meet-panel/.spk-menu/.modal-ov/ .lightbox), and the menu/panel toggles trigger an immediate sync so there's no lag; the video returns when they close. Co-Authored-By: Claude Opus 4.8 --- server/public/home.html | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/server/public/home.html b/server/public/home.html index d69fe93..ec176ff 100644 --- a/server/public/home.html +++ b/server/public/home.html @@ -2654,7 +2654,7 @@ function meetParticipantsList(){ const a=[{id:'__local', name:((ME&&ME.name)||(M let meetChatLog=[], meetChatUnread=0, meetChatTo=''; function resetMeetChat(){ meetChatLog=[]; meetChatUnread=0; meetChatTo=''; const c=document.getElementById('meetChat'); if(c) c.remove(); updateMeetChatBadge(); } function meetChatRecipients(){ const a=[]; meetPeers.forEach((pp,pid)=>a.push({id:pid, name:meetNames.get(pid)||pp.name||'Guest'})); return a; } -function toggleMeetChat(){ const ex=document.getElementById('meetChat'); if(ex){ ex.remove(); return; } const meet=document.querySelector('.meet'); if(!meet) return; const p=document.createElement('div'); p.id='meetChat'; p.className='meet-panel meet-chat'; meet.appendChild(p); meetChatUnread=0; updateMeetChatBadge(); renderMeetChat(); } +function toggleMeetChat(){ const ex=document.getElementById('meetChat'); if(ex){ ex.remove(); try{ bzNativeSyncTiles(); }catch(_){} return; } const meet=document.querySelector('.meet'); if(!meet) return; const p=document.createElement('div'); p.id='meetChat'; p.className='meet-panel meet-chat'; meet.appendChild(p); meetChatUnread=0; updateMeetChatBadge(); renderMeetChat(); try{ bzNativeSyncTiles(); }catch(_){} } function updateMeetChatBadge(){ const b=document.getElementById('meetChatBadge'); if(!b) return; if(meetChatUnread>0){ b.textContent=meetChatUnread>9?'9+':meetChatUnread; b.style.display='grid'; } else b.style.display='none'; } function renderMeetChat(){ const p=document.getElementById('meetChat'); if(!p) return; @@ -2719,7 +2719,7 @@ function copyMeetingLink(){ function fallbackCopy(text,cb){ try{ const ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.opacity='0'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove(); cb&&cb(); }catch(_){ prompt('Copy this link:', text); } } let meetPanelTab='people'; // 'people' | 'add' let _addPool=null; // cached candidates for "Add people" (group members for a group call) -function toggleMeetPanel(){ const ex=document.getElementById('meetPanel'); if(ex){ ex.remove(); return; } const meet=document.querySelector('.meet'); if(!meet) return; meetPanelTab='people'; const p=document.createElement('div'); p.id='meetPanel'; p.className='meet-panel'; meet.appendChild(p); renderMeetPanel(); } +function toggleMeetPanel(){ const ex=document.getElementById('meetPanel'); if(ex){ ex.remove(); try{ bzNativeSyncTiles(); }catch(_){} return; } const meet=document.querySelector('.meet'); if(!meet) return; meetPanelTab='people'; const p=document.createElement('div'); p.id='meetPanel'; p.className='meet-panel'; meet.appendChild(p); renderMeetPanel(); try{ bzNativeSyncTiles(); }catch(_){} } function renderMeetPanel(){ const p=document.getElementById('meetPanel'); if(!p) return; const list=meetParticipantsList(); @@ -5060,9 +5060,10 @@ function openMeetMore(anchor){ const r=anchor.getBoundingClientRect(); menu.style.left=Math.max(8,Math.min(r.left-100, window.innerWidth-menu.offsetWidth-8))+'px'; menu.style.top=Math.max(8,(r.top-menu.offsetHeight-10))+'px'; - menu.querySelectorAll('.mm-opt').forEach(b=>b.onclick=()=>{ const it=items[+b.dataset.i]; menu.remove(); try{ it.fn(); }catch(_){} }); - const close=(e)=>{ if(!menu.contains(e.target) && !anchor.contains(e.target)){ menu.remove(); document.removeEventListener('mousedown',close); document.removeEventListener('touchstart',close); } }; + menu.querySelectorAll('.mm-opt').forEach(b=>b.onclick=()=>{ const it=items[+b.dataset.i]; menu.remove(); try{ it.fn(); }catch(_){} try{ if(meetNative) bzNativeSyncTiles(); }catch(_){} }); + const close=(e)=>{ if(!menu.contains(e.target) && !anchor.contains(e.target)){ menu.remove(); document.removeEventListener('mousedown',close); document.removeEventListener('touchstart',close); try{ if(meetNative) bzNativeSyncTiles(); }catch(_){} } }; setTimeout(()=>{ document.addEventListener('mousedown',close); document.addEventListener('touchstart',close); },0); + try{ if(meetNative) bzNativeSyncTiles(); }catch(_){} // clear the native video NOW so the menu isn't hidden behind it } async function setSpeakerDevice(id){ meetSinkId=id||''; try{ localStorage.setItem('bzc_sink', meetSinkId); }catch(_){} @@ -5390,9 +5391,14 @@ let meetNative=false, meetNativeUuid=null, _ncEnding=false; // native call: WebV // join-leave / camera-toggle changes; a no-op on web calls. getBoundingClientRect coords are CSS px, which // equal points in the WKWebView, so the plugin can use them directly. let _ncTileTimer=null; +// Any web menu/panel/modal that opens over the meeting would be hidden BEHIND the native video (which draws +// on top of the WebView). While one is open, clear the native video so the web UI is usable; the poll restores +// it when they close. (Covers the More/audio menus, participant panel, meeting chat, modals, image lightbox.) +function bzMeetOverlayOpen(){ return !!document.querySelector('.meet-panel, .spk-menu, .modal-ov, .lightbox'); } function bzNativeSyncTiles(){ if(!meetNative) return; const NC=nativeCallPlugin(); if(!NC||!NC.syncVideoTiles) return; + if(bzMeetOverlayOpen()){ try{ NC.syncVideoTiles({ tiles:[] }); }catch(_){} return; } // a menu/panel is open → don't cover it const tiles=[]; // Native views draw ON TOP of the WebView, so a full-height tile would cover the control bar. Never let a // tile's native video extend past the top of the meeting controls (or, in normal grid mode, below the grid).