import { useEffect, useMemo, useState } from "react"; import { useLockFn } from "ahooks"; import { Box, Button, MenuItem, Paper, Select, TextField } from "@mui/material"; import { Virtuoso } from "react-virtuoso"; import { useTranslation } from "react-i18next"; import { closeAllConnections, getInformation } from "@/services/api"; import BasePage from "@/components/base/base-page"; import BaseEmpty from "@/components/base/base-empty"; import ConnectionItem from "@/components/connection/connection-item"; const initConn = { uploadTotal: 0, downloadTotal: 0, connections: [] }; type OrderFunc = (list: ApiType.ConnectionsItem[]) => ApiType.ConnectionsItem[]; const ConnectionsPage = () => { const { t, i18n } = useTranslation(); const [filterText, setFilterText] = useState(""); const [curOrderOpt, setOrderOpt] = useState("Default"); const [connData, setConnData] = useState(initConn); const orderOpts: Record = { Default: (list) => list, // "Download Traffic": (list) => list, // "Upload Traffic": (list) => list, "Upload Speed": (list) => list.sort((a, b) => b.curUpload! - a.curUpload!), "Download Speed": (list) => list.sort((a, b) => b.curDownload! - a.curDownload!), }; const filterConn = useMemo(() => { const orderFunc = orderOpts[curOrderOpt]; const connetions = connData.connections.filter((conn) => (conn.metadata.host || conn.metadata.destinationIP)?.includes(filterText) ); if (orderFunc) return orderFunc(connetions); return connetions; }, [connData, filterText, curOrderOpt]); useEffect(() => { let ws: WebSocket | null = null; getInformation().then((result) => { const { server = "", secret = "" } = result; ws = new WebSocket(`ws://${server}/connections?token=${secret}`); ws.addEventListener("message", (event) => { const data = JSON.parse(event.data) as ApiType.Connections; // 与前一次connections的展示顺序尽量保持一致 setConnData((old) => { const oldConn = old.connections; const maxLen = data.connections.length; const connections: typeof oldConn = []; const rest = data.connections.filter((each) => { const index = oldConn.findIndex((o) => o.id === each.id); if (index >= 0 && index < maxLen) { const old = oldConn[index]; each.curUpload = each.upload - old.upload; each.curDownload = each.download - old.download; connections[index] = each; return false; } return true; }); for (let i = 0; i < maxLen; ++i) { if (!connections[i] && rest.length > 0) { connections[i] = rest.shift()!; connections[i].curUpload = 0; connections[i].curDownload = 0; } } return { ...data, connections }; }); }); }); return () => ws?.close(); }, []); const onCloseAll = useLockFn(closeAllConnections); return ( {t("Close All")} } > setFilterText(e.target.value)} sx={{ input: { py: 0.65, px: 1.25 } }} /> {filterConn.length > 0 ? ( } /> ) : ( )} ); }; export default ConnectionsPage;