import { useEffect, useMemo, useState } from "react"; import { useLockFn } from "ahooks"; import { Box, Button, IconButton, MenuItem, Paper, Select, TextField, } from "@mui/material"; import { useRecoilState } from "recoil"; import { Virtuoso } from "react-virtuoso"; import { useTranslation } from "react-i18next"; import { TableChartRounded, TableRowsRounded } from "@mui/icons-material"; import { closeAllConnections, getInformation } from "@/services/api"; import { atomConnectionSetting } from "@/services/states"; import { BaseEmpty, BasePage } from "@/components/base"; import ConnectionItem from "@/components/connection/connection-item"; import ConnectionTable from "@/components/connection/connection-table"; const initConn = { uploadTotal: 0, downloadTotal: 0, connections: [] }; type OrderFunc = (list: IConnectionsItem[]) => IConnectionsItem[]; const ConnectionsPage = () => { const { t, i18n } = useTranslation(); const [filterText, setFilterText] = useState(""); const [curOrderOpt, setOrderOpt] = useState("Default"); const [connData, setConnData] = useState(initConn); const [setting, setSetting] = useRecoilState(atomConnectionSetting); const isTableLayout = setting.layout === "table"; const orderOpts: Record = { Default: (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 connections = connData.connections.filter((conn) => (conn.metadata.host || conn.metadata.destinationIP)?.includes(filterText) ); if (orderFunc) return orderFunc(connections); return connections; }, [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 IConnections; // 与前一次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 ( setSetting((o) => o.layout === "list" ? { ...o, layout: "table" } : { ...o, layout: "list" } ) } > {isTableLayout ? ( ) : ( )} } > {!isTableLayout && ( )} setFilterText(e.target.value)} sx={{ input: { py: 0.65, px: 1.25 } }} /> {filterConn.length === 0 ? ( ) : isTableLayout ? ( ) : ( } /> )} ); }; export default ConnectionsPage;