diff --git a/surfsense_backend/alembic/versions/65_add_message_author_id.py b/surfsense_backend/alembic/versions/65_add_message_author_id.py index 8d891db81..2253c2d4e 100644 --- a/surfsense_backend/alembic/versions/65_add_message_author_id.py +++ b/surfsense_backend/alembic/versions/65_add_message_author_id.py @@ -37,10 +37,5 @@ def upgrade() -> None: def downgrade() -> None: """Remove author_id column from new_chat_messages table.""" - op.execute( - """ - DROP INDEX IF EXISTS ix_new_chat_messages_author_id; - ALTER TABLE new_chat_messages - DROP COLUMN IF EXISTS author_id; - """ - ) + op.execute("DROP INDEX IF EXISTS ix_new_chat_messages_author_id") + op.execute("ALTER TABLE new_chat_messages DROP COLUMN IF EXISTS author_id") diff --git a/surfsense_backend/alembic/versions/67_add_chat_comment_mentions_table.py b/surfsense_backend/alembic/versions/67_add_chat_comment_mentions_table.py index 02f1740c3..a718d4a0b 100644 --- a/surfsense_backend/alembic/versions/67_add_chat_comment_mentions_table.py +++ b/surfsense_backend/alembic/versions/67_add_chat_comment_mentions_table.py @@ -22,7 +22,6 @@ def upgrade() -> None: id SERIAL PRIMARY KEY, comment_id INTEGER NOT NULL REFERENCES chat_comments(id) ON DELETE CASCADE, mentioned_user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, - read BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (comment_id, mentioned_user_id) ) @@ -31,9 +30,6 @@ def upgrade() -> None: op.execute( "CREATE INDEX IF NOT EXISTS idx_chat_comment_mentions_comment_id ON chat_comment_mentions(comment_id)" ) - op.execute( - "CREATE INDEX IF NOT EXISTS idx_chat_comment_mentions_user_unread ON chat_comment_mentions(mentioned_user_id) WHERE read = FALSE" - ) def downgrade() -> None: diff --git a/surfsense_backend/app/db.py b/surfsense_backend/app/db.py index 759451f5f..427fabd60 100644 --- a/surfsense_backend/app/db.py +++ b/surfsense_backend/app/db.py @@ -513,7 +513,6 @@ class ChatCommentMention(BaseModel, TimestampMixin): nullable=False, index=True, ) - read = Column(Boolean, nullable=False, default=False) # Relationships comment = relationship("ChatComment", back_populates="mentions") diff --git a/surfsense_backend/app/routes/chat_comments_routes.py b/surfsense_backend/app/routes/chat_comments_routes.py index 395476b88..1c21c0f4a 100644 --- a/surfsense_backend/app/routes/chat_comments_routes.py +++ b/surfsense_backend/app/routes/chat_comments_routes.py @@ -20,8 +20,6 @@ from app.services.chat_comments_service import ( delete_comment, get_comments_for_message, get_user_mentions, - mark_all_mentions_as_read, - mark_mention_as_read, update_comment, ) from app.users import current_active_user @@ -90,28 +88,8 @@ async def remove_comment( @router.get("/mentions", response_model=MentionListResponse) async def list_mentions( search_space_id: int | None = None, - unread_only: bool = False, session: AsyncSession = Depends(get_async_session), user: User = Depends(current_active_user), ): """List mentions for the current user.""" - return await get_user_mentions(session, user, search_space_id, unread_only) - - -@router.put("/mentions/{mention_id}/read") -async def read_mention( - mention_id: int, - session: AsyncSession = Depends(get_async_session), - user: User = Depends(current_active_user), -): - """Mark a specific mention as read.""" - return await mark_mention_as_read(session, mention_id, user) - - -@router.put("/mentions/read-all") -async def read_all_mentions( - session: AsyncSession = Depends(get_async_session), - user: User = Depends(current_active_user), -): - """Mark all mentions as read for the current user.""" - return await mark_all_mentions_as_read(session, user) + return await get_user_mentions(session, user, search_space_id) diff --git a/surfsense_backend/app/schemas/chat_comments.py b/surfsense_backend/app/schemas/chat_comments.py index a8abba978..b87ee58a4 100644 --- a/surfsense_backend/app/schemas/chat_comments.py +++ b/surfsense_backend/app/schemas/chat_comments.py @@ -115,7 +115,6 @@ class MentionResponse(BaseModel): """Schema for a mention notification.""" id: int - read: bool created_at: datetime comment: MentionCommentResponse context: MentionContextResponse @@ -127,4 +126,4 @@ class MentionListResponse(BaseModel): """Response for listing user's mentions.""" mentions: list[MentionResponse] - unread_count: int + total_count: int diff --git a/surfsense_backend/app/services/chat_comments_service.py b/surfsense_backend/app/services/chat_comments_service.py index 6bed682b5..c59cbc918 100644 --- a/surfsense_backend/app/services/chat_comments_service.py +++ b/surfsense_backend/app/services/chat_comments_service.py @@ -569,7 +569,6 @@ async def get_user_mentions( session: AsyncSession, user: User, search_space_id: int | None = None, - unread_only: bool = False, ) -> MentionListResponse: """ Get mentions for the current user, optionally filtered by search space. @@ -578,10 +577,9 @@ async def get_user_mentions( session: Database session user: The current authenticated user search_space_id: Optional search space ID to filter mentions - unread_only: If True, only return unread mentions Returns: - MentionListResponse with mentions and unread count + MentionListResponse with mentions and total count """ # Build query with joins for filtering by search_space_id query = ( @@ -599,9 +597,6 @@ async def get_user_mentions( if search_space_id is not None: query = query.filter(NewChatThread.search_space_id == search_space_id) - if unread_only: - query = query.filter(ChatCommentMention.read.is_(False)) - result = await session.execute(query) mention_records = result.scalars().all() @@ -617,9 +612,6 @@ async def get_user_mentions( else: threads_map = {} - # Count unread from fetched data - unread_count = sum(1 for m in mention_records if not m.read) - mentions = [] for mention in mention_records: comment = mention.comment @@ -645,7 +637,6 @@ async def get_user_mentions( mentions.append( MentionResponse( id=mention.id, - read=mention.read, created_at=mention.created_at, comment=MentionCommentResponse( id=comment.id, @@ -665,75 +656,5 @@ async def get_user_mentions( return MentionListResponse( mentions=mentions, - unread_count=unread_count, + total_count=len(mentions), ) - - -async def mark_mention_as_read( - session: AsyncSession, - mention_id: int, - user: User, -) -> dict: - """ - Mark a specific mention as read. - - Args: - session: Database session - mention_id: ID of the mention to mark as read - user: The current authenticated user - - Returns: - Dict with mention_id and read status - - Raises: - HTTPException: If mention not found or doesn't belong to user - """ - result = await session.execute( - select(ChatCommentMention).filter(ChatCommentMention.id == mention_id) - ) - mention = result.scalars().first() - - if not mention: - raise HTTPException(status_code=404, detail="Mention not found") - - if mention.mentioned_user_id != user.id: - raise HTTPException( - status_code=403, - detail="You can only mark your own mentions as read", - ) - - mention.read = True - await session.commit() - - return {"mention_id": mention_id, "read": True} - - -async def mark_all_mentions_as_read( - session: AsyncSession, - user: User, -) -> dict: - """ - Mark all mentions for the current user as read. - - Args: - session: Database session - user: The current authenticated user - - Returns: - Dict with count of mentions marked as read - """ - from sqlalchemy import update - - result = await session.execute( - update(ChatCommentMention) - .where( - ChatCommentMention.mentioned_user_id == user.id, - ChatCommentMention.read.is_(False), - ) - .values(read=True) - .returning(ChatCommentMention.id) - ) - marked_ids = result.scalars().all() - await session.commit() - - return {"message": "All mentions marked as read", "count": len(marked_ids)} diff --git a/surfsense_web/atoms/chat-comments/comments-mutation.atoms.ts b/surfsense_web/atoms/chat-comments/comments-mutation.atoms.ts index 73fdbf330..e6a9767ca 100644 --- a/surfsense_web/atoms/chat-comments/comments-mutation.atoms.ts +++ b/surfsense_web/atoms/chat-comments/comments-mutation.atoms.ts @@ -4,7 +4,6 @@ import type { CreateCommentRequest, CreateReplyRequest, DeleteCommentRequest, - MarkMentionReadRequest, UpdateCommentRequest, } from "@/contracts/types/chat-comments.types"; import { chatCommentsApiService } from "@/lib/apis/chat-comments-api.service"; @@ -71,39 +70,3 @@ export const deleteCommentMutationAtom = atomWithMutation(() => ({ toast.error("Failed to delete comment"); }, })); - -export const markMentionReadMutationAtom = atomWithMutation(() => ({ - mutationFn: async (request: MarkMentionReadRequest & { search_space_id?: number }) => { - return chatCommentsApiService.markMentionRead(request); - }, - onSuccess: (_, variables) => { - queryClient.invalidateQueries({ - queryKey: cacheKeys.mentions.all(variables.search_space_id), - }); - queryClient.invalidateQueries({ - queryKey: cacheKeys.mentions.unreadOnly(variables.search_space_id), - }); - }, - onError: (error: Error) => { - console.error("Error marking mention as read:", error); - }, -})); - -export const markAllMentionsReadMutationAtom = atomWithMutation(() => ({ - mutationFn: async (request: { search_space_id?: number }) => { - return chatCommentsApiService.markAllMentionsRead(); - }, - onSuccess: (_, variables) => { - queryClient.invalidateQueries({ - queryKey: cacheKeys.mentions.all(variables.search_space_id), - }); - queryClient.invalidateQueries({ - queryKey: cacheKeys.mentions.unreadOnly(variables.search_space_id), - }); - toast.success("All mentions marked as read"); - }, - onError: (error: Error) => { - console.error("Error marking all mentions as read:", error); - toast.error("Failed to mark mentions as read"); - }, -})); diff --git a/surfsense_web/atoms/chat-comments/comments-query.atoms.ts b/surfsense_web/atoms/chat-comments/comments-query.atoms.ts index c8c3d2fb6..d79c7ea30 100644 --- a/surfsense_web/atoms/chat-comments/comments-query.atoms.ts +++ b/surfsense_web/atoms/chat-comments/comments-query.atoms.ts @@ -15,17 +15,3 @@ export const mentionsAtom = atomWithQuery((get) => { }, }; }); - -export const unreadMentionsAtom = atomWithQuery((get) => { - const searchSpaceId = get(activeSearchSpaceIdAtom); - - return { - queryKey: cacheKeys.mentions.unreadOnly(searchSpaceId ? Number(searchSpaceId) : undefined), - queryFn: async () => { - return chatCommentsApiService.getMentions({ - search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined, - unread_only: true, - }); - }, - }; -}); diff --git a/surfsense_web/contracts/types/chat-comments.types.ts b/surfsense_web/contracts/types/chat-comments.types.ts index 2e10cdc9d..92b3ff060 100644 --- a/surfsense_web/contracts/types/chat-comments.types.ts +++ b/surfsense_web/contracts/types/chat-comments.types.ts @@ -51,7 +51,6 @@ export const mentionComment = z.object({ export const mention = z.object({ id: z.number(), - read: z.boolean(), created_at: z.string(), comment: mentionComment, context: mentionContext, @@ -116,32 +115,11 @@ export const deleteCommentResponse = z.object({ */ export const getMentionsRequest = z.object({ search_space_id: z.number().optional(), - unread_only: z.boolean().optional(), }); export const getMentionsResponse = z.object({ mentions: z.array(mention), - unread_count: z.number(), -}); - -/** - * Mark mention as read - */ -export const markMentionReadRequest = z.object({ - mention_id: z.number(), -}); - -export const markMentionReadResponse = z.object({ - mention_id: z.number(), - read: z.boolean(), -}); - -/** - * Mark all mentions as read - */ -export const markAllMentionsReadResponse = z.object({ - message: z.string(), - count: z.number(), + total_count: z.number(), }); export type Author = z.infer; @@ -162,6 +140,3 @@ export type DeleteCommentRequest = z.infer; export type DeleteCommentResponse = z.infer; export type GetMentionsRequest = z.infer; export type GetMentionsResponse = z.infer; -export type MarkMentionReadRequest = z.infer; -export type MarkMentionReadResponse = z.infer; -export type MarkAllMentionsReadResponse = z.infer; diff --git a/surfsense_web/lib/apis/chat-comments-api.service.ts b/surfsense_web/lib/apis/chat-comments-api.service.ts index 2f76d9117..952de7a25 100644 --- a/surfsense_web/lib/apis/chat-comments-api.service.ts +++ b/surfsense_web/lib/apis/chat-comments-api.service.ts @@ -14,10 +14,6 @@ import { getCommentsResponse, getMentionsRequest, getMentionsResponse, - type MarkMentionReadRequest, - markAllMentionsReadResponse, - markMentionReadRequest, - markMentionReadResponse, type UpdateCommentRequest, updateCommentRequest, updateCommentResponse, @@ -127,39 +123,12 @@ class ChatCommentsApiService { if (parsed.data.search_space_id !== undefined) { params.set("search_space_id", String(parsed.data.search_space_id)); } - if (parsed.data.unread_only !== undefined) { - params.set("unread_only", String(parsed.data.unread_only)); - } const queryString = params.toString(); const url = queryString ? `/api/v1/mentions?${queryString}` : "/api/v1/mentions"; return baseApiService.get(url, getMentionsResponse); }; - - /** - * Mark a mention as read - */ - markMentionRead = async (request: MarkMentionReadRequest) => { - const parsed = markMentionReadRequest.safeParse(request); - - if (!parsed.success) { - const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", "); - throw new ValidationError(`Invalid request: ${errorMessage}`); - } - - return baseApiService.put( - `/api/v1/mentions/${parsed.data.mention_id}/read`, - markMentionReadResponse - ); - }; - - /** - * Mark all mentions as read - */ - markAllMentionsRead = async () => { - return baseApiService.put("/api/v1/mentions/read-all", markAllMentionsReadResponse); - }; } export const chatCommentsApiService = new ChatCommentsApiService(); diff --git a/surfsense_web/lib/query-client/cache-keys.ts b/surfsense_web/lib/query-client/cache-keys.ts index 8c1316aa4..37a38c4de 100644 --- a/surfsense_web/lib/query-client/cache-keys.ts +++ b/surfsense_web/lib/query-client/cache-keys.ts @@ -77,6 +77,5 @@ export const cacheKeys = { }, mentions: { all: (searchSpaceId?: number) => ["mentions", searchSpaceId] as const, - unreadOnly: (searchSpaceId?: number) => ["mentions", "unread", searchSpaceId] as const, }, };