mobile: iOS PWA keyboard — pin app to visual viewport + undo webview scroll so header stays; swipe-back telemetry (batch140)
This commit is contained in:
+23
-5
@@ -1158,7 +1158,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<script src="/icons.js?v=6"></script>
|
||||
<script>window.__BUILD='2026-07-19-batch139';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
|
||||
<script>window.__BUILD='2026-07-19-batch140';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
|
||||
// Emoji are rendered with the OS's own (colour) emoji font — instant, zero network.
|
||||
//
|
||||
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
|
||||
@@ -1212,11 +1212,27 @@ window.__perfProbe=function(label){ try{
|
||||
// the Safari toolbar growing/shrinking (only a real keyboard shrinks the visual viewport that much).
|
||||
(function(){ try{
|
||||
var C=window.Capacitor; if(C && C.isNativePlatform && C.isNativePlatform()) return;
|
||||
var vv=window.visualViewport; if(!vv) return; var r=document.documentElement;
|
||||
function setKb(px){ px=Math.max(0,Math.round(px)); r.style.setProperty('--kb',px+'px'); document.body.classList.toggle('kb-open',px>4); var b=document.getElementById('msgs'); if(b && (b.scrollHeight-b.scrollTop-b.clientHeight)<400) b.scrollTop=b.scrollHeight; }
|
||||
function upd(){ var d=document.documentElement.clientHeight - vv.height - vv.offsetTop; setKb(d>100?d:0); }
|
||||
var vv=window.visualViewport; if(!vv) return; var r=document.documentElement, b=document.body;
|
||||
function pinMsgs(){ var m=document.getElementById('msgs'); if(m && (m.scrollHeight-m.scrollTop-m.clientHeight)<400) m.scrollTop=m.scrollHeight; }
|
||||
function upd(){
|
||||
var cover = r.clientHeight - vv.height; // how much of the LAYOUT viewport the keyboard covers
|
||||
if(cover > 100){
|
||||
// Keyboard open. iOS/Safari does NOT resize the layout viewport (interactive-widget is ignored) and the
|
||||
// webview SCROLLS to reveal the focused box (device log: scrollY≈413), which pushes the header off the
|
||||
// top. Fix: pin the whole app to the VISUAL viewport height and undo the scroll, so header + messages +
|
||||
// composer all fit above the keyboard. On Android, interactive-widget=resizes-content already shrank
|
||||
// clientHeight to vv.height, so cover≈0 and this branch is skipped (that path is already correct).
|
||||
b.style.height = vv.height + 'px'; b.style.bottom = 'auto';
|
||||
r.style.setProperty('--kb','0px'); document.body.classList.add('kb-open');
|
||||
if(window.scrollY || vv.offsetTop) window.scrollTo(0,0);
|
||||
pinMsgs();
|
||||
} else {
|
||||
b.style.height=''; b.style.bottom='';
|
||||
r.style.setProperty('--kb','0px'); document.body.classList.remove('kb-open');
|
||||
}
|
||||
}
|
||||
vv.addEventListener('resize', upd); vv.addEventListener('scroll', upd);
|
||||
window.__bzResetKb=function(){ setKb(0); };
|
||||
window.__bzResetKb=function(){ b.style.height=''; b.style.bottom=''; r.style.setProperty('--kb','0px'); document.body.classList.remove('kb-open'); };
|
||||
}catch(_){} })();
|
||||
</script>
|
||||
<div class="loading" id="loading"><div class="ld-inner"><img src="/loaders/loader-orbit-dark.svg" alt="" width="72" height="72"><div class="ld-wm">Biz <b>Connect</b></div><span class="ld-sub">Loading…</span></div></div>
|
||||
@@ -4984,6 +5000,7 @@ window.addEventListener('popstate', ()=>{ if(bzcBack()){ try{ history.pushState(
|
||||
document.body.classList.add('chat-dragging');
|
||||
if(content) content.style.transition='none';
|
||||
if(chatcol) chatcol.style.transition='none';
|
||||
if(window.bzDbg) window.bzDbg('swipe',{s:'confirm',hasC:content?1:0,hasL:chatcol?1:0,sx:Math.round(sx),dx:Math.round(dx),w:Math.round(w),cCls:content?String(content.className).slice(0,20):'NULL'}); // TEMP
|
||||
} else return;
|
||||
}
|
||||
e.preventDefault(); // needs passive:false — claims the gesture
|
||||
@@ -4995,6 +5012,7 @@ window.addEventListener('popstate', ()=>{ if(bzcBack()){ try{ history.pushState(
|
||||
if(!dragging){ armed=false; return; }
|
||||
const t=(e.changedTouches&&e.changedTouches[0]);
|
||||
const dx=t?Math.max(0,t.clientX-sx):0, dt=Math.max(1,Date.now()-t0), v=dx/dt;
|
||||
if(window.bzDbg) window.bzDbg('swipe',{s:'end',dx:Math.round(dx),w:Math.round(w),v:Math.round(v*100)/100,done:(dx>w*0.35||(v>0.5&&dx>40))?1:0}); // TEMP
|
||||
if(dx>w*0.35 || (v>0.5 && dx>40)){ // complete the pop
|
||||
if(content){ content.style.transition='transform .17s cubic-bezier(.4,0,.7,.4)'; content.style.transform='translate3d(100%,0,0)'; }
|
||||
if(chatcol){ chatcol.style.transition='transform .17s cubic-bezier(.4,0,.7,.4)'; chatcol.style.transform='translate3d(0,0,0)'; }
|
||||
|
||||
Reference in New Issue
Block a user