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">
{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 (
)
}
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 (
)
}
// 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