Spaces:
Runtime error
Runtime error
| import { useState, useCallback, useEffect } from "react"; | |
| import type { Conversation } from "../types"; | |
| import { readConversations, writeConversations, titleFromMessages } from "../lib/storage"; | |
| export interface UseConversationsReturn { | |
| conversations: Conversation[]; | |
| upsertConversation: (conv: Conversation) => void; | |
| deleteConversation: (id: string) => void; | |
| loadConversation: (id: string) => Conversation | undefined; | |
| } | |
| /** | |
| * Manages conversation persistence in localStorage via storage.ts helpers. | |
| * | |
| * - Reads `hmc-conversations` on mount. | |
| * - Exposes `upsertConversation(conv)` to insert or update a conversation, | |
| * automatically deriving the title from the first user message and updating | |
| * `last_active_at` to the current time. | |
| * - Exposes `deleteConversation(id)` to remove a conversation by ID. | |
| * - Exposes `loadConversation(id)` to retrieve a conversation by ID from | |
| * the current in-memory list (no server round-trip). | |
| * | |
| * Requirements: 6.1, 6.2, 6.3, 6.5, 6.6, 6.7 | |
| */ | |
| export function useConversations(): UseConversationsReturn { | |
| const [conversations, setConversations] = useState<Conversation[]>(() => | |
| readConversations() | |
| ); | |
| // Sync from localStorage on mount (handles cases where another tab wrote) | |
| useEffect(() => { | |
| setConversations(readConversations()); | |
| }, []); | |
| /** | |
| * Insert or update a conversation. If a conversation with the same `id` | |
| * already exists, it is replaced. The title is re-derived from messages | |
| * and `last_active_at` is set to now. | |
| * | |
| * The cap of 20 conversations is enforced by `writeConversations` in storage.ts. | |
| */ | |
| const upsertConversation = useCallback((conv: Conversation): void => { | |
| setConversations((prev) => { | |
| const now = new Date().toISOString(); | |
| const updated: Conversation = { | |
| ...conv, | |
| title: titleFromMessages(conv.messages), | |
| last_active_at: now, | |
| created_at: conv.created_at || now, | |
| }; | |
| const existing = prev.findIndex((c) => c.id === conv.id); | |
| let next: Conversation[]; | |
| if (existing >= 0) { | |
| next = [...prev]; | |
| next[existing] = updated; | |
| } else { | |
| next = [updated, ...prev]; | |
| } | |
| writeConversations(next); | |
| return readConversations(); // re-read to get the sorted, capped result | |
| }); | |
| }, []); | |
| /** | |
| * Remove a conversation by ID from localStorage and update state. | |
| */ | |
| const deleteConversation = useCallback((id: string): void => { | |
| setConversations((prev) => { | |
| const next = prev.filter((c) => c.id !== id); | |
| writeConversations(next); | |
| return next; | |
| }); | |
| }, []); | |
| /** | |
| * Retrieve a conversation by ID from the current in-memory list. | |
| * Returns undefined if not found. No server round-trip. | |
| */ | |
| const loadConversation = useCallback( | |
| (id: string): Conversation | undefined => { | |
| return conversations.find((c) => c.id === id); | |
| }, | |
| [conversations] | |
| ); | |
| return { conversations, upsertConversation, deleteConversation, loadConversation }; | |
| } | |