122 lines
3.7 KiB
TypeScript
122 lines
3.7 KiB
TypeScript
import {
|
|
type UseQueryOptions,
|
|
useQuery,
|
|
useQueryClient,
|
|
} from "@tanstack/react-query";
|
|
import type { EndpointType } from "@/contexts/chat-context";
|
|
import { useChat } from "@/contexts/chat-context";
|
|
|
|
export interface RawConversation {
|
|
response_id: string;
|
|
title: string;
|
|
endpoint: string;
|
|
messages: Array<{
|
|
role: string;
|
|
content: string;
|
|
timestamp?: string;
|
|
response_id?: string;
|
|
}>;
|
|
created_at?: string;
|
|
last_activity?: string;
|
|
previous_response_id?: string;
|
|
total_messages: number;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
export interface ChatConversation {
|
|
response_id: string;
|
|
title: string;
|
|
endpoint: EndpointType;
|
|
messages: Array<{
|
|
role: string;
|
|
content: string;
|
|
timestamp?: string;
|
|
response_id?: string;
|
|
}>;
|
|
created_at?: string;
|
|
last_activity?: string;
|
|
previous_response_id?: string;
|
|
total_messages: number;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
export interface ConversationHistoryResponse {
|
|
conversations: RawConversation[];
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
export const useGetConversationsQuery = (
|
|
endpoint: EndpointType,
|
|
refreshTrigger?: number,
|
|
options?: Omit<UseQueryOptions, "queryKey" | "queryFn">,
|
|
) => {
|
|
const queryClient = useQueryClient();
|
|
const { isOnboardingComplete } = useChat();
|
|
|
|
async function getConversations(context: { signal?: AbortSignal }): Promise<ChatConversation[]> {
|
|
try {
|
|
// Fetch from the selected endpoint only
|
|
const apiEndpoint =
|
|
endpoint === "chat" ? "/api/chat/history" : "/api/langflow/history";
|
|
|
|
const response = await fetch(apiEndpoint, {
|
|
signal: context.signal,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.error(`Failed to fetch conversations: ${response.status}`);
|
|
return [];
|
|
}
|
|
|
|
const history: ConversationHistoryResponse = await response.json();
|
|
const rawConversations = history.conversations || [];
|
|
|
|
// Cast conversations to proper type and ensure endpoint is correct
|
|
const conversations: ChatConversation[] = rawConversations.map(
|
|
(conv: RawConversation) => ({
|
|
...conv,
|
|
endpoint: conv.endpoint as EndpointType,
|
|
}),
|
|
);
|
|
|
|
// Sort conversations by last activity (most recent first)
|
|
conversations.sort((a: ChatConversation, b: ChatConversation) => {
|
|
const aTime = new Date(a.last_activity || a.created_at || 0).getTime();
|
|
const bTime = new Date(b.last_activity || b.created_at || 0).getTime();
|
|
return bTime - aTime;
|
|
});
|
|
|
|
return conversations;
|
|
} catch (error) {
|
|
// Ignore abort errors - these are expected when requests are cancelled
|
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
return [];
|
|
}
|
|
console.error(`Failed to fetch ${endpoint} conversations:`, error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
// Extract enabled from options and combine with onboarding completion check
|
|
// Query is only enabled if onboarding is complete AND the caller's enabled condition is met
|
|
const callerEnabled = options?.enabled ?? true;
|
|
const enabled = isOnboardingComplete && callerEnabled;
|
|
|
|
const queryResult = useQuery(
|
|
{
|
|
queryKey: ["conversations", endpoint, refreshTrigger],
|
|
placeholderData: (prev) => prev,
|
|
queryFn: getConversations,
|
|
staleTime: 5000, // Consider data fresh for 5 seconds to prevent excessive refetching
|
|
gcTime: 5 * 60 * 1000, // Keep in cache for 5 minutes
|
|
networkMode: 'always', // Ensure requests can be cancelled
|
|
refetchOnMount: false, // Don't refetch on every mount
|
|
refetchOnWindowFocus: false, // Don't refetch when window regains focus
|
|
...options,
|
|
enabled, // Override enabled after spreading options to ensure onboarding check is applied
|
|
},
|
|
queryClient,
|
|
);
|
|
|
|
return queryResult;
|
|
};
|