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

Disable Resolve Comment button while comment text is being entered (#27465)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
GitOrigin-RevId: 05a2c522b6cc49237eec859f50bad8759050f9f9
Alf Eaton 1 год назад
Родитель
Сommit
68d3d63e34

+ 48 - 23
services/web/frontend/js/features/review-panel-new/components/review-panel-add-comment.tsx

@@ -7,13 +7,13 @@ import { EditorSelection } from '@codemirror/state'
 import { useTranslation } from 'react-i18next'
 import { useThreadsActionsContext } from '../context/threads-context'
 import { removeNewCommentRangeEffect } from '@/features/source-editor/extensions/review-tooltip'
-import useSubmittableTextInput from '../hooks/use-submittable-text-input'
 import AutoExpandingTextArea from '@/shared/components/auto-expanding-text-area'
 import { ReviewPanelEntry } from './review-panel-entry'
 import { ThreadId } from '../../../../../types/review-panel/review-panel'
 import { useModalsContext } from '@/features/ide-react/context/modals-context'
 import { debugConsole } from '@/utils/debugging'
 import OLButton from '@/features/ui/components/ol/ol-button'
+import { isFormSubmitKeypressEvent } from '@/features/review-panel-new/utils/form-events'
 
 export const ReviewPanelAddComment = memo<{
   docId: string
@@ -28,6 +28,7 @@ export const ReviewPanelAddComment = memo<{
   const { addComment } = useThreadsActionsContext()
   const [submitting, setSubmitting] = useState(false)
   const { showGenericMessageModal } = useModalsContext()
+  const [content, setContent] = useState('')
 
   const handleClose = useCallback(() => {
     view.dispatch({
@@ -35,32 +36,56 @@ export const ReviewPanelAddComment = memo<{
     })
   }, [view, threadId])
 
-  const submitForm = useCallback(
-    async (message: string) => {
-      setSubmitting(true)
+  const submitForm = useCallback(async () => {
+    if (content.trim().length === 0) {
+      return
+    }
 
-      const content = view.state.sliceDoc(from, to)
+    setSubmitting(true)
 
-      try {
-        await addComment(from, content, message)
-        handleClose()
-        view.dispatch({
-          selection: EditorSelection.cursor(view.state.selection.main.anchor),
-        })
-      } catch (err) {
-        debugConsole.error(err)
-        showGenericMessageModal(
-          t('add_comment_error_title'),
-          t('add_comment_error_message')
-        )
+    const text = view.state.sliceDoc(from, to)
+
+    try {
+      await addComment(from, text, content)
+      handleClose()
+      view.dispatch({
+        selection: EditorSelection.cursor(view.state.selection.main.anchor),
+      })
+    } catch (err) {
+      debugConsole.error(err)
+      showGenericMessageModal(
+        t('add_comment_error_title'),
+        t('add_comment_error_message')
+      )
+    }
+    setSubmitting(false)
+  }, [
+    content,
+    view,
+    from,
+    to,
+    addComment,
+    handleClose,
+    showGenericMessageModal,
+    t,
+  ])
+
+  const handleKeyPress = useCallback(
+    (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
+      if (isFormSubmitKeypressEvent(event)) {
+        event.preventDefault()
+        submitForm()
       }
-      setSubmitting(false)
     },
-    [addComment, view, handleClose, from, to, showGenericMessageModal, t]
+    [submitForm]
   )
 
-  const { handleChange, handleKeyPress, content } =
-    useSubmittableTextInput(submitForm)
+  const handleChange = useCallback(
+    (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+      setContent(e.target.value)
+    },
+    []
+  )
 
   const handleBlur = useCallback(() => {
     if (content === '') {
@@ -73,9 +98,9 @@ export const ReviewPanelAddComment = memo<{
   const handleSubmit = useCallback<FormEventHandler>(
     event => {
       event.preventDefault()
-      submitForm(content)
+      submitForm()
     },
-    [submitForm, content]
+    [submitForm]
   )
 
   // We only ever want to focus the element once

+ 38 - 18
services/web/frontend/js/features/review-panel-new/components/review-panel-comment-content.tsx

@@ -1,4 +1,4 @@
-import { Dispatch, memo, SetStateAction, useCallback, useState } from 'react'
+import { memo, useCallback, useState } from 'react'
 import { Change, CommentOperation } from '../../../../../types/change'
 import { ReviewPanelMessage } from './review-panel-message'
 import { useTranslation } from 'react-i18next'
@@ -6,12 +6,12 @@ import { useThreadsContext } from '../context/threads-context'
 import AutoExpandingTextArea from '@/shared/components/auto-expanding-text-area'
 import ReviewPanelResolvedMessage from './review-panel-resolved-message'
 import { ReviewPanelResolvedCommentThread } from '../../../../../types/review-panel/comment-thread'
-import useSubmittableTextInput from '../hooks/use-submittable-text-input'
 import {
   CommentId,
   ThreadId,
 } from '../../../../../types/review-panel/review-panel'
 import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
+import { isFormSubmitKeypressEvent } from '@/features/review-panel-new/utils/form-events'
 
 export const ReviewPanelCommentContent = memo<{
   comment: Change<CommentOperation>
@@ -39,27 +39,46 @@ export const ReviewPanelCommentContent = memo<{
     const threads = useThreadsContext()
     const permissions = usePermissionsContext()
     const [submitting, setSubmitting] = useState(false)
+    const [content, setContent] = useState('')
 
-    const handleSubmit = useCallback(
-      (content: string, setContent: Dispatch<SetStateAction<string>>) => {
-        if (!onReply || submitting) {
-          return
-        }
+    const hasActiveContent = content.trim().length > 0
+
+    const handleSubmit = useCallback(() => {
+      if (!onReply || submitting) {
+        return
+      }
+
+      if (!hasActiveContent) {
+        return
+      }
+
+      setSubmitting(true)
 
-        setSubmitting(true)
-        onReply(content)
-          .then(() => {
-            setContent('')
-          })
-          .finally(() => {
-            setSubmitting(false)
-          })
+      return onReply(content)
+        .then(() => {
+          setContent('')
+        })
+        .finally(() => {
+          setSubmitting(false)
+        })
+    }, [onReply, submitting, content, hasActiveContent])
+
+    const handleKeyPress = useCallback(
+      (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
+        if (isFormSubmitKeypressEvent(event)) {
+          event.preventDefault()
+          handleSubmit()
+        }
       },
-      [onReply, submitting]
+      [handleSubmit]
     )
 
-    const { handleChange, handleKeyPress, content } =
-      useSubmittableTextInput(handleSubmit)
+    const handleChange = useCallback(
+      (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+        setContent(e.target.value)
+      },
+      []
+    )
 
     const thread = threads?.[comment.op.t]
     if (!thread) {
@@ -85,6 +104,7 @@ export const ReviewPanelCommentContent = memo<{
                 isReply={isReply}
                 hasReplies={!isReply && thread.messages.length > 1}
                 onResolve={onResolve}
+                hasActiveContent={hasActiveContent}
                 onEdit={onEdit}
                 onDelete={() =>
                   isReply

+ 17 - 12
services/web/frontend/js/features/review-panel-new/components/review-panel-message.tsx

@@ -21,6 +21,7 @@ export const ReviewPanelMessage: FC<{
   hasReplies: boolean
   isReply: boolean
   onResolve?: () => Promise<void>
+  hasActiveContent?: boolean
   onEdit?: (commentId: CommentId, content: string) => Promise<void>
   onDelete?: () => void
   isThreadResolved: boolean
@@ -32,6 +33,7 @@ export const ReviewPanelMessage: FC<{
   onEdit,
   onDelete,
   isThreadResolved,
+  hasActiveContent = false,
 }) => {
   const { t } = useTranslation()
   const [editing, setEditing] = useState(false)
@@ -80,18 +82,21 @@ export const ReviewPanelMessage: FC<{
                 description={t('resolve_comment')}
                 tooltipProps={{ className: 'review-panel-tooltip' }}
               >
-                <button
-                  type="button"
-                  tabIndex={0}
-                  className="btn"
-                  onClick={onResolve}
-                >
-                  <MaterialIcon
-                    type="check"
-                    className="review-panel-entry-actions-icon"
-                    accessibilityLabel={t('resolve_comment')}
-                  />
-                </button>
+                <span>
+                  <button
+                    type="button"
+                    tabIndex={0}
+                    className="btn"
+                    onClick={onResolve}
+                    disabled={hasActiveContent}
+                  >
+                    <MaterialIcon
+                      type="check"
+                      className="review-panel-entry-actions-icon"
+                      accessibilityLabel={t('resolve_comment')}
+                    />
+                  </button>
+                </span>
               </OLTooltip>
             </PreventSelectingEntry>
           )}

+ 0 - 31
services/web/frontend/js/features/review-panel-new/hooks/use-submittable-text-input.ts

@@ -1,31 +0,0 @@
-import { useCallback, useState, Dispatch, SetStateAction } from 'react'
-
-export default function useSubmittableTextInput(
-  handleSubmit: (
-    content: string,
-    setContent: Dispatch<SetStateAction<string>>
-  ) => void
-) {
-  const [content, setContent] = useState('')
-
-  const handleKeyPress = useCallback(
-    (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
-      if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey && !e.metaKey) {
-        e.preventDefault()
-        if (content.trim().length > 0) {
-          handleSubmit(content, setContent)
-        }
-      }
-    },
-    [content, handleSubmit]
-  )
-
-  const handleChange = useCallback(
-    (e: React.ChangeEvent<HTMLTextAreaElement>) => {
-      setContent(e.target.value)
-    },
-    []
-  )
-
-  return { handleChange, handleKeyPress, content }
-}

+ 7 - 0
services/web/frontend/js/features/review-panel-new/utils/form-events.ts

@@ -0,0 +1,7 @@
+export const isFormSubmitKeypressEvent = (
+  event: React.KeyboardEvent<HTMLTextAreaElement>
+) => {
+  return (
+    event.key === 'Enter' && !event.shiftKey && !event.ctrlKey && !event.metaKey
+  )
+}