fix(frontend): isolate new chat thread messages (#3508)

* fix(frontend): isolate new chat thread messages

* fix(frontend): keep live messages visible in new chat

* fix(frontend): reset thread-local message refs
This commit is contained in:
zgenu
2026-06-11 22:12:15 +08:00
committed by GitHub
parent b6fbf0d105
commit c733d3c917
5 changed files with 253 additions and 36 deletions
@@ -1,29 +1,44 @@
"use client";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { uuid } from "@/core/utils/uuid";
export function useThreadChat() {
const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>();
const pathname = usePathname();
const actualPathname =
typeof window === "undefined" ? pathname : window.location.pathname;
const isNewPath = actualPathname.endsWith("/new");
const newThreadIdRef = useRef<string | null>(
threadIdFromPath === "new" ? uuid() : null,
);
if (isNewPath && !newThreadIdRef.current) {
newThreadIdRef.current = uuid();
}
const searchParams = useSearchParams();
const [threadId, setThreadId] = useState(() => {
return threadIdFromPath === "new" ? uuid() : threadIdFromPath;
const [threadId, setThreadIdState] = useState(() => {
return threadIdFromPath === "new"
? (newThreadIdRef.current ?? uuid())
: threadIdFromPath;
});
const [isNewThread, setIsNewThread] = useState(
const [isNewThreadState, setIsNewThreadState] = useState(
() => threadIdFromPath === "new",
);
useEffect(() => {
if (pathname.endsWith("/new")) {
setIsNewThread(true);
setThreadId(uuid());
if (isNewPath) {
const nextThreadId = newThreadIdRef.current ?? uuid();
newThreadIdRef.current = nextThreadId;
setIsNewThreadState(true);
setThreadIdState(nextThreadId);
return;
}
newThreadIdRef.current = null;
// Guard: after history.replaceState updates the URL from /chats/new to
// /chats/{UUID}, Next.js useParams may still return the stale "new" value
// because replaceState does not trigger router updates. Avoid propagating
@@ -32,9 +47,28 @@ export function useThreadChat() {
if (threadIdFromPath === "new") {
return;
}
setIsNewThread(false);
setThreadId(threadIdFromPath);
}, [pathname, threadIdFromPath]);
setIsNewThreadState(false);
setThreadIdState(threadIdFromPath);
}, [isNewPath, threadIdFromPath]);
const setThreadId = useCallback((nextThreadId: string) => {
newThreadIdRef.current = null;
setThreadIdState(nextThreadId);
}, []);
const setIsNewThread = useCallback((nextIsNewThread: boolean) => {
if (!nextIsNewThread) {
newThreadIdRef.current = null;
}
setIsNewThreadState(nextIsNewThread);
}, []);
const isMock = searchParams.get("mock") === "true";
return { threadId, setThreadId, isNewThread, setIsNewThread, isMock };
return {
threadId: isNewPath ? (newThreadIdRef.current ?? threadId) : threadId,
setThreadId,
isNewThread: isNewPath ? true : isNewThreadState,
setIsNewThread,
isMock,
};
}