fix SSE cleanup, filter add popover to completed only, derive isGenerating from pending set, handle not_found status

This commit is contained in:
Jamie Pine
2026-03-13 10:57:28 -07:00
parent 509b0e71cc
commit 49ebf6222e
4 changed files with 18 additions and 21 deletions
@@ -58,6 +58,7 @@ export function StoryContent() {
const query = searchQuery.toLowerCase();
return historyData.items.filter(
(gen) =>
gen.status === 'completed' &&
!storyGenerationIds.has(gen.id) &&
(gen.text.toLowerCase().includes(query) || gen.profile_name.toLowerCase().includes(query)),
);
+14 -11
View File
@@ -8,7 +8,7 @@ import { useServerStore } from '@/stores/serverStore';
interface GenerationStatusEvent {
id: string;
status: 'generating' | 'completed' | 'failed';
status: 'generating' | 'completed' | 'failed' | 'not_found';
duration?: number;
error?: string;
}
@@ -37,6 +37,17 @@ export function useGenerationProgress() {
// Track active EventSource instances
const eventSourcesRef = useRef<Map<string, EventSource>>(new Map());
// Unmount-only cleanup — close all SSE connections when the hook is torn down
useEffect(() => {
const sources = eventSourcesRef.current;
return () => {
for (const source of sources.values()) {
source.close();
}
sources.clear();
};
}, []);
useEffect(() => {
const currentSources = eventSourcesRef.current;
@@ -103,7 +114,7 @@ export function useGenerationProgress() {
const genAudioUrl = apiClient.getAudioUrl(id);
setAudioWithAutoPlay(genAudioUrl, id, '', '');
}
} else if (data.status === 'failed') {
} else if (data.status === 'failed' || data.status === 'not_found') {
source.close();
currentSources.delete(id);
removePendingGeneration(id);
@@ -112,7 +123,7 @@ export function useGenerationProgress() {
queryClient.invalidateQueries({ queryKey: ['history'] });
toast({
title: 'Generation failed',
title: data.status === 'not_found' ? 'Generation not found' : 'Generation failed',
description: data.error || 'An error occurred during generation',
variant: 'destructive',
});
@@ -132,14 +143,6 @@ export function useGenerationProgress() {
currentSources.set(id, source);
}
return () => {
// Cleanup on unmount
for (const source of currentSources.values()) {
source.close();
}
currentSources.clear();
};
}, [
pendingIds,
removePendingGeneration,
+2 -6
View File
@@ -15,7 +15,6 @@ const POLL_INTERVAL = 30000;
*/
export function useRestoreActiveTasks() {
const [activeDownloads, setActiveDownloads] = useState<ActiveDownloadTask[]>([]);
const setIsGenerating = useGenerationStore((state) => state.setIsGenerating);
const setActiveGenerationId = useGenerationStore((state) => state.setActiveGenerationId);
const addPendingGeneration = useGenerationStore((state) => state.addPendingGeneration);
@@ -26,18 +25,15 @@ export function useRestoreActiveTasks() {
try {
const tasks = await apiClient.getActiveTasks();
// Update generation state — restore pending generations (e.g., after page refresh)
// Restore pending generations (e.g., after page refresh)
if (tasks.generations.length > 0) {
setIsGenerating(true);
setActiveGenerationId(tasks.generations[0].task_id);
for (const gen of tasks.generations) {
addPendingGeneration(gen.task_id);
}
} else {
// Only clear if we were tracking a generation
const currentId = useGenerationStore.getState().activeGenerationId;
if (currentId) {
setIsGenerating(false);
setActiveGenerationId(null);
}
}
@@ -63,7 +59,7 @@ export function useRestoreActiveTasks() {
// Silently fail - server might be temporarily unavailable
console.debug('Failed to fetch active tasks:', error);
}
}, [setIsGenerating, setActiveGenerationId]);
}, [setActiveGenerationId, addPendingGeneration]);
useEffect(() => {
// Fetch immediately on mount
+1 -4
View File
@@ -3,7 +3,7 @@ import { create } from 'zustand';
interface GenerationState {
/** IDs of generations currently in progress */
pendingGenerationIds: Set<string>;
/** Whether any generation is in progress (derived convenience) */
/** Whether any generation is in progress (derived from pendingGenerationIds) */
isGenerating: boolean;
/** Map of generationId → storyId for deferred story additions */
pendingStoryAdds: Map<string, string>;
@@ -11,8 +11,6 @@ interface GenerationState {
removePendingGeneration: (id: string) => void;
addPendingStoryAdd: (generationId: string, storyId: string) => void;
removePendingStoryAdd: (generationId: string) => string | undefined;
/** Legacy setter for backward compat with useRestoreActiveTasks */
setIsGenerating: (generating: boolean) => void;
setActiveGenerationId: (id: string | null) => void;
activeGenerationId: string | null;
}
@@ -56,6 +54,5 @@ export const useGenerationStore = create<GenerationState>((set, get) => ({
return storyId;
},
setIsGenerating: (generating) => set({ isGenerating: generating }),
setActiveGenerationId: (id) => set({ activeGenerationId: id }),
}));