#9: mobile — single tap acts, long-press reveals message actions
On touch, tapping a message used to reveal the reply/react/more bar (so the action needed a second tap). Now a single tap performs the primary action (open image/file, jump to a reply), and the action bar is revealed by a ~420ms long-press (with a small haptic); movement or an early release cancels, and a fired long-press suppresses the trailing tap. A tap elsewhere dismisses the revealed bar. Desktop hover behaviour is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+23
-5
@@ -3368,13 +3368,31 @@ async function openConvo(kind,id){
|
||||
const ab=e.target.closest('.react-btn'); if(ab){ openEmojiForReact(ab.dataset.id, ab); return; }
|
||||
const ch=e.target.closest('.react-chip'); if(ch){ reactToMessage(ch.dataset.id, ch.dataset.emoji); return; }
|
||||
const sb=e.target.closest('.seenby,.rcpt.grp'); if(sb){ const bub=sb.closest('.bubble'); const mid=bub&&bub.dataset.id; const msg=(mid&&THREAD.find(x=>x.id===mid))||{ seenBy:(sb.dataset.seen||'').split('|').filter(Boolean) }; showSeenByModal(msg); return; }
|
||||
// Tap-to-reveal (mobile): a tap on the bubble body (not an action) reveals its reply/react/delete
|
||||
// icons; the action only fires on a SECOND tap once they're shown (icons are pointer-events:none
|
||||
// until revealed). Tapping elsewhere hides them.
|
||||
const bub=e.target.closest('.bubble'); const already=bub&&bub.classList.contains('show-actions');
|
||||
// #9 (mobile): a single tap now performs the PRIMARY action (open image/file, jump to a reply — all
|
||||
// handled above). The reply/react/more actions are revealed by LONG-PRESS (handler below), not by tap.
|
||||
// A tap that reaches here (empty space or a plain text bubble) just DISMISSES any revealed action bar.
|
||||
box.querySelectorAll('.bubble.show-actions').forEach(b=>b.classList.remove('show-actions'));
|
||||
if(bub && !already && !bub.classList.contains('deleted')) bub.classList.add('show-actions');
|
||||
});
|
||||
// #9 (mobile): LONG-PRESS a message to reveal its actions (reply / react / more). Touch only — desktop
|
||||
// still reveals on hover. Movement or an early release cancels; when it fires we suppress the trailing tap
|
||||
// so the long-press doesn't also open an image / jump a reply.
|
||||
if(box){
|
||||
let lpT=null, lpBub=null, lpX=0, lpY=0, lpFired=false;
|
||||
const lpCancel=()=>{ if(lpT){ clearTimeout(lpT); lpT=null; } lpBub=null; };
|
||||
box.addEventListener('touchstart',(e)=>{
|
||||
if(e.touches.length!==1){ lpCancel(); return; }
|
||||
const bub=e.target.closest('.bubble'); if(!bub || bub.classList.contains('deleted')){ lpCancel(); return; }
|
||||
lpBub=bub; lpX=e.touches[0].clientX; lpY=e.touches[0].clientY; lpFired=false;
|
||||
lpT=setTimeout(()=>{ lpT=null; if(!lpBub) return;
|
||||
box.querySelectorAll('.bubble.show-actions').forEach(b=>{ if(b!==lpBub) b.classList.remove('show-actions'); });
|
||||
lpBub.classList.add('show-actions'); lpFired=true;
|
||||
try{ if(navigator.vibrate) navigator.vibrate(12); }catch(_){}
|
||||
}, 420);
|
||||
},{passive:true});
|
||||
box.addEventListener('touchmove',(e)=>{ if(lpT && e.touches.length===1){ const t=e.touches[0]; if(Math.abs(t.clientX-lpX)>10||Math.abs(t.clientY-lpY)>10) lpCancel(); } },{passive:true});
|
||||
box.addEventListener('touchend',(e)=>{ if(lpFired){ try{ e.preventDefault(); }catch(_){} lpFired=false; lpCancel(); return; } lpCancel(); },{passive:false});
|
||||
box.addEventListener('touchcancel',lpCancel,{passive:true});
|
||||
}
|
||||
if(box) box.addEventListener('scroll', onMsgsScroll);
|
||||
if(box){ const _rel=()=>{ _forcePinOpen=false; _openScrolled=true; _openPinTimers.forEach(clearTimeout); _openPinTimers.length=0; }; box.addEventListener('touchmove', _rel, {passive:true}); box.addEventListener('wheel', _rel, {passive:true}); } // the user's first real scroll gesture hands control back: stop force-gluing to the newest AND cancel the pending open-pins so scrolling up for older history isn't yanked back down
|
||||
if(box) enablePullRefresh(box, loadOlder); // pull down at the top loads OLDER history (or no-ops); never jumps to the newest/bottom
|
||||
|
||||
Reference in New Issue
Block a user