Просмотр исходного кода

Merge pull request #15819 from overleaf/td-review-panel-comment-selection

Prevent selection within review panel entry being immediately cleared

GitOrigin-RevId: cca7206a6620bed8cae1bb55a064102204a00751
Tim Down 2 лет назад
Родитель
Сommit
4f13470345

+ 3 - 16
services/web/frontend/js/features/source-editor/components/review-panel/entries/aggregate-change-entry.tsx

@@ -11,6 +11,7 @@ import comparePropsWithShallowArrayCompare from '../utils/compare-props-with-sha
 import { BaseChangeEntryProps } from '../types/base-change-entry-props'
 import useIndicatorHover from '../hooks/use-indicator-hover'
 import EntryIndicator from './entry-indicator'
+import { useEntryClick } from '@/features/source-editor/components/review-panel/hooks/use-entry-click'
 
 interface AggregateChangeEntryProps extends BaseChangeEntryProps {
   replacedContent: string
@@ -30,7 +31,7 @@ function AggregateChangeEntry({
   contentLimit = 17,
 }: AggregateChangeEntryProps) {
   const { t } = useTranslation()
-  const { acceptChanges, rejectChanges, gotoEntry, handleLayoutChange } =
+  const { acceptChanges, rejectChanges, handleLayoutChange } =
     useReviewPanelUpdaterFnsContext()
   const [isDeletionCollapsed, setIsDeletionCollapsed] = useState(true)
   const [isInsertionCollapsed, setIsInsertionCollapsed] = useState(true)
@@ -53,21 +54,7 @@ function AggregateChangeEntry({
     ? content.substring(0, contentLimit)
     : content
 
-  const handleEntryClick = (e: React.MouseEvent<HTMLDivElement>) => {
-    const target = e.target as Element
-
-    for (const selector of [
-      '.rp-entry',
-      '.rp-entry-description',
-      '.rp-entry-body',
-      '.rp-entry-action-icon i',
-    ]) {
-      if (target.matches(selector)) {
-        gotoEntry(docId, offset)
-        break
-      }
-    }
-  }
+  const handleEntryClick = useEntryClick(docId, offset)
 
   const handleDeletionToggleCollapse = () => {
     setIsDeletionCollapsed(value => !value)

+ 3 - 16
services/web/frontend/js/features/source-editor/components/review-panel/entries/change-entry.tsx

@@ -12,6 +12,7 @@ import { BaseChangeEntryProps } from '../types/base-change-entry-props'
 import comparePropsWithShallowArrayCompare from '../utils/compare-props-with-shallow-array-compare'
 import useIndicatorHover from '../hooks/use-indicator-hover'
 import EntryIndicator from './entry-indicator'
+import { useEntryClick } from '@/features/source-editor/components/review-panel/hooks/use-entry-click'
 
 interface ChangeEntryProps extends BaseChangeEntryProps {
   type: ReviewPanelChangeEntry['type']
@@ -31,7 +32,7 @@ function ChangeEntry({
   contentLimit = 40,
 }: ChangeEntryProps) {
   const { t } = useTranslation()
-  const { handleLayoutChange, acceptChanges, rejectChanges, gotoEntry } =
+  const { handleLayoutChange, acceptChanges, rejectChanges } =
     useReviewPanelUpdaterFnsContext()
   const [isCollapsed, setIsCollapsed] = useState(true)
   const {
@@ -49,21 +50,7 @@ function ChangeEntry({
   const needsCollapsing = content.length > contentLimit
   const isInsert = type === 'insert'
 
-  const handleEntryClick = (e: React.MouseEvent<HTMLDivElement>) => {
-    const target = e.target as Element
-
-    for (const selector of [
-      '.rp-entry',
-      '.rp-entry-description',
-      '.rp-entry-body',
-      '.rp-entry-action-icon i',
-    ]) {
-      if (target.matches(selector)) {
-        gotoEntry(docId, offset)
-        break
-      }
-    }
-  }
+  const handleEntryClick = useEntryClick(docId, offset)
 
   const handleToggleCollapse = () => {
     setIsCollapsed(value => !value)

+ 3 - 17
services/web/frontend/js/features/source-editor/components/review-panel/entries/comment-entry.tsx

@@ -17,6 +17,7 @@ import { ReviewPanelCommentThread } from '../../../../../../../types/review-pane
 import { ReviewPanelCommentEntry } from '../../../../../../../types/review-panel/entry'
 import useIndicatorHover from '../hooks/use-indicator-hover'
 import EntryIndicator from './entry-indicator'
+import { useEntryClick } from '@/features/source-editor/components/review-panel/hooks/use-entry-click'
 
 type CommentEntryProps = {
   docId: DocId
@@ -36,7 +37,7 @@ function CommentEntry({
   permissions,
 }: CommentEntryProps) {
   const { t } = useTranslation()
-  const { gotoEntry, resolveComment, submitReply, handleLayoutChange } =
+  const { resolveComment, submitReply, handleLayoutChange } =
     useReviewPanelUpdaterFnsContext()
   const [replyContent, setReplyContent] = useState('')
   const [animating, setAnimating] = useState(false)
@@ -50,22 +51,7 @@ function CommentEntry({
     handleIndicatorClick,
   } = useIndicatorHover()
 
-  const handleEntryClick = (e: React.MouseEvent<HTMLDivElement>) => {
-    const target = e.target as Element
-
-    for (const selector of [
-      '.rp-entry',
-      '.rp-comment-loaded',
-      '.rp-comment-content',
-      '.rp-comment-reply',
-      '.rp-entry-metadata',
-    ]) {
-      if (target.matches(selector)) {
-        gotoEntry(docId, offset)
-        break
-      }
-    }
-  }
+  const handleEntryClick = useEntryClick(docId, offset)
 
   const handleAnimateAndCallOnResolve = () => {
     setAnimating(true)

+ 27 - 0
services/web/frontend/js/features/source-editor/components/review-panel/hooks/use-entry-click.ts

@@ -0,0 +1,27 @@
+import { useReviewPanelUpdaterFnsContext } from '@/features/source-editor/context/review-panel/review-panel-context'
+import { DocId } from '../../../../../../../types/project-settings'
+
+export function useEntryClick(docId: DocId, offset: number) {
+  const { gotoEntry } = useReviewPanelUpdaterFnsContext()
+
+  return (e: React.MouseEvent<HTMLDivElement>) => {
+    const target = e.target as Element
+
+    // Ignore clicks inside interactive elements
+    if (!target.closest('textarea, button, a')) {
+      // If the user was making a selection within the entry rather than
+      // clicking it, ignore the click. Do this by checking whether there is a
+      // selection that intersects with the target, in which case we assume
+      // the user was making a selection
+      const selection = window.getSelection()
+      if (
+        !selection ||
+        selection.isCollapsed ||
+        selection.rangeCount === 0 ||
+        !selection.getRangeAt(0).intersectsNode(target)
+      ) {
+        gotoEntry(docId, offset)
+      }
+    }
+  }
+}