mobile: revert finger-following swipe-back to the working release-based version (interactive one had an undiagnosable-blind layout offset) (batch142)
This commit is contained in:
+14
-63
@@ -1158,7 +1158,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<script src="/icons.js?v=6"></script>
|
||||
<script>window.__BUILD='2026-07-19-batch141';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
|
||||
<script>window.__BUILD='2026-07-20-batch142';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
|
||||
@@ -4961,71 +4961,22 @@ function bzcBack(){
|
||||
}
|
||||
try{ history.pushState(null,'',location.href); }catch(_){}
|
||||
window.addEventListener('popstate', ()=>{ if(bzcBack()){ try{ history.pushState(null,'',location.href); }catch(_){} } });
|
||||
// #4: native-style INTERACTIVE swipe-back. Drag the open conversation rightward from the left edge and it
|
||||
// FOLLOWS YOUR FINGER 1:1, with the chat list parallaxing in underneath; release past ~35% of the width (or
|
||||
// a quick flick) completes the pop, otherwise it snaps back. The old version's bug — the pane getting stuck
|
||||
// — happened because passive listeners let iOS/scroll steal the gesture and fire touchcancel. Fix: once a
|
||||
// clear horizontal drag is detected we preventDefault (passive:false) to CLAIM the gesture, and touchcancel
|
||||
// always resolves to a clean state via end()/reset(). Arms only in the left 32px so it never clashes with
|
||||
// the bubble swipe-to-reply (which starts >32px). Works in the native app, PWA and mobile browser alike.
|
||||
// #4: left-edge SWIPE-back. A drag that begins at the very left edge and releases clearly rightward closes
|
||||
// the topmost layer (popup → in-conversation search → open chat) via bzcBack() → showWelcome()'s parallax
|
||||
// slide. RELEASE-triggered on purpose: the finger-following version had a layout bug (the conversation pane
|
||||
// sat one screen-width off, so it couldn't be dragged into view) that I could not resolve without seeing it
|
||||
// render on-device. This version reliably runs the same push-slide close.
|
||||
(function(){
|
||||
const EDGE=32;
|
||||
let sx=0, sy=0, w=0, armed=false, dragging=false, content=null, chatcol=null, t0=0;
|
||||
const isMobile=()=>window.matchMedia('(max-width:760px)').matches;
|
||||
function canSwipe(){
|
||||
if(!isMobile() || selected==null) return false;
|
||||
if(!document.body.classList.contains('chat-open') || document.body.classList.contains('chat-closing')) return false;
|
||||
if(document.querySelector('.modal-ov') || document.getElementById('lightbox')) return false;
|
||||
const sh=document.getElementById('convoSearchHead'); if(sh && sh.style.display==='flex') return false; // let search close via its own X / the back button
|
||||
return true;
|
||||
}
|
||||
function reset(){ if(content){ content.style.transition=''; content.style.transform=''; } if(chatcol){ chatcol.style.transition=''; chatcol.style.transform=''; } document.body.classList.remove('chat-dragging'); armed=false; dragging=false; content=null; chatcol=null; }
|
||||
let sx=0, sy=0, edge=false;
|
||||
window.addEventListener('touchstart',(e)=>{
|
||||
if(dragging) return;
|
||||
if(e.touches.length!==1 || !canSwipe()){ armed=false; return; }
|
||||
const t=e.touches[0];
|
||||
if(t.clientX>EDGE){ armed=false; return; }
|
||||
armed=true; dragging=false; sx=t.clientX; sy=t.clientY; w=window.innerWidth||360; t0=Date.now();
|
||||
if(e.touches.length!==1){ edge=false; return; }
|
||||
const t=e.touches[0]; edge = t.clientX<=24; sx=t.clientX; sy=t.clientY; // only arm when starting at the left edge
|
||||
}, {passive:true});
|
||||
window.addEventListener('touchend',(e)=>{
|
||||
if(!edge) return; edge=false;
|
||||
const t=e.changedTouches&&e.changedTouches[0]; if(!t) return;
|
||||
if((t.clientX-sx)>60 && Math.abs(t.clientY-sy)<50) bzcBack(); // rightward, mostly-horizontal swipe = back
|
||||
}, {passive:true});
|
||||
window.addEventListener('touchmove',(e)=>{
|
||||
if(!armed || e.touches.length!==1) return;
|
||||
const t=e.touches[0], dx=t.clientX-sx, dy=t.clientY-sy;
|
||||
if(!dragging){
|
||||
if(Math.abs(dy)>Math.abs(dx) && Math.abs(dy)>8){ armed=false; return; } // vertical scroll → release
|
||||
if(dx>10 && dx>=Math.abs(dy)){ // confirmed rightward → claim it
|
||||
dragging=true;
|
||||
content=document.querySelector('.shell > .content');
|
||||
chatcol=document.getElementById('chatcol');
|
||||
renderChats(searchVal()); // make the list under the pane current
|
||||
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
|
||||
const x=Math.max(0, dx);
|
||||
if(content) content.style.transform='translate3d('+x+'px,0,0)';
|
||||
if(chatcol) chatcol.style.transform='translate3d('+(-22*(1-Math.min(1,x/w)))+'%,0,0)';
|
||||
}, {passive:false});
|
||||
function end(e){
|
||||
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;
|
||||
const _done = dx>w*0.25 || (v>0.35 && dx>50); // easier to complete (was 35%)
|
||||
if(window.bzDbg){ var _ct=content?getComputedStyle(content).transform:'x', _cl=content?Math.round(content.getBoundingClientRect().left):-1, _lt=chatcol?getComputedStyle(chatcol).transform:'x'; window.bzDbg('swipe',{s:'end',dx:Math.round(dx),done:_done?1:0,cMoved:(_ct&&_ct!=='none'&&_ct!=='x')?1:0,cLeft:_cl,lMoved:(_lt&&_lt!=='none'&&_lt!=='x')?1:0}); } // TEMP: cMoved/cLeft prove whether the conversation actually translated
|
||||
if(_done){ // 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)'; }
|
||||
setTimeout(()=>{ document.body.classList.remove('chat-dragging'); showWelcome(true); if(content){ content.style.transition=''; content.style.transform=''; } if(chatcol){ chatcol.style.transition=''; chatcol.style.transform=''; } armed=false; dragging=false; content=null; chatcol=null; }, 175);
|
||||
} else { // snap back — stay in the chat
|
||||
if(content){ content.style.transition='transform .17s'; content.style.transform='translate3d(0,0,0)'; }
|
||||
if(chatcol){ chatcol.style.transition='transform .17s'; chatcol.style.transform='translate3d(-22%,0,0)'; }
|
||||
setTimeout(reset, 175);
|
||||
}
|
||||
}
|
||||
window.addEventListener('touchend', end, {passive:true});
|
||||
window.addEventListener('touchcancel',(e)=>{ if(dragging) end(e); else armed=false; }, {passive:true}); // never leave the pane stuck
|
||||
})();
|
||||
// #9: swipe a message bubble to the right to reply to it (mobile), like WhatsApp/Teams. The bubble
|
||||
// follows the finger a little; releasing past the threshold opens the reply composer for that message.
|
||||
|
||||
Reference in New Issue
Block a user