From b9bc1d356648640e5a6f081a4d85541d8e752f4c Mon Sep 17 00:00:00 2001 From: Adityavardhan Agrawal Date: Sun, 27 Apr 2025 19:49:42 -0700 Subject: [PATCH] UI: Fix document loading speed, skeleton --- ee/ui-component/components/GraphSection.tsx | 11 +- .../components/documents/DocumentsSection.tsx | 226 ++++++++++++------ ee/ui-component/components/ui/skeleton.tsx | 3 + ee/ui-component/package.json | 16 +- ee/ui-component/react-shim.js | 3 + ee/ui-component/tsup.config.ts | 5 + 6 files changed, 179 insertions(+), 85 deletions(-) create mode 100644 ee/ui-component/react-shim.js diff --git a/ee/ui-component/components/GraphSection.tsx b/ee/ui-component/components/GraphSection.tsx index 417c5ad..e842ea1 100644 --- a/ee/ui-component/components/GraphSection.tsx +++ b/ee/ui-component/components/GraphSection.tsx @@ -20,6 +20,7 @@ import { } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; import { Switch } from '@/components/ui/switch'; +import { Skeleton } from "@/components/ui/skeleton"; // Dynamically import ForceGraphComponent to avoid SSR issues const ForceGraphComponent = dynamic(() => import('@/components/ForceGraphComponent'), { ssr: false }); @@ -516,7 +517,15 @@ const GraphSection: React.FC = ({ apiBaseUrl, onSelectGraph, {loading ? (
-
+ {/* Skeleton Loader for Graph List */} +
+ {[...Array(12)].map((_, i) => ( +
+ + +
+ ))} +
) : graphs.length === 0 ? (
diff --git a/ee/ui-component/components/documents/DocumentsSection.tsx b/ee/ui-component/components/documents/DocumentsSection.tsx index 7079135..60257cf 100644 --- a/ee/ui-component/components/documents/DocumentsSection.tsx +++ b/ee/ui-component/components/documents/DocumentsSection.tsx @@ -8,6 +8,7 @@ import DocumentDetail from './DocumentDetail'; import FolderList from './FolderList'; import { UploadDialog, useUploadDialog } from './UploadDialog'; import { cn } from '@/lib/utils'; +import { Skeleton } from "@/components/ui/skeleton"; import { Document, Folder } from '@/components/types'; @@ -237,7 +238,7 @@ const DocumentsSection: React.FC = ({ setLoading(false); } // Dependencies: URL, auth, selected folder, and the folder list itself - }, [effectiveApiUrl, authToken, selectedFolder, folders, documents.length]); + }, [effectiveApiUrl, authToken, selectedFolder, folders]); // Fetch all folders const fetchFolders = useCallback(async () => { @@ -282,47 +283,103 @@ const DocumentsSection: React.FC = ({ // Fetch documents when folders are loaded or selectedFolder changes useEffect(() => { - if (!foldersLoading && folders.length > 0) { + const effectSource = 'folders loaded or selectedFolder changed'; + console.log(`Effect triggered: ${effectSource}, foldersLoading: ${foldersLoading}, folders count: ${folders.length}, selectedFolder: ${selectedFolder}`); + + // Guard against running when folders are still loading + if (foldersLoading) { + console.log(`Effect (${effectSource}): Folders still loading, skipping.`); + return; + } + + // Handle the case where there are no folders at all + if (folders.length === 0 && selectedFolder === null) { + console.log(`Effect (${effectSource}): No folders found, clearing documents and stopping loading.`); + setDocuments([]); + setLoading(false); // Ensure loading is off + isInitialMount.current = false; + return; + } + + // Proceed if folders are loaded + if (folders.length >= 0) { // Check >= 0 to handle empty folders array correctly // Avoid fetching documents on initial mount if selectedFolder is null // unless initialFolder was specified if (isInitialMount.current && selectedFolder === null && !initialFolder) { - console.log('Initial mount with no folder selected, skipping document fetch'); + console.log(`Effect (${effectSource}): Initial mount with no folder selected, skipping document fetch`); isInitialMount.current = false; + // Ensure loading is false if we skip fetching + setLoading(false); return; } - console.log('Folders loaded or selectedFolder changed, fetching documents...', selectedFolder); - fetchDocuments('folders loaded or selectedFolder changed'); - isInitialMount.current = false; // Mark initial mount as complete - } else if (!foldersLoading && folders.length === 0 && selectedFolder === null) { - // Handle case where there are no folders at all - console.log('No folders found, clearing documents and stopping loading.'); - setDocuments([]); - setLoading(false); - isInitialMount.current = false; - } - }, [foldersLoading, folders, selectedFolder, fetchDocuments, initialFolder]); - // Poll for document status if any document is processing + // If we reach here, we intend to fetch documents + console.log(`Effect (${effectSource}): Preparing to fetch documents for folder: ${selectedFolder}`); + + // Wrap the async operation + const fetchWrapper = async () => { + // Explicitly set loading true *before* the async call within this effect's scope + // Note: fetchDocuments might also set this, but we ensure it's set here. + setLoading(true); + try { + await fetchDocuments(effectSource); + // If fetchDocuments completes successfully, it will set loading = false in its finally block. + // No need to set it here again in the try block. + console.log(`Effect (${effectSource}): fetchDocuments call completed.`); + } catch (error) { + // Catch potential errors *from* the await fetchDocuments call itself, though + // fetchDocuments has internal handling. This is an extra safeguard. + console.error(`Effect (${effectSource}): Error occurred during fetchDocuments call:`, error); + showAlert(`Error updating documents: ${error instanceof Error ? error.message : 'Unknown error'}`, { type: 'error' }); + // Ensure loading is turned off even if fetchDocuments had an issue before its finally. + setLoading(false); + } finally { + // **User Request:** Explicitly set loading to false within the effect's finally block. + // This acts as a safeguard, ensuring loading is false after the attempt, + // regardless of fetchDocuments' internal state management. + console.log(`Effect (${effectSource}): Finally block reached, ensuring loading is false.`); + setLoading(false); + isInitialMount.current = false; // Mark initial mount as complete here + } + }; + + fetchWrapper(); + + } else { + console.log(`Effect (${effectSource}): Condition not met (folders length < 0 ?), should not happen.`); + setLoading(false); // Fallback + } + + }, [foldersLoading, folders, selectedFolder, fetchDocuments, initialFolder]); // Keep fetchDocuments dependency + + // Poll for document status if any document is processing *and* user is not viewing details useEffect(() => { const hasProcessing = documents.some( (doc) => doc.system_metadata?.status === 'processing' ); - if (hasProcessing) { - console.log('Polling for document status...'); + // Only poll if there are processing documents AND no document detail view is open + if (hasProcessing && selectedDocument === null) { + console.log('Polling for document status (list view active)...'); const intervalId = setInterval(() => { console.log('Polling interval: calling refreshDocuments'); refreshDocuments(); // Fetch documents again to check status }, 5000); // Poll every 5 seconds - // Cleanup function to clear the interval when the component unmounts - // or when there are no more processing documents return () => { - console.log('Clearing polling interval'); + console.log('Clearing polling interval (list view or processing done)'); clearInterval(intervalId); }; + } else { + // Log why polling is not active + if (hasProcessing && selectedDocument !== null) { + console.log('Polling paused: Document detail view is open.'); + } else if (!hasProcessing) { + console.log('Polling stopped: No documents are processing.'); + } } - }, [documents, refreshDocuments]); + // Add selectedDocument to dependencies + }, [documents, refreshDocuments, selectedDocument]); // Collapse sidebar when a folder is selected useEffect(() => { @@ -1017,68 +1074,89 @@ const DocumentsSection: React.FC = ({ /> )} - {selectedFolder && documents.length === 0 && !loading ? ( -
-
- -

Drag and drop files here to upload to this folder.

-

Or use the upload button in the top right.

-
-
- ) : selectedFolder && loading ? ( -
-
-
-

Loading documents...

-
-
- ) : selectedFolder === null ? ( + {/* Folder Grid View (selectedFolder is null) */} + {selectedFolder === null ? (
- - } - /> + {/* Skeleton for Folder List loading state */} + {foldersLoading ? ( +
+ {[...Array(8)].map((_, i) => ( +
+ + +
+ ))} +
+ ) : ( + + } + /> + )}
) : (
+ {/* Left Panel: Document List or Skeleton or Empty State */}
- + {loading ? ( + // Skeleton Loader for Document List (within the correct layout) +
+ + + + + +
+ ) : documents.length === 0 ? ( + // Empty State (moved here, ensure it fills space) +
+
+ +

Drag and drop files here to upload to this folder.

+

Or use the upload button in the top right.

+
+
+ ) : ( + // Actual Document List + + )}
+ {/* Right Panel: Document Detail (conditionally rendered) */} {selectedDocument && (
=18" - }, - "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" + } } diff --git a/ee/ui-component/react-shim.js b/ee/ui-component/react-shim.js new file mode 100644 index 0000000..9145af8 --- /dev/null +++ b/ee/ui-component/react-shim.js @@ -0,0 +1,3 @@ +import * as React from "react"; + +export { React }; diff --git a/ee/ui-component/tsup.config.ts b/ee/ui-component/tsup.config.ts index 1835aad..3011011 100644 --- a/ee/ui-component/tsup.config.ts +++ b/ee/ui-component/tsup.config.ts @@ -16,4 +16,9 @@ export default defineConfig({ 'next/link', '@radix-ui/*', ], + esbuildOptions(options) { + options.jsx = 'automatic'; + options.jsxImportSource = 'react'; + return options; + }, });