import { useQuery } from "@tanstack/react-query"; import { AnimatePresence, m } from "framer-motion"; import { ImageMinus } from "lucide-react"; import { DateTime } from "luxon"; import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { ModeToggle } from "@/components/ThemeSwitcher"; import { Separator } from "@/components/ui/separator"; import { Skeleton } from "@/components/ui/skeleton"; import { useBackground } from "@/hooks/use-background"; import { useWebSocketContext } from "@/hooks/use-websocket-context"; import { fetchLoginUser, fetchSetting } from "@/lib/nezha-api"; import { cn } from "@/lib/utils"; import AnimateCountClient from "./AnimatedCount"; import { LanguageSwitcher } from "./LanguageSwitcher"; import { Loader, LoadingSpinner } from "./loading/Loader"; import { SearchButton } from "./SearchButton"; import { Button } from "./ui/button"; interface TimeState { hh: number; mm: number; ss: number; } const useCurrentTime = () => { const [time, setTime] = useState({ hh: DateTime.now().setLocale("en-US").hour, mm: DateTime.now().setLocale("en-US").minute, ss: DateTime.now().setLocale("en-US").second, }); useEffect(() => { const intervalId = setInterval(() => { const now = DateTime.now().setLocale("en-US"); setTime({ hh: now.hour, mm: now.minute, ss: now.second, }); }, 1000); return () => clearInterval(intervalId); }, []); return time; }; function Header() { const { t } = useTranslation(); const navigate = useNavigate(); const { backgroundImage, updateBackground } = useBackground(); const { data: settingData, isLoading } = useQuery({ queryKey: ["setting"], queryFn: () => fetchSetting(), refetchOnMount: true, refetchOnWindowFocus: true, }); const { lastMessage, connected } = useWebSocketContext(); const onlineCount = connected ? lastMessage ? JSON.parse(lastMessage.data).online || 0 : 0 : "..."; const siteName = settingData?.data?.config?.site_name; const customLogo = window.CustomLogo || "/apple-touch-icon.png"; const customDesc = window.CustomDesc || t("nezha"); const customMobileBackgroundImage = window.CustomMobileBackgroundImage !== "" ? window.CustomMobileBackgroundImage : undefined; useEffect(() => { const link = document.querySelector("link[rel*='icon']") || document.createElement("link"); // @ts-expect-error set link.type link.type = "image/x-icon"; // @ts-expect-error set link.rel link.rel = "shortcut icon"; // @ts-expect-error set link.href link.href = customLogo; document.getElementsByTagName("head")[0].appendChild(link); }, [customLogo]); useEffect(() => { document.title = siteName || "哪吒监控 Nezha Monitoring"; }, [siteName]); const handleBackgroundToggle = () => { if (window.CustomBackgroundImage) { // Store the current background image before removing it sessionStorage.setItem( "savedBackgroundImage", window.CustomBackgroundImage, ); updateBackground(undefined); } else { // Restore the saved background image const savedImage = sessionStorage.getItem("savedBackgroundImage"); if (savedImage) { updateBackground(savedImage); } } }; const customBackgroundImage = backgroundImage; return (
{ sessionStorage.removeItem("selectedGroup"); navigate("/"); }} className="cursor-pointer flex items-center sm:text-base text-sm font-medium" >
apple-touch-icon
{isLoading ? ( ) : ( siteName || "NEZHA" )}

{customDesc}

{(customBackgroundImage || sessionStorage.getItem("savedBackgroundImage")) && ( )}
); } type links = { link: string; name: string; }; function Links() { const customLinks = window.CustomLinks as string; const links: links[] | null = customLinks ? JSON.parse(customLinks) : null; if (!links) return null; return (
{links.map((link, index) => { return ( {link.name} ); })}
); } export function RefreshToast() { const { t } = useTranslation(); const navigate = useNavigate(); const { needReconnect } = useWebSocketContext(); if (!needReconnect) { return null; } if (needReconnect) { sessionStorage.removeItem("needRefresh"); setTimeout(() => { navigate(0); }, 1000); } return (

{t("refreshing")}...

); } function DashboardLink() { const { t } = useTranslation(); const { setNeedReconnect } = useWebSocketContext(); const previousLoginState = useRef(null); const { data: userData, isFetched, isLoadingError, isError, refetch, } = useQuery({ queryKey: ["login-user"], queryFn: () => fetchLoginUser(), refetchOnMount: false, refetchOnWindowFocus: true, refetchIntervalInBackground: true, refetchInterval: 1000 * 30, retry: 0, }); const isLogin = isError ? false : userData ? !!userData?.data?.id && !!document.cookie : false; if (isLoadingError) { previousLoginState.current = isLogin; } useEffect(() => { refetch(); }, [refetch]); useEffect(() => { if (isFetched || isError) { // 只有当登录状态发生变化时才设置needReconnect if ( previousLoginState.current !== null && previousLoginState.current !== isLogin ) { setNeedReconnect(true); } previousLoginState.current = isLogin; } }, [isLogin, isError, isFetched, setNeedReconnect]); return (
{!isLogin && t("login")} {isLogin && t("dashboard")}
); } function Overview() { const { t } = useTranslation(); const time = useCurrentTime(); const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); }, []); return (

👋 {t("overview")}

{t("whereTheTimeIs")}

{mounted ? (
: :
) : ( )}
); } export default Header;