From dd15455031a5fe2eec6573e018123cae2be4fb6f Mon Sep 17 00:00:00 2001
From: GyDi <segydi@foxmail.com>
Date: Wed, 23 Feb 2022 02:00:45 +0800
Subject: [PATCH] feat: enhance log data

---
 src/components/layout/layout-traffic.tsx |  8 +++-
 src/components/layout/use-log-setup.ts   | 49 ++++++++++++++++++++++++
 src/components/setting/setting-clash.tsx |  2 +-
 src/pages/logs.tsx                       | 34 ++--------------
 src/services/states.ts                   | 12 ++++++
 src/states/setting.ts                    |  6 ---
 6 files changed, 72 insertions(+), 39 deletions(-)
 create mode 100644 src/components/layout/use-log-setup.ts
 create mode 100644 src/services/states.ts
 delete mode 100644 src/states/setting.ts

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<any>) => {
+      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 (
     <BasePage
@@ -43,7 +17,7 @@ const LogPage = () => {
           size="small"
           sx={{ mt: 1 }}
           variant="contained"
-          onClick={onClear}
+          onClick={() => setLogData([])}
         >
           Clear
         </Button>
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<number>({
+  key: "atomClashPort",
+  default: 0,
+});
+
+export const atomLogData = atom<ApiType.LogItem[]>({
+  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<number>({
-  key: "atomClashPort",
-  default: 0,
-});
-- 
GitLab