From 687662cf1fb96aec3b1ff946e6376943cbea2418 Mon Sep 17 00:00:00 2001 From: hjlarry Date: Thu, 18 Sep 2025 13:27:27 +0800 Subject: [PATCH] comment sync --- .../core/collaboration-manager.ts | 22 +++++++ .../workflow/hooks/use-workflow-comment.ts | 60 ++++++++++++++----- .../workflow/panel/comments-panel/index.tsx | 4 ++ 3 files changed, 72 insertions(+), 14 deletions(-) diff --git a/web/app/components/workflow/collaboration/core/collaboration-manager.ts b/web/app/components/workflow/collaboration/core/collaboration-manager.ts index 54bc67f774..72aa8777e3 100644 --- a/web/app/components/workflow/collaboration/core/collaboration-manager.ts +++ b/web/app/components/workflow/collaboration/core/collaboration-manager.ts @@ -276,6 +276,24 @@ export class CollaborationManager { return this.eventEmitter.on('leaderChange', callback) } + onCommentsUpdate(callback: (update: { appId: string; timestamp: number }) => void): () => void { + return this.eventEmitter.on('commentsUpdate', callback) + } + + emitCommentsUpdate(appId: string): void { + if (!this.currentAppId || !webSocketClient.isConnected(this.currentAppId)) return + + const socket = webSocketClient.getSocket(this.currentAppId) + if (socket) { + console.log('Emitting Comments update event') + socket.emit('collaboration_event', { + type: 'commentsUpdate', + data: { appId, timestamp: Date.now() }, + timestamp: Date.now(), + }) + } + } + onUndoRedoStateChange(callback: (state: { canUndo: boolean; canRedo: boolean }) => void): () => void { return this.eventEmitter.on('undoRedoStateChange', callback) } @@ -613,6 +631,10 @@ export class CollaborationManager { console.log('Processing workflowUpdate event:', update) this.eventEmitter.emit('workflowUpdate', update.data) } + else if (update.type === 'commentsUpdate') { + console.log('Processing commentsUpdate event:', update) + this.eventEmitter.emit('commentsUpdate', update.data) + } else if (update.type === 'syncRequest') { console.log('Received sync request from another user') // Only process if we are the leader diff --git a/web/app/components/workflow/hooks/use-workflow-comment.ts b/web/app/components/workflow/hooks/use-workflow-comment.ts index cf94fbe7f1..93c11f1d4c 100644 --- a/web/app/components/workflow/hooks/use-workflow-comment.ts +++ b/web/app/components/workflow/hooks/use-workflow-comment.ts @@ -5,6 +5,7 @@ import { useStore } from '../store' import { ControlMode } from '../types' import type { WorkflowCommentDetail, WorkflowCommentList } from '@/service/workflow-comment' import { createWorkflowComment, createWorkflowCommentReply, deleteWorkflowComment, deleteWorkflowCommentReply, fetchWorkflowComment, fetchWorkflowComments, resolveWorkflowComment, updateWorkflowCommentReply } from '@/service/workflow-comment' +import { collaborationManager } from '@/app/components/workflow/collaboration' export const useWorkflowComment = () => { const params = useParams() @@ -37,6 +38,20 @@ export const useWorkflowComment = () => { commentDetailCacheRef.current = commentDetailCache }, [commentDetailCache]) + const refreshActiveComment = useCallback(async (commentId: string) => { + if (!appId) return + + const detailResponse = await fetchWorkflowComment(appId, commentId) + const detail = (detailResponse as any)?.data ?? detailResponse + + commentDetailCacheRef.current = { + ...commentDetailCacheRef.current, + [commentId]: detail, + } + setCommentDetailCache(commentDetailCacheRef.current) + setActiveComment(detail) + }, [appId, setActiveComment, setCommentDetailCache]) + const loadComments = useCallback(async () => { if (!appId) return @@ -53,6 +68,19 @@ export const useWorkflowComment = () => { } }, [appId, setComments, setCommentsLoading]) + // Setup collaboration + useEffect(() => { + if (!appId) return + + const unsubscribe = collaborationManager.onCommentsUpdate(() => { + loadComments() + if (activeCommentIdRef.current) + refreshActiveComment(activeCommentIdRef.current) + }) + + return unsubscribe + }, [appId, loadComments, refreshActiveComment]) + useEffect(() => { loadComments() }, [loadComments]) @@ -80,6 +108,9 @@ export const useWorkflowComment = () => { }) console.log('Comment created successfully:', newComment) + + collaborationManager.emitCommentsUpdate(appId) + await loadComments() setPendingComment(null) setControlMode(ControlMode.Pointer) @@ -133,26 +164,15 @@ export const useWorkflowComment = () => { } }, [appId, reactflow, setActiveComment, setActiveCommentId, setActiveCommentLoading, setCommentDetailCache, setControlMode, setPendingComment]) - const refreshActiveComment = useCallback(async (commentId: string) => { - if (!appId) return - - const detailResponse = await fetchWorkflowComment(appId, commentId) - const detail = (detailResponse as any)?.data ?? detailResponse - - commentDetailCacheRef.current = { - ...commentDetailCacheRef.current, - [commentId]: detail, - } - setCommentDetailCache(commentDetailCacheRef.current) - setActiveComment(detail) - }, [appId, setActiveComment, setCommentDetailCache]) - const handleCommentResolve = useCallback(async (commentId: string) => { if (!appId) return setActiveCommentLoading(true) try { await resolveWorkflowComment(appId, commentId) + + collaborationManager.emitCommentsUpdate(appId) + await refreshActiveComment(commentId) await loadComments() } @@ -170,6 +190,9 @@ export const useWorkflowComment = () => { setActiveCommentLoading(true) try { await deleteWorkflowComment(appId, commentId) + + collaborationManager.emitCommentsUpdate(appId) + const updatedCache = { ...commentDetailCacheRef.current } delete updatedCache[commentId] commentDetailCacheRef.current = updatedCache @@ -210,6 +233,9 @@ export const useWorkflowComment = () => { setActiveCommentLoading(true) try { await createWorkflowCommentReply(appId, commentId, { content: trimmed, mentioned_user_ids: mentionedUserIds }) + + collaborationManager.emitCommentsUpdate(appId) + await refreshActiveComment(commentId) await loadComments() } @@ -229,6 +255,9 @@ export const useWorkflowComment = () => { setActiveCommentLoading(true) try { await updateWorkflowCommentReply(appId, commentId, replyId, { content: trimmed, mentioned_user_ids: mentionedUserIds }) + + collaborationManager.emitCommentsUpdate(appId) + await refreshActiveComment(commentId) await loadComments() } @@ -246,6 +275,9 @@ export const useWorkflowComment = () => { setActiveCommentLoading(true) try { await deleteWorkflowCommentReply(appId, commentId, replyId) + + collaborationManager.emitCommentsUpdate(appId) + await refreshActiveComment(commentId) await loadComments() } diff --git a/web/app/components/workflow/panel/comments-panel/index.tsx b/web/app/components/workflow/panel/comments-panel/index.tsx index cdea73557a..a8a825b072 100644 --- a/web/app/components/workflow/panel/comments-panel/index.tsx +++ b/web/app/components/workflow/panel/comments-panel/index.tsx @@ -10,6 +10,7 @@ import { resolveWorkflowComment } from '@/service/workflow-comment' import { useParams } from 'next/navigation' import { useFormatTimeFromNow } from '@/app/components/workflow/hooks' import { useAppContext } from '@/context/app-context' +import { collaborationManager } from '@/app/components/workflow/collaboration' const CommentsPanel = () => { const activeCommentId = useStore(s => s.activeCommentId) @@ -43,6 +44,9 @@ const CommentsPanel = () => { if (!appId) return try { await resolveWorkflowComment(appId, comment.id) + + collaborationManager.emitCommentsUpdate(appId) + await loadComments() setActiveCommentId(comment.id) }