diff --git a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx index 81429190b..9f302a91b 100644 --- a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx +++ b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx @@ -128,12 +128,12 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid enabled: !!searchSpaceId, }); - // Separate sidebar states for shared and private chats - const [isAllSharedChatsSidebarOpen, setIsAllSharedChatsSidebarOpen] = useState(false); - const [isAllPrivateChatsSidebarOpen, setIsAllPrivateChatsSidebarOpen] = useState(false); + // Unified slide-out panel state (only one can be open at a time) + type SlideoutPanel = "inbox" | "shared" | "private" | "announcements" | null; + const [activeSlideoutPanel, setActiveSlideoutPanel] = useState(null); - // Inbox sidebar state - const [isInboxSidebarOpen, setIsInboxSidebarOpen] = useState(false); + const isInboxSidebarOpen = activeSlideoutPanel === "inbox"; + const isAnnouncementsSidebarOpen = activeSlideoutPanel === "announcements"; // Documents sidebar state (shared atom so Composer can toggle it) const [isDocumentsSidebarOpen, setIsDocumentsSidebarOpen] = useAtom(documentsSidebarOpenAtom); @@ -152,9 +152,6 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid } }, [setIsDocumentsSidebarOpen]); - // Announcements sidebar state - const [isAnnouncementsSidebarOpen, setIsAnnouncementsSidebarOpen] = useState(false); - // Search space dialog state const [isCreateSearchSpaceDialogOpen, setIsCreateSearchSpaceDialogOpen] = useState(false); @@ -271,12 +268,8 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid }, [pendingNewChat, params?.chat_id, router, searchSpaceId, resetCurrentThread]); // Reset transient slide-out panels when switching search spaces. - // Some browsers can retain overlay/backdrop state across route transitions. useEffect(() => { - setIsInboxSidebarOpen(false); - setIsAllSharedChatsSidebarOpen(false); - setIsAllPrivateChatsSidebarOpen(false); - setIsAnnouncementsSidebarOpen(false); + setActiveSlideoutPanel(null); }, [searchSpaceId]); const searchSpaces: SearchSpace[] = useMemo(() => { @@ -486,14 +479,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid const handleNavItemClick = useCallback( (item: NavItem) => { if (item.url === "#inbox") { - setIsInboxSidebarOpen((prev) => { - if (!prev) { - setIsAllSharedChatsSidebarOpen(false); - setIsAllPrivateChatsSidebarOpen(false); - setIsAnnouncementsSidebarOpen(false); - } - return !prev; - }); + setActiveSlideoutPanel((prev) => (prev === "inbox" ? null : "inbox")); return; } if (item.url === "#documents") { @@ -501,20 +487,14 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid if (!isDocumentsSidebarOpen) { setIsDocumentsSidebarOpen(true); setIsRightPanelCollapsed(false); - setIsInboxSidebarOpen(false); - setIsAllSharedChatsSidebarOpen(false); - setIsAllPrivateChatsSidebarOpen(false); - setIsAnnouncementsSidebarOpen(false); + setActiveSlideoutPanel(null); } else { setIsRightPanelCollapsed((prev) => !prev); } } else { setIsDocumentsSidebarOpen((prev) => { if (!prev) { - setIsInboxSidebarOpen(false); - setIsAllSharedChatsSidebarOpen(false); - setIsAllPrivateChatsSidebarOpen(false); - setIsAnnouncementsSidebarOpen(false); + setActiveSlideoutPanel(null); } return !prev; }); @@ -522,14 +502,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid return; } if (item.url === "#announcements") { - setIsAnnouncementsSidebarOpen((prev) => { - if (!prev) { - setIsInboxSidebarOpen(false); - setIsAllSharedChatsSidebarOpen(false); - setIsAllPrivateChatsSidebarOpen(false); - } - return !prev; - }); + setActiveSlideoutPanel((prev) => (prev === "announcements" ? null : "announcements")); return; } router.push(item.url); @@ -628,25 +601,11 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid }, [router]); const handleViewAllSharedChats = useCallback(() => { - setIsAllSharedChatsSidebarOpen((prev) => { - if (!prev) { - setIsAllPrivateChatsSidebarOpen(false); - setIsInboxSidebarOpen(false); - setIsAnnouncementsSidebarOpen(false); - } - return !prev; - }); + setActiveSlideoutPanel((prev) => (prev === "shared" ? null : "shared")); }, []); const handleViewAllPrivateChats = useCallback(() => { - setIsAllPrivateChatsSidebarOpen((prev) => { - if (!prev) { - setIsAllSharedChatsSidebarOpen(false); - setIsInboxSidebarOpen(false); - setIsAnnouncementsSidebarOpen(false); - } - return !prev; - }); + setActiveSlideoutPanel((prev) => (prev === "private" ? null : "private")); }, []); // Delete handlers @@ -752,9 +711,10 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid setTheme={setTheme} isChatPage={isChatPage} isLoadingChats={isLoadingThreads} + activeSlideoutPanel={activeSlideoutPanel} + onSlideoutPanelChange={setActiveSlideoutPanel} inbox={{ isOpen: isInboxSidebarOpen, - onOpenChange: setIsInboxSidebarOpen, totalUnreadCount, comments: { items: commentsInbox.inboxItems, @@ -777,18 +737,10 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid markAllAsRead: statusInbox.markAllAsRead, }, }} - announcementsPanel={{ - open: isAnnouncementsSidebarOpen, - onOpenChange: setIsAnnouncementsSidebarOpen, - }} allSharedChatsPanel={{ - open: isAllSharedChatsSidebarOpen, - onOpenChange: setIsAllSharedChatsSidebarOpen, searchSpaceId, }} allPrivateChatsPanel={{ - open: isAllPrivateChatsSidebarOpen, - onOpenChange: setIsAllPrivateChatsSidebarOpen, searchSpaceId, }} documentsPanel={{ diff --git a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx index e99bae22d..28c2a0bc7 100644 --- a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx +++ b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx @@ -1,6 +1,7 @@ "use client"; -import { useMemo, useState } from "react"; +import { AnimatePresence, motion } from "motion/react"; +import { useCallback, useMemo, useState } from "react"; import { TooltipProvider } from "@/components/ui/tooltip"; import type { InboxItem } from "@/hooks/use-inbox"; import { useIsMobile } from "@/hooks/use-mobile"; @@ -12,15 +13,16 @@ import { Header } from "../header"; import { IconRail } from "../icon-rail"; import { RightPanel } from "../right-panel/RightPanel"; import { - AllPrivateChatsSidebar, - AllSharedChatsSidebar, - AnnouncementsSidebar, + AllPrivateChatsSidebarContent, + AllSharedChatsSidebarContent, + AnnouncementsSidebarContent, DocumentsSidebar, - InboxSidebar, + InboxSidebarContent, MobileSidebar, MobileSidebarTrigger, Sidebar, } from "../sidebar"; +import { SidebarSlideOutPanel } from "../sidebar/SidebarSlideOutPanel"; // Per-tab data source interface TabDataSource { @@ -34,10 +36,11 @@ interface TabDataSource { markAllAsRead: () => Promise; } +export type ActiveSlideoutPanel = "inbox" | "shared" | "private" | "announcements" | null; + // Inbox-related props — per-tab data sources with independent loading/pagination interface InboxProps { isOpen: boolean; - onOpenChange: (open: boolean) => void; totalUnreadCount: number; comments: TabDataSource; status: TabDataSource; @@ -75,22 +78,17 @@ interface LayoutShellProps { isChatPage?: boolean; children: React.ReactNode; className?: string; + // Unified slide-out panel state + activeSlideoutPanel?: ActiveSlideoutPanel; + onSlideoutPanelChange?: (panel: ActiveSlideoutPanel) => void; // Inbox props inbox?: InboxProps; - announcementsPanel?: { - open: boolean; - onOpenChange: (open: boolean) => void; - }; isLoadingChats?: boolean; // All chats panel props allSharedChatsPanel?: { - open: boolean; - onOpenChange: (open: boolean) => void; searchSpaceId: string; }; allPrivateChatsPanel?: { - open: boolean; - onOpenChange: (open: boolean) => void; searchSpaceId: string; }; documentsPanel?: { @@ -133,8 +131,9 @@ export function LayoutShell({ isChatPage = false, children, className, + activeSlideoutPanel = null, + onSlideoutPanelChange, inbox, - announcementsPanel, isLoadingChats = false, allSharedChatsPanel, allPrivateChatsPanel, @@ -155,6 +154,26 @@ export function LayoutShell({ [isCollapsed, setIsCollapsed, toggleCollapsed, sidebarWidth] ); + const closeSlideout = useCallback( + (open: boolean) => { + if (!open) onSlideoutPanelChange?.(null); + }, + [onSlideoutPanelChange] + ); + + const anySlideOutOpen = activeSlideoutPanel !== null; + + const panelAriaLabel = + activeSlideoutPanel === "inbox" + ? "Inbox" + : activeSlideoutPanel === "shared" + ? "Shared Chats" + : activeSlideoutPanel === "private" + ? "Private Chats" + : activeSlideoutPanel === "announcements" + ? "Announcements" + : "Panel"; + // Mobile layout if (isMobile) { return ( @@ -171,8 +190,6 @@ export function LayoutShell({ searchSpaces={searchSpaces} activeSearchSpaceId={activeSearchSpaceId} onSearchSpaceSelect={onSearchSpaceSelect} - onSearchSpaceDelete={onSearchSpaceDelete} - onSearchSpaceSettings={onSearchSpaceSettings} onAddSearchSpace={onAddSearchSpace} searchSpace={searchSpace} navItems={navItems} @@ -187,8 +204,8 @@ export function LayoutShell({ onChatArchive={onChatArchive} onViewAllSharedChats={onViewAllSharedChats} onViewAllPrivateChats={onViewAllPrivateChats} - isSharedChatsPanelOpen={allSharedChatsPanel?.open} - isPrivateChatsPanelOpen={allPrivateChatsPanel?.open} + isSharedChatsPanelOpen={activeSlideoutPanel === "shared"} + isPrivateChatsPanelOpen={activeSlideoutPanel === "private"} user={user} onSettings={onSettings} onManageMembers={onManageMembers} @@ -200,70 +217,98 @@ export function LayoutShell({ isLoadingChats={isLoadingChats} /> -
- {children} -
+
+ {children} +
- {/* Mobile Inbox Sidebar - only render when open to avoid scroll blocking */} - {inbox?.isOpen && ( - setMobileMenuOpen(false)} - /> - )} + {/* Mobile unified slide-out panel */} + + + {activeSlideoutPanel === "inbox" && inbox && ( + + closeSlideout(open)} + comments={inbox.comments} + status={inbox.status} + totalUnreadCount={inbox.totalUnreadCount} + onCloseMobileSidebar={() => setMobileMenuOpen(false)} + /> + + )} + {activeSlideoutPanel === "announcements" && ( + + closeSlideout(open)} + onCloseMobileSidebar={() => setMobileMenuOpen(false)} + /> + + )} + {activeSlideoutPanel === "shared" && allSharedChatsPanel && ( + + closeSlideout(open)} + searchSpaceId={allSharedChatsPanel.searchSpaceId} + onCloseMobileSidebar={() => setMobileMenuOpen(false)} + /> + + )} + {activeSlideoutPanel === "private" && allPrivateChatsPanel && ( + + closeSlideout(open)} + searchSpaceId={allPrivateChatsPanel.searchSpaceId} + onCloseMobileSidebar={() => setMobileMenuOpen(false)} + /> + + )} + + - {/* Mobile Documents Sidebar - slide-out panel */} - {documentsPanel && ( - - )} - - {/* Mobile Announcements Sidebar */} - {announcementsPanel?.open && ( - setMobileMenuOpen(false)} - /> - )} - - {/* Mobile All Shared Chats - slide-out panel */} - {allSharedChatsPanel && ( - setMobileMenuOpen(false)} - /> - )} - - {/* Mobile All Private Chats - slide-out panel */} - {allPrivateChatsPanel && ( - setMobileMenuOpen(false)} - /> - )} + {/* Mobile Documents Sidebar - separate (not part of slide-out group) */} + {documentsPanel && ( + + )} ); } - const anySlideOutOpen = - inbox?.isOpen || - announcementsPanel?.open || - allSharedChatsPanel?.open || - allPrivateChatsPanel?.open; - // Desktop layout return ( @@ -305,8 +350,8 @@ export function LayoutShell({ onChatArchive={onChatArchive} onViewAllSharedChats={onViewAllSharedChats} onViewAllPrivateChats={onViewAllPrivateChats} - isSharedChatsPanelOpen={allSharedChatsPanel?.open} - isPrivateChatsPanelOpen={allPrivateChatsPanel?.open} + isSharedChatsPanelOpen={activeSlideoutPanel === "shared"} + isPrivateChatsPanelOpen={activeSlideoutPanel === "private"} user={user} onSettings={onSettings} onManageMembers={onManageMembers} @@ -324,39 +369,76 @@ export function LayoutShell({ isResizing={isResizing} /> - {/* Slide-out panels render as siblings next to the sidebar */} - {inbox && ( - - )} - - {announcementsPanel && ( - - )} - - {allSharedChatsPanel && ( - - )} - - {allPrivateChatsPanel && ( - - )} + {/* Unified slide-out panel — shell stays open, content cross-fades */} + + + {activeSlideoutPanel === "inbox" && inbox && ( + + closeSlideout(open)} + comments={inbox.comments} + status={inbox.status} + totalUnreadCount={inbox.totalUnreadCount} + /> + + )} + {activeSlideoutPanel === "announcements" && ( + + closeSlideout(open)} + /> + + )} + {activeSlideoutPanel === "shared" && allSharedChatsPanel && ( + + closeSlideout(open)} + searchSpaceId={allSharedChatsPanel.searchSpaceId} + /> + + )} + {activeSlideoutPanel === "private" && allPrivateChatsPanel && ( + + closeSlideout(open)} + searchSpaceId={allPrivateChatsPanel.searchSpaceId} + /> + + )} + + {/* Resize handle — negative margins eat the flex gap so spacing stays unchanged */} diff --git a/surfsense_web/components/layout/ui/sidebar/AllPrivateChatsSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/AllPrivateChatsSidebar.tsx index 1e857ec97..fc5fb3873 100644 --- a/surfsense_web/components/layout/ui/sidebar/AllPrivateChatsSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/AllPrivateChatsSidebar.tsx @@ -16,7 +16,7 @@ import { } from "lucide-react"; import { useParams, useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useMemo, useRef, useState } from "react"; import { toast } from "sonner"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/animated-tabs"; import { Button } from "@/components/ui/button"; @@ -50,19 +50,21 @@ import { import { cn } from "@/lib/utils"; import { SidebarSlideOutPanel } from "./SidebarSlideOutPanel"; -interface AllPrivateChatsSidebarProps { - open: boolean; +export interface AllPrivateChatsSidebarContentProps { onOpenChange: (open: boolean) => void; searchSpaceId: string; onCloseMobileSidebar?: () => void; } -export function AllPrivateChatsSidebar({ - open, +interface AllPrivateChatsSidebarProps extends AllPrivateChatsSidebarContentProps { + open: boolean; +} + +export function AllPrivateChatsSidebarContent({ onOpenChange, searchSpaceId, onCloseMobileSidebar, -}: AllPrivateChatsSidebarProps) { +}: AllPrivateChatsSidebarContentProps) { const t = useTranslations("sidebar"); const router = useRouter(); const params = useParams(); @@ -96,16 +98,6 @@ export function AllPrivateChatsSidebar({ const isSearchMode = !!debouncedSearchQuery.trim(); - useEffect(() => { - const handleEscape = (e: KeyboardEvent) => { - if (e.key === "Escape" && open) { - onOpenChange(false); - } - }; - document.addEventListener("keydown", handleEscape); - return () => document.removeEventListener("keydown", handleEscape); - }, [open, onOpenChange]); - const { data: threadsData, error: threadsError, @@ -113,7 +105,7 @@ export function AllPrivateChatsSidebar({ } = useQuery({ queryKey: ["all-threads", searchSpaceId], queryFn: () => fetchThreads(Number(searchSpaceId)), - enabled: !!searchSpaceId && open && !isSearchMode, + enabled: !!searchSpaceId && !isSearchMode, }); const { @@ -123,7 +115,7 @@ export function AllPrivateChatsSidebar({ } = useQuery({ queryKey: ["search-threads", searchSpaceId, debouncedSearchQuery], queryFn: () => searchThreads(Number(searchSpaceId), debouncedSearchQuery.trim()), - enabled: !!searchSpaceId && open && isSearchMode, + enabled: !!searchSpaceId && isSearchMode, }); // Filter to only private chats (PRIVATE visibility or no visibility set) @@ -250,11 +242,7 @@ export function AllPrivateChatsSidebar({ const archivedCount = archivedChats.length; return ( - + <>
{isMobile && ( @@ -530,6 +518,29 @@ export function AllPrivateChatsSidebar({ + + ); +} + +export function AllPrivateChatsSidebar({ + open, + onOpenChange, + searchSpaceId, + onCloseMobileSidebar, +}: AllPrivateChatsSidebarProps) { + const t = useTranslations("sidebar"); + + return ( + + ); } diff --git a/surfsense_web/components/layout/ui/sidebar/AllSharedChatsSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/AllSharedChatsSidebar.tsx index 01fa1714f..03673955a 100644 --- a/surfsense_web/components/layout/ui/sidebar/AllSharedChatsSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/AllSharedChatsSidebar.tsx @@ -16,7 +16,7 @@ import { } from "lucide-react"; import { useParams, useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useMemo, useRef, useState } from "react"; import { toast } from "sonner"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/animated-tabs"; import { Button } from "@/components/ui/button"; @@ -50,19 +50,21 @@ import { import { cn } from "@/lib/utils"; import { SidebarSlideOutPanel } from "./SidebarSlideOutPanel"; -interface AllSharedChatsSidebarProps { - open: boolean; +export interface AllSharedChatsSidebarContentProps { onOpenChange: (open: boolean) => void; searchSpaceId: string; onCloseMobileSidebar?: () => void; } -export function AllSharedChatsSidebar({ - open, +interface AllSharedChatsSidebarProps extends AllSharedChatsSidebarContentProps { + open: boolean; +} + +export function AllSharedChatsSidebarContent({ onOpenChange, searchSpaceId, onCloseMobileSidebar, -}: AllSharedChatsSidebarProps) { +}: AllSharedChatsSidebarContentProps) { const t = useTranslations("sidebar"); const router = useRouter(); const params = useParams(); @@ -96,16 +98,6 @@ export function AllSharedChatsSidebar({ const isSearchMode = !!debouncedSearchQuery.trim(); - useEffect(() => { - const handleEscape = (e: KeyboardEvent) => { - if (e.key === "Escape" && open) { - onOpenChange(false); - } - }; - document.addEventListener("keydown", handleEscape); - return () => document.removeEventListener("keydown", handleEscape); - }, [open, onOpenChange]); - const { data: threadsData, error: threadsError, @@ -113,7 +105,7 @@ export function AllSharedChatsSidebar({ } = useQuery({ queryKey: ["all-threads", searchSpaceId], queryFn: () => fetchThreads(Number(searchSpaceId)), - enabled: !!searchSpaceId && open && !isSearchMode, + enabled: !!searchSpaceId && !isSearchMode, }); const { @@ -123,7 +115,7 @@ export function AllSharedChatsSidebar({ } = useQuery({ queryKey: ["search-threads", searchSpaceId, debouncedSearchQuery], queryFn: () => searchThreads(Number(searchSpaceId), debouncedSearchQuery.trim()), - enabled: !!searchSpaceId && open && isSearchMode, + enabled: !!searchSpaceId && isSearchMode, }); // Filter to only shared chats (SEARCH_SPACE visibility) @@ -250,11 +242,7 @@ export function AllSharedChatsSidebar({ const archivedCount = archivedChats.length; return ( - + <>
{isMobile && ( @@ -530,6 +518,29 @@ export function AllSharedChatsSidebar({ + + ); +} + +export function AllSharedChatsSidebar({ + open, + onOpenChange, + searchSpaceId, + onCloseMobileSidebar, +}: AllSharedChatsSidebarProps) { + const t = useTranslations("sidebar"); + + return ( + + ); } diff --git a/surfsense_web/components/layout/ui/sidebar/AnnouncementsSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/AnnouncementsSidebar.tsx index 2d99edc5e..6a1233537 100644 --- a/surfsense_web/components/layout/ui/sidebar/AnnouncementsSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/AnnouncementsSidebar.tsx @@ -9,26 +9,27 @@ import { useAnnouncements } from "@/hooks/use-announcements"; import { useMediaQuery } from "@/hooks/use-media-query"; import { SidebarSlideOutPanel } from "./SidebarSlideOutPanel"; -interface AnnouncementsSidebarProps { - open: boolean; +export interface AnnouncementsSidebarContentProps { onOpenChange: (open: boolean) => void; onCloseMobileSidebar?: () => void; } -export function AnnouncementsSidebar({ - open, +interface AnnouncementsSidebarProps extends AnnouncementsSidebarContentProps { + open: boolean; +} + +export function AnnouncementsSidebarContent({ onOpenChange, onCloseMobileSidebar, -}: AnnouncementsSidebarProps) { +}: AnnouncementsSidebarContentProps) { const isMobile = !useMediaQuery("(min-width: 640px)"); const { announcements, markAllRead } = useAnnouncements(); useEffect(() => { - if (!open) return; markAllRead(); - }, [open, markAllRead]); + }, [markAllRead]); - const body = ( + return (
@@ -65,10 +66,19 @@ export function AnnouncementsSidebar({
); +} +export function AnnouncementsSidebar({ + open, + onOpenChange, + onCloseMobileSidebar, +}: AnnouncementsSidebarProps) { return ( - {body} + ); } diff --git a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx index f888c5299..2d9820bf1 100644 --- a/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/InboxSidebar.tsx @@ -140,8 +140,7 @@ interface TabDataSource { markAllAsRead: () => Promise; } -interface InboxSidebarProps { - open: boolean; +export interface InboxSidebarContentProps { onOpenChange: (open: boolean) => void; comments: TabDataSource; status: TabDataSource; @@ -149,14 +148,17 @@ interface InboxSidebarProps { onCloseMobileSidebar?: () => void; } -export function InboxSidebar({ - open, +interface InboxSidebarProps extends InboxSidebarContentProps { + open: boolean; +} + +export function InboxSidebarContent({ onOpenChange, comments, status, totalUnreadCount, onCloseMobileSidebar, -}: InboxSidebarProps) { +}: InboxSidebarContentProps) { const t = useTranslations("sidebar"); const router = useRouter(); const params = useParams(); @@ -199,7 +201,7 @@ export function InboxSidebar({ }, }), staleTime: 30 * 1000, - enabled: isSearchMode && open, + enabled: isSearchMode, }); useEffect(() => { @@ -207,23 +209,13 @@ export function InboxSidebar({ }, []); useEffect(() => { - const handleEscape = (e: KeyboardEvent) => { - if (e.key === "Escape" && open) { - onOpenChange(false); - } - }; - document.addEventListener("keydown", handleEscape); - return () => document.removeEventListener("keydown", handleEscape); - }, [open, onOpenChange]); - - useEffect(() => { - if (!open || !isMobile) return; + if (!isMobile) return; const originalOverflow = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = originalOverflow; }; - }, [open, isMobile]); + }, [isMobile]); useEffect(() => { if (activeTab !== "status") { @@ -239,7 +231,7 @@ export function InboxSidebar({ queryKey: cacheKeys.notifications.sourceTypes(searchSpaceId), queryFn: () => notificationsApiService.getSourceTypes(searchSpaceId ?? undefined), staleTime: 60 * 1000, - enabled: open && activeTab === "status", + enabled: activeTab === "status", }); const statusSourceOptions = useMemo(() => { @@ -327,7 +319,7 @@ export function InboxSidebar({ // Infinite scroll — uses active tab's pagination useEffect(() => { - if (!activeSource.hasMore || activeSource.loadingMore || !open || isSearchMode) return; + if (!activeSource.hasMore || activeSource.loadingMore || isSearchMode) return; const observer = new IntersectionObserver( (entries) => { @@ -347,7 +339,7 @@ export function InboxSidebar({ } return () => observer.disconnect(); - }, [activeSource.hasMore, activeSource.loadingMore, activeSource.loadMore, open, isSearchMode]); + }, [activeSource.hasMore, activeSource.loadingMore, activeSource.loadMore, isSearchMode]); const handleItemClick = useCallback( async (item: InboxItem) => { @@ -522,7 +514,7 @@ export function InboxSidebar({ const isLoading = isSearchMode ? isSearchLoading : activeSource.loading; - const inboxContent = ( + return ( <>
@@ -546,7 +538,7 @@ export function InboxSidebar({ @@ -790,7 +782,7 @@ export function InboxSidebar({
); +} + +export function InboxSidebar({ + open, + onOpenChange, + comments, + status, + totalUnreadCount, + onCloseMobileSidebar, +}: InboxSidebarProps) { + const t = useTranslations("sidebar"); return ( - {inboxContent} + ); } diff --git a/surfsense_web/components/layout/ui/sidebar/index.ts b/surfsense_web/components/layout/ui/sidebar/index.ts index 4da08ef50..33023c9e8 100644 --- a/surfsense_web/components/layout/ui/sidebar/index.ts +++ b/surfsense_web/components/layout/ui/sidebar/index.ts @@ -1,9 +1,9 @@ -export { AllPrivateChatsSidebar } from "./AllPrivateChatsSidebar"; -export { AllSharedChatsSidebar } from "./AllSharedChatsSidebar"; -export { AnnouncementsSidebar } from "./AnnouncementsSidebar"; +export { AllPrivateChatsSidebar, AllPrivateChatsSidebarContent } from "./AllPrivateChatsSidebar"; +export { AllSharedChatsSidebar, AllSharedChatsSidebarContent } from "./AllSharedChatsSidebar"; +export { AnnouncementsSidebar, AnnouncementsSidebarContent } from "./AnnouncementsSidebar"; export { ChatListItem } from "./ChatListItem"; export { DocumentsSidebar } from "./DocumentsSidebar"; -export { InboxSidebar } from "./InboxSidebar"; +export { InboxSidebar, InboxSidebarContent } from "./InboxSidebar"; export { MobileSidebar, MobileSidebarTrigger } from "./MobileSidebar"; export { NavSection } from "./NavSection"; export { PageUsageDisplay } from "./PageUsageDisplay";