import { useState, useEffect, useRef, useCallback } from 'react'; export interface ConversationSummary { id: string; title: string | null; archived: boolean; createdAt: string; updatedAt: string; } export interface UseConversationsOptions { gatewayUrl: string; sessionCookie?: string; } export interface UseConversationsReturn { conversations: ConversationSummary[]; loading: boolean; error: string | null; refresh: () => Promise; createConversation: (title?: string) => Promise; deleteConversation: (id: string) => Promise; renameConversation: (id: string, title: string) => Promise; } export function useConversations(opts: UseConversationsOptions): UseConversationsReturn { const { gatewayUrl, sessionCookie } = opts; const [conversations, setConversations] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const mountedRef = useRef(true); const headers = useCallback( (includeContentType = true): Record => { const h: Record = { Origin: gatewayUrl }; if (includeContentType) h['Content-Type'] = 'application/json'; if (sessionCookie) h['Cookie'] = sessionCookie; return h; }, [gatewayUrl, sessionCookie], ); const refresh = useCallback(async () => { if (!mountedRef.current) return; setLoading(true); setError(null); try { const res = await fetch(`${gatewayUrl}/api/conversations`, { headers: headers(false) }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = (await res.json()) as ConversationSummary[]; if (mountedRef.current) { setConversations(data); } } catch (err) { if (mountedRef.current) { setError(err instanceof Error ? err.message : 'Unknown error'); } } finally { if (mountedRef.current) { setLoading(false); } } }, [gatewayUrl, headers]); useEffect(() => { mountedRef.current = true; void refresh(); return () => { mountedRef.current = false; }; }, [refresh]); const createConversation = useCallback( async (title?: string): Promise => { try { const res = await fetch(`${gatewayUrl}/api/conversations`, { method: 'POST', headers: headers(), body: JSON.stringify({ title: title ?? null }), }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = (await res.json()) as ConversationSummary; if (mountedRef.current) { setConversations((prev) => [data, ...prev]); } return data; } catch { return null; } }, [gatewayUrl, headers], ); const deleteConversation = useCallback( async (id: string): Promise => { try { const res = await fetch(`${gatewayUrl}/api/conversations/${id}`, { method: 'DELETE', headers: headers(false), }); if (!res.ok) return false; if (mountedRef.current) { setConversations((prev) => prev.filter((c) => c.id !== id)); } return true; } catch { return false; } }, [gatewayUrl, headers], ); const renameConversation = useCallback( async (id: string, title: string): Promise => { try { const res = await fetch(`${gatewayUrl}/api/conversations/${id}`, { method: 'PATCH', headers: headers(), body: JSON.stringify({ title }), }); if (!res.ok) return false; if (mountedRef.current) { setConversations((prev) => prev.map((c) => (c.id === id ? { ...c, title } : c))); } return true; } catch { return false; } }, [gatewayUrl, headers], ); return { conversations, loading, error, refresh, createConversation, deleteConversation, renameConversation, }; }