The popup menu for quoting text is unresponsive on Android

AI (GPT 5.6 Sol Ultra) diagnosis in Codex (if useful):

Found it: Discourse Reactions leaks a global “no text selection” state.

  • Reaction touchstart adds discourse-reactions-no-select to in plugins/discourse-reactions/assets/javascripts/discourse/components/discourse-reactions-actions.gjs:222.
  • That class applies user-select: none and disables WebKit’s long-touch callout for the entire document in plugins/discourse-reactions/assets/stylesheets/common/discourse-reactions.scss:1.
  • If a vertical scroll starts over the reaction button, plugins/discourse-reactions/assets/javascripts/discourse/components/discourse-reactions-actions.gjs:249 invalidates the gesture without removing the class.
  • plugins/discourse-reactions/assets/javascripts/discourse/components/discourse-reactions-actions.gjs:257 then returns early, leaving the class behind indefinitely.
  • There is also no touchcancel or component-destruction cleanup.

The only removal is in plugins/discourse-reactions/assets/javascripts/discourse/components/discourse-reactions-actions.gjs:690. That explains the reported workaround (The popup menu for quoting text is unresponsive on Android): opening and dismissing the reactions popup calls collapseAllPanels(), restoring selection.

The quote toolbar itself is downstream—it listens for selectionchange and requires a real browser selection in frontend/discourse/app/components/post-text-selection.gjs:58. With global user-select: none, Android never creates that selection.

History confirms this bug family: a 2023 scrolling fix (FIX: prevents triggering a reaction while scrolling (#224) · discourse/discourse-reactions@d1970cf · GitHub) introduced the invalid-touch early return, and a 2024 partial fix (FIX: couldn't select text after dismissing a reaction (#296) · discourse/discourse-reactions@c6710ef · GitHub) moved cleanup to panel collapse but left scroll/cancel paths uncovered.

So in summary it’s the combination:

  1. touchStart adds the global discourse-reactions-no-select class.

  2. touchMove marks the scrolling gesture invalid but does not clear it.

  3. touchEnd sees the invalid gesture and returns early.

  4. No popup opened, so collapseAllPanels() never clears the class.

That leaves the entire page with user-select: none. touchcancel is another uncovered path, but the vertical-scroll path is the primary crux.

The fix should clear the class on every completed, rejected, cancelled, or destroyed touch lifecycle, with regression tests for touchstart → touchmove → touchend and touchcancel.

3 Likes