From 329e911327ec3a0cb52b4f1f835649e59356e726 Mon Sep 17 00:00:00 2001 From: nesquena-hermes Date: Fri, 3 Jul 2026 23:34:37 +0000 Subject: [PATCH] fix: prevent unintended session switch on stray pointerup (#5462) Clean rebase of webtecnica's #5487 (rebase-first). Co-authored-by: webtecnica --- static/sessions.js | 1 + tests/test_session_touch_actions.py | 30 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/static/sessions.js b/static/sessions.js index 73560c49..62de0816 100644 --- a/static/sessions.js +++ b/static/sessions.js @@ -7632,6 +7632,7 @@ function renderSessionListFromCache(){ } }; const _finishSessionGesture=(clientX,clientY,target,pointerType)=>{ + if(_gestureState==='idle') return false; // press never began on this row const wasDragging=_gestureState==='dragging'||_swipeTracking; _clearLongPressTimer(); if(_renamingSid){_gestureState='idle';return false;} diff --git a/tests/test_session_touch_actions.py b/tests/test_session_touch_actions.py index 2c73e610..faba4fad 100644 --- a/tests/test_session_touch_actions.py +++ b/tests/test_session_touch_actions.py @@ -283,3 +283,33 @@ def test_touch_session_rows_preserve_vertical_scroll(): assert "user-select:none" in item_rule assert "-webkit-user-select:none" in item_rule assert "-webkit-touch-callout:none" in item_rule + + +def test_session_gesture_finish_ignores_idle_state(): + """Test that _finishSessionGesture bails early when _gestureState is 'idle'. + + This prevents unintended session switches when a drag starts outside the + session row and ends on top of it. The gesture must have begun with a + pointerdown on the row itself (_gestureState='pressing'), otherwise the + pointerup should be ignored. + + See: https://github.com/nesquena/hermes-webui/issues/5462 + """ + # Find the _finishSessionGesture function + fn_start = SESSIONS_JS.find("const _finishSessionGesture=(clientX,clientY,target,pointerType)=>{") + assert fn_start != -1, "_finishSessionGesture function not found" + + # Extract the first ~200 characters of the function body + fn_body_start = SESSIONS_JS[fn_start:fn_start+200] + + # Verify the idle-state guard is present at the beginning + assert "if(_gestureState==='idle') return false;" in fn_body_start, \ + "Missing idle-state guard at the beginning of _finishSessionGesture" + + # Also verify that the fork row handler has the same guard (consistency check) + # Fork handler pattern: rowEl.onpointerup followed by pointerType/button check, then idle guard + fork_handler_start = SESSIONS_JS.find("rowEl.onpointerup=(e)=>{") + assert fork_handler_start != -1, "Fork row pointerup handler not found" + fork_handler_snippet = SESSIONS_JS[fork_handler_start:fork_handler_start+200] + assert "if(_gestureState==='idle') return;" in fork_handler_snippet, \ + "Fork row pointerup handler should have idle guard"