"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { Menu, ArrowLeft, Plus, Search, X, MoreVertical, Terminal, Bell } from "@/components/icons"; import { Button } from "@/components/ui/button"; import { useUIStore } from "@/stores/ui-store"; import { useShallow } from "zustand/react/shallow"; import { useSettingsStore } from "@/stores/settings-store"; import { useIsDesktop } from "@/hooks/use-media-query"; import { cn } from "@/lib/utils"; import { useTranslations } from "next-intl"; import { AccountSwitcher } from "@/components/layout/account-switcher"; import { PluginActions } from "@/components/plugins/plugin-actions"; import { useMenuNavigation } from "@/hooks/use-menu-navigation"; import { ContextMenuItem } from "@/components/ui/context-menu"; import { openCommandPalette } from "@/lib/app-commands"; import { NotificationSheet, formatBadgeCount, useUnreadNotificationCount } from "@/components/notifications/notification-center"; /** Plugin buttons in the top bar are not about any one item. */ const TOP_BAR_CONTEXT: Record = {}; /** * The phone header's overflow menu: the notifications (the desktop bell), the * command palette (no Ctrl+K on a phone) and what the desktop top bar shows * as buttons (plugin top-bar-actions) and has no room for here. */ function MobileHeaderMenu() { const t = useTranslations('top_bar'); const tPalette = useTranslations('command_palette'); const tNotifications = useTranslations('notification_center'); const unread = useUnreadNotificationCount(); const [showNotifications, setShowNotifications] = useState(false); const [open, setOpen] = useState(false); const triggerRef = useRef(null); const close = useCallback(() => setOpen(false), []); const { menuRef, onKeyDown } = useMenuNavigation({ open, onClose: close, triggerRef }); useEffect(() => { if (!open) return; const onPointerDown = (e: MouseEvent) => { if (triggerRef.current?.contains(e.target as Node) || menuRef.current?.contains(e.target as Node)) return; setOpen(false); }; document.addEventListener('mousedown', onPointerDown); return () => document.removeEventListener('mousedown', onPointerDown); }, [open, menuRef]); return (
{open && (
0 ? formatBadgeCount(unread) : undefined} onClick={() => { close(); setShowNotifications(true); }} /> { close(); openCommandPalette(); }} />
)} {showNotifications && setShowNotifications(false)} />}
); } interface MobileHeaderProps { title: string; showBack?: boolean; onBack?: () => void; onCompose?: () => void; onSearch?: () => void; className?: string; /** Id of the sidebar this header's menu button toggles. */ sidebarId?: string; /** * When set, the mailbox title is replaced by a tappable search field and * this fires instead. The folder name stays visible in the drawer, so the * bar spends its width on the thing you act on rather than a label. */ onOpenSearch?: () => void; /** Placeholder shown inside the search field. */ searchPlaceholder?: string; /** Clears the active query without opening the search panel. */ onClearSearch?: () => void; /** Whether a query is currently applied, which reveals the clear button. */ searchActive?: boolean; /** Mountain View: opens the merged across-accounts inbox from the avatar's popover. */ onSelectAllInboxes?: () => void; allInboxesSelected?: boolean; } export function MobileHeader({ title, showBack = false, onBack, onCompose, onSearch, className, sidebarId, onOpenSearch, searchPlaceholder, onClearSearch, searchActive = false, onSelectAllInboxes, allInboxesSelected = false, }: MobileHeaderProps) { const t = useTranslations('sidebar'); const { toggleSidebar, goBack, sidebarOpen } = useUIStore(useShallow((state) => ({ toggleSidebar: state.toggleSidebar, goBack: state.goBack, sidebarOpen: state.sidebarOpen }))); const mountainView = useSettingsStore((state) => state.interfaceLayout === 'mountain-view'); // Pane-aware: in Pro split mode the viewport is desktop-wide while the // pane is narrow. The Tailwind `lg:hidden` variant alone would never fire // there, so we additionally hide via JS when the surrounding pane is // desktop-sized. Outside of Pro this still returns the viewport value. const isPaneDesktop = useIsDesktop(); if (isPaneDesktop) return null; const handleLeftAction = () => { if (showBack && onBack) { onBack(); } else if (showBack) { goBack(); } else { toggleSidebar(); } }; return (
{/* Left action: Menu or Back button */}
{/* Title, unless the bar is in search-first mode */} {!onOpenSearch &&

{title}

}
{onOpenSearch && ( /* Not a single button: the clear control is a button of its own, and nesting one inside another is invalid markup. */
{searchActive && onClearSearch && ( )}
)} {/* Right actions */}
{onSearch && ( )} {onCompose && ( )} {/* Which account you are reading is otherwise only visible after opening the drawer. Last in this group so it sits on the far edge — opposite the menu button, and mirrored under RTL because the header is laid out with flex rather than fixed sides. */}
); }