mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-22 09:32:33 +00:00
fix(webui): keep new chat during session refresh
This commit is contained in:
parent
3d3ebf1110
commit
de0a8f5e41
@ -27,13 +27,25 @@ export function useSessions(): {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const tokenRef = useRef(token);
|
||||
const optimisticKeysRef = useRef<Set<string>>(new Set());
|
||||
tokenRef.current = token;
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const rows = await listSessions(tokenRef.current);
|
||||
setSessions(rows);
|
||||
const serverKeys = new Set(rows.map((row) => row.key));
|
||||
setSessions((prev) => [
|
||||
...rows,
|
||||
...prev.filter(
|
||||
(session) =>
|
||||
optimisticKeysRef.current.has(session.key) &&
|
||||
!serverKeys.has(session.key),
|
||||
),
|
||||
]);
|
||||
for (const key of Array.from(optimisticKeysRef.current)) {
|
||||
if (serverKeys.has(key)) optimisticKeysRef.current.delete(key);
|
||||
}
|
||||
setError(null);
|
||||
} catch (e) {
|
||||
const msg =
|
||||
@ -57,6 +69,7 @@ export function useSessions(): {
|
||||
const createChat = useCallback(async (): Promise<string> => {
|
||||
const chatId = await client.newChat();
|
||||
const key = `websocket:${chatId}`;
|
||||
optimisticKeysRef.current.add(key);
|
||||
// Optimistic insert; a subsequent refresh will replace it with the
|
||||
// authoritative row once the server persists the session.
|
||||
setSessions((prev) => [
|
||||
@ -77,6 +90,7 @@ export function useSessions(): {
|
||||
const deleteChat = useCallback(
|
||||
async (key: string) => {
|
||||
await apiDeleteSession(tokenRef.current, key);
|
||||
optimisticKeysRef.current.delete(key);
|
||||
setSessions((prev) => prev.filter((s) => s.key !== key));
|
||||
},
|
||||
[],
|
||||
|
||||
@ -157,6 +157,53 @@ describe("useSessions", () => {
|
||||
expect(api.listSessions).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("keeps a newly created chat visible until the server session list catches up", async () => {
|
||||
vi.mocked(api.listSessions)
|
||||
.mockResolvedValueOnce([])
|
||||
.mockResolvedValueOnce([])
|
||||
.mockResolvedValueOnce([
|
||||
{
|
||||
key: "websocket:chat-new",
|
||||
channel: "websocket",
|
||||
chatId: "chat-new",
|
||||
createdAt: "2026-05-20T10:00:00Z",
|
||||
updatedAt: "2026-05-20T10:01:00Z",
|
||||
title: "Generated title",
|
||||
preview: "First message",
|
||||
},
|
||||
]);
|
||||
const client = fakeClient();
|
||||
client.newChat.mockResolvedValue("chat-new");
|
||||
|
||||
const { result } = renderHook(() => useSessions(), {
|
||||
wrapper: wrap(client),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current.loading).toBe(false));
|
||||
expect(result.current.sessions).toEqual([]);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.createChat();
|
||||
});
|
||||
|
||||
expect(result.current.sessions.map((s) => s.key)).toEqual(["websocket:chat-new"]);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.refresh();
|
||||
});
|
||||
|
||||
expect(result.current.sessions.map((s) => s.key)).toEqual(["websocket:chat-new"]);
|
||||
expect(result.current.sessions[0]?.preview).toBe("");
|
||||
|
||||
await act(async () => {
|
||||
await result.current.refresh();
|
||||
});
|
||||
|
||||
expect(result.current.sessions.map((s) => s.key)).toEqual(["websocket:chat-new"]);
|
||||
expect(result.current.sessions[0]?.preview).toBe("First message");
|
||||
expect(result.current.sessions[0]?.title).toBe("Generated title");
|
||||
});
|
||||
|
||||
it("passes through WebUI transcript user media as images and media", async () => {
|
||||
vi.mocked(api.fetchWebuiThread).mockResolvedValue({
|
||||
schemaVersion: 3,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user