From dbdeaa1bcffa4dd8ca60df2e1f706c656f76279e Mon Sep 17 00:00:00 2001
From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com>
Date: Mon, 27 Apr 2026 04:03:53 +0530
Subject: [PATCH] feat(sidebar): add loading skeletons to DocumentsSidebar and
LocalFilesystemBrowser during data fetching
---
.../layout/ui/sidebar/DocumentsSidebar.tsx | 116 +++++++++++-------
.../ui/sidebar/LocalFilesystemBrowser.tsx | 94 ++++++++++++--
2 files changed, 162 insertions(+), 48 deletions(-)
diff --git a/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx
index ce9b80d49..f4c5c03ac 100644
--- a/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx
+++ b/surfsense_web/components/layout/ui/sidebar/DocumentsSidebar.tsx
@@ -73,6 +73,7 @@ import {
} from "@/components/ui/dropdown-menu";
import { Drawer, DrawerContent, DrawerHandle, DrawerTitle } from "@/components/ui/drawer";
import { Input } from "@/components/ui/input";
+import { Skeleton } from "@/components/ui/skeleton";
import { Separator } from "@/components/ui/separator";
import { Spinner } from "@/components/ui/spinner";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
@@ -99,6 +100,32 @@ const NON_DELETABLE_DOCUMENT_TYPES: readonly string[] = ["SURFSENSE_DOCS"];
const LOCAL_FILESYSTEM_TRUST_KEY = "surfsense.local-filesystem-trust.v1";
const MAX_LOCAL_FILESYSTEM_ROOTS = 5;
+function CloudDocumentsSkeleton() {
+ const rows = [
+ { id: "row-1", widthClass: "w-44" },
+ { id: "row-2", widthClass: "w-32" },
+ { id: "row-3", widthClass: "w-32" },
+ { id: "row-4", widthClass: "w-44" },
+ { id: "row-5", widthClass: "w-32" },
+ { id: "row-6", widthClass: "w-32" },
+ { id: "row-7", widthClass: "w-44" },
+ { id: "row-8", widthClass: "w-32" },
+ ];
+
+ return (
+
+
+ {rows.map((row) => (
+
+
+
+
+ ))}
+
+
+ );
+}
+
type FilesystemSettings = {
mode: "cloud" | "desktop_local_folder";
localRootPaths: string[];
@@ -407,8 +434,8 @@ function AuthenticatedDocumentsSidebar({
);
// Zero queries for tree data
- const [zeroFolders] = useQuery(queries.folders.bySpace({ searchSpaceId }));
- const [zeroAllDocs] = useQuery(queries.documents.bySpace({ searchSpaceId }));
+ const [zeroFolders, zeroFoldersResult] = useQuery(queries.folders.bySpace({ searchSpaceId }));
+ const [zeroAllDocs, zeroAllDocsResult] = useQuery(queries.documents.bySpace({ searchSpaceId }));
const [agentCreatedDocs, setAgentCreatedDocs] = useAtom(agentCreatedDocumentsAtom);
const treeFolders: FolderDisplay[] = useMemo(
@@ -989,6 +1016,9 @@ function AuthenticatedDocumentsSidebar({
const showFilesystemTabs = !isMobile && !!electronAPI && !!filesystemSettings;
const currentFilesystemTab = filesystemSettings?.mode === "desktop_local_folder" ? "local" : "cloud";
+ const showCloudSkeleton =
+ currentFilesystemTab === "cloud" &&
+ (zeroFoldersResult.type !== "complete" || zeroAllDocsResult.type !== "complete");
const cloudContent = (
<>
@@ -1101,45 +1131,49 @@ function AuthenticatedDocumentsSidebar({
)}
- {
- openEditorPanel({
- documentId: doc.id,
- searchSpaceId,
- title: doc.title,
- });
- }}
- onEditDocument={(doc) => {
- openEditorPanel({
- documentId: doc.id,
- searchSpaceId,
- title: doc.title,
- });
- }}
- onDeleteDocument={(doc) => handleDeleteDocument(doc.id)}
- onMoveDocument={handleMoveDocument}
- onExportDocument={handleExportDocument}
- onVersionHistory={(doc) => setVersionDocId(doc.id)}
- activeTypes={activeTypes}
- onDropIntoFolder={handleDropIntoFolder}
- onReorderFolder={handleReorderFolder}
- watchedFolderIds={watchedFolderIds}
- onRescanFolder={handleRescanFolder}
- onStopWatchingFolder={handleStopWatching}
- onExportFolder={handleExportFolder}
- />
+ {showCloudSkeleton ? (
+
+ ) : (
+ {
+ openEditorPanel({
+ documentId: doc.id,
+ searchSpaceId,
+ title: doc.title,
+ });
+ }}
+ onEditDocument={(doc) => {
+ openEditorPanel({
+ documentId: doc.id,
+ searchSpaceId,
+ title: doc.title,
+ });
+ }}
+ onDeleteDocument={(doc) => handleDeleteDocument(doc.id)}
+ onMoveDocument={handleMoveDocument}
+ onExportDocument={handleExportDocument}
+ onVersionHistory={(doc) => setVersionDocId(doc.id)}
+ activeTypes={activeTypes}
+ onDropIntoFolder={handleDropIntoFolder}
+ onReorderFolder={handleReorderFolder}
+ watchedFolderIds={watchedFolderIds}
+ onRescanFolder={handleRescanFolder}
+ onStopWatchingFolder={handleStopWatching}
+ onExportFolder={handleExportFolder}
+ />
+ )}
>
diff --git a/surfsense_web/components/layout/ui/sidebar/LocalFilesystemBrowser.tsx b/surfsense_web/components/layout/ui/sidebar/LocalFilesystemBrowser.tsx
index 5b08f2e37..30e532896 100644
--- a/surfsense_web/components/layout/ui/sidebar/LocalFilesystemBrowser.tsx
+++ b/surfsense_web/components/layout/ui/sidebar/LocalFilesystemBrowser.tsx
@@ -1,8 +1,9 @@
"use client";
import { ChevronDown, ChevronRight, FileText, Folder } from "lucide-react";
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { DEFAULT_EXCLUDE_PATTERNS } from "@/components/sources/FolderWatchDialog";
+import { Skeleton } from "@/components/ui/skeleton";
import { Spinner } from "@/components/ui/spinner";
import { useElectronAPI } from "@/hooks/use-platform";
import { getSupportedExtensionsSet } from "@/lib/supported-extensions";
@@ -39,6 +40,8 @@ type LocalRootMount = {
rootPath: string;
};
+type MountLoadStatus = "idle" | "loading" | "complete" | "error";
+
const getFolderDisplayName = (rootPath: string): string =>
rootPath.split(/[\\/]/).at(-1) || rootPath;
@@ -79,6 +82,10 @@ export function LocalFilesystemBrowser({
const [rootStateMap, setRootStateMap] = useState>({});
const [expandedFolderKeys, setExpandedFolderKeys] = useState>(new Set());
const [mountByRootKey, setMountByRootKey] = useState