diff --git a/src/components/layout/layout-traffic.tsx b/src/components/layout/layout-traffic.tsx index 9659eea..19db259 100644 --- a/src/components/layout/layout-traffic.tsx +++ b/src/components/layout/layout-traffic.tsx @@ -7,9 +7,10 @@ import { listen } from "@tauri-apps/api/event"; import { ApiType } from "../../services/types"; import { getInfomation } from "../../services/api"; import { getVergeConfig } from "../../services/cmds"; -import { atomClashPort } from "../../states/setting"; -import parseTraffic from "../../utils/parse-traffic"; +import { atomClashPort } from "../../services/states"; +import useLogSetup from "./use-log-setup"; import useTrafficGraph from "./use-traffic-graph"; +import parseTraffic from "../../utils/parse-traffic"; const LayoutTraffic = () => { const portValue = useRecoilValue(atomClashPort); @@ -21,6 +22,9 @@ const LayoutTraffic = () => { const { data } = useSWR("getVergeConfig", getVergeConfig); const trafficGraph = data?.traffic_graph ?? true; + // setup log ws during layout + useLogSetup(); + useEffect(() => { let unlisten: () => void = null!; diff --git a/src/components/layout/use-log-setup.ts b/src/components/layout/use-log-setup.ts new file mode 100644 index 0000000..6d34304 --- /dev/null +++ b/src/components/layout/use-log-setup.ts @@ -0,0 +1,49 @@ +import dayjs from "dayjs"; +import { useEffect } from "react"; +import { useSetRecoilState } from "recoil"; +import { listen } from "@tauri-apps/api/event"; +import { ApiType } from "../../services/types"; +import { getInfomation } from "../../services/api"; +import { atomLogData } from "../../services/states"; + +const MAX_LOG_NUM = 1000; + +// setup the log websocket +export default function useLogSetup() { + const setLogData = useSetRecoilState(atomLogData); + + useEffect(() => { + let ws: WebSocket = null!; + let unlisten: () => void = null!; + + const handler = (event: MessageEvent) => { + const data = JSON.parse(event.data) as ApiType.LogItem; + const time = dayjs().format("MM-DD HH:mm:ss"); + setLogData((l) => { + if (l.length >= MAX_LOG_NUM) l.shift(); + return [...l, { ...data, time }]; + }); + }; + + (async () => { + const { server = "", secret = "" } = await getInfomation(); + + ws = new WebSocket(`ws://${server}/logs?token=${secret}`); + ws.addEventListener("message", handler); + + // reconnect the websocket + unlisten = await listen("restart_clash", async () => { + const { server = "", secret = "" } = await getInfomation(); + + ws?.close(); + ws = new WebSocket(`ws://${server}/logs?token=${secret}`); + ws.addEventListener("message", handler); + }); + })(); + + return () => { + ws?.close(); + unlisten?.(); + }; + }, []); +} diff --git a/src/components/setting/setting-clash.tsx b/src/components/setting/setting-clash.tsx index 395eab4..610a23f 100644 --- a/src/components/setting/setting-clash.tsx +++ b/src/components/setting/setting-clash.tsx @@ -11,7 +11,7 @@ import { Typography, } from "@mui/material"; import { ApiType } from "../../services/types"; -import { atomClashPort } from "../../states/setting"; +import { atomClashPort } from "../../services/states"; import { patchClashConfig } from "../../services/cmds"; import { SettingList, SettingItem } from "./setting"; import { getClashConfig, getVersion, updateConfigs } from "../../services/api"; diff --git a/src/pages/logs.tsx b/src/pages/logs.tsx index 882248e..78b1f82 100644 --- a/src/pages/logs.tsx +++ b/src/pages/logs.tsx @@ -1,38 +1,12 @@ -import dayjs from "dayjs"; -import { useEffect, useState } from "react"; +import { useRecoilState } from "recoil"; import { Button, Paper } from "@mui/material"; import { Virtuoso } from "react-virtuoso"; -import { ApiType } from "../services/types"; -import { getInfomation } from "../services/api"; +import { atomLogData } from "../services/states"; import BasePage from "../components/base/base-page"; import LogItem from "../components/log/log-item"; -let logCache: ApiType.LogItem[] = []; - const LogPage = () => { - const [logData, setLogData] = useState(logCache); - - useEffect(() => { - let ws: WebSocket | null = null; - - getInfomation().then((result) => { - const { server = "", secret = "" } = result; - ws = new WebSocket(`ws://${server}/logs?token=${secret}`); - - ws.addEventListener("message", (event) => { - const data = JSON.parse(event.data) as ApiType.LogItem; - const time = dayjs().format("MM-DD HH:mm:ss"); - setLogData((l) => (logCache = [...l, { ...data, time }])); - }); - }); - - return () => ws?.close(); - }, []); - - const onClear = () => { - setLogData([]); - logCache = []; - }; + const [logData, setLogData] = useRecoilState(atomLogData); return ( { size="small" sx={{ mt: 1 }} variant="contained" - onClick={onClear} + onClick={() => setLogData([])} > Clear diff --git a/src/services/states.ts b/src/services/states.ts new file mode 100644 index 0000000..210c427 --- /dev/null +++ b/src/services/states.ts @@ -0,0 +1,12 @@ +import { atom } from "recoil"; +import { ApiType } from "./types"; + +export const atomClashPort = atom({ + key: "atomClashPort", + default: 0, +}); + +export const atomLogData = atom({ + key: "atomLogData", + default: [], +}); diff --git a/src/states/setting.ts b/src/states/setting.ts deleted file mode 100644 index a135de0..0000000 --- a/src/states/setting.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { atom } from "recoil"; - -export const atomClashPort = atom({ - key: "atomClashPort", - default: 0, -});