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:249invalidates the gesture without removing the class. plugins/discourse-reactions/assets/javascripts/discourse/components/discourse-reactions-actions.gjs:257then 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:
-
touchStartadds the globaldiscourse-reactions-no-selectclass. -
touchMovemarks the scrolling gesture invalid but does not clear it. -
touchEndsees the invalid gesture and returns early. -
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.