Files
clash-verge-rev-lite/src/components/home/enhanced-traffic-stats.tsx
Tunglies 627119bb22 Refactor imports and improve code organization across multiple components and hooks
- Consolidated and reordered imports in various files for better readability and maintainability.
- Removed unused imports and ensured consistent import styles.
- Enhanced the structure of components by grouping related imports together.
- Updated the layout and organization of hooks to streamline functionality.
- Improved the overall code quality by following best practices in import management.
2025-09-18 23:34:38 +08:00

350 lines
9.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
ArrowUpwardRounded,
ArrowDownwardRounded,
MemoryRounded,
LinkRounded,
CloudUploadRounded,
CloudDownloadRounded,
} from "@mui/icons-material";
import {
Typography,
Paper,
alpha,
useTheme,
PaletteColor,
Grid,
Box,
} from "@mui/material";
import { useRef, useCallback, memo, useMemo } from "react";
import { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import useSWR from "swr";
import {
EnhancedCanvasTrafficGraph,
type EnhancedCanvasTrafficGraphRef,
} from "./enhanced-canvas-traffic-graph";
import { TrafficErrorBoundary } from "@/components/common/traffic-error-boundary";
import { useTrafficDataEnhanced } from "@/hooks/use-traffic-monitor";
import { useVerge } from "@/hooks/use-verge";
import { useVisibility } from "@/hooks/use-visibility";
import { useAppData } from "@/providers/app-data-provider";
import { isDebugEnabled, gc } from "@/services/cmds";
import parseTraffic from "@/utils/parse-traffic";
interface StatCardProps {
icon: ReactNode;
title: string;
value: string | number;
unit: string;
color: "primary" | "secondary" | "error" | "warning" | "info" | "success";
onClick?: () => void;
}
// 全局变量类型定义
declare global {
interface Window {
animationFrameId?: number;
lastTrafficData?: {
up: number;
down: number;
};
}
}
// 统计卡片组件 - 使用memo优化
const CompactStatCard = memo(
({ icon, title, value, unit, color, onClick }: StatCardProps) => {
const theme = useTheme();
// 获取调色板颜色 - 使用useMemo避免重复计算
const colorValue = useMemo(() => {
const palette = theme.palette;
if (
color in palette &&
palette[color as keyof typeof palette] &&
"main" in (palette[color as keyof typeof palette] as PaletteColor)
) {
return (palette[color as keyof typeof palette] as PaletteColor).main;
}
return palette.primary.main;
}, [theme.palette, color]);
return (
<Paper
elevation={0}
sx={{
display: "flex",
alignItems: "center",
borderRadius: 2,
bgcolor: alpha(colorValue, 0.05),
border: `1px solid ${alpha(colorValue, 0.15)}`,
padding: "8px",
transition: "all 0.2s ease-in-out",
cursor: onClick ? "pointer" : "default",
"&:hover": onClick
? {
bgcolor: alpha(colorValue, 0.1),
border: `1px solid ${alpha(colorValue, 0.3)}`,
boxShadow: `0 4px 8px rgba(0,0,0,0.05)`,
}
: {},
}}
onClick={onClick}
>
{/* 图标容器 */}
<Grid
component="div"
sx={{
mr: 1,
ml: "2px",
display: "flex",
alignItems: "center",
justifyContent: "center",
width: 32,
height: 32,
borderRadius: "50%",
bgcolor: alpha(colorValue, 0.1),
color: colorValue,
}}
>
{icon}
</Grid>
{/* 文本内容 */}
<Grid component="div" sx={{ flexGrow: 1, minWidth: 0 }}>
<Typography variant="caption" color="text.secondary" noWrap>
{title}
</Typography>
<Grid
component="div"
sx={{ display: "flex", alignItems: "baseline" }}
>
<Typography
variant="body1"
fontWeight="bold"
noWrap
sx={{ mr: 0.5 }}
>
{value}
</Typography>
<Typography variant="caption" color="text.secondary">
{unit}
</Typography>
</Grid>
</Grid>
</Paper>
);
},
);
// 添加显示名称
CompactStatCard.displayName = "CompactStatCard";
export const EnhancedTrafficStats = () => {
const { t } = useTranslation();
const theme = useTheme();
const { verge } = useVerge();
const trafficRef = useRef<EnhancedCanvasTrafficGraphRef>(null);
const pageVisible = useVisibility();
// 使用AppDataProvider
const { connections } = useAppData();
// 使用增强版的统一流量数据Hook
const { traffic, memory, isLoading, isDataFresh, hasValidData } =
useTrafficDataEnhanced();
// 是否显示流量图表
const trafficGraph = verge?.traffic_graph ?? true;
// 检查是否支持调试
// TODO: merge this hook with layout-traffic.tsx
const { data: isDebug } = useSWR(
`clash-verge-rev-internal://isDebugEnabled`,
() => isDebugEnabled(),
{
// default value before is fetched
fallbackData: false,
},
);
// Canvas组件现在直接从全局Hook获取数据无需手动添加数据点
// 执行垃圾回收
const handleGarbageCollection = useCallback(async () => {
if (isDebug) {
try {
await gc();
console.log("[Debug] 垃圾回收已执行");
} catch (err) {
console.error("[Debug] 垃圾回收失败:", err);
}
}
}, [isDebug]);
// 使用useMemo计算解析后的流量数据
const parsedData = useMemo(() => {
const [up, upUnit] = parseTraffic(traffic?.raw?.up_rate || 0);
const [down, downUnit] = parseTraffic(traffic?.raw?.down_rate || 0);
const [inuse, inuseUnit] = parseTraffic(memory?.raw?.inuse || 0);
const [uploadTotal, uploadTotalUnit] = parseTraffic(
connections.uploadTotal,
);
const [downloadTotal, downloadTotalUnit] = parseTraffic(
connections.downloadTotal,
);
return {
up,
upUnit,
down,
downUnit,
inuse,
inuseUnit,
uploadTotal,
uploadTotalUnit,
downloadTotal,
downloadTotalUnit,
connectionsCount: connections.count,
};
}, [traffic, memory, connections]);
// 渲染流量图表 - 使用useMemo缓存渲染结果
const trafficGraphComponent = useMemo(() => {
if (!trafficGraph || !pageVisible) return null;
return (
<Paper
elevation={0}
sx={{
height: 130,
cursor: "pointer",
border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
borderRadius: 2,
overflow: "hidden",
}}
onClick={() => trafficRef.current?.toggleStyle()}
>
<div style={{ height: "100%", position: "relative" }}>
<EnhancedCanvasTrafficGraph ref={trafficRef} />
{isDebug && (
<div
style={{
position: "absolute",
top: "2px",
left: "2px",
zIndex: 10,
backgroundColor: "rgba(0,0,0,0.5)",
color: "white",
fontSize: "8px",
padding: "2px 4px",
borderRadius: "4px",
}}
>
DEBUG: {trafficRef.current ? "图表已初始化" : "图表未初始化"}
<br />
: {isDataFresh ? "active" : "inactive"}
<br />
: {traffic?.is_fresh ? "Fresh" : "Stale"}
<br />
{new Date().toISOString().slice(11, 19)}
</div>
)}
</div>
</Paper>
);
}, [trafficGraph, pageVisible, theme.palette.divider, isDebug]);
// 使用useMemo计算统计卡片配置
const statCards = useMemo(
() => [
{
icon: <ArrowUpwardRounded fontSize="small" />,
title: t("Upload Speed"),
value: parsedData.up,
unit: `${parsedData.upUnit}/s`,
color: "secondary" as const,
},
{
icon: <ArrowDownwardRounded fontSize="small" />,
title: t("Download Speed"),
value: parsedData.down,
unit: `${parsedData.downUnit}/s`,
color: "primary" as const,
},
{
icon: <LinkRounded fontSize="small" />,
title: t("Active Connections"),
value: parsedData.connectionsCount,
unit: "",
color: "success" as const,
},
{
icon: <CloudUploadRounded fontSize="small" />,
title: t("Uploaded"),
value: parsedData.uploadTotal,
unit: parsedData.uploadTotalUnit,
color: "secondary" as const,
},
{
icon: <CloudDownloadRounded fontSize="small" />,
title: t("Downloaded"),
value: parsedData.downloadTotal,
unit: parsedData.downloadTotalUnit,
color: "primary" as const,
},
{
icon: <MemoryRounded fontSize="small" />,
title: t("Memory Usage"),
value: parsedData.inuse,
unit: parsedData.inuseUnit,
color: "error" as const,
onClick: isDebug ? handleGarbageCollection : undefined,
},
],
[t, parsedData, isDebug, handleGarbageCollection],
);
return (
<TrafficErrorBoundary
onError={(error, errorInfo) => {
console.error("[EnhancedTrafficStats] 组件错误:", error, errorInfo);
}}
>
<Grid container spacing={1} columns={{ xs: 8, sm: 8, md: 12 }}>
{trafficGraph && (
<Grid size={12}>
{/* 流量图表区域 */}
{trafficGraphComponent}
</Grid>
)}
{/* 统计卡片区域 */}
{statCards.map((card, index) => (
<Grid key={index} size={4}>
<CompactStatCard {...card} />
</Grid>
))}
{/* 数据状态指示器(调试用)*/}
{isDebug && (
<Grid size={12}>
<Box
sx={{
p: 1,
bgcolor: "action.hover",
borderRadius: 1,
fontSize: "0.75rem",
}}
>
: {isDataFresh ? "新鲜" : "过期"} | :{" "}
{hasValidData ? "是" : "否"} | : {isLoading ? "是" : "否"}
</Box>
</Grid>
)}
</Grid>
</TrafficErrorBoundary>
);
};