feat(chats): implement ChatsPage component and update layout for all chats functionality

This commit is contained in:
Anish Sarkar 2026-07-06 10:26:32 +05:30
parent 248e3aae57
commit 420ce3e153
7 changed files with 57 additions and 119 deletions

View file

@ -27,7 +27,6 @@ import {
RightPanelToggleButton,
} from "../right-panel/RightPanel";
import {
AllChatsSidebarContent,
DocumentsSidebar,
InboxSidebarContent,
MobileSidebar,
@ -93,7 +92,7 @@ interface TabDataSource {
markAllAsRead: () => Promise<boolean>;
}
export type ActiveSlideoutPanel = "inbox" | "chats" | null;
export type ActiveSlideoutPanel = "inbox" | null;
// Inbox-related props — per-tab data sources with independent loading/pagination
interface InboxProps {
@ -134,6 +133,7 @@ interface LayoutShellProps {
setTheme?: (theme: "light" | "dark" | "system") => void;
defaultCollapsed?: boolean;
isChatPage?: boolean;
isAllChatsPage?: boolean;
useWorkspacePanel?: boolean;
workspacePanelViewportClassName?: string;
workspacePanelContentClassName?: string;
@ -145,10 +145,6 @@ interface LayoutShellProps {
// Inbox props
inbox?: InboxProps;
isLoadingChats?: boolean;
// All chats panel props
allChatsPanel?: {
searchSpaceId: string;
};
documentsPanel?: {
open: boolean;
onOpenChange: (open: boolean) => void;
@ -245,6 +241,7 @@ export function LayoutShell({
setTheme,
defaultCollapsed = false,
isChatPage = false,
isAllChatsPage = false,
useWorkspacePanel = false,
workspacePanelViewportClassName,
workspacePanelContentClassName,
@ -254,7 +251,6 @@ export function LayoutShell({
onSlideoutPanelChange,
inbox,
isLoadingChats = false,
allChatsPanel,
documentsPanel,
onTabSwitch,
onTabPrefetch,
@ -285,8 +281,7 @@ export function LayoutShell({
const anySlideOutOpen = activeSlideoutPanel !== null;
const panelAriaLabel =
activeSlideoutPanel === "inbox" ? "Inbox" : activeSlideoutPanel === "chats" ? "Chats" : "Panel";
const panelAriaLabel = activeSlideoutPanel === "inbox" ? "Inbox" : "Panel";
// Mobile layout
if (isMobile) {
@ -317,7 +312,7 @@ export function LayoutShell({
onChatDelete={onChatDelete}
onChatArchive={onChatArchive}
onViewAllChats={onViewAllChats}
isChatsPanelOpen={activeSlideoutPanel === "chats"}
isAllChatsActive={isAllChatsPage}
user={user}
onSettings={onSettings}
onManageMembers={onManageMembers}
@ -369,22 +364,6 @@ export function LayoutShell({
/>
</motion.div>
)}
{activeSlideoutPanel === "chats" && allChatsPanel && (
<motion.div
key="chats"
className="h-full flex flex-col"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<AllChatsSidebarContent
onOpenChange={(open) => closeSlideout(open)}
searchSpaceId={allChatsPanel.searchSpaceId}
onCloseMobileSidebar={() => setMobileMenuOpen(false)}
/>
</motion.div>
)}
</AnimatePresence>
</SidebarSlideOutPanel>
@ -460,7 +439,7 @@ export function LayoutShell({
onChatDelete={onChatDelete}
onChatArchive={onChatArchive}
onViewAllChats={onViewAllChats}
isChatsPanelOpen={activeSlideoutPanel === "chats"}
isAllChatsActive={isAllChatsPage}
user={user}
onSettings={onSettings}
onManageMembers={onManageMembers}
@ -526,21 +505,6 @@ export function LayoutShell({
/>
</motion.div>
)}
{activeSlideoutPanel === "chats" && allChatsPanel && (
<motion.div
key="chats"
className="h-full flex flex-col"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<AllChatsSidebarContent
onOpenChange={(open) => closeSlideout(open)}
searchSpaceId={allChatsPanel.searchSpaceId}
/>
</motion.div>
)}
</AnimatePresence>
</SidebarSlideOutPanel>
</div>

View file

@ -4,7 +4,6 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useSetAtom } from "jotai";
import {
ArchiveIcon,
ChevronLeft,
MessageCircleMore,
MoreHorizontal,
Pencil,
@ -48,23 +47,13 @@ import { useArchiveThread, useDeleteThread, useRenameThread } from "@/hooks/use-
import { fetchThreads, searchThreads, type ThreadListItem } from "@/lib/chat/thread-persistence";
import { formatThreadTimestamp } from "@/lib/format-date";
import { cn } from "@/lib/utils";
import { SidebarSlideOutPanel } from "./SidebarSlideOutPanel";
export interface AllChatsSidebarContentProps {
onOpenChange: (open: boolean) => void;
interface AllChatsContentProps {
searchSpaceId: string;
onCloseMobileSidebar?: () => void;
className?: string;
}
interface AllChatsSidebarProps extends AllChatsSidebarContentProps {
open: boolean;
}
export function AllChatsSidebarContent({
onOpenChange,
searchSpaceId,
onCloseMobileSidebar,
}: AllChatsSidebarContentProps) {
function AllChatsContent({ searchSpaceId, className }: AllChatsContentProps) {
const t = useTranslations("sidebar");
const router = useRouter();
const params = useParams();
@ -150,10 +139,8 @@ export function AllChatsSidebarContent({
searchSpaceId,
visibility: thread.visibility,
});
onOpenChange(false);
onCloseMobileSidebar?.();
},
[activateChatThread, onOpenChange, searchSpaceId, onCloseMobileSidebar]
[activateChatThread, searchSpaceId]
);
const handleDeleteThread = useCallback(
@ -165,7 +152,6 @@ export function AllChatsSidebarContent({
toast.success(t("chat_deleted") || "Chat deleted successfully");
if (currentChatId === threadId) {
onOpenChange(false);
setTimeout(() => {
if (
fallbackTab?.type === "chat" &&
@ -196,16 +182,7 @@ export function AllChatsSidebarContent({
setDeletingThreadId(null);
}
},
[
activateChatThread,
deleteThread,
t,
currentChatId,
router,
onOpenChange,
removeChatTab,
searchSpaceId,
]
[activateChatThread, deleteThread, t, currentChatId, router, removeChatTab, searchSpaceId]
);
const handleToggleArchive = useCallback(
@ -266,20 +243,9 @@ export function AllChatsSidebarContent({
const archivedCount = archivedChats.length;
return (
<>
<div className={cn("flex h-full min-h-0 flex-col", className)}>
<div className="shrink-0 p-3 pb-1.5 space-y-2">
<div className="flex items-center gap-2">
{isMobile && (
<Button
variant="ghost"
size="icon"
className="h-8 w-8 rounded-full text-muted-foreground hover:text-accent-foreground"
onClick={() => onOpenChange(false)}
>
<ChevronLeft className="h-4 w-4" />
<span className="sr-only">{t("close") || "Close"}</span>
</Button>
)}
<h2 className="text-lg font-semibold">{t("chats") || "Chats"}</h2>
</div>
@ -584,25 +550,14 @@ export function AllChatsSidebarContent({
</DialogFooter>
</DialogContent>
</Dialog>
</>
</div>
);
}
export function AllChatsSidebar({
open,
onOpenChange,
searchSpaceId,
onCloseMobileSidebar,
}: AllChatsSidebarProps) {
const t = useTranslations("sidebar");
export function AllChatsWorkspaceContent({ searchSpaceId }: { searchSpaceId: string }) {
return (
<SidebarSlideOutPanel open={open} onOpenChange={onOpenChange} ariaLabel={t("chats") || "Chats"}>
<AllChatsSidebarContent
onOpenChange={onOpenChange}
searchSpaceId={searchSpaceId}
onCloseMobileSidebar={onCloseMobileSidebar}
/>
</SidebarSlideOutPanel>
<div className="flex h-[min(760px,calc(100vh-8rem))] w-full overflow-hidden rounded-xl border bg-sidebar text-sidebar-foreground shadow-sm">
<AllChatsContent searchSpaceId={searchSpaceId} />
</div>
);
}

View file

@ -27,7 +27,7 @@ interface MobileSidebarProps {
onChatDelete?: (chat: ChatItem) => void;
onChatArchive?: (chat: ChatItem) => void;
onViewAllChats?: () => void;
isChatsPanelOpen?: boolean;
isAllChatsActive?: boolean;
user: User;
onSettings?: () => void;
onManageMembers?: () => void;
@ -75,7 +75,7 @@ export function MobileSidebar({
onChatDelete,
onChatArchive,
onViewAllChats,
isChatsPanelOpen = false,
isAllChatsActive = false,
user,
onSettings,
onManageMembers,
@ -166,7 +166,7 @@ export function MobileSidebar({
}
: undefined
}
isChatsPanelOpen={isChatsPanelOpen}
isAllChatsActive={isAllChatsActive}
user={user}
onSettings={
onSettings

View file

@ -75,7 +75,7 @@ interface SidebarProps {
onChatDelete?: (chat: ChatItem) => void;
onChatArchive?: (chat: ChatItem) => void;
onViewAllChats?: () => void;
isChatsPanelOpen?: boolean;
isAllChatsActive?: boolean;
user: User;
onSettings?: () => void;
onManageMembers?: () => void;
@ -112,7 +112,7 @@ export function Sidebar({
onChatDelete,
onChatArchive,
onViewAllChats,
isChatsPanelOpen = false,
isAllChatsActive = false,
user,
onSettings,
onManageMembers,
@ -281,7 +281,7 @@ export function Sidebar({
title={t("recents")}
defaultOpen={true}
fillHeight={true}
alwaysShowAction={!disableTooltips && isChatsPanelOpen}
alwaysShowAction={!disableTooltips && isAllChatsActive}
action={
onViewAllChats ? (
<Button
@ -290,7 +290,7 @@ export function Sidebar({
onClick={onViewAllChats}
className="h-auto cursor-pointer whitespace-nowrap bg-transparent p-0 text-xs font-medium text-muted-foreground/60 transition-colors hover:bg-transparent hover:text-muted-foreground"
>
{!disableTooltips && isChatsPanelOpen ? t("hide") : t("show_all")}
{!disableTooltips && isAllChatsActive ? t("hide") : t("show_all")}
</Button>
) : undefined
}

View file

@ -1,4 +1,4 @@
export { AllChatsSidebar, AllChatsSidebarContent } from "./AllChatsSidebar";
export { AllChatsWorkspaceContent } from "./AllChatsSidebar";
export { ChatListItem } from "./ChatListItem";
export { CreditBalanceDisplay } from "./CreditBalanceDisplay";
export { DocumentsSidebar } from "./DocumentsSidebar";