안드로이드에서 인용 텍스트 팝업 메뉴가 반응하지 않습니다

Codex에서 AI(GPT 5.6 Sol Ultra) 진단 (참고 사항):

원인을 찾았습니다: Discourse Reactions 플러그인이 전역적인 “텍스트 선택 금지” 상태를 누출시키고 있습니다.

  • 반응(reaction) 버튼의 touchstart 이벤트가 plugins/discourse-reactions/assets/javascripts/discourse/components/discourse-reactions-actions.gjs:222에서 <html> 태그에 discourse-reactions-no-select 클래스를 추가합니다.
  • 해당 클래스는 plugins/discourse-reactions/assets/stylesheets/common/discourse-reactions.scss:1에서 문서 전체에 user-select: none을 적용하고 WebKit의 롱터치 콜아웃(long-touch callout)을 비활성화합니다.
  • 반응 버튼 위에서 세로 스크롤이 시작되면, plugins/discourse-reactions/assets/javascripts/discourse/components/discourse-reactions-actions.gjs:249에서 제스처를 무효(invalid)로 처리하지만 클래스는 제거하지 않습니다.
  • 이후 plugins/discourse-reactions/assets/javascripts/discourse/components/discourse-reactions-actions.gjs:257에서 조기 반환(early return)이 발생하여 클래스가 영원히 남아 있게 됩니다.
  • 또한 touchcancel 이벤트나 컴포넌트 파괴 시의 클린업(cleanup) 로직도 존재하지 않습니다.

클래스가 제거되는 유일한 위치는 plugins/discourse-reactions/assets/javascripts/discourse/components/discourse-reactions-actions.gjs:690입니다. 이는 보고된 우회 방법(The popup menu for quoting text is unresponsive on Android)을 설명합니다: 반응 팝업을 열었다가 닫으면 collapseAllPanels()가 호출되어 텍스트 선택이 복구됩니다.

인용(quote) 툴바 자체는 하류(downstream)에 위치합니다. frontend/discourse/app/components/post-text-selection.gjs:58에서 selectionchange 이벤트를 듣고 실제 브라우저 선택 상태를 요구합니다. 전역 user-select: none이 적용된 상태에서는 Android에서 해당 선택이 생성되지 않습니다.

히스토리는 이 버그 계열을 확인해 줍니다: 2023년 스크롤 수정(FIX: prevents triggering a reaction while scrolling (#224) · discourse/discourse-reactions@d1970cf · GitHub)에서 무효 터치 시 조기 반환이 도입되었고, 2024년 부분 수정(FIX: couldn't select text after dismissing a reaction (#296) · discourse/discourse-reactions@c6710ef · GitHub)에서 클린업을 패널 접힘 시점으로 옮겼지만 스크롤/취소 경로는 여전히 미처리 상태였습니다.

요약하자면, 문제는 다음 요소들의 조합입니다:

  1. touchStart가 전역 discourse-reactions-no-select 클래스를 추가합니다.

  2. touchMove가 스크롤 제스처를 무효로 표시하지만 이를 해제하지 않습니다.

  3. touchEnd가 무효 제스처를 감지하고 조기 반환합니다.

  4. 팝업이 열리지 않았으므로 collapseAllPanels()가 클래스를 해제하지 않습니다.

그 결과 페이지 전체에 user-select: none이 유지됩니다. touchcancel도 미처리 경로이지만, vertical-scroll 경로가 주된 핵심 문제입니다.

수정 방법은 완료, 거부, 취소 또는 파괴된 모든 터치 라이프사이클에서 클래스를 해제하는 것이며, touchstart → touchmove → touchendtouchcancel 시나리오에 대한 회귀 테스트(regression tests)를 포함해야 합니다.

3개의 좋아요