diff --git a/web/app/components/workflow/comment/thread.tsx b/web/app/components/workflow/comment/thread.tsx index ec098deb69..48417550cd 100644 --- a/web/app/components/workflow/comment/thread.tsx +++ b/web/app/components/workflow/comment/thread.tsx @@ -1,10 +1,13 @@ 'use client' +import { useParams } from 'next/navigation' import type { FC } from 'react' import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { useReactFlow, useViewport } from 'reactflow' -import { RiArrowDownSLine, RiArrowUpSLine, RiCheckboxCircleFill, RiCheckboxCircleLine, RiCloseLine, RiDeleteBinLine } from '@remixicon/react' +import { RiArrowDownSLine, RiArrowUpSLine, RiCheckboxCircleFill, RiCheckboxCircleLine, RiCloseLine, RiDeleteBinLine, RiMoreFill } from '@remixicon/react' +import Textarea from 'react-textarea-autosize' import Avatar from '@/app/components/base/avatar' +import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' import cn from '@/utils/classnames' import { useFormatTimeFromNow } from '@/app/components/workflow/hooks' @@ -23,6 +26,8 @@ type CommentThreadProps = { canGoPrev?: boolean canGoNext?: boolean onReply?: (content: string, mentionedUserIds?: string[]) => Promise | void + onReplyEdit?: (replyId: string, content: string, mentionedUserIds?: string[]) => Promise | void + onReplyDelete?: (replyId: string) => void } const ThreadMessage: FC<{ @@ -56,16 +61,6 @@ const ThreadMessage: FC<{ ) } -const renderReply = (reply: WorkflowCommentDetailReply) => ( - -) - export const CommentThread: FC = memo(({ comment, loading = false, @@ -77,11 +72,16 @@ export const CommentThread: FC = memo(({ canGoPrev, canGoNext, onReply, + onReplyEdit, + onReplyDelete, }) => { + const params = useParams() const { flowToScreenPosition } = useReactFlow() const viewport = useViewport() const { userProfile } = useAppContext() const [replyContent, setReplyContent] = useState('') + const [activeReplyMenuId, setActiveReplyMenuId] = useState(null) + const [editingReply, setEditingReply] = useState<{ id: string; content: string }>({ id: '', content: '' }) useEffect(() => { setReplyContent('') @@ -106,6 +106,25 @@ export const CommentThread: FC = memo(({ }) }, [comment.position_x, comment.position_y, viewport.x, viewport.y, viewport.zoom, flowToScreenPosition]) + const handleStartEdit = useCallback((reply: WorkflowCommentDetailReply) => { + setEditingReply({ id: reply.id, content: reply.content }) + setActiveReplyMenuId(null) + }, []) + + const handleCancelEdit = useCallback(() => { + setEditingReply({ id: '', content: '' }) + }, []) + + const handleSaveEdit = useCallback(async () => { + if (!onReplyEdit || !editingReply) return + const trimmed = editingReply.content.trim() + if (!trimmed) return + await onReplyEdit(editingReply.id, trimmed, []) + setEditingReply({ id: '', content: '' }) + }, [editingReply, onReplyEdit]) + + const replies = comment.replies || [] + return (
= memo(({ createdAt={comment.created_at} content={comment.content} /> - {comment.replies?.length > 0 && ( -
- {comment.replies.map(renderReply)} + {replies.length > 0 && ( +
+ {replies.map((reply) => { + const isReplyEditing = editingReply?.id === reply.id + return ( +
+
+ + {activeReplyMenuId === reply.id && ( +
+ + +
+ )} +
+ {isReplyEditing ? ( +
+