import { ModeToggle } from "@/components/ThemeSwitcher" import { Separator } from "@/components/ui/separator" import { Skeleton } from "@/components/ui/skeleton" import { useWebSocketContext } from "@/hooks/use-websocket-context" import { fetchLoginUser, fetchSetting } from "@/lib/nezha-api" import { cn } from "@/lib/utils" import { useQuery } from "@tanstack/react-query" import { AnimatePresence, m } from "framer-motion" import { DateTime } from "luxon" import { useEffect, useRef, useState } from "react" import { useTranslation } from "react-i18next" import { useNavigate } from "react-router-dom" import { LanguageSwitcher } from "./LanguageSwitcher" import { Loader, LoadingSpinner } from "./loading/Loader" import { Button } from "./ui/button" function Header() { const { t } = useTranslation() const navigate = useNavigate() 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?.site_name // @ts-expect-error CustomLogo is a global variable const customLogo = window.CustomLogo || "/apple-touch-icon.png" // @ts-expect-error CustomDesc is a global variable const customDesc = window.CustomDesc || t("nezha") const customBackgroundImage = // @ts-expect-error CustomBackgroundImage is a global variable (window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : 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]) return (
navigate("/")} className="cursor-pointer flex items-center sm:text-base text-sm font-medium">
apple-touch-icon
{isLoading ? : siteName || "NEZHA"}

{customDesc}

) } type links = { link: string name: string } function Links() { // @ts-expect-error CustomLinks is a global variable 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() }, [document.cookie]) useEffect(() => { if (isFetched || isError) { // 只有当登录状态发生变化时才设置needReconnect if (previousLoginState.current !== null && previousLoginState.current !== isLogin) { setNeedReconnect(true) } previousLoginState.current = isLogin } }, [isLogin]) return (
{!isLogin && t("login")} {isLogin && t("dashboard")}
) } // https://github.com/streamich/react-use/blob/master/src/useInterval.ts const useInterval = (callback: () => void, delay: number | null) => { const savedCallback = useRef<() => void>(() => {}) useEffect(() => { savedCallback.current = callback }) useEffect(() => { if (delay !== null) { const interval = setInterval(() => savedCallback.current(), delay || 0) return () => clearInterval(interval) } return undefined }, [delay]) } function Overview() { const { t } = useTranslation() const [mouted, setMounted] = useState(false) useEffect(() => { setMounted(true) }, []) const timeOption = DateTime.TIME_SIMPLE timeOption.hour12 = true const [timeString, setTimeString] = useState(DateTime.now().setLocale("en-US").toLocaleString(timeOption)) useInterval(() => { setTimeString(DateTime.now().setLocale("en-US").toLocaleString(timeOption)) }, 1000) return (

👋 {t("overview")}

{t("whereTheTimeIs")}

{mouted ? (

{timeString}

) : ( )}
) } export default Header