swipe bug fix

This commit is contained in:
2026-03-28 10:00:52 -04:00
parent f50f2aaba1
commit abd4574ee3
3 changed files with 18 additions and 8 deletions

View File

@@ -4,15 +4,16 @@ import { createPortal } from 'react-dom';
export default function ImageLightbox({ src, onClose }) { export default function ImageLightbox({ src, onClose }) {
const overlayRef = useRef(null); const overlayRef = useRef(null);
// Close on Escape // Close on Escape; signal global pinch handler to stand down while open
useEffect(() => { useEffect(() => {
const handler = (e) => { if (e.key === 'Escape') onClose(); }; const handler = (e) => { if (e.key === 'Escape') onClose(); };
window.addEventListener('keydown', handler); window.addEventListener('keydown', handler);
// Prevent body scroll while open
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
document.documentElement.dataset.lightboxOpen = '1';
return () => { return () => {
window.removeEventListener('keydown', handler); window.removeEventListener('keydown', handler);
document.body.style.overflow = ''; document.body.style.overflow = '';
delete document.documentElement.dataset.lightboxOpen;
}; };
}, [onClose]); }, [onClose]);

View File

@@ -59,7 +59,9 @@ if ('serviceWorker' in navigator) {
document.addEventListener('touchmove', function (e) { document.addEventListener('touchmove', function (e) {
if (e.touches.length === 2 && pinchStartDist !== null) { if (e.touches.length === 2 && pinchStartDist !== null) {
// Two-finger pinch: scale fonts, not viewport // Two-finger pinch: scale fonts, not viewport.
// Skip when a lightbox is open — let the browser handle pinch natively there.
if (document.documentElement.dataset.lightboxOpen) return;
e.preventDefault(); e.preventDefault();
const ratio = getTouchDist(e) / pinchStartDist; const ratio = getTouchDist(e) / pinchStartDist;
const newScale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, pinchStartScale * ratio)); const newScale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, pinchStartScale * ratio));

View File

@@ -455,11 +455,15 @@ export default function Chat() {
setUnreadGroups(prev => { const next = new Map(prev); next.delete(id); return next; }); setUnreadGroups(prev => { const next = new Map(prev); next.delete(id); return next; });
}; };
// Establish one history sentinel on mount (mobile only) so back gestures are // Establish two history entries on mount (mobile only):
// always interceptable without accumulating extra entries. // floor — marks the true exit point; always stays below the sentinel
// sentinel — intercepted by handlePopState on every back gesture
// Two entries are required so that iOS fires popstate (same-document navigation)
// before exiting, giving the handler a chance to push a new sentinel.
useEffect(() => { useEffect(() => {
if (window.innerWidth < 768) { if (window.innerWidth < 768) {
window.history.replaceState({ rc: 'chat' }, ''); window.history.replaceState({ rc: 'floor' }, '');
window.history.pushState({ rc: 'chat' }, '');
} }
}, []); }, []);
@@ -486,8 +490,11 @@ export default function Chat() {
return; return;
} }
// Already at root (Messages list, no chat open) — let the browser handle // Already at root (Messages list, no chat open) — we just popped the sentinel
// it so the next gesture actually exits the PWA. Don't re-push. // and are now on the floor entry. Step one more back so the browser exits the
// PWA (or navigates to the previous URL). Without this explicit go(-1), iOS
// leaves the user stranded on the invisible floor state.
window.history.go(-1);
}; };
window.addEventListener('popstate', handlePopState); window.addEventListener('popstate', handlePopState);
return () => window.removeEventListener('popstate', handlePopState); return () => window.removeEventListener('popstate', handlePopState);