fix(coding): rehydrate selected conversation on foreground
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
abortCodingConversation,
|
||||
forkCodingConversation,
|
||||
} from '@/lib/coding-conversations';
|
||||
import { subscribeHostEvent } from '@/lib/host-events';
|
||||
import {
|
||||
codingConversationStore,
|
||||
selectCodingConversationDraft,
|
||||
@@ -148,6 +149,7 @@ export function CodingChatPanel({
|
||||
>({});
|
||||
const appliedNavigationDraftRef = useRef<string | null>(null);
|
||||
const automaticCreationKeyRef = useRef<string | null>(null);
|
||||
const selectedConversationContextRef = useRef<string | null>(null);
|
||||
const attachmentsRef = useRef(attachmentsByDraftKey);
|
||||
const uploadedAttachmentsRef = useRef(new Map<string, CodingAttachmentRef>());
|
||||
const uploadFlightsRef = useRef(new Map<string, Promise<CodingAttachmentRef>>());
|
||||
@@ -179,6 +181,11 @@ export function CodingChatPanel({
|
||||
|
||||
attachmentsRef.current = attachmentsByDraftKey;
|
||||
|
||||
const selectProjectConversation = useCallback((projectId: string, conversationId: string) => {
|
||||
selectedConversationContextRef.current = `${projectId}:${conversationId}`;
|
||||
return selectConversation(conversationId);
|
||||
}, [selectConversation]);
|
||||
|
||||
const selectDraft = useCallback((state: CodingConversationStoreState) => (
|
||||
targetConversationId
|
||||
? selectCodingConversationDraft(targetConversationId)(state)
|
||||
@@ -232,6 +239,24 @@ export function CodingChatPanel({
|
||||
|
||||
useEffect(() => () => disconnectEvents(), [disconnectEvents]);
|
||||
|
||||
useEffect(() => subscribeHostEvent('lifecycle:sleep', () => {
|
||||
disconnectEvents();
|
||||
}), [disconnectEvents]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!targetConversationId) return undefined;
|
||||
const refreshVisibleConversation = () => {
|
||||
if (document.visibilityState !== 'visible') return;
|
||||
void selectConversation(targetConversationId).catch(() => undefined);
|
||||
};
|
||||
window.addEventListener('focus', refreshVisibleConversation);
|
||||
document.addEventListener('visibilitychange', refreshVisibleConversation);
|
||||
return () => {
|
||||
window.removeEventListener('focus', refreshVisibleConversation);
|
||||
document.removeEventListener('visibilitychange', refreshVisibleConversation);
|
||||
};
|
||||
}, [selectConversation, targetConversationId]);
|
||||
|
||||
useEffect(() => () => {
|
||||
for (const attachments of Object.values(attachmentsRef.current)) {
|
||||
for (const attachment of attachments) URL.revokeObjectURL(attachment.previewUrl);
|
||||
@@ -247,15 +272,23 @@ export function CodingChatPanel({
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeProject || !selectedAgent) {
|
||||
selectedConversationContextRef.current = null;
|
||||
clearConversationSelection();
|
||||
return;
|
||||
}
|
||||
if (selectedConversation?.agentId === selectedAgent.id && !selectedConversation.archivedAt) return;
|
||||
if (selectedConversation?.agentId === selectedAgent.id && !selectedConversation.archivedAt) {
|
||||
const selectionContext = `${activeProject.id}:${selectedConversation.id}`;
|
||||
if (selectedConversationContextRef.current !== selectionContext) {
|
||||
void selectProjectConversation(activeProject.id, selectedConversation.id)
|
||||
.catch(() => undefined);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const next = newestConversation(conversations, selectedAgent.id);
|
||||
if (next) {
|
||||
markConversationUnread(next.id, false);
|
||||
if (next.unread) void patchConversation(next.id, { unread: false }).catch(() => undefined);
|
||||
void selectConversation(next.id).catch(() => undefined);
|
||||
void selectProjectConversation(activeProject.id, next.id).catch(() => undefined);
|
||||
return;
|
||||
}
|
||||
const creationKey = `${activeProject.id}:${selectedAgent.id}`;
|
||||
@@ -267,7 +300,8 @@ export function CodingChatPanel({
|
||||
const current = codingWorkspaceStore.getState();
|
||||
if (current.activeProjectId === activeProject.id
|
||||
&& current.selectedAgentId === selectedAgent.id) {
|
||||
void selectConversation(conversation.id).catch(() => undefined);
|
||||
void selectProjectConversation(activeProject.id, conversation.id)
|
||||
.catch(() => undefined);
|
||||
}
|
||||
})
|
||||
.catch(() => undefined);
|
||||
@@ -279,7 +313,7 @@ export function CodingChatPanel({
|
||||
markConversationUnread,
|
||||
patchConversation,
|
||||
primeConversation,
|
||||
selectConversation,
|
||||
selectProjectConversation,
|
||||
selectedAgent,
|
||||
selectedConversation,
|
||||
]);
|
||||
@@ -328,8 +362,8 @@ export function CodingChatPanel({
|
||||
if (conversation.unread) {
|
||||
void patchConversation(conversation.id, { unread: false }).catch(() => undefined);
|
||||
}
|
||||
void selectConversation(conversation.id).catch(() => undefined);
|
||||
}, [activeProject, markConversationUnread, patchConversation, primeConversation, selectConversation]);
|
||||
void selectProjectConversation(activeProject.id, conversation.id).catch(() => undefined);
|
||||
}, [activeProject, markConversationUnread, patchConversation, primeConversation, selectProjectConversation]);
|
||||
|
||||
const handleCreateConversation = useCallback(async () => {
|
||||
if (!activeProject || !selectedAgent || creatingAgentIds[selectedAgent.id]) return;
|
||||
@@ -340,14 +374,14 @@ export function CodingChatPanel({
|
||||
primeConversation(createLocalConversationSnapshot(projectId, conversation));
|
||||
const current = codingWorkspaceStore.getState();
|
||||
if (current.activeProjectId === projectId && current.selectedAgentId === agentId) {
|
||||
await selectConversation(conversation.id).catch(() => undefined);
|
||||
await selectProjectConversation(projectId, conversation.id).catch(() => undefined);
|
||||
}
|
||||
}, [
|
||||
activeProject,
|
||||
createConversation,
|
||||
creatingAgentIds,
|
||||
primeConversation,
|
||||
selectConversation,
|
||||
selectProjectConversation,
|
||||
selectedAgent,
|
||||
]);
|
||||
|
||||
@@ -365,9 +399,9 @@ export function CodingChatPanel({
|
||||
if (workspace.activeProjectId === sourceProjectId
|
||||
&& workspace.selectedAgentId === sourceAgentId
|
||||
&& selectedId === sourceConversationId) {
|
||||
await selectConversation(forked.id);
|
||||
await selectProjectConversation(sourceProjectId, forked.id);
|
||||
}
|
||||
}, [activeProject, primeConversation, selectConversation, selectedAgent, targetConversationId, upsertConversation]);
|
||||
}, [activeProject, primeConversation, selectProjectConversation, selectedAgent, targetConversationId, upsertConversation]);
|
||||
|
||||
const handleAddFiles = useCallback((files: File[]) => {
|
||||
if (!draftKey
|
||||
|
||||
@@ -511,14 +511,12 @@ export function createCodingConversationStore(
|
||||
};
|
||||
});
|
||||
const entry = get().entriesByConversationId[conversationId];
|
||||
const snapshotFlight = !entry?.reducer.snapshot
|
||||
|| entry.reducer.invalidation
|
||||
|| entry.loadState !== 'live'
|
||||
? get().loadSnapshot(
|
||||
conversationId,
|
||||
entry?.reducer.invalidation ? 'recovering' : 'loading',
|
||||
)
|
||||
: Promise.resolve(entry.reducer.snapshot);
|
||||
const refreshMode = entry?.reducer.invalidation
|
||||
? 'recovering'
|
||||
: entry?.reducer.snapshot && entry.loadState === 'live'
|
||||
? 'silent'
|
||||
: 'loading';
|
||||
const snapshotFlight = get().loadSnapshot(conversationId, refreshMode);
|
||||
await Promise.all([snapshotFlight, get().connectEvents()]);
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user