onEnter(comment.id))}
- onMouseLeave={onLeave && (() => onLeave(comment.id))}
+ onMouseEnter={onEnter}
+ onMouseLeave={onLeave}
>
{thread.messages.map((message, i) => {
const isReply = i !== 0
diff --git a/services/web/frontend/js/features/review-panel-new/components/review-panel-comment.tsx b/services/web/frontend/js/features/review-panel-new/components/review-panel-comment.tsx
index dd6366f091..c7f60aac35 100644
--- a/services/web/frontend/js/features/review-panel-new/components/review-panel-comment.tsx
+++ b/services/web/frontend/js/features/review-panel-new/components/review-panel-comment.tsx
@@ -1,4 +1,4 @@
-import { memo, useCallback, useState } from 'react'
+import { memo, useCallback, useMemo, useState } from 'react'
import { Change, CommentOperation } from '../../../../../types/change'
import {
useThreadsActionsContext,
@@ -21,160 +21,169 @@ export const ReviewPanelComment = memo<{
docId: string
top?: number
hoverRanges?: boolean
- onEnter?: (changeId: string) => void
- onLeave?: (changeId: string) => void
+ handleEnter?: (changeId: string) => void
+ handleLeave?: (changeId: string) => void
hovered?: boolean
-}>(({ comment, top, hovered, onEnter, onLeave, docId, hoverRanges }) => {
- const threads = useThreadsContext()
- const {
- resolveThread,
- editMessage,
- deleteMessage,
- deleteOwnMessage,
- deleteThread,
- addMessage,
- } = useThreadsActionsContext()
- const { showGenericMessageModal } = useModalsContext()
- const { t } = useTranslation()
- const permissions = usePermissionsContext()
-
- const [processing, setProcessing] = useState(false)
-
- const handleResolveComment = useCallback(async () => {
- setProcessing(true)
- try {
- await resolveThread(comment.op.t)
- } catch (err) {
- debugConsole.error(err)
- showGenericMessageModal(
- t('resolve_comment_error_title'),
- t('resolve_comment_error_message')
- )
- } finally {
- setProcessing(false)
- }
- }, [comment.op.t, resolveThread, showGenericMessageModal, t])
-
- const handleEditMessage = useCallback(
- async (commentId: CommentId, content: string) => {
- setProcessing(true)
- try {
- await editMessage(comment.op.t, commentId, content)
- } catch (err) {
- debugConsole.error(err)
- showGenericMessageModal(
- t('edit_comment_error_title'),
- t('edit_comment_error_message')
- )
- } finally {
- setProcessing(false)
- }
- },
- [comment.op.t, editMessage, showGenericMessageModal, t]
- )
-
- const handleDeleteMessage = useCallback(
- async (commentId: CommentId) => {
- setProcessing(true)
- try {
- if (permissions.resolveAllComments) {
- // Owners and editors can delete any message
- await deleteMessage(comment.op.t, commentId)
- } else if (permissions.resolveOwnComments) {
- // Reviewers can only delete their own messages
- await deleteOwnMessage(comment.op.t, commentId)
- }
- } catch (err) {
- debugConsole.error(err)
- showGenericMessageModal(
- t('delete_comment_error_title'),
- t('delete_comment_error_message')
- )
- } finally {
- setProcessing(false)
- }
- },
- [
- comment.op.t,
+}>(
+ ({ comment, top, hovered, handleEnter, handleLeave, docId, hoverRanges }) => {
+ const threads = useThreadsContext()
+ const {
+ resolveThread,
+ editMessage,
deleteMessage,
deleteOwnMessage,
- showGenericMessageModal,
- t,
- permissions.resolveOwnComments,
- permissions.resolveAllComments,
- ]
- )
+ deleteThread,
+ addMessage,
+ } = useThreadsActionsContext()
+ const { showGenericMessageModal } = useModalsContext()
+ const { t } = useTranslation()
+ const permissions = usePermissionsContext()
- const handleDeleteThread = useCallback(
- async (commentId: ThreadId) => {
+ const [processing, setProcessing] = useState(false)
+
+ const handleResolveComment = useCallback(async () => {
setProcessing(true)
try {
- await deleteThread(commentId)
+ await resolveThread(comment.op.t)
} catch (err) {
debugConsole.error(err)
showGenericMessageModal(
- t('delete_comment_error_title'),
- t('delete_comment_error_message')
+ t('resolve_comment_error_title'),
+ t('resolve_comment_error_message')
)
} finally {
setProcessing(false)
}
- },
- [deleteThread, showGenericMessageModal, t]
- )
+ }, [comment.op.t, resolveThread, showGenericMessageModal, t])
- const handleSubmitReply = useCallback(
- async (content: string) => {
- setProcessing(true)
- try {
- await addMessage(comment.op.t, content)
- } catch (err) {
- debugConsole.error(err)
- showGenericMessageModal(
- t('add_comment_error_title'),
- t('add_comment_error_message')
- )
- throw err
- } finally {
- setProcessing(false)
+ const handleEditMessage = useCallback(
+ async (commentId: CommentId, content: string) => {
+ setProcessing(true)
+ try {
+ await editMessage(comment.op.t, commentId, content)
+ } catch (err) {
+ debugConsole.error(err)
+ showGenericMessageModal(
+ t('edit_comment_error_title'),
+ t('edit_comment_error_message')
+ )
+ } finally {
+ setProcessing(false)
+ }
+ },
+ [comment.op.t, editMessage, showGenericMessageModal, t]
+ )
+
+ const handleDeleteMessage = useCallback(
+ async (commentId: CommentId) => {
+ setProcessing(true)
+ try {
+ if (permissions.resolveAllComments) {
+ // Owners and editors can delete any message
+ await deleteMessage(comment.op.t, commentId)
+ } else if (permissions.resolveOwnComments) {
+ // Reviewers can only delete their own messages
+ await deleteOwnMessage(comment.op.t, commentId)
+ }
+ } catch (err) {
+ debugConsole.error(err)
+ showGenericMessageModal(
+ t('delete_comment_error_title'),
+ t('delete_comment_error_message')
+ )
+ } finally {
+ setProcessing(false)
+ }
+ },
+ [
+ comment.op.t,
+ deleteMessage,
+ deleteOwnMessage,
+ showGenericMessageModal,
+ t,
+ permissions.resolveOwnComments,
+ permissions.resolveAllComments,
+ ]
+ )
+
+ const handleDeleteThread = useCallback(
+ async (commentId: ThreadId) => {
+ setProcessing(true)
+ try {
+ await deleteThread(commentId)
+ } catch (err) {
+ debugConsole.error(err)
+ showGenericMessageModal(
+ t('delete_comment_error_title'),
+ t('delete_comment_error_message')
+ )
+ } finally {
+ setProcessing(false)
+ }
+ },
+ [deleteThread, showGenericMessageModal, t]
+ )
+
+ const handleSubmitReply = useCallback(
+ async (content: string) => {
+ setProcessing(true)
+ try {
+ await addMessage(comment.op.t, content)
+ } catch (err) {
+ debugConsole.error(err)
+ showGenericMessageModal(
+ t('add_comment_error_title'),
+ t('add_comment_error_message')
+ )
+ throw err
+ } finally {
+ setProcessing(false)
+ }
+ },
+ [addMessage, comment.op.t, showGenericMessageModal, t]
+ )
+
+ const { handleMouseEnter, handleMouseLeave } = useMemo(() => {
+ return {
+ handleMouseEnter: handleEnter && (() => handleEnter(comment.id)),
+ handleMouseLeave: handleLeave && (() => handleLeave(comment.id)),
}
- },
- [addMessage, comment.op.t, showGenericMessageModal, t]
- )
+ }, [comment.id, handleEnter, handleLeave])
- const thread = threads?.[comment.op.t]
- if (!thread || thread.resolved || thread.messages.length === 0) {
- return null
+ const thread = threads?.[comment.op.t]
+ if (!thread || thread.resolved || thread.messages.length === 0) {
+ return null
+ }
+
+ return (
+
+
+
+ )
}
-
- return (
-
onEnter(comment.id))}
- onLeaveEntryIndicator={onLeave && (() => onLeave(comment.id))}
- entryIndicator="comment"
- >
-
-
- )
-})
+)
ReviewPanelComment.displayName = 'ReviewPanelComment'
diff --git a/services/web/frontend/js/features/review-panel-new/components/review-panel-current-file.tsx b/services/web/frontend/js/features/review-panel-new/components/review-panel-current-file.tsx
index 5f65e9fbb0..b79fc85f1c 100644
--- a/services/web/frontend/js/features/review-panel-new/components/review-panel-current-file.tsx
+++ b/services/web/frontend/js/features/review-panel-new/components/review-panel-current-file.tsx
@@ -53,7 +53,7 @@ const ReviewPanelCurrentFile: FC = () => {
setHoveredEntry(id)
}, [])
- const handleEntryLeave = useCallback((id: string) => {
+ const handleEntryLeave = useCallback(() => {
clearTimeout(hoverTimeout.current)
hoverTimeout.current = window.setTimeout(() => {
setHoveredEntry(null)
@@ -333,8 +333,8 @@ const ReviewPanelCurrentFile: FC = () => {
top={positions.get(change.id)}
aggregate={aggregatedRanges.aggregates.get(change.id)}
hovered={hoveredEntry === change.id}
- onEnter={handleEntryEnter}
- onLeave={handleEntryLeave}
+ handleEnter={handleEntryEnter}
+ handleLeave={handleEntryLeave}
/>
)
)}
@@ -348,8 +348,8 @@ const ReviewPanelCurrentFile: FC = () => {
comment={comment}
top={positions.get(comment.id)}
hovered={hoveredEntry === comment.id}
- onEnter={handleEntryEnter}
- onLeave={handleEntryLeave}
+ handleEnter={handleEntryEnter}
+ handleLeave={handleEntryLeave}
/>
)
)}
diff --git a/services/web/frontend/js/features/review-panel-new/components/review-panel-entry-indicator.tsx b/services/web/frontend/js/features/review-panel-new/components/review-panel-entry-indicator.tsx
new file mode 100644
index 0000000000..ee14dc24f5
--- /dev/null
+++ b/services/web/frontend/js/features/review-panel-new/components/review-panel-entry-indicator.tsx
@@ -0,0 +1,27 @@
+import { memo, MouseEventHandler } from 'react'
+import MaterialIcon from '@/shared/components/material-icon'
+
+export const EntryIndicator = memo<{
+ handleMouseEnter?: MouseEventHandler
+ handleMouseLeave?: MouseEventHandler
+ handleMouseDown?: MouseEventHandler
+ type: string
+}>(function EntryIndicator({
+ handleMouseEnter,
+ handleMouseLeave,
+ handleMouseDown,
+ type,
+}) {
+ return (
+
+
+
+ )
+})
diff --git a/services/web/frontend/js/features/review-panel-new/components/review-panel-entry.tsx b/services/web/frontend/js/features/review-panel-new/components/review-panel-entry.tsx
index 4192dd518e..3576b7b542 100644
--- a/services/web/frontend/js/features/review-panel-new/components/review-panel-entry.tsx
+++ b/services/web/frontend/js/features/review-panel-new/components/review-panel-entry.tsx
@@ -12,9 +12,9 @@ import {
} from '@/features/source-editor/extensions/ranges'
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
import { EditorSelection } from '@codemirror/state'
-import MaterialIcon from '@/shared/components/material-icon'
import { OFFSET_FOR_ENTRIES_ABOVE } from '../utils/position-items'
import useReviewPanelLayout from '../hooks/use-review-panel-layout'
+import { EntryIndicator } from './review-panel-entry-indicator'
export const ReviewPanelEntry: FC<
React.PropsWithChildren<{
@@ -26,8 +26,8 @@ export const ReviewPanelEntry: FC<
selectLineOnFocus?: boolean
hoverRanges?: boolean
disabled?: boolean
- onEnterEntryIndicator?: () => void
- onLeaveEntryIndicator?: () => void
+ handleEnter?: () => void
+ handleLeave?: () => void
entryIndicator?: 'comment' | 'edit'
}>
> = ({
@@ -40,8 +40,8 @@ export const ReviewPanelEntry: FC<
docId,
hoverRanges = true,
disabled,
- onEnterEntryIndicator,
- onLeaveEntryIndicator,
+ handleEnter,
+ handleLeave,
entryIndicator,
}) => {
const state = useCodeMirrorStateContext()
@@ -194,19 +194,12 @@ export const ReviewPanelEntry: FC<
}}
>
{entryIndicator && (
-
-
-
+
)}
{children}
diff --git a/services/web/frontend/js/features/review-panel-new/components/review-panel-expandable-content.tsx b/services/web/frontend/js/features/review-panel-new/components/review-panel-expandable-content.tsx
index 3850614281..7a8d29f032 100644
--- a/services/web/frontend/js/features/review-panel-new/components/review-panel-expandable-content.tsx
+++ b/services/web/frontend/js/features/review-panel-new/components/review-panel-expandable-content.tsx
@@ -1,24 +1,24 @@
-import { FC, useCallback, useRef, useState } from 'react'
+import { memo, useCallback, useRef, useState } from 'react'
import OLButton from '@/features/ui/components/ol/ol-button'
import { useTranslation } from 'react-i18next'
import classNames from 'classnames'
import { PreventSelectingEntry } from './review-panel-prevent-selecting'
-export const ExpandableContent: FC<{
+export const ExpandableContent = memo<{
className?: string
content: string
contentLimit?: number
newLineCharsLimit?: number
checkNewLines?: boolean
inline?: boolean
-}> = ({
+}>(function ExpandableContent({
content,
className,
contentLimit = 50,
newLineCharsLimit = 3,
checkNewLines = true,
inline = false,
-}) => {
+}) {
const { t } = useTranslation()
const contentRef = useRef