import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { Header } from './components/Header'; import { Feed } from './components/Feed'; import { Sidebar } from './components/Sidebar'; import { useSSE } from './hooks/useSSE'; import { useSettings } from './hooks/useSettings'; import { useStats } from './hooks/useStats'; import { usePagination } from './hooks/usePagination'; import { useTheme } from './hooks/useTheme'; import { Observation, Summary, UserPrompt } from './types'; import { mergeAndDeduplicateByProject } from './utils/data'; export function App() { const [currentFilter, setCurrentFilter] = useState(''); const [sidebarOpen, setSidebarOpen] = useState(false); const [paginatedObservations, setPaginatedObservations] = useState([]); const [paginatedSummaries, setPaginatedSummaries] = useState([]); const [paginatedPrompts, setPaginatedPrompts] = useState([]); const { observations, summaries, prompts, projects, isProcessing, isConnected } = useSSE(); const { settings, saveSettings, isSaving, saveStatus } = useSettings(); const { stats } = useStats(); const { preference, resolvedTheme, setThemePreference } = useTheme(); const pagination = usePagination(currentFilter); // Reset paginated data when filter changes useEffect(() => { setPaginatedObservations([]); setPaginatedSummaries([]); setPaginatedPrompts([]); }, [currentFilter]); // Merge real-time data with paginated data, removing duplicates and filtering by project const allObservations = useMemo( () => mergeAndDeduplicateByProject(observations, paginatedObservations, currentFilter), [observations, paginatedObservations, currentFilter] ); const allSummaries = useMemo( () => mergeAndDeduplicateByProject(summaries, paginatedSummaries, currentFilter), [summaries, paginatedSummaries, currentFilter] ); const allPrompts = useMemo( () => mergeAndDeduplicateByProject(prompts, paginatedPrompts, currentFilter), [prompts, paginatedPrompts, currentFilter] ); // Toggle sidebar const toggleSidebar = useCallback(() => { setSidebarOpen(prev => !prev); }, []); // Handle loading more data const handleLoadMore = useCallback(async () => { try { const [newObservations, newSummaries, newPrompts] = await Promise.all([ pagination.observations.loadMore(), pagination.summaries.loadMore(), pagination.prompts.loadMore() ]); if (newObservations.length > 0) { setPaginatedObservations(prev => [...prev, ...newObservations]); } if (newSummaries.length > 0) { setPaginatedSummaries(prev => [...prev, ...newSummaries]); } if (newPrompts.length > 0) { setPaginatedPrompts(prev => [...prev, ...newPrompts]); } } catch (error) { console.error('Failed to load more data:', error); } }, [pagination.observations, pagination.summaries, pagination.prompts]); // Load first page only when filter changes useEffect(() => { handleLoadMore(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentFilter]); // Only re-run when filter changes, not when handleLoadMore changes return (
); }