mirror of
https://github.com/Buriburizaem0n/nezha-dash-v1.git
synced 2026-09-19 09:40:14 +00:00
fix: enhance loading state handling in NetworkChart component
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||||
import { m } from "framer-motion";
|
import { m } from "framer-motion";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
@@ -41,7 +41,6 @@ import {
|
|||||||
} from "@/lib/nezha-api";
|
} from "@/lib/nezha-api";
|
||||||
import { cn, formatTime } from "@/lib/utils";
|
import { cn, formatTime } from "@/lib/utils";
|
||||||
import type { NezhaMonitor, ServerMonitorChart } from "@/types/nezha-api";
|
import type { NezhaMonitor, ServerMonitorChart } from "@/types/nezha-api";
|
||||||
|
|
||||||
import NetworkChartLoading from "./NetworkChartLoading";
|
import NetworkChartLoading from "./NetworkChartLoading";
|
||||||
import { Label } from "./ui/label";
|
import { Label } from "./ui/label";
|
||||||
import { Switch } from "./ui/switch";
|
import { Switch } from "./ui/switch";
|
||||||
@@ -51,6 +50,8 @@ interface ResultItem {
|
|||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MIN_PERIOD_LOADING_MS = 500;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to calculate packet loss from delay data
|
* Helper method to calculate packet loss from delay data
|
||||||
*/
|
*/
|
||||||
@@ -148,10 +149,11 @@ export function NetworkChart({
|
|||||||
}
|
}
|
||||||
}, [isLogin, period]);
|
}, [isLogin, period]);
|
||||||
|
|
||||||
const { data: monitorData } = useQuery({
|
const { data: monitorData, isPlaceholderData } = useQuery({
|
||||||
queryKey: ["monitor", server_id, period],
|
queryKey: ["monitor", server_id, period],
|
||||||
queryFn: () => fetchMonitor(server_id, period),
|
queryFn: () => fetchMonitor(server_id, period),
|
||||||
enabled: show,
|
enabled: show,
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
refetchInterval: 10000,
|
refetchInterval: 10000,
|
||||||
@@ -215,6 +217,7 @@ export function NetworkChart({
|
|||||||
chartData={transformedData}
|
chartData={transformedData}
|
||||||
serverName={monitorData.data[0].server_name}
|
serverName={monitorData.data[0].server_name}
|
||||||
formattedData={formattedData}
|
formattedData={formattedData}
|
||||||
|
isPeriodLoading={isPlaceholderData}
|
||||||
period={period}
|
period={period}
|
||||||
onPeriodChange={setPeriod}
|
onPeriodChange={setPeriod}
|
||||||
isLogin={isLogin}
|
isLogin={isLogin}
|
||||||
@@ -228,6 +231,7 @@ export const NetworkChartClient = React.memo(function NetworkChart({
|
|||||||
chartData,
|
chartData,
|
||||||
serverName,
|
serverName,
|
||||||
formattedData,
|
formattedData,
|
||||||
|
isPeriodLoading,
|
||||||
period,
|
period,
|
||||||
onPeriodChange,
|
onPeriodChange,
|
||||||
isLogin,
|
isLogin,
|
||||||
@@ -237,11 +241,14 @@ export const NetworkChartClient = React.memo(function NetworkChart({
|
|||||||
chartData: ServerMonitorChart;
|
chartData: ServerMonitorChart;
|
||||||
serverName: string;
|
serverName: string;
|
||||||
formattedData: ResultItem[];
|
formattedData: ResultItem[];
|
||||||
|
isPeriodLoading: boolean;
|
||||||
period: MonitorPeriod;
|
period: MonitorPeriod;
|
||||||
onPeriodChange: (period: MonitorPeriod) => void;
|
onPeriodChange: (period: MonitorPeriod) => void;
|
||||||
isLogin: boolean;
|
isLogin: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [showPeriodLoading, setShowPeriodLoading] = React.useState(false);
|
||||||
|
const loadingStartedAtRef = React.useRef<number | null>(null);
|
||||||
|
|
||||||
const TIME_RANGE_OPTIONS: { value: MonitorPeriod; label: string }[] = [
|
const TIME_RANGE_OPTIONS: { value: MonitorPeriod; label: string }[] = [
|
||||||
{ value: "1d", label: t("monitor.period1d") },
|
{ value: "1d", label: t("monitor.period1d") },
|
||||||
@@ -249,6 +256,36 @@ export const NetworkChartClient = React.memo(function NetworkChart({
|
|||||||
{ value: "30d", label: t("monitor.period30d") },
|
{ 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 =
|
const customBackgroundImage =
|
||||||
(window.CustomBackgroundImage as string) !== ""
|
(window.CustomBackgroundImage as string) !== ""
|
||||||
? window.CustomBackgroundImage
|
? window.CustomBackgroundImage
|
||||||
@@ -631,7 +668,10 @@ export const NetworkChartClient = React.memo(function NetworkChart({
|
|||||||
)}
|
)}
|
||||||
<ChartContainer
|
<ChartContainer
|
||||||
config={chartConfig}
|
config={chartConfig}
|
||||||
className="aspect-auto h-[250px] w-full"
|
className={cn(
|
||||||
|
"aspect-auto h-62.5 w-full transition-opacity",
|
||||||
|
showPeriodLoading && "opacity-60",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ComposedChart
|
<ComposedChart
|
||||||
accessibilityLayer
|
accessibilityLayer
|
||||||
@@ -746,6 +786,15 @@ export const NetworkChartClient = React.memo(function NetworkChart({
|
|||||||
{chartElements}
|
{chartElements}
|
||||||
</ComposedChart>
|
</ComposedChart>
|
||||||
</ChartContainer>
|
</ChartContainer>
|
||||||
|
{showPeriodLoading && (
|
||||||
|
<div className="pointer-events-none absolute inset-0 z-20 flex items-center justify-center rounded-md backdrop-blur-[1px]">
|
||||||
|
<div className="flex size-9 items-center justify-center">
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
|
<div className="size-4 rounded-full border-2 border-muted-foreground/20 border-t-muted-foreground/70 animate-spin" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -40,12 +40,14 @@ const RESPONSIVE_CHART_TYPES = new Set<React.ElementType>([
|
|||||||
function shouldUseResponsiveContainer(children: React.ReactNode) {
|
function shouldUseResponsiveContainer(children: React.ReactNode) {
|
||||||
const elements = React.Children.toArray(children);
|
const elements = React.Children.toArray(children);
|
||||||
|
|
||||||
return elements.length > 0 &&
|
return (
|
||||||
|
elements.length > 0 &&
|
||||||
elements.every(
|
elements.every(
|
||||||
(child) =>
|
(child) =>
|
||||||
React.isValidElement(child) &&
|
React.isValidElement(child) &&
|
||||||
RESPONSIVE_CHART_TYPES.has(child.type as React.ElementType),
|
RESPONSIVE_CHART_TYPES.has(child.type as React.ElementType),
|
||||||
);
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function useChart() {
|
function useChart() {
|
||||||
|
|||||||
Reference in New Issue
Block a user