diff --git a/src/components/NetworkChart.tsx b/src/components/NetworkChart.tsx index e38e870..166edd9 100644 --- a/src/components/NetworkChart.tsx +++ b/src/components/NetworkChart.tsx @@ -1,6 +1,6 @@ "use client"; -import { useQuery } from "@tanstack/react-query"; +import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { m } from "framer-motion"; import * as React from "react"; import { useCallback, useMemo } from "react"; @@ -41,7 +41,6 @@ import { } from "@/lib/nezha-api"; import { cn, formatTime } from "@/lib/utils"; import type { NezhaMonitor, ServerMonitorChart } from "@/types/nezha-api"; - import NetworkChartLoading from "./NetworkChartLoading"; import { Label } from "./ui/label"; import { Switch } from "./ui/switch"; @@ -51,6 +50,8 @@ interface ResultItem { [key: string]: number; } +const MIN_PERIOD_LOADING_MS = 500; + /** * Helper method to calculate packet loss from delay data */ @@ -148,10 +149,11 @@ export function NetworkChart({ } }, [isLogin, period]); - const { data: monitorData } = useQuery({ + const { data: monitorData, isPlaceholderData } = useQuery({ queryKey: ["monitor", server_id, period], queryFn: () => fetchMonitor(server_id, period), enabled: show, + placeholderData: keepPreviousData, refetchOnMount: true, refetchOnWindowFocus: true, refetchInterval: 10000, @@ -215,6 +217,7 @@ export function NetworkChart({ chartData={transformedData} serverName={monitorData.data[0].server_name} formattedData={formattedData} + isPeriodLoading={isPlaceholderData} period={period} onPeriodChange={setPeriod} isLogin={isLogin} @@ -228,6 +231,7 @@ export const NetworkChartClient = React.memo(function NetworkChart({ chartData, serverName, formattedData, + isPeriodLoading, period, onPeriodChange, isLogin, @@ -237,11 +241,14 @@ export const NetworkChartClient = React.memo(function NetworkChart({ chartData: ServerMonitorChart; serverName: string; formattedData: ResultItem[]; + isPeriodLoading: boolean; period: MonitorPeriod; onPeriodChange: (period: MonitorPeriod) => void; isLogin: boolean; }) { const { t } = useTranslation(); + const [showPeriodLoading, setShowPeriodLoading] = React.useState(false); + const loadingStartedAtRef = React.useRef(null); const TIME_RANGE_OPTIONS: { value: MonitorPeriod; label: string }[] = [ { value: "1d", label: t("monitor.period1d") }, @@ -249,6 +256,36 @@ export const NetworkChartClient = React.memo(function NetworkChart({ { value: "30d", label: t("monitor.period30d") }, ]; + React.useEffect(() => { + let timeoutId: number | undefined; + + if (isPeriodLoading) { + loadingStartedAtRef.current = Date.now(); + setShowPeriodLoading(true); + return; + } + + const loadingStartedAt = loadingStartedAtRef.current; + if (loadingStartedAt === null) { + setShowPeriodLoading(false); + return; + } + + const elapsed = Date.now() - loadingStartedAt; + const remaining = Math.max(0, MIN_PERIOD_LOADING_MS - elapsed); + + timeoutId = window.setTimeout(() => { + setShowPeriodLoading(false); + loadingStartedAtRef.current = null; + }, remaining); + + return () => { + if (timeoutId !== undefined) { + window.clearTimeout(timeoutId); + } + }; + }, [isPeriodLoading]); + const customBackgroundImage = (window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage @@ -631,7 +668,10 @@ export const NetworkChartClient = React.memo(function NetworkChart({ )} + {showPeriodLoading && ( +
+
+
+
+
+
+
+ )}
diff --git a/src/components/ui/chart.tsx b/src/components/ui/chart.tsx index d93fae1..3f7c26b 100644 --- a/src/components/ui/chart.tsx +++ b/src/components/ui/chart.tsx @@ -40,12 +40,14 @@ const RESPONSIVE_CHART_TYPES = new Set([ function shouldUseResponsiveContainer(children: React.ReactNode) { const elements = React.Children.toArray(children); - return elements.length > 0 && + return ( + elements.length > 0 && elements.every( (child) => React.isValidElement(child) && RESPONSIVE_CHART_TYPES.has(child.type as React.ElementType), - ); + ) + ); } function useChart() {