diff --git a/web/app/components/workflow/hooks/use-workflow-comment.ts b/web/app/components/workflow/hooks/use-workflow-comment.ts index 27180cb1ab..e7e15da6d9 100644 --- a/web/app/components/workflow/hooks/use-workflow-comment.ts +++ b/web/app/components/workflow/hooks/use-workflow-comment.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef } from 'react' import { useParams } from 'next/navigation' import { useReactFlow } from 'reactflow' import { useStore } from '../store' @@ -16,31 +16,42 @@ export const useWorkflowComment = () => { const setPendingComment = useStore(s => s.setPendingComment) const setActiveCommentId = useStore(s => s.setActiveCommentId) const activeCommentId = useStore(s => s.activeCommentId) - const [comments, setComments] = useState([]) - const [loading, setLoading] = useState(false) - const [activeComment, setActiveComment] = useState(null) - const [activeCommentLoading, setActiveCommentLoading] = useState(false) - const commentDetailCacheRef = useRef>({}) + const comments = useStore(s => s.comments) + const setComments = useStore(s => s.setComments) + const loading = useStore(s => s.commentsLoading) + const setCommentsLoading = useStore(s => s.setCommentsLoading) + const activeComment = useStore(s => s.activeCommentDetail) + const setActiveComment = useStore(s => s.setActiveCommentDetail) + const activeCommentLoading = useStore(s => s.activeCommentDetailLoading) + const setActiveCommentLoading = useStore(s => s.setActiveCommentDetailLoading) + const commentDetailCache = useStore(s => s.commentDetailCache) + const setCommentDetailCache = useStore(s => s.setCommentDetailCache) + const commentDetailCacheRef = useRef>(commentDetailCache) const activeCommentIdRef = useRef(null) + useEffect(() => { activeCommentIdRef.current = activeCommentId ?? null }, [activeCommentId]) + useEffect(() => { + commentDetailCacheRef.current = commentDetailCache + }, [commentDetailCache]) + const loadComments = useCallback(async () => { if (!appId) return - setLoading(true) + setCommentsLoading(true) try { const commentsData = await fetchWorkflowComments(appId) setComments(commentsData) } - catch (error) { + catch (error) { console.error('Failed to fetch comments:', error) } - finally { - setLoading(false) + finally { + setCommentsLoading(false) } - }, [appId]) + }, [appId, setComments, setCommentsLoading]) useEffect(() => { loadComments() @@ -89,14 +100,14 @@ export const useWorkflowComment = () => { setActiveCommentId(comment.id) const cachedDetail = commentDetailCacheRef.current[comment.id] - setActiveComment(cachedDetail || comment) + const fallbackDetail = cachedDetail ?? comment + setActiveComment(fallbackDetail) reactflow.setCenter(comment.position_x, comment.position_y, { zoom: 1, duration: 600 }) if (!appId) return - if (!cachedDetail) - setActiveCommentLoading(true) + setActiveCommentLoading(!cachedDetail) try { const detailResponse = await fetchWorkflowComment(appId, comment.id) @@ -106,6 +117,7 @@ export const useWorkflowComment = () => { ...commentDetailCacheRef.current, [comment.id]: detail, } + setCommentDetailCache(commentDetailCacheRef.current) if (activeCommentIdRef.current === comment.id) setActiveComment(detail) @@ -116,7 +128,7 @@ export const useWorkflowComment = () => { finally { setActiveCommentLoading(false) } - }, [appId, reactflow, setPendingComment]) + }, [appId, reactflow, setActiveComment, setActiveCommentId, setActiveCommentLoading, setCommentDetailCache, setControlMode, setPendingComment]) const handleActiveCommentClose = useCallback(() => { setActiveComment(null) @@ -124,7 +136,7 @@ export const useWorkflowComment = () => { setActiveCommentId(null) setControlMode(ControlMode.Pointer) activeCommentIdRef.current = null - }, [setActiveCommentId, setControlMode]) + }, [setActiveComment, setActiveCommentId, setActiveCommentLoading, setControlMode]) const handleCreateComment = useCallback((mousePosition: { pageX: number; pageY: number }) => { if (controlMode === ControlMode.Comment) { diff --git a/web/app/components/workflow/panel/comments-panel/index.tsx b/web/app/components/workflow/panel/comments-panel/index.tsx index c2fbc1e903..0c79b55f9f 100644 --- a/web/app/components/workflow/panel/comments-panel/index.tsx +++ b/web/app/components/workflow/panel/comments-panel/index.tsx @@ -1,5 +1,4 @@ import { memo, useCallback, useMemo, useState } from 'react' -import { useReactFlow } from 'reactflow' import { RiCheckLine, RiCheckboxCircleFill, RiCheckboxCircleLine, RiCloseLine, RiFilter3Line } from '@remixicon/react' import { useStore } from '@/app/components/workflow/store' import type { WorkflowCommentList } from '@/service/workflow-comment' @@ -16,8 +15,7 @@ const CommentsPanel = () => { const activeCommentId = useStore(s => s.activeCommentId) const setActiveCommentId = useStore(s => s.setActiveCommentId) const setControlMode = useStore(s => s.setControlMode) - const { comments, loading, loadComments } = useWorkflowComment() - const reactFlow = useReactFlow() + const { comments, loading, loadComments, handleCommentIconClick } = useWorkflowComment() const params = useParams() const appId = params.appId as string const { formatTimeFromNow } = useFormatTimeFromNow() @@ -26,10 +24,8 @@ const CommentsPanel = () => { const [showFilter, setShowFilter] = useState(false) const handleSelect = useCallback((comment: WorkflowCommentList) => { - // center viewport on the comment position and activate it - reactFlow.setCenter(comment.position_x, comment.position_y, { zoom: 1, duration: 600 }) - setActiveCommentId(comment.id) - }, [reactFlow, setActiveCommentId]) + handleCommentIconClick(comment) + }, [handleCommentIconClick]) const { userProfile } = useAppContext() diff --git a/web/app/components/workflow/store/workflow/comment-slice.ts b/web/app/components/workflow/store/workflow/comment-slice.ts new file mode 100644 index 0000000000..b870020ba4 --- /dev/null +++ b/web/app/components/workflow/store/workflow/comment-slice.ts @@ -0,0 +1,28 @@ +import type { StateCreator } from 'zustand' +import type { WorkflowCommentDetail, WorkflowCommentList } from '@/service/workflow-comment' + +export type CommentSliceShape = { + comments: WorkflowCommentList[] + setComments: (comments: WorkflowCommentList[]) => void + commentsLoading: boolean + setCommentsLoading: (loading: boolean) => void + activeCommentDetail: WorkflowCommentDetail | null + setActiveCommentDetail: (comment: WorkflowCommentDetail | null) => void + activeCommentDetailLoading: boolean + setActiveCommentDetailLoading: (loading: boolean) => void + commentDetailCache: Record + setCommentDetailCache: (cache: Record) => void +} + +export const createCommentSlice: StateCreator = set => ({ + comments: [], + setComments: comments => set({ comments }), + commentsLoading: false, + setCommentsLoading: commentsLoading => set({ commentsLoading }), + activeCommentDetail: null, + setActiveCommentDetail: activeCommentDetail => set({ activeCommentDetail }), + activeCommentDetailLoading: false, + setActiveCommentDetailLoading: activeCommentDetailLoading => set({ activeCommentDetailLoading }), + commentDetailCache: {}, + setCommentDetailCache: commentDetailCache => set({ commentDetailCache }), +}) diff --git a/web/app/components/workflow/store/workflow/index.ts b/web/app/components/workflow/store/workflow/index.ts index fe17cc21e0..52dd6aa732 100644 --- a/web/app/components/workflow/store/workflow/index.ts +++ b/web/app/components/workflow/store/workflow/index.ts @@ -20,6 +20,8 @@ import type { NodeSliceShape } from './node-slice' import { createNodeSlice } from './node-slice' import type { PanelSliceShape } from './panel-slice' import { createPanelSlice } from './panel-slice' +import type { CommentSliceShape } from './comment-slice' +import { createCommentSlice } from './comment-slice' import type { ToolSliceShape } from './tool-slice' import { createToolSlice } from './tool-slice' import type { VersionSliceShape } from './version-slice' @@ -44,6 +46,7 @@ export type Shape = HistorySliceShape & NodeSliceShape & PanelSliceShape & + CommentSliceShape & ToolSliceShape & VersionSliceShape & WorkflowDraftSliceShape & @@ -67,6 +70,7 @@ export const createWorkflowStore = (params: CreateWorkflowStoreParams) => { ...createHistorySlice(...args), ...createNodeSlice(...args), ...createPanelSlice(...args), + ...createCommentSlice(...args), ...createToolSlice(...args), ...createVersionSlice(...args), ...createWorkflowDraftSlice(...args),