diff --git a/echo/frontend/src/components/conversation/ConversationAccordion.tsx b/echo/frontend/src/components/conversation/ConversationAccordion.tsx index 0a827a6a..8e2a0582 100644 --- a/echo/frontend/src/components/conversation/ConversationAccordion.tsx +++ b/echo/frontend/src/components/conversation/ConversationAccordion.tsx @@ -700,6 +700,7 @@ export const ConversationAccordion = ({ deep: { chunks: { _limit: 25, + _sort: ["-timestamp", "-created_at"], }, }, // Override filter to add tag filtering while preserving project scope diff --git a/echo/frontend/src/components/conversation/OngoingConversationsSummaryCard.tsx b/echo/frontend/src/components/conversation/OngoingConversationsSummaryCard.tsx index 852994c2..cc517917 100644 --- a/echo/frontend/src/components/conversation/OngoingConversationsSummaryCard.tsx +++ b/echo/frontend/src/components/conversation/OngoingConversationsSummaryCard.tsx @@ -1,47 +1,66 @@ -import { readItems } from "@directus/sdk"; +import { aggregate } from "@directus/sdk"; import { t } from "@lingui/core/macro"; import { ActionIcon, Group, Stack, Text } from "@mantine/core"; import { IconRefresh, IconUsersGroup } from "@tabler/icons-react"; -import { useQuery } from "@tanstack/react-query"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useState } from "react"; import { directus } from "@/lib/directus"; import { SummaryCard } from "../common/SummaryCard"; -const TIME_INTERVAL_SECONDS = 40; +const TIME_INTERVAL_SECONDS = 30; export const OngoingConversationsSummaryCard = ({ projectId, }: { projectId: string; }) => { - // FIXME: could potentially use the "Aggregate" API to just get the count + const queryClient = useQueryClient(); + // Track previous state to detect changes + const [hasOngoingConversations, setHasOngoingConversations] = useState(false); + // const hasOngoingConversationsRef = useRef(false); + const conversationChunksQuery = useQuery({ queryFn: async () => { - const chunks = await directus.request( - readItems("conversation_chunk", { - fields: ["conversation_id"], - filter: { - conversation_id: { - project_id: projectId, - }, - source: { - // @ts-expect-error source is not typed - _nin: ["DASHBOARD_UPLOAD", "CLONE"], - }, - timestamp: { - // @ts-expect-error gt is not typed - _gt: new Date( - Date.now() - TIME_INTERVAL_SECONDS * 1000, - ).toISOString(), + const result = await directus.request( + aggregate("conversation_chunk", { + aggregate: { + countDistinct: ["conversation_id"], + }, + query: { + filter: { + conversation_id: { + project_id: projectId, + }, + source: { + // @ts-expect-error source is not typed + _nin: ["DASHBOARD_UPLOAD", "CLONE"], + }, + timestamp: { + // @ts-expect-error gt is not typed + _gt: new Date( + Date.now() - TIME_INTERVAL_SECONDS * 1000, + ).toISOString(), + }, }, }, }), ); - const uniqueConversations = new Set( - chunks.map((chunk) => chunk.conversation_id), + const currentCount = Number( + // @ts-expect-error aggregate response type is not properly typed + (result[0]?.countDistinct?.conversation_id as string) ?? "0", ); - return uniqueConversations.size; + if (currentCount > 0 || hasOngoingConversations) { + queryClient.invalidateQueries({ + queryKey: ["projects", projectId, "conversations"], + }); + setHasOngoingConversations(false); + } + + setHasOngoingConversations(currentCount > 0); + + return currentCount; }, queryKey: ["conversation_chunks", projectId], refetchInterval: 30000, diff --git a/echo/frontend/src/components/conversation/hooks/index.ts b/echo/frontend/src/components/conversation/hooks/index.ts index ad2fa1db..d9b358ca 100644 --- a/echo/frontend/src/components/conversation/hooks/index.ts +++ b/echo/frontend/src/components/conversation/hooks/index.ts @@ -658,7 +658,7 @@ export const useConversationsByProjectId = ( query?: Partial>, filterBySource?: string[], ) => { - const TIME_INTERVAL_SECONDS = 40; + const TIME_INTERVAL_SECONDS = 30; return useQuery({ queryFn: async () => { @@ -667,6 +667,7 @@ export const useConversationsByProjectId = ( deep: { chunks: { _limit: loadChunks ? 1000 : 1, + _sort: ["-timestamp", "-created_at"], }, }, fields: [ @@ -868,7 +869,7 @@ export const useInfiniteConversationsByProjectId = ( }, ) => { const { initialLimit = 15 } = options ?? {}; - const TIME_INTERVAL_SECONDS = 40; + const TIME_INTERVAL_SECONDS = 30; return useInfiniteQuery({ getNextPageParam: (lastPage: { nextOffset?: number }) => @@ -880,6 +881,7 @@ export const useInfiniteConversationsByProjectId = ( deep: { chunks: { _limit: loadChunks ? 1000 : 1, + _sort: ["-timestamp", "-created_at"], }, }, fields: [