refactor(layout): remove Inbox component and integrate NotificationsDropdown

- Eliminated the Inbox component and its related logic from FreeLayoutDataProvider and LayoutDataProvider.
- Introduced NotificationsDropdown to handle notifications in the sidebar and mobile sidebar.
- Updated Sidebar and LayoutShell components to accommodate the new notifications structure, enhancing user experience and code clarity.
This commit is contained in:
Anish Sarkar 2026-07-07 20:47:48 +05:30
parent 0592215bad
commit 93a09e1978
9 changed files with 420 additions and 219 deletions

View file

@ -1,14 +1,12 @@
"use client";
import { useAtomValue } from "jotai";
import { AnimatePresence, motion } from "motion/react";
import dynamic from "next/dynamic";
import { useCallback, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { activeTabAtom, type Tab } from "@/atoms/tabs/tabs.atom";
import { Logo } from "@/components/Logo";
import { Spinner } from "@/components/ui/spinner";
import { TooltipProvider } from "@/components/ui/tooltip";
import type { InboxItem } from "@/hooks/use-inbox";
import { useIsMobile } from "@/hooks/use-mobile";
import { useElectronAPI } from "@/hooks/use-platform";
import { cn } from "@/lib/utils";
@ -27,14 +25,13 @@ import {
RightPanelToggleButton,
} from "../right-panel/RightPanel";
import {
InboxSidebarContent,
MobileSidebar,
MobileSidebarTrigger,
PlaygroundSidebar,
Sidebar,
SidebarCollapseButton,
} from "../sidebar";
import { SidebarSlideOutPanel } from "../sidebar/SidebarSlideOutPanel";
import type { NotificationsDropdownData } from "../sidebar/NotificationsDropdown";
import { TabBar } from "../tabs/TabBar";
import { WorkspacePanel } from "./WorkspacePanel";
@ -80,28 +77,6 @@ function MacDesktopTitleBar({
);
}
// Per-tab data source
interface TabDataSource {
items: InboxItem[];
unreadCount: number;
loading: boolean;
loadingMore: boolean;
hasMore: boolean;
loadMore: () => void;
markAsRead: (id: number) => Promise<boolean>;
markAllAsRead: () => Promise<boolean>;
}
export type ActiveSlideoutPanel = "inbox" | null;
// Inbox-related props — per-tab data sources with independent loading/pagination
interface InboxProps {
isOpen: boolean;
totalUnreadCount: number;
comments: TabDataSource;
status: TabDataSource;
}
interface LayoutShellProps {
workspaces: Workspace[];
activeWorkspaceId: number | null;
@ -140,11 +115,7 @@ interface LayoutShellProps {
workspacePanelContentClassName?: string;
children: React.ReactNode;
className?: string;
// Unified slide-out panel state
activeSlideoutPanel?: ActiveSlideoutPanel;
onSlideoutPanelChange?: (panel: ActiveSlideoutPanel) => void;
// Inbox props
inbox?: InboxProps;
notifications?: NotificationsDropdownData;
isLoadingChats?: boolean;
onTabSwitch?: (tab: Tab) => void;
onTabPrefetch?: (tab: Tab) => void;
@ -245,9 +216,7 @@ export function LayoutShell({
workspacePanelContentClassName,
children,
className,
activeSlideoutPanel = null,
onSlideoutPanelChange,
inbox,
notifications,
isLoadingChats = false,
onTabSwitch,
onTabPrefetch,
@ -269,17 +238,6 @@ export function LayoutShell({
[isCollapsed, setIsCollapsed, toggleCollapsed]
);
const closeSlideout = useCallback(
(open: boolean) => {
if (!open) onSlideoutPanelChange?.(null);
},
[onSlideoutPanelChange]
);
const anySlideOutOpen = activeSlideoutPanel !== null;
const panelAriaLabel = activeSlideoutPanel === "inbox" ? "Inbox" : "Panel";
// Mobile layout
if (isMobile) {
return (
@ -316,6 +274,7 @@ export function LayoutShell({
onUserSettings={onUserSettings}
onAnnouncements={onAnnouncements}
announcementUnreadCount={announcementUnreadCount}
notifications={notifications}
onLogout={onLogout}
pageUsage={pageUsage}
theme={theme}
@ -335,34 +294,6 @@ export function LayoutShell({
{children}
</main>
)}
{/* Mobile unified slide-out panel */}
<SidebarSlideOutPanel
open={anySlideOutOpen}
onOpenChange={closeSlideout}
ariaLabel={panelAriaLabel}
>
<AnimatePresence mode="popLayout" initial={false}>
{activeSlideoutPanel === "inbox" && inbox && (
<motion.div
key="inbox"
className="h-full flex flex-col"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<InboxSidebarContent
onOpenChange={(open) => closeSlideout(open)}
comments={inbox.comments}
status={inbox.status}
totalUnreadCount={inbox.totalUnreadCount}
onCloseMobileSidebar={() => setMobileMenuOpen(false)}
/>
</motion.div>
)}
</AnimatePresence>
</SidebarSlideOutPanel>
</div>
</TooltipProvider>
</SidebarProvider>
@ -400,6 +331,7 @@ export function LayoutShell({
onUserSettings={onUserSettings}
onAnnouncements={onAnnouncements}
announcementUnreadCount={announcementUnreadCount}
notifications={notifications}
onLogout={onLogout}
theme={theme}
setTheme={setTheme}
@ -425,10 +357,7 @@ export function LayoutShell({
className={cn(
"relative hidden md:flex shrink-0 z-20 -mr-2 bg-panel",
isMacDesktop
? cn(
"border-t border-r",
!showPlaygroundSidebar && "rounded-tl-xl border-l"
)
? cn("border-t border-r", !showPlaygroundSidebar && "rounded-tl-xl border-l")
: "border-r"
)}
>
@ -491,33 +420,6 @@ export function LayoutShell({
)}
/>
)}
{/* Unified slide-out panel — shell stays open, content cross-fades */}
<SidebarSlideOutPanel
open={anySlideOutOpen}
onOpenChange={closeSlideout}
ariaLabel={panelAriaLabel}
>
<AnimatePresence mode="popLayout" initial={false}>
{activeSlideoutPanel === "inbox" && inbox && (
<motion.div
key="inbox"
className="h-full flex flex-col"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<InboxSidebarContent
onOpenChange={(open) => closeSlideout(open)}
comments={inbox.comments}
status={inbox.status}
totalUnreadCount={inbox.totalUnreadCount}
/>
</motion.div>
)}
</AnimatePresence>
</SidebarSlideOutPanel>
</div>
<DesktopWorkspaceRegion>