diff --git a/surfsense_web/components/layout/providers/FreeLayoutDataProvider.tsx b/surfsense_web/components/layout/providers/FreeLayoutDataProvider.tsx index 1c5d7af6e..9e2a99f2a 100644 --- a/surfsense_web/components/layout/providers/FreeLayoutDataProvider.tsx +++ b/surfsense_web/components/layout/providers/FreeLayoutDataProvider.tsx @@ -3,7 +3,7 @@ import { Inbox, LibraryBig } from "lucide-react"; import { useRouter } from "next/navigation"; import type { ReactNode } from "react"; -import { Fragment, useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { useAnonymousMode } from "@/contexts/anonymous-mode"; import { useLoginGate } from "@/contexts/login-gate"; import { useAnnouncements } from "@/hooks/use-announcements"; @@ -110,15 +110,13 @@ export function FreeLayoutDataProvider({ children }: FreeLayoutDataProviderProps navItems={navItems} onNavItemClick={handleNavItemClick} chats={[]} - sharedChats={[]} activeChatId={null} onNewChat={resetChat} onChatSelect={handleChatSelect} onChatRename={gatedAction("rename chats")} onChatDelete={gatedAction("delete chats")} onChatArchive={gatedAction("archive chats")} - onViewAllSharedChats={gatedAction("view shared chats")} - onViewAllPrivateChats={gatedAction("view chat history")} + onViewAllChats={gatedAction("view chat history")} user={{ email: "Guest", name: "Guest", @@ -137,7 +135,7 @@ export function FreeLayoutDataProvider({ children }: FreeLayoutDataProviderProps onOpenChange: setIsDocsSidebarOpen, }} > - {children} + {children} ); } diff --git a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx index 34fd15e3b..57a295cb5 100644 --- a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx +++ b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx @@ -121,7 +121,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid }); // Unified slide-out panel state (only one can be open at a time) - type SlideoutPanel = "inbox" | "shared" | "private" | null; + type SlideoutPanel = "inbox" | "chats" | null; const [activeSlideoutPanel, setActiveSlideoutPanel] = useState(null); const isInboxSidebarOpen = activeSlideoutPanel === "inbox"; @@ -304,34 +304,17 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid }); }, [currentChatId, searchSpaceId, threadsData?.threads, syncChatTab]); - // Transform and split chats into private and shared based on visibility - const { myChats, sharedChats } = useMemo(() => { - if (!threadsData?.threads) return { myChats: [], sharedChats: [] }; + const chats = useMemo(() => { + if (!threadsData?.threads) return []; - const privateChats: ChatItem[] = []; - const sharedChatsList: ChatItem[] = []; - - for (const thread of threadsData.threads) { - const chatItem: ChatItem = { - id: thread.id, - name: thread.title || `Chat ${thread.id}`, - url: `/dashboard/${searchSpaceId}/new-chat/${thread.id}`, - visibility: thread.visibility, - isOwnThread: thread.is_own_thread, - archived: thread.archived, - }; - - // Split based on visibility, not ownership: - // - PRIVATE chats go to "Private Chats" section - // - SEARCH_SPACE chats go to "Shared Chats" section - if (thread.visibility === "SEARCH_SPACE") { - sharedChatsList.push(chatItem); - } else { - privateChats.push(chatItem); - } - } - - return { myChats: privateChats, sharedChats: sharedChatsList }; + return threadsData.threads.map((thread) => ({ + id: thread.id, + name: thread.title || `Chat ${thread.id}`, + url: `/dashboard/${searchSpaceId}/new-chat/${thread.id}`, + visibility: thread.visibility, + isOwnThread: thread.is_own_thread, + archived: thread.archived, + })); }, [threadsData, searchSpaceId]); // Navigation items @@ -599,12 +582,8 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid } }, [router]); - const handleViewAllSharedChats = useCallback(() => { - setActiveSlideoutPanel((prev) => (prev === "shared" ? null : "shared")); - }, []); - - const handleViewAllPrivateChats = useCallback(() => { - setActiveSlideoutPanel((prev) => (prev === "private" ? null : "private")); + const handleViewAllChats = useCallback(() => { + setActiveSlideoutPanel((prev) => (prev === "chats" ? null : "chats")); }, []); // Delete handlers @@ -695,16 +674,14 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid searchSpace={activeSearchSpace} navItems={navItems} onNavItemClick={handleNavItemClick} - chats={myChats} - sharedChats={sharedChats} + chats={chats} activeChatId={currentChatId} onNewChat={handleNewChat} onChatSelect={handleChatSelect} onChatRename={handleChatRename} onChatDelete={handleChatDelete} onChatArchive={handleChatArchive} - onViewAllSharedChats={handleViewAllSharedChats} - onViewAllPrivateChats={handleViewAllPrivateChats} + onViewAllChats={handleViewAllChats} user={{ email: user?.email || "", name: user?.display_name || user?.email?.split("@")[0], @@ -759,10 +736,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid markAllAsRead: statusInbox.markAllAsRead, }, }} - allSharedChatsPanel={{ - searchSpaceId, - }} - allPrivateChatsPanel={{ + allChatsPanel={{ searchSpaceId, }} documentsPanel={{ diff --git a/surfsense_web/components/layout/types/layout.types.ts b/surfsense_web/components/layout/types/layout.types.ts index 720aaecf1..1bb0a089e 100644 --- a/surfsense_web/components/layout/types/layout.types.ts +++ b/surfsense_web/components/layout/types/layout.types.ts @@ -70,8 +70,7 @@ export interface ChatsSectionProps { activeChatId?: number | null; onChatSelect: (chat: ChatItem) => void; onChatDelete?: (chat: ChatItem) => void; - onViewAllSharedChats?: () => void; - onViewAllPrivateChats?: () => void; + onViewAllChats?: () => void; searchSpaceId?: string; } @@ -96,13 +95,11 @@ export interface SidebarProps { searchSpaceId?: string; navItems: NavItem[]; chats: ChatItem[]; - sharedChats?: ChatItem[]; activeChatId?: number | null; onNewChat: () => void; onChatSelect: (chat: ChatItem) => void; onChatDelete?: (chat: ChatItem) => void; - onViewAllSharedChats?: () => void; - onViewAllPrivateChats?: () => void; + onViewAllChats?: () => void; user: User; theme?: string; onSettings?: () => void; diff --git a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx index 91d85cc1e..8823f573c 100644 --- a/surfsense_web/components/layout/ui/shell/LayoutShell.tsx +++ b/surfsense_web/components/layout/ui/shell/LayoutShell.tsx @@ -27,8 +27,7 @@ import { RightPanelToggleButton, } from "../right-panel/RightPanel"; import { - AllPrivateChatsSidebarContent, - AllSharedChatsSidebarContent, + AllChatsSidebarContent, DocumentsSidebar, InboxSidebarContent, MobileSidebar, @@ -94,7 +93,7 @@ interface TabDataSource { markAllAsRead: () => Promise; } -export type ActiveSlideoutPanel = "inbox" | "shared" | "private" | null; +export type ActiveSlideoutPanel = "inbox" | "chats" | null; // Inbox-related props — per-tab data sources with independent loading/pagination interface InboxProps { @@ -115,15 +114,13 @@ interface LayoutShellProps { navItems: NavItem[]; onNavItemClick?: (item: NavItem) => void; chats: ChatItem[]; - sharedChats?: ChatItem[]; activeChatId?: number | null; onNewChat: () => void; onChatSelect: (chat: ChatItem) => void; onChatRename?: (chat: ChatItem) => void; onChatDelete?: (chat: ChatItem) => void; onChatArchive?: (chat: ChatItem) => void; - onViewAllSharedChats?: () => void; - onViewAllPrivateChats?: () => void; + onViewAllChats?: () => void; user: User; onSettings?: () => void; onManageMembers?: () => void; @@ -148,10 +145,7 @@ interface LayoutShellProps { inbox?: InboxProps; isLoadingChats?: boolean; // All chats panel props - allSharedChatsPanel?: { - searchSpaceId: string; - }; - allPrivateChatsPanel?: { + allChatsPanel?: { searchSpaceId: string; }; documentsPanel?: { @@ -226,15 +220,13 @@ export function LayoutShell({ navItems, onNavItemClick, chats, - sharedChats, activeChatId, onNewChat, onChatSelect, onChatRename, onChatDelete, onChatArchive, - onViewAllSharedChats, - onViewAllPrivateChats, + onViewAllChats, user, onSettings, onManageMembers, @@ -256,8 +248,7 @@ export function LayoutShell({ onSlideoutPanelChange, inbox, isLoadingChats = false, - allSharedChatsPanel, - allPrivateChatsPanel, + allChatsPanel, documentsPanel, onTabSwitch, }: LayoutShellProps) { @@ -288,13 +279,7 @@ export function LayoutShell({ const anySlideOutOpen = activeSlideoutPanel !== null; const panelAriaLabel = - activeSlideoutPanel === "inbox" - ? "Inbox" - : activeSlideoutPanel === "shared" - ? "Shared Chats" - : activeSlideoutPanel === "private" - ? "Private Chats" - : "Panel"; + activeSlideoutPanel === "inbox" ? "Inbox" : activeSlideoutPanel === "chats" ? "Chats" : "Panel"; // Mobile layout if (isMobile) { @@ -317,17 +302,14 @@ export function LayoutShell({ navItems={navItems} onNavItemClick={onNavItemClick} chats={chats} - sharedChats={sharedChats} activeChatId={activeChatId} onNewChat={onNewChat} onChatSelect={onChatSelect} onChatRename={onChatRename} onChatDelete={onChatDelete} onChatArchive={onChatArchive} - onViewAllSharedChats={onViewAllSharedChats} - onViewAllPrivateChats={onViewAllPrivateChats} - isSharedChatsPanelOpen={activeSlideoutPanel === "shared"} - isPrivateChatsPanelOpen={activeSlideoutPanel === "private"} + onViewAllChats={onViewAllChats} + isChatsPanelOpen={activeSlideoutPanel === "chats"} user={user} onSettings={onSettings} onManageMembers={onManageMembers} @@ -379,34 +361,18 @@ export function LayoutShell({ /> )} - {activeSlideoutPanel === "shared" && allSharedChatsPanel && ( + {activeSlideoutPanel === "chats" && allChatsPanel && ( - closeSlideout(open)} - searchSpaceId={allSharedChatsPanel.searchSpaceId} - onCloseMobileSidebar={() => setMobileMenuOpen(false)} - /> - - )} - {activeSlideoutPanel === "private" && allPrivateChatsPanel && ( - - closeSlideout(open)} - searchSpaceId={allPrivateChatsPanel.searchSpaceId} + searchSpaceId={allChatsPanel.searchSpaceId} onCloseMobileSidebar={() => setMobileMenuOpen(false)} /> @@ -478,17 +444,14 @@ export function LayoutShell({ navItems={navItems} onNavItemClick={onNavItemClick} chats={chats} - sharedChats={sharedChats} activeChatId={activeChatId} onNewChat={onNewChat} onChatSelect={onChatSelect} onChatRename={onChatRename} onChatDelete={onChatDelete} onChatArchive={onChatArchive} - onViewAllSharedChats={onViewAllSharedChats} - onViewAllPrivateChats={onViewAllPrivateChats} - isSharedChatsPanelOpen={activeSlideoutPanel === "shared"} - isPrivateChatsPanelOpen={activeSlideoutPanel === "private"} + onViewAllChats={onViewAllChats} + isChatsPanelOpen={activeSlideoutPanel === "chats"} user={user} onSettings={onSettings} onManageMembers={onManageMembers} @@ -554,33 +517,18 @@ export function LayoutShell({ /> )} - {activeSlideoutPanel === "shared" && allSharedChatsPanel && ( + {activeSlideoutPanel === "chats" && allChatsPanel && ( - closeSlideout(open)} - searchSpaceId={allSharedChatsPanel.searchSpaceId} - /> - - )} - {activeSlideoutPanel === "private" && allPrivateChatsPanel && ( - - closeSlideout(open)} - searchSpaceId={allPrivateChatsPanel.searchSpaceId} + searchSpaceId={allChatsPanel.searchSpaceId} /> )} diff --git a/surfsense_web/components/layout/ui/sidebar/AllPrivateChatsSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/AllChatsSidebar.tsx similarity index 92% rename from surfsense_web/components/layout/ui/sidebar/AllPrivateChatsSidebar.tsx rename to surfsense_web/components/layout/ui/sidebar/AllChatsSidebar.tsx index 762a210a9..bb5c0db39 100644 --- a/surfsense_web/components/layout/ui/sidebar/AllPrivateChatsSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/AllChatsSidebar.tsx @@ -12,6 +12,7 @@ import { Search, Trash2, User, + Users, X, } from "lucide-react"; import { useParams, useRouter } from "next/navigation"; @@ -52,21 +53,21 @@ import { formatThreadTimestamp } from "@/lib/format-date"; import { cn } from "@/lib/utils"; import { SidebarSlideOutPanel } from "./SidebarSlideOutPanel"; -export interface AllPrivateChatsSidebarContentProps { +export interface AllChatsSidebarContentProps { onOpenChange: (open: boolean) => void; searchSpaceId: string; onCloseMobileSidebar?: () => void; } -interface AllPrivateChatsSidebarProps extends AllPrivateChatsSidebarContentProps { +interface AllChatsSidebarProps extends AllChatsSidebarContentProps { open: boolean; } -export function AllPrivateChatsSidebarContent({ +export function AllChatsSidebarContent({ onOpenChange, searchSpaceId, onCloseMobileSidebar, -}: AllPrivateChatsSidebarContentProps) { +}: AllChatsSidebarContentProps) { const t = useTranslations("sidebar"); const router = useRouter(); const params = useParams(); @@ -122,28 +123,20 @@ export function AllPrivateChatsSidebarContent({ enabled: !!searchSpaceId && isSearchMode, }); - // Filter to only private chats (PRIVATE visibility or no visibility set) const { activeChats, archivedChats } = useMemo(() => { if (isSearchMode) { - const privateSearchResults = (searchData ?? []).filter( - (thread) => thread.visibility !== "SEARCH_SPACE" - ); return { - activeChats: privateSearchResults.filter((t) => !t.archived), - archivedChats: privateSearchResults.filter((t) => t.archived), + activeChats: (searchData ?? []).filter((t) => !t.archived), + archivedChats: (searchData ?? []).filter((t) => t.archived), }; } if (!threadsData) return { activeChats: [], archivedChats: [] }; - const activePrivate = threadsData.threads.filter( - (thread) => thread.visibility !== "SEARCH_SPACE" - ); - const archivedPrivate = threadsData.archived_threads.filter( - (thread) => thread.visibility !== "SEARCH_SPACE" - ); - - return { activeChats: activePrivate, archivedChats: archivedPrivate }; + return { + activeChats: threadsData.threads, + archivedChats: threadsData.archived_threads, + }; }, [threadsData, searchData, isSearchMode]); const threads = showArchived ? archivedChats : activeChats; @@ -265,7 +258,7 @@ export function AllPrivateChatsSidebarContent({ {t("close") || "Close"} )} -

