"use client"; import Link from "next/link"; import Image from "next/image"; import { usePathname } from "next/navigation"; import { useEffect, useRef, useState } from "react"; import { useLanguage } from "../i18n/LanguageProvider"; import { Menu, X, Sun, Moon, Monitor, Plus, BarChart2 } from "lucide-react"; import { translations } from "../i18n/translations"; import LanguageToggle from "./LanguageToggle"; import { AskAIButton } from "./AskAIButton"; import ReleaseChannelSelector from "./ReleaseChannelSelector"; import { useUserStore } from "@lib/userStore"; import { useMoltbotStore } from "@lib/moltbotStore"; import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; import { createNavConfig, filterNavItems, type NavItem, type ReleaseChannel, DEFAULT_CHANNELS, RELEASE_CHANNEL_STORAGE_KEY, CHANNEL_ORDER, } from "@lib/navigation"; const getLabel = ( label: string | ((lang: string) => string), lang: string, ): string => { return typeof label === "function" ? label(lang) : label; }; export default function UnifiedNavigation() { const pathname = usePathname(); const [menuOpen, setMenuOpen] = useState(false); const [selectedChannels, setSelectedChannels] = useState([ "stable", ]); const navRef = useRef(null); const { language } = useLanguage(); const user = useUserStore((state) => state.user); const { setIsOpen, setMode } = useMoltbotStore(); const nav = translations[language].nav; const accountCopy = nav.account; const accountInitial = user?.username?.charAt(0)?.toUpperCase() ?? user?.email?.charAt(0)?.toUpperCase() ?? "?"; const [accountMenuOpen, setAccountMenuOpen] = useState(false); const accountMenuRef = useRef(null); const isChinese = language === "zh"; useEffect(() => { if (typeof window === "undefined") return; const stored = window.localStorage.getItem(RELEASE_CHANNEL_STORAGE_KEY); if (!stored) return; try { const parsed = JSON.parse(stored) as unknown; if (!Array.isArray(parsed)) return; const normalized = CHANNEL_ORDER.filter((channel) => parsed.includes(channel), ); if (normalized.length === 0) return; const restored: ReleaseChannel[] = normalized.includes("stable") ? normalized : [...DEFAULT_CHANNELS, ...normalized]; setSelectedChannels((current) => { if ( current.length === restored.length && current.every((value, index) => value === restored[index]) ) { return current; } return restored; }); } catch (error) { console.warn("Failed to restore release channels selection", error); } }, []); useEffect(() => { if (typeof window === "undefined") return; window.localStorage.setItem( RELEASE_CHANNEL_STORAGE_KEY, JSON.stringify(selectedChannels), ); }, [selectedChannels]); useEffect(() => { if (!accountMenuOpen) { return; } const handleClickOutside = (event: MouseEvent) => { if ( accountMenuRef.current && !accountMenuRef.current.contains(event.target as Node) ) { setAccountMenuOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [accountMenuOpen]); useEffect(() => { setAccountMenuOpen(false); }, [user]); useEffect(() => { if (typeof window === "undefined") { return; } const element = navRef.current; if (!element) { return; } const updateOffset = () => { const height = element.getBoundingClientRect().height; document.documentElement.style.setProperty( "--app-shell-nav-offset", `${height}px`, ); }; updateOffset(); const resizeObserver = new ResizeObserver(() => { updateOffset(); }); resizeObserver.observe(element); window.addEventListener("resize", updateOffset); return () => { window.removeEventListener("resize", updateOffset); resizeObserver.disconnect(); }; }, []); const toggleChannel = (channel: ReleaseChannel) => { if (channel === "stable") return; setSelectedChannels((prev) => prev.includes(channel) ? prev.filter((value) => value !== channel) : [...prev, channel], ); }; const { mainNav, secondaryNav, accountNav } = createNavConfig( language, !!user, !!user?.isAdmin, !!user?.isOperator, ); const filteredMainNav = filterNavItems(mainNav, user); const filteredSecondaryNav = filterNavItems(secondaryNav, user); const isHiddenRoute = pathname ? [ "/login", "/register", "/xstream", "/xcloudflow", "/xscopehub", "/blogs", ].some((prefix) => pathname.startsWith(prefix)) : false; if (isHiddenRoute) { return null; } const isActive = (item: NavItem): boolean => { if (item.active) { return item.active(pathname || ""); } return pathname === item.href; }; return ( <>
); }