From b10a2b4cdb72ba105971b335517c62e825ecdc65 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Thu, 19 Mar 2026 21:36:10 +0100 Subject: [PATCH] Refactor library handling and remove unused state variables in App.jsx --- src/App.jsx | 140 ++++++++++++---------------------------------------- 1 file changed, 32 insertions(+), 108 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 0a10ac9..662ad40 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -705,20 +705,12 @@ async function regenerateFromIndex(index, overrideUserText = null) { if (nextLibraries.length === 0) { setActiveLibrarySlug(null); - setChatLibrarySlug(null); - setPendingChatLibrarySlug(null); return; } if (!nextLibraries.some(lib => lib.slug === activeLibrarySlug)) { setActiveLibrarySlug(nextLibraries[0].slug); } - if (chatLibrarySlug && !nextLibraries.some(lib => lib.slug === chatLibrarySlug)) { - setChatLibrarySlug(null); - } - if (pendingChatLibrarySlug && !nextLibraries.some(lib => lib.slug === pendingChatLibrarySlug)) { - setPendingChatLibrarySlug(null); - } } catch (error) { console.warn('Failed to load libraries', error); } @@ -811,13 +803,9 @@ async function regenerateFromIndex(index, overrideUserText = null) { useEffect(() => { try { - if (chatLibrarySlug) { - localStorage.setItem(CHAT_LIBRARY_KEY, chatLibrarySlug); - } else { - localStorage.removeItem(CHAT_LIBRARY_KEY); - } + localStorage.setItem(CHAT_LIBRARY_MAP_KEY, JSON.stringify(chatLibraryBySession || {})); } catch {} - }, [chatLibrarySlug]); + }, [chatLibraryBySession]); useEffect(() => { if (!ollamaApiUrl) return; @@ -826,13 +814,30 @@ async function regenerateFromIndex(index, overrideUserText = null) { refreshLibraryJobs(); }, 3000); return () => clearInterval(interval); - }, [ollamaApiUrl, activeSidebarMode, activeLibrarySlug, chatLibrarySlug]); + }, [ollamaApiUrl, activeSidebarMode, activeLibrarySlug]); // Load messages for the active session useEffect(() => { fetchHistory(activeSessionId); }, [activeSessionId]); + useEffect(() => { + if (!libraries.length) return + const validSlugs = new Set(libraries.map(library => library.slug)) + setChatLibraryBySession(prev => { + let changed = false + const next = {} + for (const [sessionId, slug] of Object.entries(prev || {})) { + if (validSlugs.has(slug)) { + next[sessionId] = slug + } else { + changed = true + } + } + return changed ? next : prev + }) + }, [libraries]) + const handleSidebarClick = (mode) => { // Saving happens in the centralized cleanup effect below setActiveSidebarMode(mode); @@ -851,104 +856,23 @@ async function regenerateFromIndex(index, overrideUserText = null) { return libraries.find(lib => lib.slug === activeLibrarySlug) || null; }, [activeLibrarySlug, libraries]); + const chatLibrarySlug = activeSessionId ? (chatLibraryBySession[activeSessionId] || null) : null + const chatLibrary = useMemo(() => { return libraries.find(lib => lib.slug === chatLibrarySlug) || null; }, [chatLibrarySlug, libraries]); - const pendingChatLibrary = useMemo(() => { - return libraries.find(lib => lib.slug === pendingChatLibrarySlug) || null; - }, [pendingChatLibrarySlug, libraries]); + const chatLibraryHasActiveJob = useMemo(() => { + if (!chatLibrarySlug) return false + return libraryJobs.some(job => job.slug === chatLibrarySlug && (job.status === 'queued' || job.status === 'running')) + }, [chatLibrarySlug, libraryJobs]) - const chatAttachmentLibrary = useMemo(() => { - return chatLibrary || pendingChatLibrary || null; - }, [chatLibrary, pendingChatLibrary]); - - useEffect(() => { - if (chatLibrarySlug && chatLibrary && !chatLibrary.states?.is_indexed) { - if (chatLibrary.files?.length) { - setPendingChatLibrarySlug(chatLibrarySlug) - } - setChatLibrarySlug(null) - } - }, [chatLibrarySlug, chatLibrary]); - - useEffect(() => { - if (!pendingChatLibrarySlug || !ollamaApiUrl) return - - const jobsForLibrary = libraryJobs - .filter(job => job.slug === pendingChatLibrarySlug) - .sort((a, b) => String(b.created_at || '').localeCompare(String(a.created_at || ''))) - const hasActiveJob = jobsForLibrary.some(job => job.status === 'queued' || job.status === 'running') - const latestJob = jobsForLibrary[0] || null - - if (!pendingChatLibrary) { - setPendingChatLibrarySlug(null) - return - } - - if (pendingChatLibrary.states?.is_indexed) { - setChatLibrarySlug(pendingChatLibrarySlug) - setPendingChatLibrarySlug(null) - return - } - - if (hasActiveJob) return - - if (latestJob?.status === 'failed') { - setPendingChatLibrarySlug(null) - return - } - - if (!pendingChatLibrary.files?.length) { - setPendingChatLibrarySlug(null) - return - } - - let cancelled = false - ;(async () => { - try { - await startLibraryJob(pendingChatLibrarySlug, 'prepare') - if (!cancelled) { - await refreshLibraries() - } - } catch (error) { - console.error('Failed to prepare library for chat', error) - if (!cancelled) { - setPendingChatLibrarySlug(null) - } - } - })() - - return () => { - cancelled = true - } - }, [pendingChatLibrarySlug, pendingChatLibrary, libraryJobs, ollamaApiUrl]) - - async function toggleChatLibrary(libraryOrSlug) { - const slug = typeof libraryOrSlug === 'string' ? libraryOrSlug : libraryOrSlug?.slug - if (!slug) return - - if (chatLibrarySlug === slug || pendingChatLibrarySlug === slug) { - setChatLibrarySlug(current => (current === slug ? null : current)) - setPendingChatLibrarySlug(current => (current === slug ? null : current)) - return - } - - const library = libraries.find(lib => lib.slug === slug) - if (!library) return - - if (library.states?.is_indexed) { - setChatLibrarySlug(slug) - setPendingChatLibrarySlug(null) - return - } - - if (!library.files?.length) { - throw new Error('Add files before adding this database to chat.') - } - - setPendingChatLibrarySlug(slug) - } + const chatLibraryStatusSuffix = useMemo(() => { + if (!chatLibrary) return '' + if (!chatLibrary.files?.length) return ' (empty)' + if (chatLibrary.states?.is_indexed) return '' + return chatLibraryHasActiveJob ? ' (syncing)' : ' (needs sync)' + }, [chatLibrary, chatLibraryHasActiveJob]) // Persist the scrollTop of the session we are LEAVING (on chat change or when leaving the chat view) useEffect(() => {