Fix port traffic wall blank chart when enabling baseline compare.

Set uPlot data before toggling baseline series, keep previous compare data while refetching, and resize after legend layout changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-01 07:34:14 +08:00
parent 653baa1b9f
commit 5e940252e6
2 changed files with 33 additions and 13 deletions

View file

@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient, keepPreviousData } from "@tanstack/react-query";
import { import {
collectPortTrafficNow, collectPortTrafficNow,
createPortTrafficTask, createPortTrafficTask,
@ -17,13 +17,14 @@ import {
import { queryKeys } from "../../constants/queryKeys"; import { queryKeys } from "../../constants/queryKeys";
import { useI18n } from "../../i18n"; import { useI18n } from "../../i18n";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../hooks/useToast";
import type { CliTargetItem, PortTrafficDiscoverPort, PortTrafficTargetIn } from "../../types"; import type { CliTargetItem, PortTrafficDiscoverPort, PortTrafficSamplePoint, PortTrafficTargetIn } from "../../types";
import { pageCount } from "../../utils/display"; import { pageCount } from "../../utils/display";
import { formatSystemTime } from "../../utils/time"; import { formatSystemTime } from "../../utils/time";
import { PortTrafficWall } from "./PortTrafficWall"; import { PortTrafficWall } from "./PortTrafficWall";
const POLL_MS = 5000; const POLL_MS = 5000;
const TARGET_PAGE_SIZE = 20; const TARGET_PAGE_SIZE = 20;
const EMPTY_COMPARE_POINTS: PortTrafficSamplePoint[] = [];
type PortPick = PortTrafficTargetIn & { key: string }; type PortPick = PortTrafficTargetIn & { key: string };
type BaselineMode = "off" | "shift" | "day" | "week" | "custom"; type BaselineMode = "off" | "shift" | "day" | "week" | "custom";
@ -136,12 +137,19 @@ export function PortTrafficPage() {
}), }),
enabled: view === "wall" && Boolean(wallTargetId), enabled: view === "wall" && Boolean(wallTargetId),
staleTime: 1000, staleTime: 1000,
placeholderData: keepPreviousData,
refetchInterval: (q) => { refetchInterval: (q) => {
const n = q.state.data?.current?.length ?? 0; const n = q.state.data?.current?.length ?? 0;
return n === 0 ? 2500 : POLL_MS; return n === 0 ? 2500 : POLL_MS;
}, },
}); });
const wallPoints = compareQuery.data?.current ?? EMPTY_COMPARE_POINTS;
const wallBaselinePoints =
baseline === "off" && !mapBaselineTargetId
? EMPTY_COMPARE_POINTS
: (compareQuery.data?.baseline ?? EMPTY_COMPARE_POINTS);
const invalidateAll = () => { const invalidateAll = () => {
void queryClient.invalidateQueries({ queryKey: queryKeys.portTrafficTasksAll }); void queryClient.invalidateQueries({ queryKey: queryKeys.portTrafficTasksAll });
void queryClient.invalidateQueries({ queryKey: queryKeys.portTrafficDashboard }); void queryClient.invalidateQueries({ queryKey: queryKeys.portTrafficDashboard });
@ -788,12 +796,8 @@ export function PortTrafficPage() {
) : null} ) : null}
<PortTrafficWall <PortTrafficWall
target={selectedWallTarget} target={selectedWallTarget}
points={compareQuery.data?.current || []} points={wallPoints}
baselinePoints={ baselinePoints={wallBaselinePoints}
baseline === "off" && !mapBaselineTargetId
? []
: compareQuery.data?.baseline || []
}
rangeLabel={`${rangeHours}h${ rangeLabel={`${rangeHours}h${
baseline === "off" baseline === "off"
? "" ? ""

View file

@ -65,6 +65,8 @@ function chartSize(el: HTMLElement): { width: number; height: number } {
return { width, height }; return { width, height };
} }
const EMPTY_POINTS: PortTrafficSamplePoint[] = [];
type Props = { type Props = {
target: PortTrafficTarget | null; target: PortTrafficTarget | null;
points: PortTrafficSamplePoint[]; points: PortTrafficSamplePoint[];
@ -77,7 +79,7 @@ type Props = {
export function PortTrafficWall({ export function PortTrafficWall({
target, target,
points, points,
baselinePoints = [], baselinePoints = EMPTY_POINTS,
rangeLabel, rangeLabel,
loading, loading,
hint, hint,
@ -287,11 +289,25 @@ export function PortTrafficWall({
useEffect(() => { useEffect(() => {
const plot = plotRef.current; const plot = plotRef.current;
if (!plot) return; if (!plot) return;
// Always setData with resetScales; toggling series.show before setData can leave
// uPlot stuck on a blank [0,1] y-range until a later update.
const data = toAlignedSeries(points, baselinePoints);
const showBaseline = baselinePoints.length > 0; const showBaseline = baselinePoints.length > 0;
plot.setSeries(3, { show: showBaseline }); plot.setData(data, true);
plot.setSeries(4, { show: showBaseline }); const s3 = Boolean(plot.series[3]?.show);
plot.setData(toAlignedSeries(points, baselinePoints)); const s4 = Boolean(plot.series[4]?.show);
if (mountRef.current) plot.setSize(chartSize(mountRef.current)); if (s3 !== showBaseline || s4 !== showBaseline) {
plot.setSeries(3, { show: showBaseline }, false);
plot.setSeries(4, { show: showBaseline }, false);
}
const el = mountRef.current;
if (el) {
// Legend show/hide changes layout; size after paint.
requestAnimationFrame(() => {
if (!plotRef.current || !mountRef.current) return;
plotRef.current.setSize(chartSize(mountRef.current));
});
}
}, [points, baselinePoints]); }, [points, baselinePoints]);
const empty = points.length === 0 && baselinePoints.length === 0; const empty = points.length === 0 && baselinePoints.length === 0;