diff --git a/web/app/components/workflow/hooks/__tests__/use-workflow-comment.spec.ts b/web/app/components/workflow/hooks/__tests__/use-workflow-comment.spec.ts index 88c0d66079c..944cfe1c7a9 100644 --- a/web/app/components/workflow/hooks/__tests__/use-workflow-comment.spec.ts +++ b/web/app/components/workflow/hooks/__tests__/use-workflow-comment.spec.ts @@ -669,4 +669,68 @@ describe('useWorkflowComment', () => { expect(store.getState().pendingComment).toBeNull() }) + + it('does not overwrite the active comment when a different comment is resolved/refreshed', async () => { + const commentA = baseComment() + const commentB: WorkflowCommentList = { + ...baseComment(), + id: 'comment-2', + content: 'second', + position_x: 50, + position_y: 80, + } + const activeBDetail = { ...baseCommentDetail(), id: commentB.id, content: 'B detail' } + // refreshActiveComment(A) — reached via resolving A — fetches A's detail: + mockFetchWorkflowComment.mockResolvedValue({ + ...baseCommentDetail(), + id: commentA.id, + content: 'A detail (must not clobber B)', + }) + mockResolveWorkflowComment.mockResolvedValue({}) + mockFetchWorkflowComments.mockResolvedValue({ + data: [{ ...commentA, resolved: true }, commentB], + }) + + const { result, store } = renderWorkflowHook(() => useWorkflowComment(), { + queryClient: createSeededQueryClient(), + initialStoreState: { + comments: [commentA, commentB], + activeCommentId: commentB.id, + activeCommentDetail: activeBDetail, + }, + }) + + await act(async () => { + await result.current.handleCommentResolve(commentA.id) + }) + + // B is still the selected comment; A's fetched detail must not replace it. + expect(store.getState().activeCommentId).toBe(commentB.id) + expect(store.getState().activeCommentDetail?.id).toBe(commentB.id) + }) + + it('still refreshes the active comment', async () => { + const commentA = baseComment() + mockFetchWorkflowComment.mockResolvedValue({ + ...baseCommentDetail(), + id: commentA.id, + content: 'refreshed content', + }) + + const { result, store } = renderWorkflowHook(() => useWorkflowComment(), { + queryClient: createSeededQueryClient(), + initialStoreState: { + comments: [commentA], + activeCommentId: commentA.id, + activeCommentDetail: { ...baseCommentDetail(), id: commentA.id, content: 'stale content' }, + }, + }) + + await act(async () => { + await result.current.refreshActiveComment(commentA.id) + }) + + expect(store.getState().activeCommentDetail?.id).toBe(commentA.id) + expect(store.getState().activeCommentDetail?.content).toBe('refreshed content') + }) }) diff --git a/web/app/components/workflow/hooks/use-workflow-comment.ts b/web/app/components/workflow/hooks/use-workflow-comment.ts index 863e1df7028..34a75525f50 100644 --- a/web/app/components/workflow/hooks/use-workflow-comment.ts +++ b/web/app/components/workflow/hooks/use-workflow-comment.ts @@ -98,7 +98,9 @@ export const useWorkflowComment = () => { [commentId]: detail, } setCommentDetailCache(commentDetailCacheRef.current) - setActiveComment(detail) + // Only apply the fetched detail if this comment is still the active one; + // otherwise a refresh for another comment can clobber the current selection. + if (activeCommentIdRef.current === commentId) setActiveComment(detail) }, [appId, setActiveComment, setCommentDetailCache], )