diff --git a/src/app/dashboard/chat/chat-view.tsx b/src/app/dashboard/chat/chat-view.tsx index 37734f5..fb49b88 100644 --- a/src/app/dashboard/chat/chat-view.tsx +++ b/src/app/dashboard/chat/chat-view.tsx @@ -64,6 +64,10 @@ interface UiMemory { } const ACTOR_STORAGE_KEY = "mega-ask-user"; +/** Per-actor: the thread they were last in, so leaving the page (e.g. via + the sidebar nav) and coming back resumes it instead of starting blank. + "New thread" clears this; picking a thread or sending a message sets it. */ +const ACTIVE_THREAD_STORAGE_PREFIX = "mega-ask-active-thread:"; function loadStoredActor(): Actor | null { if (typeof window === "undefined") return null; @@ -78,6 +82,25 @@ function loadStoredActor(): Actor | null { return null; } +function loadStoredThreadId(actorId: string): string | null { + if (typeof window === "undefined") return null; + try { + return window.localStorage.getItem(ACTIVE_THREAD_STORAGE_PREFIX + actorId); + } catch { + return null; + } +} + +function saveStoredThreadId(actorId: string, threadId: string | null) { + if (typeof window === "undefined") return; + try { + if (threadId) window.localStorage.setItem(ACTIVE_THREAD_STORAGE_PREFIX + actorId, threadId); + else window.localStorage.removeItem(ACTIVE_THREAD_STORAGE_PREFIX + actorId); + } catch { + /* ignore */ + } +} + /** Relative time for the thread list ("2h ago", "Just now"). */ function relativeTime(iso?: string): string { if (!iso) return ""; @@ -317,6 +340,7 @@ function ChatViewInner() { const loadThread = React.useCallback( async (threadId: string, forActor: Actor) => { setActiveThreadId(threadId); + saveStoredThreadId(forActor.id, threadId); setTurns([]); setError(null); try { @@ -361,20 +385,6 @@ function ChatViewInner() { [], ); - // React to the URL's ?thread= — covers first load and back/forward. - React.useEffect(() => { - if (!actor) return; - const fromUrl = searchParams.get("thread"); - if (fromUrl && fromUrl !== activeThreadIdRef.current) { - loadThread(fromUrl, actor); - } else if (!fromUrl && activeThreadIdRef.current) { - setActiveThreadId(null); - setTurns([]); - } - // Only re-run when the actor first resolves or the URL changes. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [actor, searchParams]); - const goToThread = React.useCallback( (threadId: string | null, mode: "push" | "replace" = "push") => { const url = threadId ? `${pathname}?thread=${threadId}` : pathname; @@ -384,13 +394,35 @@ function ChatViewInner() { [pathname, router], ); + // React to the URL's ?thread= — covers first load and back/forward. + React.useEffect(() => { + if (!actor) return; + const fromUrl = searchParams.get("thread"); + if (fromUrl && fromUrl !== activeThreadIdRef.current) { + loadThread(fromUrl, actor); + } else if (!fromUrl && activeThreadIdRef.current) { + setActiveThreadId(null); + setTurns([]); + } else if (!fromUrl && !activeThreadIdRef.current) { + // No thread in the URL and nothing loaded yet — resume the last + // thread this actor was in (e.g. after leaving via the sidebar nav + // and coming back), rather than silently starting a blank one. + // "New thread" clears the stored id, so that still lands here empty. + const stored = loadStoredThreadId(actor.id); + if (stored) goToThread(stored, "replace"); + } + // Only re-run when the actor first resolves or the URL changes. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [actor, searchParams]); + const startNewThread = React.useCallback(() => { + if (actor) saveStoredThreadId(actor.id, null); setActiveThreadId(null); setTurns([]); setError(null); setSidebarOpen(false); goToThread(null, "push"); - }, [goToThread]); + }, [actor, goToThread]); const selectThread = React.useCallback( (t: ThreadSummary) => { @@ -614,6 +646,7 @@ function ChatViewInner() { // reflect it in the URL and sidebar. activeThreadIdRef.current = data.id; setActiveThreadId(data.id); + saveStoredThreadId(actor.id, data.id); setThreads((prev) => [ { id: data.id, title: data.title, last_message_at: new Date().toISOString() }, ...prev.filter((t) => t.id !== data.id), @@ -761,6 +794,7 @@ function ChatViewInner() { if (!activeThreadIdRef.current && data.threadId) { activeThreadIdRef.current = data.threadId; setActiveThreadId(data.threadId); + saveStoredThreadId(actor.id, data.threadId); setThreads((prev) => [ { id: data.threadId, title: data.threadTitle ?? file.name, last_message_at: new Date().toISOString() }, ...prev.filter((t) => t.id !== data.threadId),