diff --git a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx index 2f71adad9..b87cc4883 100644 --- a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx +++ b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx @@ -88,7 +88,7 @@ export function LayoutDataProvider({ }); // Fetch threads (40 total to allow up to 20 per section - shared/private) - const { data: threadsData } = useQuery({ + const { data: threadsData, isPending: isLoadingThreads } = useQuery({ queryKey: ["threads", searchSpaceId, { limit: 40 }], queryFn: () => fetchThreads(Number(searchSpaceId), 40), enabled: !!searchSpaceId, @@ -547,6 +547,7 @@ export function LayoutDataProvider({ theme={theme} setTheme={setTheme} isChatPage={isChatPage} + isLoadingChats={isLoadingThreads} inbox={{ isOpen: isInboxSidebarOpen, onOpenChange: setIsInboxSidebarOpen, diff --git a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx index 3624c90a3..a33149669 100644 --- a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx +++ b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx @@ -73,6 +73,7 @@ interface LayoutShellProps { className?: string; // Inbox props inbox?: InboxProps; + isLoadingChats?: boolean; } export function LayoutShell({ @@ -108,6 +109,7 @@ export function LayoutShell({ children, className, inbox, + isLoadingChats = false, }: LayoutShellProps) { const isMobile = useIsMobile(); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); @@ -159,6 +161,7 @@ export function LayoutShell({ pageUsage={pageUsage} theme={theme} setTheme={setTheme} + isLoadingChats={isLoadingChats} />
@@ -228,6 +231,7 @@ export function LayoutShell({ theme={theme} setTheme={setTheme} className="hidden md:flex border-r shrink-0" + isLoadingChats={isLoadingChats} /> {/* Docked Inbox Sidebar - renders as flex sibling between sidebar and content */} diff --git a/surfsense_web/components/layout/ui/sidebar/MobileSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/MobileSidebar.tsx index 85f907611..3e9d624c9 100644 --- a/surfsense_web/components/layout/ui/sidebar/MobileSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/MobileSidebar.tsx @@ -36,6 +36,7 @@ interface MobileSidebarProps { pageUsage?: PageUsage; theme?: string; setTheme?: (theme: "light" | "dark" | "system") => void; + isLoadingChats?: boolean; } export function MobileSidebarTrigger({ onClick }: { onClick: () => void }) { @@ -76,6 +77,7 @@ export function MobileSidebar({ pageUsage, theme, setTheme, + isLoadingChats = false, }: MobileSidebarProps) { const handleSearchSpaceSelect = (id: number) => { onSearchSpaceSelect(id); @@ -155,6 +157,7 @@ export function MobileSidebar({ theme={theme} setTheme={setTheme} className="w-full border-none" + isLoadingChats={isLoadingChats} /> diff --git a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx index db04bf6dc..fb29448c5 100644 --- a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx @@ -3,6 +3,7 @@ import { FolderOpen, PenSquare } from "lucide-react"; import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; +import { Skeleton } from "@/components/ui/skeleton"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import type { ChatItem, NavItem, PageUsage, SearchSpace, User } from "../../types/layout.types"; @@ -14,6 +15,15 @@ import { SidebarHeader } from "./SidebarHeader"; import { SidebarSection } from "./SidebarSection"; import { SidebarUserProfile } from "./SidebarUserProfile"; +function ChatListItemSkeleton() { + return ( +
+ + +
+ ); +} + interface SidebarProps { searchSpace: SearchSpace | null; isCollapsed?: boolean; @@ -38,6 +48,7 @@ interface SidebarProps { theme?: string; setTheme?: (theme: "light" | "dark" | "system") => void; className?: string; + isLoadingChats?: boolean; } export function Sidebar({ @@ -64,6 +75,7 @@ export function Sidebar({ theme, setTheme, className, + isLoadingChats = false, }: SidebarProps) { const t = useTranslations("sidebar"); @@ -151,7 +163,15 @@ export function Sidebar({ ) : undefined } > - {sharedChats.length > 0 ? ( + {isLoadingChats ? ( +
+ + + + + +
+ ) : sharedChats.length > 0 ? (
4 ? "pb-8" : ""}`} @@ -203,7 +223,15 @@ export function Sidebar({ ) : undefined } > - {chats.length > 0 ? ( + {isLoadingChats ? ( +
+ + + + + +
+ ) : chats.length > 0 ? (
4 ? "pb-8" : ""}`}