comments display on canvas

This commit is contained in:
hjlarry 2025-09-15 14:16:06 +08:00
parent d7f5da5df4
commit dd8577f832
3 changed files with 48 additions and 31 deletions

View File

@ -1,5 +1,6 @@
import type { FC } from 'react'
import { memo } from 'react'
import { memo, useMemo } from 'react'
import { useReactFlow, useViewport } from 'reactflow'
import Avatar from '@/app/components/base/avatar'
import type { WorkflowCommentList } from '@/service/workflow-comment'
@ -9,12 +10,22 @@ type CommentIconProps = {
}
export const CommentIcon: FC<CommentIconProps> = memo(({ comment, onClick }) => {
const { flowToScreenPosition } = useReactFlow()
const viewport = useViewport()
const screenPosition = useMemo(() => {
return flowToScreenPosition({
x: comment.position_x,
y: comment.position_y,
})
}, [comment.position_x, comment.position_y, viewport.x, viewport.y, viewport.zoom, flowToScreenPosition])
return (
<div
className="absolute z-40 cursor-pointer"
className="absolute z-10 cursor-pointer"
style={{
left: comment.position_x,
top: comment.position_y,
left: screenPosition.x,
top: screenPosition.y,
transform: 'translate(-50%, -50%)',
}}
onClick={onClick}
@ -24,7 +35,7 @@ export const CommentIcon: FC<CommentIconProps> = memo(({ comment, onClick }) =>
<div className="flex h-full w-full items-center justify-center">
<div className="h-10 w-10 overflow-hidden rounded-full">
<Avatar
avatar={comment.created_by_account.avatar_url}
avatar={comment.created_by_account.avatar_url || null}
name={comment.created_by_account.name}
size={40}
className="h-full w-full"
@ -35,6 +46,13 @@ export const CommentIcon: FC<CommentIconProps> = memo(({ comment, onClick }) =>
</div>
</div>
)
}, (prevProps, nextProps) => {
return (
prevProps.comment.id === nextProps.comment.id
&& prevProps.comment.position_x === nextProps.comment.position_x
&& prevProps.comment.position_y === nextProps.comment.position_y
&& prevProps.onClick === nextProps.onClick
)
})
CommentIcon.displayName = 'CommentIcon'

View File

@ -1,7 +1,8 @@
import type { FC } from 'react'
import { memo, useCallback, useEffect, useRef, useState } from 'react'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import Textarea from 'react-textarea-autosize'
import { RiSendPlane2Fill } from '@remixicon/react'
import { useReactFlow, useViewport } from 'reactflow'
import cn from '@/utils/classnames'
import Button from '@/app/components/base/button'
import Avatar from '@/app/components/base/avatar'
@ -17,6 +18,12 @@ export const CommentInput: FC<CommentInputProps> = memo(({ position, onSubmit, o
const [content, setContent] = useState('')
const textareaRef = useRef<HTMLTextAreaElement>(null)
const { userProfile } = useAppContext()
const { flowToScreenPosition } = useReactFlow()
const viewport = useViewport()
const screenPosition = useMemo(() => {
return flowToScreenPosition(position)
}, [position.x, position.y, viewport.x, viewport.y, viewport.zoom, flowToScreenPosition])
useEffect(() => {
const handleGlobalKeyDown = (e: KeyboardEvent) => {
@ -59,8 +66,8 @@ export const CommentInput: FC<CommentInputProps> = memo(({ position, onSubmit, o
<div
className="absolute z-50 w-96"
style={{
left: position.x + 10,
top: position.y + 10,
left: screenPosition.x,
top: screenPosition.y,
}}
>
<div className="flex items-start gap-3">

View File

@ -1,21 +1,22 @@
import { useCallback, useEffect, useState } from 'react'
import { useParams } from 'next/navigation'
import { useReactFlow } from 'reactflow'
import { useStore } from '../store'
import { ControlMode } from '../types'
import type { WorkflowComment } from '@/service/workflow-comment'
import type { WorkflowCommentList } from '@/service/workflow-comment'
import { createWorkflowComment, fetchWorkflowComments } from '@/service/workflow-comment'
export const useWorkflowComment = () => {
const params = useParams()
const appId = params.appId as string
const reactflow = useReactFlow()
const controlMode = useStore(s => s.controlMode)
const setControlMode = useStore(s => s.setControlMode)
const pendingComment = useStore(s => s.pendingComment)
const setPendingComment = useStore(s => s.setPendingComment)
const [comments, setComments] = useState<WorkflowComment[]>([])
const [comments, setComments] = useState<WorkflowCommentList[]>([])
const [loading, setLoading] = useState(false)
// 加载评论列表
const loadComments = useCallback(async () => {
if (!appId) return
@ -32,7 +33,6 @@ export const useWorkflowComment = () => {
}
}, [appId])
// 初始化时加载评论
useEffect(() => {
loadComments()
}, [loadComments])
@ -56,47 +56,39 @@ export const useWorkflowComment = () => {
})
console.log('Comment created successfully:', newComment)
setComments(prev => [...prev, newComment])
await loadComments()
setPendingComment(null)
setControlMode(ControlMode.Pointer)
}
catch (error) {
catch (error) {
console.error('Failed to create comment:', error)
setPendingComment(null)
setControlMode(ControlMode.Pointer)
}
}, [appId, pendingComment, setControlMode, setPendingComment, setComments])
}, [appId, pendingComment, setControlMode, setPendingComment, loadComments])
const handleCommentCancel = useCallback(() => {
setPendingComment(null)
setControlMode(ControlMode.Pointer)
}, [setControlMode, setPendingComment])
const handleCommentIconClick = useCallback((comment: WorkflowComment) => {
const handleCommentIconClick = useCallback((comment: WorkflowCommentList) => {
// TODO: display comment details
console.log('Comment clicked:', comment)
}, [])
const handleCreateComment = useCallback((mousePosition: { pageX: number; pageY: number }) => {
if (controlMode === ControlMode.Comment) {
const containerElement = document.querySelector('#workflow-container')
if (containerElement) {
const containerBounds = containerElement.getBoundingClientRect()
const position = {
x: mousePosition.pageX - containerBounds.left,
y: mousePosition.pageY - containerBounds.top,
}
console.log('Setting pending comment at position:', position)
setPendingComment(position)
}
else {
console.error('Could not find workflow container element')
}
const { screenToFlowPosition } = reactflow
const flowPosition = screenToFlowPosition({ x: mousePosition.pageX, y: mousePosition.pageY })
console.log('Setting pending comment at flow position:', flowPosition)
setPendingComment(flowPosition)
}
else {
else {
console.log('Control mode is not Comment:', controlMode)
}
}, [controlMode, setPendingComment])
}, [controlMode, setPendingComment, reactflow])
return {
comments,