{t("chats") || "Private Chats"}

+

{t("chats") || "Chats"}

@@ -370,7 +363,13 @@ export function AllPrivateChatsSidebarContent({ isBusy && "opacity-50 pointer-events-none" )} > - {thread.title || "New Chat"} + {thread.title || "New Chat"} + {thread.visibility === "SEARCH_SPACE" ? ( + + ) : null} ) : ( @@ -388,7 +387,15 @@ export function AllPrivateChatsSidebarContent({ isBusy && "opacity-50 pointer-events-none" )} > - {thread.title || "New Chat"} + + {thread.title || "New Chat"} + + {thread.visibility === "SEARCH_SPACE" ? ( + + ) : null} @@ -486,7 +493,7 @@ export function AllPrivateChatsSidebarContent({

{showArchived ? t("no_archived_chats") || "No archived chats" - : t("no_chats") || "No private chats"} + : t("no_chats") || "No chats"}

{!showArchived && (

@@ -545,21 +552,17 @@ export function AllPrivateChatsSidebarContent({ ); } -export function AllPrivateChatsSidebar({ +export function AllChatsSidebar({ open, onOpenChange, searchSpaceId, onCloseMobileSidebar, -}: AllPrivateChatsSidebarProps) { +}: AllChatsSidebarProps) { const t = useTranslations("sidebar"); return ( - - + void; - searchSpaceId: string; - onCloseMobileSidebar?: () => void; -} - -interface AllSharedChatsSidebarProps extends AllSharedChatsSidebarContentProps { - open: boolean; -} - -export function AllSharedChatsSidebarContent({ - onOpenChange, - searchSpaceId, - onCloseMobileSidebar, -}: AllSharedChatsSidebarContentProps) { - const t = useTranslations("sidebar"); - const router = useRouter(); - const params = useParams(); - const queryClient = useQueryClient(); - const isMobile = useIsMobile(); - const removeChatTab = useSetAtom(removeChatTabAtom); - - const currentChatId = Array.isArray(params.chat_id) - ? Number(params.chat_id[0]) - : params.chat_id - ? Number(params.chat_id) - : null; - const [deletingThreadId, setDeletingThreadId] = useState(null); - const [archivingThreadId, setArchivingThreadId] = useState(null); - const [searchQuery, setSearchQuery] = useState(""); - const [showArchived, setShowArchived] = useState(false); - const [openDropdownId, setOpenDropdownId] = useState(null); - const [showRenameDialog, setShowRenameDialog] = useState(false); - const [renamingThread, setRenamingThread] = useState<{ id: number; title: string } | null>(null); - const [newTitle, setNewTitle] = useState(""); - const [isRenaming, setIsRenaming] = useState(false); - const debouncedSearchQuery = useDebouncedValue(searchQuery, 300); - - const pendingThreadIdRef = useRef(null); - const { handlers: longPressHandlers, wasLongPress } = useLongPress( - useCallback(() => { - if (pendingThreadIdRef.current !== null) { - setOpenDropdownId(pendingThreadIdRef.current); - } - }, []) - ); - - const isSearchMode = !!debouncedSearchQuery.trim(); - - const { - data: threadsData, - error: threadsError, - isLoading: isLoadingThreads, - } = useQuery({ - queryKey: ["all-threads", searchSpaceId], - queryFn: () => fetchThreads(Number(searchSpaceId)), - enabled: !!searchSpaceId && !isSearchMode, - }); - - const { - data: searchData, - error: searchError, - isLoading: isLoadingSearch, - } = useQuery({ - queryKey: ["search-threads", searchSpaceId, debouncedSearchQuery], - queryFn: () => searchThreads(Number(searchSpaceId), debouncedSearchQuery.trim()), - enabled: !!searchSpaceId && isSearchMode, - }); - - // Filter to only shared chats (SEARCH_SPACE visibility) - const { activeChats, archivedChats } = useMemo(() => { - if (isSearchMode) { - const sharedSearchResults = (searchData ?? []).filter( - (thread) => thread.visibility === "SEARCH_SPACE" - ); - return { - activeChats: sharedSearchResults.filter((t) => !t.archived), - archivedChats: sharedSearchResults.filter((t) => t.archived), - }; - } - - if (!threadsData) return { activeChats: [], archivedChats: [] }; - - const activeShared = threadsData.threads.filter( - (thread) => thread.visibility === "SEARCH_SPACE" - ); - const archivedShared = threadsData.archived_threads.filter( - (thread) => thread.visibility === "SEARCH_SPACE" - ); - - return { activeChats: activeShared, archivedChats: archivedShared }; - }, [threadsData, searchData, isSearchMode]); - - const threads = showArchived ? archivedChats : activeChats; - - const handleThreadClick = useCallback( - (threadId: number) => { - router.push(`/dashboard/${searchSpaceId}/new-chat/${threadId}`); - onOpenChange(false); - onCloseMobileSidebar?.(); - }, - [router, onOpenChange, searchSpaceId, onCloseMobileSidebar] - ); - - const handleDeleteThread = useCallback( - async (threadId: number) => { - setDeletingThreadId(threadId); - try { - await deleteThread(threadId); - const fallbackTab = removeChatTab(threadId); - toast.success(t("chat_deleted") || "Chat deleted successfully"); - queryClient.invalidateQueries({ queryKey: ["all-threads", searchSpaceId] }); - queryClient.invalidateQueries({ queryKey: ["search-threads", searchSpaceId] }); - queryClient.invalidateQueries({ queryKey: ["threads", searchSpaceId] }); - - if (currentChatId === threadId) { - onOpenChange(false); - setTimeout(() => { - if (fallbackTab?.type === "chat" && fallbackTab.chatUrl) { - router.push(fallbackTab.chatUrl); - return; - } - router.push(`/dashboard/${searchSpaceId}/new-chat`); - }, 250); - } - } catch (error) { - console.error("Error deleting thread:", error); - toast.error(t("error_deleting_chat") || "Failed to delete chat"); - } finally { - setDeletingThreadId(null); - } - }, - [queryClient, searchSpaceId, t, currentChatId, router, onOpenChange, removeChatTab] - ); - - const handleToggleArchive = useCallback( - async (threadId: number, currentlyArchived: boolean) => { - setArchivingThreadId(threadId); - try { - await updateThread(threadId, { archived: !currentlyArchived }); - toast.success( - currentlyArchived - ? t("chat_unarchived") || "Chat restored" - : t("chat_archived") || "Chat archived" - ); - queryClient.invalidateQueries({ queryKey: ["all-threads", searchSpaceId] }); - queryClient.invalidateQueries({ queryKey: ["search-threads", searchSpaceId] }); - queryClient.invalidateQueries({ queryKey: ["threads", searchSpaceId] }); - } catch (error) { - console.error("Error archiving thread:", error); - toast.error(t("error_archiving_chat") || "Failed to archive chat"); - } finally { - setArchivingThreadId(null); - } - }, - [queryClient, searchSpaceId, t] - ); - - const handleStartRename = useCallback((threadId: number, title: string) => { - setRenamingThread({ id: threadId, title }); - setNewTitle(title); - setShowRenameDialog(true); - }, []); - - const handleConfirmRename = useCallback(async () => { - if (!renamingThread || !newTitle.trim()) return; - setIsRenaming(true); - try { - await updateThread(renamingThread.id, { title: newTitle.trim() }); - toast.success(t("chat_renamed") || "Chat renamed"); - queryClient.invalidateQueries({ queryKey: ["all-threads", searchSpaceId] }); - queryClient.invalidateQueries({ queryKey: ["search-threads", searchSpaceId] }); - queryClient.invalidateQueries({ queryKey: ["threads", searchSpaceId] }); - queryClient.invalidateQueries({ - queryKey: ["threads", searchSpaceId, "detail", String(renamingThread.id)], - }); - } catch (error) { - console.error("Error renaming thread:", error); - toast.error(t("error_renaming_chat") || "Failed to rename chat"); - } finally { - setIsRenaming(false); - setShowRenameDialog(false); - setRenamingThread(null); - setNewTitle(""); - } - }, [renamingThread, newTitle, queryClient, searchSpaceId, t]); - - const handleClearSearch = useCallback(() => { - setSearchQuery(""); - }, []); - - const isLoading = isSearchMode ? isLoadingSearch : isLoadingThreads; - const error = isSearchMode ? searchError : threadsError; - - const activeCount = activeChats.length; - const archivedCount = archivedChats.length; - - return ( - <> -

-
- {isMobile && ( - - )} -

{t("shared_chats") || "Shared Chats"}

-
- -
- - setSearchQuery(e.target.value)} - className="h-8 border-0 bg-muted pl-8 pr-7 text-sm shadow-none" - /> - {searchQuery && ( - - )} -
-
- - {!isSearchMode && ( - setShowArchived(value === "archived")} - className="shrink-0 mx-3 mt-1.5" - > - - - - - Active - - {activeCount} - - - - - - - Archived - - {archivedCount} - - - - - - )} - -
- {isLoading ? ( -
- {[75, 90, 55, 80, 65, 85].map((titleWidth) => ( -
- - -
- ))} -
- ) : error ? ( -
- {t("error_loading_chats") || "Error loading chats"} -
- ) : threads.length > 0 ? ( -
- {threads.map((thread) => { - const isDeleting = deletingThreadId === thread.id; - const isArchiving = archivingThreadId === thread.id; - const isBusy = isDeleting || isArchiving; - const isActive = currentChatId === thread.id; - - return ( -
- {isMobile ? ( - - ) : ( - - - - - -

- {t("updated") || "Updated"}: {formatThreadTimestamp(thread.updatedAt)} -

-
-
- )} - -
- setOpenDropdownId(isOpen ? thread.id : null)} - > - - - - - {!thread.archived && ( - handleStartRename(thread.id, thread.title || "New Chat")} - > - - {t("rename") || "Rename"} - - )} - handleToggleArchive(thread.id, thread.archived)} - disabled={isArchiving} - > - {thread.archived ? ( - <> - - {t("unarchive") || "Restore"} - - ) : ( - <> - - {t("archive") || "Archive"} - - )} - - handleDeleteThread(thread.id)}> - - {t("delete") || "Delete"} - - - -
-
- ); - })} -
- ) : isSearchMode ? ( -
- -

- {t("no_chats_found") || "No chats found"} -

-

- {t("try_different_search") || "Try a different search term"} -

-
- ) : ( -
- -

- {showArchived - ? t("no_archived_chats") || "No archived chats" - : t("no_shared_chats") || "No shared chats"} -

- {!showArchived && ( -

- Share a chat to collaborate with your team -

- )} -
- )} -
- - - - - {t("rename_chat") || "Rename Chat"} - - - {t("rename_chat_description") || "Enter a new name for this conversation."} - - - setNewTitle(e.target.value)} - placeholder={t("chat_title_placeholder") || "Chat title"} - onKeyDown={(e) => { - if (e.key === "Enter" && !isRenaming && newTitle.trim()) { - handleConfirmRename(); - } - }} - /> - - - - - - - - ); -} - -export function AllSharedChatsSidebar({ - open, - onOpenChange, - searchSpaceId, - onCloseMobileSidebar, -}: AllSharedChatsSidebarProps) { - const t = useTranslations("sidebar"); - - return ( - - - - ); -} diff --git a/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx b/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx index ea4f946c2..ec0ba534a 100644 --- a/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx +++ b/surfsense_web/components/layout/ui/sidebar/ChatListItem.tsx @@ -1,6 +1,6 @@ "use client"; -import { ArchiveIcon, MoreHorizontal, Pencil, RotateCcwIcon, Trash2 } from "lucide-react"; +import { ArchiveIcon, MoreHorizontal, Pencil, RotateCcwIcon, Trash2, Users } from "lucide-react"; import { useTranslations } from "next-intl"; import { useCallback, useState } from "react"; import { Button } from "@/components/ui/button"; @@ -18,6 +18,7 @@ import { cn } from "@/lib/utils"; interface ChatListItemProps { name: string; isActive?: boolean; + isShared?: boolean; archived?: boolean; dropdownOpen?: boolean; onDropdownOpenChange?: (open: boolean) => void; @@ -30,6 +31,7 @@ interface ChatListItemProps { export function ChatListItem({ name, isActive, + isShared, archived, dropdownOpen: controlledOpen, onDropdownOpenChange, @@ -68,7 +70,13 @@ export function ChatListItem({ isActive && "bg-accent text-accent-foreground" )} > - {animatedName} + {animatedName} + {isShared ? ( + + ) : null} {/* Actions dropdown - trigger hidden on mobile, long-press opens it instead */} diff --git a/surfsense_web/components/layout/ui/sidebar/MobileSidebar.tsx b/surfsense_web/components/layout/ui/sidebar/MobileSidebar.tsx index 83d423ace..bd9a95094 100644 --- a/surfsense_web/components/layout/ui/sidebar/MobileSidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/MobileSidebar.tsx @@ -19,17 +19,14 @@ interface MobileSidebarProps { navItems: NavItem[]; onNavItemClick?: (item: NavItem) => void; chats: ChatItem[]; - sharedChats?: ChatItem[]; activeChatId?: number | null; onNewChat: () => void; onChatSelect: (chat: ChatItem) => void; onChatRename?: (chat: ChatItem) => void; onChatDelete?: (chat: ChatItem) => void; onChatArchive?: (chat: ChatItem) => void; - onViewAllSharedChats?: () => void; - onViewAllPrivateChats?: () => void; - isSharedChatsPanelOpen?: boolean; - isPrivateChatsPanelOpen?: boolean; + onViewAllChats?: () => void; + isChatsPanelOpen?: boolean; user: User; onSettings?: () => void; onManageMembers?: () => void; @@ -69,17 +66,14 @@ export function MobileSidebar({ navItems, onNavItemClick, chats, - sharedChats, activeChatId, onNewChat, onChatSelect, onChatRename, onChatDelete, onChatArchive, - onViewAllSharedChats, - onViewAllPrivateChats, - isSharedChatsPanelOpen = false, - isPrivateChatsPanelOpen = false, + onViewAllChats, + isChatsPanelOpen = false, user, onSettings, onManageMembers, @@ -152,7 +146,6 @@ export function MobileSidebar({ navItems={navItems} onNavItemClick={handleNavItemClick} chats={chats} - sharedChats={sharedChats} activeChatId={activeChatId} onNewChat={() => { onNewChat(); @@ -162,24 +155,15 @@ export function MobileSidebar({ onChatRename={onChatRename} onChatDelete={onChatDelete} onChatArchive={onChatArchive} - onViewAllSharedChats={ - onViewAllSharedChats + onViewAllChats={ + onViewAllChats ? () => { onOpenChange(false); - onViewAllSharedChats(); + onViewAllChats(); } : undefined } - onViewAllPrivateChats={ - onViewAllPrivateChats - ? () => { - onOpenChange(false); - onViewAllPrivateChats(); - } - : undefined - } - isSharedChatsPanelOpen={isSharedChatsPanelOpen} - isPrivateChatsPanelOpen={isPrivateChatsPanelOpen} + isChatsPanelOpen={isChatsPanelOpen} user={user} onSettings={ onSettings diff --git a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx index 805f8bfd3..b7c8d94c7 100644 --- a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx @@ -67,17 +67,14 @@ interface SidebarProps { navItems: NavItem[]; onNavItemClick?: (item: NavItem) => void; chats: ChatItem[]; - sharedChats?: ChatItem[]; activeChatId?: number | null; onNewChat: () => void; onChatSelect: (chat: ChatItem) => void; onChatRename?: (chat: ChatItem) => void; onChatDelete?: (chat: ChatItem) => void; onChatArchive?: (chat: ChatItem) => void; - onViewAllSharedChats?: () => void; - onViewAllPrivateChats?: () => void; - isSharedChatsPanelOpen?: boolean; - isPrivateChatsPanelOpen?: boolean; + onViewAllChats?: () => void; + isChatsPanelOpen?: boolean; user: User; onSettings?: () => void; onManageMembers?: () => void; @@ -106,17 +103,14 @@ export function Sidebar({ navItems, onNavItemClick, chats, - sharedChats = [], activeChatId, onNewChat, onChatSelect, onChatRename, onChatDelete, onChatArchive, - onViewAllSharedChats, - onViewAllPrivateChats, - isSharedChatsPanelOpen = false, - isPrivateChatsPanelOpen = false, + onViewAllChats, + isChatsPanelOpen = false, user, onSettings, onManageMembers, @@ -264,73 +258,20 @@ export function Sidebar({
) : (
- {/* Shared Chats Section - takes only space needed, max 50% */} - - {!disableTooltips && isSharedChatsPanelOpen ? t("hide") : t("show_all")} - - ) : undefined - } - > - {isLoadingChats ? ( - - ) : sharedChats.length > 0 ? ( -
-
4 ? "pb-2" : ""}`} - > - {sharedChats.slice(0, 20).map((chat) => ( - setOpenDropdownChatId(open ? chat.id : null)} - onClick={() => onChatSelect(chat)} - onRename={() => onChatRename?.(chat)} - onArchive={() => onChatArchive?.(chat)} - onDelete={() => onChatDelete?.(chat)} - /> - ))} -
- {/* Gradient fade indicator when more than 4 items */} - {sharedChats.length > 4 && ( -
- )} -
- ) : ( -

{t("no_shared_chats")}

- )} - - - {/* Private Chats Section - fills remaining space */} - {!disableTooltips && isPrivateChatsPanelOpen ? t("hide") : t("show_all")} + {!disableTooltips && isChatsPanelOpen ? t("hide") : t("show_all")} ) : undefined } @@ -347,6 +288,7 @@ export function Sidebar({ key={chat.id} name={chat.name} isActive={chat.id === activeChatId} + isShared={chat.visibility === "SEARCH_SPACE"} archived={chat.archived} dropdownOpen={openDropdownChatId === chat.id} onDropdownOpenChange={(open) => setOpenDropdownChatId(open ? chat.id : null)} diff --git a/surfsense_web/components/layout/ui/sidebar/index.ts b/surfsense_web/components/layout/ui/sidebar/index.ts index d72f86c8a..e25149b06 100644 --- a/surfsense_web/components/layout/ui/sidebar/index.ts +++ b/surfsense_web/components/layout/ui/sidebar/index.ts @@ -1,5 +1,4 @@ -export { AllPrivateChatsSidebar, AllPrivateChatsSidebarContent } from "./AllPrivateChatsSidebar"; -export { AllSharedChatsSidebar, AllSharedChatsSidebarContent } from "./AllSharedChatsSidebar"; +export { AllChatsSidebar, AllChatsSidebarContent } from "./AllChatsSidebar"; export { ChatListItem } from "./ChatListItem"; export { DocumentsSidebar } from "./DocumentsSidebar"; export { InboxSidebar, InboxSidebarContent } from "./InboxSidebar"; diff --git a/surfsense_web/messages/en.json b/surfsense_web/messages/en.json index 97ae05101..de31e00b9 100644 --- a/surfsense_web/messages/en.json +++ b/surfsense_web/messages/en.json @@ -650,13 +650,14 @@ "created": "Created" }, "sidebar": { - "chats": "Private Chats", + "chats": "Chats", "shared_chats": "Shared Chats", "search_chats": "Search chats", "no_chats_found": "No chats found", "no_shared_chats": "No shared chats", + "shared_chat": "Shared chat", "view_all_shared_chats": "View all shared chats", - "view_all_private_chats": "View all private chats", + "view_all_chats": "View all chats", "show_all": "Show all", "hide": "Hide", "no_chats": "No chats", diff --git a/surfsense_web/messages/es.json b/surfsense_web/messages/es.json index 2ac168d0a..658c9f7cf 100644 --- a/surfsense_web/messages/es.json +++ b/surfsense_web/messages/es.json @@ -650,13 +650,14 @@ "created": "Creado" }, "sidebar": { - "chats": "Chats privados", + "chats": "Chats", "shared_chats": "Chats compartidos", "search_chats": "Buscar chats", "no_chats_found": "No se encontraron chats", "no_shared_chats": "No hay chats compartidos", + "shared_chat": "Chat compartido", "view_all_shared_chats": "Ver todos los chats compartidos", - "view_all_private_chats": "Ver todos los chats privados", + "view_all_chats": "Ver todos los chats", "show_all": "Ver todo", "hide": "Ocultar", "no_chats": "Sin chats", diff --git a/surfsense_web/messages/hi.json b/surfsense_web/messages/hi.json index 2bfa9a59f..33a0c8a36 100644 --- a/surfsense_web/messages/hi.json +++ b/surfsense_web/messages/hi.json @@ -650,13 +650,14 @@ "created": "बनाया गया" }, "sidebar": { - "chats": "निजी चैट", + "chats": "चैट", "shared_chats": "साझा चैट", "search_chats": "चैट खोजें", "no_chats_found": "कोई चैट नहीं मिला", "no_shared_chats": "कोई साझा चैट नहीं", + "shared_chat": "साझा चैट", "view_all_shared_chats": "सभी साझा चैट देखें", - "view_all_private_chats": "सभी निजी चैट देखें", + "view_all_chats": "सभी चैट देखें", "show_all": "सभी देखें", "hide": "छिपाएँ", "no_chats": "कोई चैट नहीं", diff --git a/surfsense_web/messages/pt.json b/surfsense_web/messages/pt.json index 029959689..6ea0ae5cf 100644 --- a/surfsense_web/messages/pt.json +++ b/surfsense_web/messages/pt.json @@ -650,13 +650,14 @@ "created": "Criado" }, "sidebar": { - "chats": "Chats privados", + "chats": "Chats", "shared_chats": "Chats compartilhados", "search_chats": "Pesquisar chats", "no_chats_found": "Nenhum chat encontrado", "no_shared_chats": "Nenhum chat compartilhado", + "shared_chat": "Chat compartilhado", "view_all_shared_chats": "Ver todos os chats compartilhados", - "view_all_private_chats": "Ver todos os chats privados", + "view_all_chats": "Ver todos os chats", "show_all": "Ver tudo", "hide": "Ocultar", "no_chats": "Nenhum chat", diff --git a/surfsense_web/messages/zh.json b/surfsense_web/messages/zh.json index 6f5b2cafb..1404c6ef0 100644 --- a/surfsense_web/messages/zh.json +++ b/surfsense_web/messages/zh.json @@ -634,13 +634,14 @@ "created": "创建于" }, "sidebar": { - "chats": "私人对话", + "chats": "对话", "shared_chats": "共享对话", "search_chats": "搜索对话...", "no_chats_found": "未找到对话", "no_shared_chats": "暂无共享对话", + "shared_chat": "共享对话", "view_all_shared_chats": "查看所有共享对话", - "view_all_private_chats": "查看所有私人对话", + "view_all_chats": "查看所有对话", "show_all": "查看全部", "hide": "隐藏", "no_chats": "无对话",