Preserve draft views during collection refreshes
This commit is contained in:
@@ -93,6 +93,11 @@ type ActiveRunsState = {
|
|||||||
chats: Record<string, true>;
|
chats: Record<string, true>;
|
||||||
searches: Record<string, true>;
|
searches: Record<string, true>;
|
||||||
};
|
};
|
||||||
|
type RefreshCollectionsOptions = {
|
||||||
|
preferredSelection?: SidebarSelection;
|
||||||
|
reportTransientError?: boolean;
|
||||||
|
selectFallback?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
function readSidebarSelectionFromUrl(): SidebarSelection | null {
|
function readSidebarSelectionFromUrl(): SidebarSelection | null {
|
||||||
if (typeof window === "undefined") return null;
|
if (typeof window === "undefined") return null;
|
||||||
@@ -1125,7 +1130,11 @@ export default function App() {
|
|||||||
resetWorkspaceState();
|
resetWorkspaceState();
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshCollections = async (preferredSelection?: SidebarSelection, reportTransientError = true) => {
|
const refreshCollections = async ({
|
||||||
|
preferredSelection,
|
||||||
|
reportTransientError = true,
|
||||||
|
selectFallback = false,
|
||||||
|
}: RefreshCollectionsOptions = {}) => {
|
||||||
setIsLoadingCollections(true);
|
setIsLoadingCollections(true);
|
||||||
try {
|
try {
|
||||||
const nextWorkspaceItems = await listWorkspaceItems();
|
const nextWorkspaceItems = await listWorkspaceItems();
|
||||||
@@ -1146,6 +1155,9 @@ export default function App() {
|
|||||||
if (hasItem(current)) {
|
if (hasItem(current)) {
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
if (!selectFallback) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const first = nextWorkspaceItems[0];
|
const first = nextWorkspaceItems[0];
|
||||||
return first ? { kind: first.type, id: first.id } : null;
|
return first ? { kind: first.type, id: first.id } : null;
|
||||||
});
|
});
|
||||||
@@ -1242,7 +1254,12 @@ export default function App() {
|
|||||||
if (!isAuthenticated) return;
|
if (!isAuthenticated) return;
|
||||||
const preferredSelection = initialRouteSelectionRef.current;
|
const preferredSelection = initialRouteSelectionRef.current;
|
||||||
initialRouteSelectionRef.current = null;
|
initialRouteSelectionRef.current = null;
|
||||||
void Promise.all([refreshCollections(preferredSelection ?? undefined), refreshModels(), refreshChatTools(), refreshActiveRuns()]);
|
void Promise.all([
|
||||||
|
refreshCollections({ preferredSelection: preferredSelection ?? undefined, selectFallback: true }),
|
||||||
|
refreshModels(),
|
||||||
|
refreshChatTools(),
|
||||||
|
refreshActiveRuns(),
|
||||||
|
]);
|
||||||
}, [isAuthenticated]);
|
}, [isAuthenticated]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1269,8 +1286,8 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
setError((current) => (current && isRecoverableStreamDisconnect(new Error(current)) ? null : current));
|
setError((current) => (current && isRecoverableStreamDisconnect(new Error(current)) ? null : current));
|
||||||
void refreshActiveRuns();
|
void refreshActiveRuns();
|
||||||
void refreshCollections(selectedItemRef.current ?? undefined, false);
|
|
||||||
const currentSelection = selectedItemRef.current;
|
const currentSelection = selectedItemRef.current;
|
||||||
|
void refreshCollections({ reportTransientError: false });
|
||||||
if (currentSelection?.kind === "chat") {
|
if (currentSelection?.kind === "chat") {
|
||||||
void refreshChat(currentSelection.id);
|
void refreshChat(currentSelection.id);
|
||||||
} else if (currentSelection?.kind === "search") {
|
} else if (currentSelection?.kind === "search") {
|
||||||
@@ -1385,10 +1402,18 @@ export default function App() {
|
|||||||
|
|
||||||
const selectedKey = selectedItem ? `${selectedItem.kind}:${selectedItem.id}` : null;
|
const selectedKey = selectedItem ? `${selectedItem.kind}:${selectedItem.id}` : null;
|
||||||
const transcriptViewKey = draftKind ? `draft:${draftKind}` : selectedKey ?? "empty";
|
const transcriptViewKey = draftKind ? `draft:${draftKind}` : selectedKey ?? "empty";
|
||||||
const selectedChatPendingState = selectedItem?.kind === "chat" ? pendingChatStates[selectedItem.id] ?? null : null;
|
const selectedChatPendingState =
|
||||||
const selectedSearchRunState = selectedItem?.kind === "search" ? runningSearchStates[selectedItem.id] ?? null : null;
|
draftKind === null && selectedItem?.kind === "chat" ? pendingChatStates[selectedItem.id] ?? null : null;
|
||||||
const selectedChatIsActive = selectedItem?.kind === "chat" && (!!selectedChatPendingState || !!activeRuns.chats[selectedItem.id]);
|
const selectedSearchRunState =
|
||||||
const selectedSearchIsActive = selectedItem?.kind === "search" && (!!selectedSearchRunState || !!activeRuns.searches[selectedItem.id]);
|
draftKind === null && selectedItem?.kind === "search" ? runningSearchStates[selectedItem.id] ?? null : null;
|
||||||
|
const selectedChatIsActive =
|
||||||
|
draftKind === null &&
|
||||||
|
selectedItem?.kind === "chat" &&
|
||||||
|
(!!selectedChatPendingState || !!activeRuns.chats[selectedItem.id]);
|
||||||
|
const selectedSearchIsActive =
|
||||||
|
draftKind === null &&
|
||||||
|
selectedItem?.kind === "search" &&
|
||||||
|
(!!selectedSearchRunState || !!activeRuns.searches[selectedItem.id]);
|
||||||
const isSearchMode = draftKind ? draftKind === "search" : selectedItem?.kind === "search";
|
const isSearchMode = draftKind ? draftKind === "search" : selectedItem?.kind === "search";
|
||||||
const isSearchRunning = !!selectedSearchIsActive;
|
const isSearchRunning = !!selectedSearchIsActive;
|
||||||
const isSendingActiveChat = draftKind !== "search" && !!selectedChatIsActive;
|
const isSendingActiveChat = draftKind !== "search" && !!selectedChatIsActive;
|
||||||
@@ -1495,7 +1520,10 @@ export default function App() {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const messages = selectedChat?.messages ?? [];
|
const messages =
|
||||||
|
draftKind === null && selectedItem?.kind === "chat" && selectedChat?.id === selectedItem.id
|
||||||
|
? selectedChat.messages
|
||||||
|
: [];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSearchMode && pendingAttachments.length) {
|
if (isSearchMode && pendingAttachments.length) {
|
||||||
@@ -1602,6 +1630,7 @@ export default function App() {
|
|||||||
setError(null);
|
setError(null);
|
||||||
setContextMenu(null);
|
setContextMenu(null);
|
||||||
setDraftKind("chat");
|
setDraftKind("chat");
|
||||||
|
selectedItemRef.current = null;
|
||||||
setSelectedItem(null);
|
setSelectedItem(null);
|
||||||
setSelectedChat(null);
|
setSelectedChat(null);
|
||||||
setSelectedSearch(null);
|
setSelectedSearch(null);
|
||||||
@@ -1738,6 +1767,7 @@ export default function App() {
|
|||||||
setError(null);
|
setError(null);
|
||||||
setContextMenu(null);
|
setContextMenu(null);
|
||||||
setDraftKind("search");
|
setDraftKind("search");
|
||||||
|
selectedItemRef.current = null;
|
||||||
setSelectedItem(null);
|
setSelectedItem(null);
|
||||||
setSelectedChat(null);
|
setSelectedChat(null);
|
||||||
setSelectedSearch(null);
|
setSelectedSearch(null);
|
||||||
@@ -2065,7 +2095,7 @@ export default function App() {
|
|||||||
} else {
|
} else {
|
||||||
await deleteSearch(target.id);
|
await deleteSearch(target.id);
|
||||||
}
|
}
|
||||||
await refreshCollections();
|
await refreshCollections({ selectFallback: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err instanceof Error ? err.message : String(err);
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
if (message.includes("bearer token")) {
|
if (message.includes("bearer token")) {
|
||||||
@@ -2450,7 +2480,7 @@ export default function App() {
|
|||||||
|
|
||||||
backgroundSuspendedChatStreamsRef.current.delete(chatId);
|
backgroundSuspendedChatStreamsRef.current.delete(chatId);
|
||||||
const persistedChat = await retryAfterAppResume(() => getChat(chatId));
|
const persistedChat = await retryAfterAppResume(() => getChat(chatId));
|
||||||
await refreshCollections(target, false);
|
await refreshCollections({ preferredSelection: target, reportTransientError: false });
|
||||||
if (isCurrentSelection(target)) {
|
if (isCurrentSelection(target)) {
|
||||||
setSelectedChat(persistedChat);
|
setSelectedChat(persistedChat);
|
||||||
setSelectedSearch(null);
|
setSelectedSearch(null);
|
||||||
@@ -2664,7 +2694,7 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await refreshCollections(target);
|
await refreshCollections({ preferredSelection: target });
|
||||||
return target;
|
return target;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2783,7 +2813,7 @@ export default function App() {
|
|||||||
setSelectedChat(persistedChat);
|
setSelectedChat(persistedChat);
|
||||||
setSelectedSearch(null);
|
setSelectedSearch(null);
|
||||||
}
|
}
|
||||||
void refreshCollections(target);
|
void refreshCollections({ preferredSelection: target });
|
||||||
return;
|
return;
|
||||||
} catch (resumeError) {
|
} catch (resumeError) {
|
||||||
err = resumeError;
|
err = resumeError;
|
||||||
@@ -2889,7 +2919,7 @@ export default function App() {
|
|||||||
{ signal: abortController.signal }
|
{ signal: abortController.signal }
|
||||||
);
|
);
|
||||||
|
|
||||||
await refreshCollections(target);
|
await refreshCollections({ preferredSelection: target });
|
||||||
if (isCurrentSelection(target)) {
|
if (isCurrentSelection(target)) {
|
||||||
await refreshSearch(searchId);
|
await refreshSearch(searchId);
|
||||||
}
|
}
|
||||||
@@ -2957,7 +2987,7 @@ export default function App() {
|
|||||||
messages: [],
|
messages: [],
|
||||||
});
|
});
|
||||||
setSelectedSearch(null);
|
setSelectedSearch(null);
|
||||||
await refreshCollections({ kind: "chat", id: chat.id });
|
await refreshCollections({ preferredSelection: { kind: "chat", id: chat.id } });
|
||||||
await refreshChat(chat.id);
|
await refreshChat(chat.id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err instanceof Error ? err.message : String(err);
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
@@ -3119,7 +3149,7 @@ export default function App() {
|
|||||||
messages: [],
|
messages: [],
|
||||||
});
|
});
|
||||||
setSelectedSearch(null);
|
setSelectedSearch(null);
|
||||||
await refreshCollections({ kind: "chat", id: chat.id });
|
await refreshCollections({ preferredSelection: { kind: "chat", id: chat.id } });
|
||||||
await refreshChat(chat.id);
|
await refreshChat(chat.id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err instanceof Error ? err.message : String(err);
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
|
|||||||
Reference in New Issue
Block a user