"use client" import { useState, useRef, useEffect, useMemo } from "react" import { useIsXs } from "@/hooks/use-xs" import { useTouchNav } from "@/hooks/use-touch-nav" import { readTouchNavMatches } from "@/hooks/use-touch-nav" import { isSystemNavLabelId } from "@/lib/sidebar-nav-data" import { useSidebarNav } from "@/lib/sidebar-nav-context" import { ancestorFolderIdsForTarget } from "@/lib/sidebar-folder-tree-utils" import { mainItems, CATEGORY_IDS_IN_PLUS_ONLY, sortSystemLabelRows, } from "@/components/gmail/sidebar/sidebar-nav-constants" import { folderParentSelectOptions } from "@/components/gmail/sidebar/sidebar-nav-primitives" import { useSidebarNavDrag } from "@/hooks/use-sidebar-nav-drag" import { MAIL_SIDEBAR_PANEL_SURFACE_CLASS, MAIL_SIDEBAR_PANEL_SURFACE_MOBILE_CLASS, } from "@/lib/mail-chrome-classes" export interface SidebarProps { selectedFolder: string onSelectFolder: (folder: string) => void collapsed: boolean folderUnreadCounts?: Record splitView?: boolean } export function useSidebarState({ selectedFolder, onSelectFolder, collapsed, folderUnreadCounts = {}, }: SidebarProps) { const [hoverExpanded, setHoverExpanded] = useState(false) const [navMoreOpen, setNavMoreOpen] = useState(false) const [expandedFolderIds, setExpandedFolderIds] = useState>(() => new Set()) const hoverTimeoutRef = useRef(null) const sidebarRef = useRef(null) const touchNav = useTouchNav() const isXs = useIsXs() const isExpanded = !collapsed || (!touchNav && hoverExpanded) const isOverlayOpen = touchNav && !collapsed const nav = useSidebarNav() const { folderTree, labelRows, folderIdToLabel, addFolder, addLabelRowFromSidebar, getNavItemPrefs, setNavItemSidebarVisibility, setNavItemMessageVisibility, updateFolderOrLabelColor, renameFolderOrLabel, removeFolderOrLabelRow, moveFolder, reorderLabelRows, moveFolderRelative, addSubfolder, addChildLabelRow, setLabelRowEnabled, } = nav const drag = useSidebarNavDrag({ reorderLabelRows, moveFolderRelative, setExpandedFolderIds, }) const visibleNavLabelRows = useMemo(() => { return labelRows.filter((row) => { if (row.enabled === false) return false if (isSystemNavLabelId(row.id)) return false const p = getNavItemPrefs(row.id) if (p.sidebar === "hide") return false if ( p.sidebar === "showUnread" && (folderUnreadCounts[row.id] ?? 0) === 0 ) { return false } return true }) }, [labelRows, getNavItemPrefs, folderUnreadCounts]) const validNavFolderIds = useMemo(() => { const s = new Set() for (const i of mainItems) s.add(i.id) for (const k of Object.keys(folderIdToLabel)) s.add(k) return s }, [folderIdToLabel]) useEffect(() => { if (selectedFolder !== "search" && !validNavFolderIds.has(selectedFolder)) { onSelectFolder("inbox") } }, [validNavFolderIds, selectedFolder, onSelectFolder]) const [folderDialogOpen, setFolderDialogOpen] = useState(false) const [labelDialogOpen, setLabelDialogOpen] = useState(false) const [newFolderName, setNewFolderName] = useState("") const [newFolderParent, setNewFolderParent] = useState("__root__") const [newLabelName, setNewLabelName] = useState("") const newFolderNameInputRef = useRef(null) const newLabelNameInputRef = useRef(null) const folderParentOptions = useMemo( () => folderParentSelectOptions(folderTree), [folderTree] ) const { primaryVisibleCategories, plusOnlyVisibleCategories } = useMemo(() => { const systemEnabled = sortSystemLabelRows( labelRows.filter((r) => r.enabled !== false && isSystemNavLabelId(r.id)) ).map((r) => ({ id: r.id, label: r.label, icon: r.icon })) return { primaryVisibleCategories: systemEnabled.filter( (c) => !CATEGORY_IDS_IN_PLUS_ONLY.has(c.id) ), plusOnlyVisibleCategories: systemEnabled.filter((c) => CATEGORY_IDS_IN_PLUS_ONLY.has(c.id) ), } }, [labelRows]) const disabledSystemNavItems = useMemo(() => { return sortSystemLabelRows( labelRows.filter((r) => r.enabled === false && isSystemNavLabelId(r.id)) ).map((r) => ({ id: r.id, label: r.label, icon: r.icon })) }, [labelRows]) const visibleMainItems = useMemo(() => { const scheduledTotal = folderUnreadCounts.scheduled ?? 0 if (scheduledTotal > 0) return mainItems return mainItems.filter((item) => item.id !== "scheduled") }, [folderUnreadCounts.scheduled]) const toggleFolderExpanded = (id: string) => { setExpandedFolderIds((prev) => { const next = new Set(prev) if (next.has(id)) next.delete(id) else next.add(id) return next }) } const handleSubmitNewFolder = () => { const name = newFolderName.trim() if (!name) return const parentId = newFolderParent === "__root__" ? null : newFolderParent addFolder(parentId, name) setNewFolderName("") setFolderDialogOpen(false) } const handleSubmitNewLabel = () => { const name = newLabelName.trim() if (!name) return addLabelRowFromSidebar(name) setNewLabelName("") setLabelDialogOpen(false) } useEffect(() => { const row = labelRows.find((r) => r.id === selectedFolder) if (row && row.enabled === false) { onSelectFolder("inbox") } }, [labelRows, selectedFolder, onSelectFolder]) useEffect(() => { if (selectedFolder !== "scheduled") return if ((folderUnreadCounts.scheduled ?? 0) > 0) return onSelectFolder("inbox") }, [folderUnreadCounts.scheduled, selectedFolder, onSelectFolder]) useEffect(() => { if (CATEGORY_IDS_IN_PLUS_ONLY.has(selectedFolder) && !navMoreOpen) { setNavMoreOpen(true) } }, [selectedFolder, navMoreOpen]) useEffect(() => { const ancestors = ancestorFolderIdsForTarget(folderTree, selectedFolder) if (ancestors?.length) { setExpandedFolderIds((prev) => { const next = new Set(prev) ancestors.forEach((id) => next.add(id)) return next }) } }, [selectedFolder, folderTree]) const handleMouseEnter = () => { if (readTouchNavMatches()) return if (collapsed) { hoverTimeoutRef.current = setTimeout(() => { setHoverExpanded(true) }, 300) } } const handleMouseLeave = () => { if (hoverTimeoutRef.current) { clearTimeout(hoverTimeoutRef.current) hoverTimeoutRef.current = null } if (readTouchNavMatches()) return setHoverExpanded(false) } useEffect(() => { if (touchNav) setHoverExpanded(false) }, [touchNav, collapsed]) useEffect(() => { return () => { if (hoverTimeoutRef.current) { clearTimeout(hoverTimeoutRef.current) } } }, []) const navRailInset = "pr-3.5" const splitViewLogoIconClass = "size-9 shrink-0" const splitViewLogoHeaderClass = "box-border min-h-[80px] pt-3 pl-4 pr-3.5 pb-4" const navDragBindings = useMemo( () => ({ navDragRef: drag.navDragRef, navDropPlacementRef: drag.navDropPlacementRef, beginNavDrag: drag.beginNavDrag, clearNavDrag: drag.clearNavDrag, updateNavDropTarget: drag.updateNavDropTarget, clearNavDropTarget: drag.clearNavDropTarget, commitNavDrop: drag.commitNavDrop, }), [drag] ) const folderRowProps = useMemo( () => ({ ...navDragBindings, selectedFolder, folderUnreadCounts, expandedFolderIds, isExpanded, isOverlayOpen, touchNav, folderTree, onSelectFolder, toggleFolderExpanded, getNavItemPrefs, setNavItemSidebarVisibility, setNavItemMessageVisibility, updateFolderOrLabelColor, renameFolderOrLabel, removeFolderOrLabelRow, moveFolder, addSubfolder, }), [ navDragBindings, selectedFolder, folderUnreadCounts, expandedFolderIds, isExpanded, isOverlayOpen, touchNav, folderTree, onSelectFolder, getNavItemPrefs, setNavItemSidebarVisibility, setNavItemMessageVisibility, updateFolderOrLabelColor, renameFolderOrLabel, removeFolderOrLabelRow, moveFolder, addSubfolder, ] ) const collapsedFolderOpts = useMemo( () => ({ getNavItemPrefs, folderUnreadCounts, expandedFolderIds, isExpanded, selectedFolder, onSelectFolder, }), [ getNavItemPrefs, folderUnreadCounts, expandedFolderIds, isExpanded, selectedFolder, onSelectFolder, ] ) const labelRowProps = useMemo( () => ({ ...navDragBindings, selectedFolder, touchNav, onSelectFolder, getNavItemPrefs, setNavItemSidebarVisibility, setNavItemMessageVisibility, updateFolderOrLabelColor, renameFolderOrLabel, removeFolderOrLabelRow, addChildLabelRow, }), [ navDragBindings, selectedFolder, touchNav, onSelectFolder, getNavItemPrefs, setNavItemSidebarVisibility, setNavItemMessageVisibility, updateFolderOrLabelColor, renameFolderOrLabel, removeFolderOrLabelRow, addChildLabelRow, ] ) const panelSurfaceClass = isOverlayOpen && isXs ? MAIL_SIDEBAR_PANEL_SURFACE_MOBILE_CLASS : MAIL_SIDEBAR_PANEL_SURFACE_CLASS return { sidebarRef, touchNav, hoverExpanded, isExpanded, isOverlayOpen, panelSurfaceClass, navRailInset, splitViewLogoIconClass, splitViewLogoHeaderClass, handleMouseEnter, handleMouseLeave, navMoreOpen, setNavMoreOpen, visibleMainItems, primaryVisibleCategories, plusOnlyVisibleCategories, disabledSystemNavItems, folderTree, folderRowProps, collapsedFolderOpts, visibleNavLabelRows, labelRowProps, setLabelRowEnabled, folderDialogOpen, setFolderDialogOpen, labelDialogOpen, setLabelDialogOpen, newFolderName, setNewFolderName, newFolderParent, setNewFolderParent, newLabelName, setNewLabelName, newFolderNameInputRef, newLabelNameInputRef, folderParentOptions, handleSubmitNewFolder, handleSubmitNewLabel, } }