Preserve thread selection during background updates

This commit is contained in:
2026-07-26 17:37:39 -07:00
parent 048456b8a5
commit e2443162b0
5 changed files with 98 additions and 30 deletions

View File

@@ -63,9 +63,12 @@ import {
type WorkspaceItem,
} from "@/lib/api";
import { useSessionAuth } from "@/hooks/use-session-auth";
import {
resolveSidebarSelectionAfterRefresh,
type SidebarSelection,
} from "@/lib/sidebar-selection";
import { cn } from "@/lib/utils";
type SidebarSelection = { kind: "chat" | "search"; id: string };
type DraftSelectionKind = "chat" | "search";
type SidebarItem = SidebarSelection & {
title: string;
@@ -94,7 +97,7 @@ type ActiveRunsState = {
searches: Record<string, true>;
};
type RefreshCollectionsOptions = {
preferredSelection?: SidebarSelection;
initialSelection?: SidebarSelection;
reportTransientError?: boolean;
selectFallback?: boolean;
};
@@ -1131,7 +1134,7 @@ export default function App() {
};
const refreshCollections = async ({
preferredSelection,
initialSelection,
reportTransientError = true,
selectFallback = false,
}: RefreshCollectionsOptions = {}) => {
@@ -1143,24 +1146,12 @@ export default function App() {
setChats(nextChats);
setSearches(nextSearches);
setSelectedItem((current) => {
const hasItem = (candidate: SidebarSelection | null) => {
if (!candidate) return false;
return nextWorkspaceItems.some((item) => item.type === candidate.kind && item.id === candidate.id);
};
if (preferredSelection && hasItem(preferredSelection)) {
return preferredSelection;
}
if (hasItem(current)) {
return current;
}
if (!selectFallback) {
return null;
}
const first = nextWorkspaceItems[0];
return first ? { kind: first.type, id: first.id } : null;
});
setSelectedItem((current) =>
resolveSidebarSelectionAfterRefresh(current, nextWorkspaceItems, {
initialSelection,
selectFallback,
})
);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (message.includes("bearer token")) {
@@ -1252,10 +1243,10 @@ export default function App() {
useEffect(() => {
if (!isAuthenticated) return;
const preferredSelection = initialRouteSelectionRef.current;
const initialSelection = initialRouteSelectionRef.current;
initialRouteSelectionRef.current = null;
void Promise.all([
refreshCollections({ preferredSelection: preferredSelection ?? undefined, selectFallback: true }),
refreshCollections({ initialSelection: initialSelection ?? undefined, selectFallback: true }),
refreshModels(),
refreshChatTools(),
refreshActiveRuns(),
@@ -2480,7 +2471,7 @@ export default function App() {
backgroundSuspendedChatStreamsRef.current.delete(chatId);
const persistedChat = await retryAfterAppResume(() => getChat(chatId));
await refreshCollections({ preferredSelection: target, reportTransientError: false });
await refreshCollections({ reportTransientError: false });
if (isCurrentSelection(target)) {
setSelectedChat(persistedChat);
setSelectedSearch(null);
@@ -2694,7 +2685,7 @@ export default function App() {
}
}
await refreshCollections({ preferredSelection: target });
await refreshCollections();
return target;
};
@@ -2813,7 +2804,7 @@ export default function App() {
setSelectedChat(persistedChat);
setSelectedSearch(null);
}
void refreshCollections({ preferredSelection: target });
void refreshCollections();
return;
} catch (resumeError) {
err = resumeError;
@@ -2919,7 +2910,7 @@ export default function App() {
{ signal: abortController.signal }
);
await refreshCollections({ preferredSelection: target });
await refreshCollections();
if (isCurrentSelection(target)) {
await refreshSearch(searchId);
}
@@ -2987,7 +2978,7 @@ export default function App() {
messages: [],
});
setSelectedSearch(null);
await refreshCollections({ preferredSelection: { kind: "chat", id: chat.id } });
await refreshCollections();
await refreshChat(chat.id);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
@@ -3149,7 +3140,7 @@ export default function App() {
messages: [],
});
setSelectedSearch(null);
await refreshCollections({ preferredSelection: { kind: "chat", id: chat.id } });
await refreshCollections();
await refreshChat(chat.id);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);