diff --git a/src/gui/src/UI/Dashboard/TabApps.js b/src/gui/src/UI/Dashboard/TabApps.js index 2691be6f8..ea4a96e52 100644 --- a/src/gui/src/UI/Dashboard/TabApps.js +++ b/src/gui/src/UI/Dashboard/TabApps.js @@ -603,6 +603,18 @@ const TabApps = { document.addEventListener('keydown', d.onKey); window.addEventListener('blur', d.onBlur); + // iOS/WebKit latches touch-action at gesture start and treats an + // in-progress pan as non-cancelable, so preventing pointermove can't + // stop the pager from scrolling out from under a drag (it just fires + // pointercancel and kills the reorder). A non-passive touchmove that + // preventDefaults *once reordering is armed* is what actually holds the + // scroller still. Before arming (readyToDrag false) it's a no-op, so a + // quick pre-long-press swipe still flips pages natively. + if ( pointerType === 'touch' ) { + d.onTouchMove = ev => { if ( d.readyToDrag ) ev.preventDefault(); }; + document.addEventListener('touchmove', d.onTouchMove, { passive: false }); + } + // Touch: a long-press *arms* reordering (it doesn't grab the tile yet). // Moving after that begins the drag; holding still instead lets the // native long-press context menu (Uninstall) fire. A finger that moves @@ -831,6 +843,7 @@ const TabApps = { document.removeEventListener('pointercancel', d.onCancel); document.removeEventListener('keydown', d.onKey); window.removeEventListener('blur', d.onBlur); + if ( d.onTouchMove ) document.removeEventListener('touchmove', d.onTouchMove, { passive: false }); clearTimeout(d.longPressTimer); clearTimeout(d.edgeTimer); clearTimeout(d.flipClearTimer); diff --git a/src/gui/src/css/dashboard.css b/src/gui/src/css/dashboard.css index c98ba0152..926372aa7 100644 --- a/src/gui/src/css/dashboard.css +++ b/src/gui/src/css/dashboard.css @@ -776,7 +776,12 @@ input.myapps-search::-webkit-search-decoration { padding-top: 8px; scroll-snap-type: x mandatory; overscroll-behavior-x: contain; - touch-action: pan-x; + /* Deliberately not pan-x: an explicit pan value makes horizontal touchmove + non-cancelable on iOS/WebKit, so the pager pans out from under a + long-press drag and cancels it. With the default (auto) the non-passive + touchmove listener installed during a reorder can preventDefault the + native pan; a quick pre-long-press swipe still flips pages natively. */ + touch-action: auto; scrollbar-width: none; }