diff --git a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx index 865dd6e68..43c33ba5a 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx @@ -33,7 +33,6 @@ import { GeneratePodcastToolUI } from "@/components/tool-ui/generate-podcast"; import { LinkPreviewToolUI } from "@/components/tool-ui/link-preview"; import { ScrapeWebpageToolUI } from "@/components/tool-ui/scrape-webpage"; // import { WriteTodosToolUI } from "@/components/tool-ui/write-todos"; -import { useChatMessagesLive } from "@/hooks/use-chat-messages-live"; import { getBearerToken } from "@/lib/auth-utils"; import { createAttachmentAdapter, extractAttachmentContent } from "@/lib/chat/attachment-adapter"; import { @@ -258,9 +257,6 @@ export default function NewChatPage() { // Get current user for author info in shared chats const { data: currentUser } = useAtomValue(currentUserAtom); - // Live sync for other users' messages (shared chats) - const { messages: liveMessages } = useChatMessagesLive(threadId); - // Create the attachment adapter for file processing const attachmentAdapter = useMemo(() => createAttachmentAdapter(), []); @@ -413,45 +409,6 @@ export default function NewChatPage() { }); }, [currentThread, setCurrentThreadState]); - // Live sync: Merge messages from other users (shared chats) - useEffect(() => { - if (!liveMessages.length || !currentUser?.id || isRunning) return; - - // Get IDs of messages we already have locally - const localMessageIds = new Set( - messages - .map((m) => { - // Extract numeric ID from "msg-{id}" format - const match = m.id?.match(/^msg-(\d+)$/); - return match ? Number.parseInt(match[1], 10) : null; - }) - .filter((id): id is number => id !== null) - ); - - // Find live messages from OTHER users that we don't have locally - const newOtherUserMessages = liveMessages.filter((liveMsg) => { - // Skip if we already have this message - if (localMessageIds.has(liveMsg.id)) return false; - // Skip if this is our own message (we added it optimistically) - if (liveMsg.author_id === currentUser.id) return false; - return true; - }); - - if (newOtherUserMessages.length > 0) { - // Convert and add new messages from other users - const converted = newOtherUserMessages.map(convertToThreadMessage); - setMessages((prev) => { - // Merge and sort by ID to maintain order - const merged = [...prev, ...converted]; - return merged.sort((a, b) => { - const aId = Number.parseInt((a.id ?? "").replace(/^msg-/, ""), 10) || 0; - const bId = Number.parseInt((b.id ?? "").replace(/^msg-/, ""), 10) || 0; - return aId - bId; - }); - }); - } - }, [liveMessages, currentUser?.id, messages, isRunning]); - // Cancel ongoing request const cancelRun = useCallback(async () => { if (abortControllerRef.current) { diff --git a/surfsense_web/components/assistant-ui/assistant-message.tsx b/surfsense_web/components/assistant-ui/assistant-message.tsx index 513242d1b..681dc315a 100644 --- a/surfsense_web/components/assistant-ui/assistant-message.tsx +++ b/surfsense_web/components/assistant-ui/assistant-message.tsx @@ -25,7 +25,7 @@ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button import { CommentPanelContainer } from "@/components/chat-comments/comment-panel-container/comment-panel-container"; import { CommentSheet } from "@/components/chat-comments/comment-sheet/comment-sheet"; import { CommentTrigger } from "@/components/chat-comments/comment-trigger/comment-trigger"; -import { useCommentsLive } from "@/hooks/use-comments-live"; +import { useComments } from "@/hooks/use-comments"; import { useMediaQuery } from "@/hooks/use-media-query"; import { cn } from "@/lib/utils"; @@ -115,8 +115,12 @@ export const AssistantMessage: FC = () => { const isLastMessage = useAssistantState(({ message }) => message?.isLast ?? false); const isMessageStreaming = isThreadRunning && isLastMessage; - // Live sync for real-time comment count - const { commentCount } = useCommentsLive(dbMessageId); + const { data: commentsData } = useComments({ + messageId: dbMessageId ?? 0, + enabled: !!dbMessageId, + }); + + const commentCount = commentsData?.total_count ?? 0; const hasComments = commentCount > 0; const isAddingComment = dbMessageId !== null && addingCommentToMessageId === dbMessageId; const showCommentPanel = hasComments || isAddingComment; diff --git a/surfsense_web/components/chat-comments/comment-panel-container/comment-panel-container.tsx b/surfsense_web/components/chat-comments/comment-panel-container/comment-panel-container.tsx index 8281ca8fd..197ac0798 100644 --- a/surfsense_web/components/chat-comments/comment-panel-container/comment-panel-container.tsx +++ b/surfsense_web/components/chat-comments/comment-panel-container/comment-panel-container.tsx @@ -10,7 +10,7 @@ import { } from "@/atoms/chat-comments/comments-mutation.atoms"; import { membersAtom } from "@/atoms/members/members-query.atoms"; import { currentUserAtom } from "@/atoms/user/user-query.atoms"; -import { useCommentsLive } from "@/hooks/use-comments-live"; +import { useComments } from "@/hooks/use-comments"; import { CommentPanel } from "../comment-panel/comment-panel"; import type { CommentPanelContainerProps } from "./types"; import { transformComment, transformMember } from "./utils"; @@ -21,10 +21,10 @@ export function CommentPanelContainer({ maxHeight, variant = "desktop", }: CommentPanelContainerProps) { - // Live sync for real-time comment updates - const { comments: liveComments, isLoading: isCommentsLoading } = useCommentsLive( - isOpen ? messageId : null - ); + const { data: commentsData, isLoading: isCommentsLoading } = useComments({ + messageId, + enabled: isOpen, + }); const [{ data: membersData, isLoading: isMembersLoading }] = useAtom(membersAtom); const [{ data: currentUser }] = useAtom(currentUserAtom); @@ -35,8 +35,9 @@ export function CommentPanelContainer({ const [{ mutate: deleteComment, isPending: isDeleting }] = useAtom(deleteCommentMutationAtom); const commentThreads = useMemo(() => { - return liveComments.map(transformComment); - }, [liveComments]); + if (!commentsData?.comments) return []; + return commentsData.comments.map(transformComment); + }, [commentsData]); const members = useMemo(() => { if (!membersData) return []; diff --git a/surfsense_web/contracts/types/chat-messages.types.ts b/surfsense_web/contracts/types/chat-messages.types.ts index 01edef3f2..faba71bff 100644 --- a/surfsense_web/contracts/types/chat-messages.types.ts +++ b/surfsense_web/contracts/types/chat-messages.types.ts @@ -6,7 +6,7 @@ import { z } from "zod"; export const rawMessage = z.object({ id: z.number(), thread_id: z.number(), - role: z.enum(["user", "assistant", "system"]), + role: z.string(), content: z.unknown(), author_id: z.string().nullable(), created_at: z.string(), diff --git a/surfsense_web/hooks/use-chat-messages-live.ts b/surfsense_web/hooks/use-chat-messages-live.ts index 39341d479..4a8ae97e6 100644 --- a/surfsense_web/hooks/use-chat-messages-live.ts +++ b/surfsense_web/hooks/use-chat-messages-live.ts @@ -55,10 +55,15 @@ export function useChatMessagesLive(threadId: number | null) { // Transform raw messages to MessageRecord with author info return [...messagesData].map((msg): MessageRecord => { const author = msg.author_id ? memberMap.get(msg.author_id) : null; + + const role = (typeof msg.role === "string" ? msg.role.toLowerCase() : msg.role) as + | "user" + | "assistant" + | "system"; return { id: msg.id, thread_id: msg.thread_id, - role: msg.role, + role, content: msg.content, created_at: msg.created_at, author_id: msg.author_id,