"use client" import { Suspense, useCallback, useEffect, useLayoutEffect, useMemo, useState, type CSSProperties, } from "react" import { useIsXs } from "@/hooks/use-xs" import { readTouchNavMatches, useTouchNav } from "@/hooks/use-touch-nav" import { useMailSplitView } from "@/hooks/use-mail-split-view" import { MobileBottomBar } from "@/components/gmail/mobile-bottom-bar" import { Toaster } from "sonner" import { useRouter, usePathname } from "next/navigation" import { Sidebar } from "@/components/gmail/sidebar" import { Header } from "@/components/gmail/header" import { EmailList } from "@/components/gmail/email-list" import { RightPanel } from "@/components/gmail/right-panel" import { ContactsPanel } from "@/components/gmail/contacts/contacts-panel" import { EmailDragProvider } from "@/lib/drag-context" import { MoveDragIndicator } from "@/components/gmail/move-drag-indicator" import { ComposeProvider } from "@/lib/compose-context" import { ScheduledMailProvider } from "@/lib/scheduled-mail-context" import { ComposeModalManager } from "@/components/gmail/compose-modal" import { SidebarNavProvider } from "@/lib/sidebar-nav-context" import { mailNavVisitKey } from "@/lib/mail-folder-display" import { useMailStore } from "@/lib/stores/mail-store" import { parseMailSegments, buildMailPath, DEFAULT_INBOX_TAB, type MailRouteState, } from "@/lib/mail-url" import { cn } from "@/lib/utils" function segmentsFromPathname(pathname: string | null): string[] | undefined { if (!pathname?.startsWith("/mail")) return undefined const rest = pathname.slice("/mail".length).replace(/^\//, "") if (!rest) return [] return rest.split("/").filter(Boolean) } function MailAppInner() { const router = useRouter() const pathname = usePathname() const segments = useMemo(() => segmentsFromPathname(pathname), [pathname]) const route = useMemo(() => parseMailSegments(segments), [segments]) const isXs = useIsXs() const touchNav = useTouchNav() const splitView = useMailSplitView() const pushRecentFolderVisit = useMailStore((s) => s.pushRecentFolderVisit) /** Start closed so narrow viewports match SSR/CSS before JS runs; desktop opens in layout. */ const [sidebarCollapsed, setSidebarCollapsed] = useState(true) useLayoutEffect(() => { if (!readTouchNavMatches()) setSidebarCollapsed(false) }, []) useEffect(() => { if (isXs) setSidebarCollapsed(true) }, [isXs]) useEffect(() => { pushRecentFolderVisit(mailNavVisitKey(route.folderId, route.inboxTab)) }, [route.folderId, route.inboxTab, pushRecentFolderVisit]) const [folderUnreadCounts, setFolderUnreadCounts] = useState< Record >({}) const navigateRoute = useCallback( (patch: Partial) => { const next: MailRouteState = { folderId: patch.folderId ?? route.folderId, inboxTab: patch.inboxTab !== undefined && patch.inboxTab !== null ? patch.inboxTab : route.inboxTab, page: patch.page !== undefined ? patch.page : route.page, mailId: patch.mailId !== undefined ? patch.mailId : route.mailId, } router.push(buildMailPath(next), { scroll: false }) }, [router, route] ) const handleSelectFolder = useCallback( (id: string) => { navigateRoute({ folderId: id, inboxTab: DEFAULT_INBOX_TAB, page: 1, mailId: null, }) if (readTouchNavMatches()) setSidebarCollapsed(true) }, [navigateRoute] ) return ( navigateRoute({ folderId: nextFolderId, inboxTab: DEFAULT_INBOX_TAB, page: 1, mailId: null, }) } >
{!splitView ? (
setSidebarCollapsed(!sidebarCollapsed)} />
) : null}
{!sidebarCollapsed && touchNav && (
) } export function MailAppShell({ children: _routeOutlet, }: { children: React.ReactNode }) { useEffect(() => { const blockPinch = (event: Event) => event.preventDefault() document.addEventListener("gesturestart", blockPinch, { passive: false }) document.addEventListener("gesturechange", blockPinch, { passive: false }) document.addEventListener("gestureend", blockPinch, { passive: false }) return () => { document.removeEventListener("gesturestart", blockPinch) document.removeEventListener("gesturechange", blockPinch) document.removeEventListener("gestureend", blockPinch) } }, []) return (
} > ) }