From 28d3691e0b7f7155a3bf322b4baf1d68ba123627 Mon Sep 17 00:00:00 2001 From: GyDi <zzzgydi@gmail.com> Date: Wed, 23 Nov 2022 17:44:40 +0800 Subject: [PATCH] feat: add use clash hook --- src/components/layout/layout-traffic.tsx | 51 ++++-------- src/components/layout/traffic-graph.tsx | 44 +++++----- src/components/layout/use-log-setup.ts | 36 +++----- .../setting/mods/clash-port-viewer.tsx | 38 +++------ .../setting/mods/controller-viewer.tsx | 16 ++-- src/components/setting/mods/web-ui-viewer.tsx | 11 +-- src/components/setting/setting-clash.tsx | 29 ++----- src/hooks/use-clash.ts | 83 +++++++++++++++++++ src/pages/connections.tsx | 69 +++++++-------- src/services/api.ts | 1 + src/services/states.ts | 5 -- src/services/types.d.ts | 4 +- 12 files changed, 196 insertions(+), 191 deletions(-) create mode 100644 src/hooks/use-clash.ts diff --git a/src/components/layout/layout-traffic.tsx b/src/components/layout/layout-traffic.tsx index 296f677..7d8787d 100644 --- a/src/components/layout/layout-traffic.tsx +++ b/src/components/layout/layout-traffic.tsx @@ -1,57 +1,40 @@ import { useEffect, useRef, useState } from "react"; -import { useRecoilValue } from "recoil"; import { Box, Typography } from "@mui/material"; import { ArrowDownward, ArrowUpward } from "@mui/icons-material"; -import { listen } from "@tauri-apps/api/event"; -import { getInformation } from "@/services/api"; -import { atomClashPort } from "@/services/states"; +import { useClashInfo } from "@/hooks/use-clash"; import { useVerge } from "@/hooks/use-verge"; -import TrafficGraph from "./traffic-graph"; -import useLogSetup from "./use-log-setup"; +import { TrafficGraph, type TrafficRef } from "./traffic-graph"; +import { useLogSetup } from "./use-log-setup"; import parseTraffic from "@/utils/parse-traffic"; // setup the traffic const LayoutTraffic = () => { - const portValue = useRecoilValue(atomClashPort); - const [traffic, setTraffic] = useState({ up: 0, down: 0 }); - const [refresh, setRefresh] = useState({}); - - const trafficRef = useRef<any>(); + const { clashInfo } = useClashInfo(); // whether hide traffic graph const { verge } = useVerge(); const trafficGraph = verge?.traffic_graph ?? true; + const trafficRef = useRef<TrafficRef>(null); + const [traffic, setTraffic] = useState({ up: 0, down: 0 }); + // setup log ws during layout useLogSetup(); useEffect(() => { - // should reconnect the traffic ws - const unlisten = listen("verge://refresh-clash-config", () => - setRefresh({}) - ); - - return () => { - unlisten.then((fn) => fn()); - }; - }, []); - - useEffect(() => { - let ws: WebSocket | null = null; + if (!clashInfo) return; - getInformation().then((result) => { - const { server = "", secret = "" } = result; - ws = new WebSocket(`ws://${server}/traffic?token=${secret}`); + const { server = "", secret = "" } = clashInfo; + const ws = new WebSocket(`ws://${server}/traffic?token=${secret}`); - ws.addEventListener("message", (event) => { - const data = JSON.parse(event.data) as ITrafficItem; - trafficRef.current?.appendData(data); - setTraffic(data); - }); + ws.addEventListener("message", (event) => { + const data = JSON.parse(event.data) as ITrafficItem; + trafficRef.current?.appendData(data); + setTraffic(data); }); return () => ws?.close(); - }, [portValue, refresh]); + }, [clashInfo]); const [up, upUnit] = parseTraffic(traffic.up); const [down, downUnit] = parseTraffic(traffic.down); @@ -60,7 +43,7 @@ const LayoutTraffic = () => { component: "span", color: "primary", textAlign: "center", - sx: { flex: "1 1 54px" }, + sx: { flex: "1 1 54px", userSelect: "none" }, }; const unitStyle: any = { component: "span", @@ -78,7 +61,7 @@ const LayoutTraffic = () => { > {trafficGraph && ( <div style={{ width: "100%", height: 60, marginBottom: 6 }}> - <TrafficGraph instance={trafficRef} /> + <TrafficGraph ref={trafficRef} /> </div> )} diff --git a/src/components/layout/traffic-graph.tsx b/src/components/layout/traffic-graph.tsx index 0a9d376..5c1a6b7 100644 --- a/src/components/layout/traffic-graph.tsx +++ b/src/components/layout/traffic-graph.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from "react"; +import { forwardRef, useEffect, useImperativeHandle, useRef } from "react"; import { useTheme } from "@mui/material"; const maxPoint = 30; @@ -16,34 +16,40 @@ const defaultList = Array(maxPoint + 2).fill({ up: 0, down: 0 }); type TrafficData = { up: number; down: number }; -interface Props { - instance: React.MutableRefObject<{ - appendData: (data: TrafficData) => void; - toggleStyle: () => void; - }>; +export interface TrafficRef { + appendData: (data: TrafficData) => void; + toggleStyle: () => void; } /** * draw the traffic graph */ -const TrafficGraph = (props: Props) => { - const { instance } = props; - +export const TrafficGraph = forwardRef<TrafficRef>((props, ref) => { const countRef = useRef(0); const styleRef = useRef(true); const listRef = useRef<TrafficData[]>(defaultList); const canvasRef = useRef<HTMLCanvasElement>(null!); + const cacheRef = useRef<TrafficData | null>(null); + const { palette } = useTheme(); + useImperativeHandle(ref, () => ({ + appendData: (data: TrafficData) => { + cacheRef.current = data; + }, + toggleStyle: () => { + styleRef.current = !styleRef.current; + }, + })); + useEffect(() => { let timer: any; - let cache: TrafficData | null = null; const zero = { up: 0, down: 0 }; const handleData = () => { - const data = cache ? cache : zero; - cache = null; + const data = cacheRef.current ? cacheRef.current : zero; + cacheRef.current = null; const list = listRef.current; if (list.length > maxPoint + 2) list.shift(); @@ -53,19 +59,9 @@ const TrafficGraph = (props: Props) => { timer = setTimeout(handleData, 1000); }; - instance.current = { - appendData: (data: TrafficData) => { - cache = data; - }, - toggleStyle: () => { - styleRef.current = !styleRef.current; - }, - }; - handleData(); return () => { - instance.current = null!; if (timer) clearTimeout(timer); }; }, []); @@ -196,6 +192,4 @@ const TrafficGraph = (props: Props) => { }, [palette]); return <canvas ref={canvasRef} style={{ width: "100%", height: "100%" }} />; -}; - -export default TrafficGraph; +}); diff --git a/src/components/layout/use-log-setup.ts b/src/components/layout/use-log-setup.ts index a563393..d7f06a1 100644 --- a/src/components/layout/use-log-setup.ts +++ b/src/components/layout/use-log-setup.ts @@ -1,48 +1,36 @@ import dayjs from "dayjs"; -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { useRecoilValue, useSetRecoilState } from "recoil"; -import { listen } from "@tauri-apps/api/event"; -import { getInformation } from "@/services/api"; import { getClashLogs } from "@/services/cmds"; +import { useClashInfo } from "@/hooks/use-clash"; import { atomEnableLog, atomLogData } from "@/services/states"; const MAX_LOG_NUM = 1000; // setup the log websocket -export default function useLogSetup() { - const [refresh, setRefresh] = useState({}); +export const useLogSetup = () => { + const { clashInfo } = useClashInfo(); const enableLog = useRecoilValue(atomEnableLog); const setLogData = useSetRecoilState(atomLogData); useEffect(() => { - if (!enableLog) return; + if (!enableLog || !clashInfo) return; getClashLogs().then(setLogData); - const handler = (event: MessageEvent<any>) => { + const { server = "", secret = "" } = clashInfo; + const ws = new WebSocket(`ws://${server}/logs?token=${secret}`); + + ws.addEventListener("message", (event) => { const data = JSON.parse(event.data) as ILogItem; const time = dayjs().format("MM-DD HH:mm:ss"); setLogData((l) => { if (l.length >= MAX_LOG_NUM) l.shift(); return [...l, { ...data, time }]; }); - }; - - const ws = getInformation().then((info) => { - const { server = "", secret = "" } = info; - const ws = new WebSocket(`ws://${server}/logs?token=${secret}`); - ws.addEventListener("message", handler); - return ws; }); - const unlisten = listen("verge://refresh-clash-config", () => - setRefresh({}) - ); - - return () => { - ws.then((ws) => ws?.close()); - unlisten.then((fn) => fn()); - }; - }, [refresh, enableLog]); -} + return () => ws?.close(); + }, [clashInfo, enableLog]); +}; diff --git a/src/components/setting/mods/clash-port-viewer.tsx b/src/components/setting/mods/clash-port-viewer.tsx index e09fe54..2079740 100644 --- a/src/components/setting/mods/clash-port-viewer.tsx +++ b/src/components/setting/mods/clash-port-viewer.tsx @@ -1,55 +1,37 @@ -import useSWR from "swr"; import { forwardRef, useImperativeHandle, useState } from "react"; -import { useSetRecoilState } from "recoil"; import { useTranslation } from "react-i18next"; import { useLockFn } from "ahooks"; import { List, ListItem, ListItemText, TextField } from "@mui/material"; -import { atomClashPort } from "@/services/states"; -import { getClashConfig } from "@/services/api"; -import { patchClashConfig } from "@/services/cmds"; +import { useClashInfo } from "@/hooks/use-clash"; import { BaseDialog, DialogRef, Notice } from "@/components/base"; export const ClashPortViewer = forwardRef<DialogRef>((props, ref) => { const { t } = useTranslation(); - const { data: config, mutate: mutateClash } = useSWR( - "getClashConfig", - getClashConfig - ); + const { clashInfo, patchInfo } = useClashInfo(); const [open, setOpen] = useState(false); - const [port, setPort] = useState(config?.["mixed-port"] ?? 9090); - - const setGlobalClashPort = useSetRecoilState(atomClashPort); + const [port, setPort] = useState(clashInfo?.port ?? 7890); useImperativeHandle(ref, () => ({ open: () => { - if (config?.["mixed-port"]) { - setPort(config["mixed-port"]); - } + if (clashInfo?.port) setPort(clashInfo?.port); setOpen(true); }, close: () => setOpen(false), })); const onSave = useLockFn(async () => { - if (port < 1000) { - return Notice.error("The port should not < 1000"); - } - if (port > 65536) { - return Notice.error("The port should not > 65536"); + if (port === clashInfo?.port) { + setOpen(false); + return; } - - setOpen(false); - if (port === config?.["mixed-port"]) return; - try { - await patchClashConfig({ "mixed-port": port }); - setGlobalClashPort(port); + await patchInfo({ "mixed-port": port }); + setOpen(false); Notice.success("Change Clash port successfully!", 1000); - mutateClash(); } catch (err: any) { - Notice.error(err.message || err.toString(), 5000); + Notice.error(err.message || err.toString(), 4000); } }); diff --git a/src/components/setting/mods/controller-viewer.tsx b/src/components/setting/mods/controller-viewer.tsx index d3d6a43..17205d9 100644 --- a/src/components/setting/mods/controller-viewer.tsx +++ b/src/components/setting/mods/controller-viewer.tsx @@ -1,17 +1,16 @@ -import useSWR from "swr"; import { forwardRef, useImperativeHandle, useState } from "react"; import { useLockFn } from "ahooks"; import { useTranslation } from "react-i18next"; import { List, ListItem, ListItemText, TextField } from "@mui/material"; -import { getClashInfo, patchClashConfig } from "@/services/cmds"; -import { getAxios } from "@/services/api"; +import { useClashInfo } from "@/hooks/use-clash"; import { BaseDialog, DialogRef, Notice } from "@/components/base"; export const ControllerViewer = forwardRef<DialogRef>((props, ref) => { const { t } = useTranslation(); const [open, setOpen] = useState(false); - const { data: clashInfo, mutate } = useSWR("getClashInfo", getClashInfo); + const { clashInfo, patchInfo } = useClashInfo(); + const [controller, setController] = useState(clashInfo?.server || ""); const [secret, setSecret] = useState(clashInfo?.secret || ""); @@ -26,14 +25,11 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => { const onSave = useLockFn(async () => { try { - await patchClashConfig({ "external-controller": controller, secret }); - mutate(); - // åˆ·æ–°æŽ¥å£ - getAxios(true); + await patchInfo({ "external-controller": controller, secret }); Notice.success("Change Clash Config successfully!", 1000); setOpen(false); - } catch (err) { - console.log(err); + } catch (err: any) { + Notice.error(err.message || err.toString(), 4000); } }); diff --git a/src/components/setting/mods/web-ui-viewer.tsx b/src/components/setting/mods/web-ui-viewer.tsx index bb47a99..ae1bf31 100644 --- a/src/components/setting/mods/web-ui-viewer.tsx +++ b/src/components/setting/mods/web-ui-viewer.tsx @@ -1,23 +1,22 @@ -import useSWR from "swr"; import { forwardRef, useImperativeHandle, useState } from "react"; import { useLockFn } from "ahooks"; import { useTranslation } from "react-i18next"; import { Button, Box, Typography } from "@mui/material"; import { useVerge } from "@/hooks/use-verge"; -import { getClashInfo, openWebUrl } from "@/services/cmds"; +import { openWebUrl } from "@/services/cmds"; import { BaseDialog, BaseEmpty, DialogRef, Notice } from "@/components/base"; +import { useClashInfo } from "@/hooks/use-clash"; import { WebUIItem } from "./web-ui-item"; export const WebUIViewer = forwardRef<DialogRef>((props, ref) => { const { t } = useTranslation(); + const { clashInfo } = useClashInfo(); const { verge, patchVerge, mutateVerge } = useVerge(); const [open, setOpen] = useState(false); const [editing, setEditing] = useState(false); - const { data: clashInfo } = useSWR("getClashInfo", getClashInfo); - useImperativeHandle(ref, () => ({ open: () => setOpen(true), close: () => setOpen(false), @@ -53,9 +52,7 @@ export const WebUIViewer = forwardRef<DialogRef>((props, ref) => { if (url.includes("%port") || url.includes("%secret")) { if (!clashInfo) throw new Error("failed to get clash info"); if (!clashInfo.server?.includes(":")) { - throw new Error( - `failed to parse server with status ${clashInfo.status}` - ); + throw new Error(`failed to parse the server "${clashInfo.server}"`); } const port = clashInfo.server diff --git a/src/components/setting/setting-clash.tsx b/src/components/setting/setting-clash.tsx index caaf5de..a3af397 100644 --- a/src/components/setting/setting-clash.tsx +++ b/src/components/setting/setting-clash.tsx @@ -1,4 +1,3 @@ -import useSWR from "swr"; import { useRef } from "react"; import { useTranslation } from "react-i18next"; import { @@ -10,9 +9,8 @@ import { IconButton, } from "@mui/material"; import { ArrowForward } from "@mui/icons-material"; -import { patchClashConfig } from "@/services/cmds"; -import { getClashConfig, getVersion, updateConfigs } from "@/services/api"; import { DialogRef } from "@/components/base"; +import { useClash } from "@/hooks/use-clash"; import { GuardState } from "./mods/guard-state"; import { CoreSwitch } from "./mods/core-switch"; import { WebUIViewer } from "./mods/web-ui-viewer"; @@ -28,18 +26,14 @@ interface Props { const SettingClash = ({ onError }: Props) => { const { t } = useTranslation(); - const { data: clashConfig, mutate: mutateClash } = useSWR( - "getClashConfig", - getClashConfig - ); - const { data: versionData } = useSWR("getVersion", getVersion); + const { clash, version, mutateClash, patchClash } = useClash(); const { ipv6, "allow-lan": allowLan, "log-level": logLevel, "mixed-port": mixedPort, - } = clashConfig ?? {}; + } = clash ?? {}; const webRef = useRef<DialogRef>(null); const fieldRef = useRef<DialogRef>(null); @@ -50,15 +44,6 @@ const SettingClash = ({ onError }: Props) => { const onChangeData = (patch: Partial<IConfigData>) => { mutateClash((old) => ({ ...(old! || {}), ...patch }), false); }; - const onUpdateData = async (patch: Partial<IConfigData>) => { - await updateConfigs(patch); - await patchClashConfig(patch); - }; - - // get clash core version - const clashVer = versionData?.premium - ? `${versionData.version} Premium` - : versionData?.version || "-"; return ( <SettingList title={t("Clash Setting")}> @@ -74,7 +59,7 @@ const SettingClash = ({ onError }: Props) => { onCatch={onError} onFormat={onSwitchFormat} onChange={(e) => onChangeData({ "allow-lan": e })} - onGuard={(e) => onUpdateData({ "allow-lan": e })} + onGuard={(e) => patchClash({ "allow-lan": e })} > <Switch edge="end" /> </GuardState> @@ -87,7 +72,7 @@ const SettingClash = ({ onError }: Props) => { onCatch={onError} onFormat={onSwitchFormat} onChange={(e) => onChangeData({ ipv6: e })} - onGuard={(e) => onUpdateData({ ipv6: e })} + onGuard={(e) => patchClash({ ipv6: e })} > <Switch edge="end" /> </GuardState> @@ -100,7 +85,7 @@ const SettingClash = ({ onError }: Props) => { onCatch={onError} onFormat={(e: any) => e.target.value} onChange={(e) => onChangeData({ "log-level": e })} - onGuard={(e) => onUpdateData({ "log-level": e })} + onGuard={(e) => patchClash({ "log-level": e })} > <Select size="small" sx={{ width: 100, "> div": { py: "7.5px" } }}> <MenuItem value="debug">Debug</MenuItem> @@ -159,7 +144,7 @@ const SettingClash = ({ onError }: Props) => { </SettingItem> <SettingItem label={t("Clash Core")} extra={<CoreSwitch />}> - <Typography sx={{ py: "7px" }}>{clashVer}</Typography> + <Typography sx={{ py: "7px", pr: 1 }}>{version}</Typography> </SettingItem> </SettingList> ); diff --git a/src/hooks/use-clash.ts b/src/hooks/use-clash.ts new file mode 100644 index 0000000..9804c5f --- /dev/null +++ b/src/hooks/use-clash.ts @@ -0,0 +1,83 @@ +import useSWR, { mutate } from "swr"; +import { useLockFn } from "ahooks"; +import { + getAxios, + getClashConfig, + getVersion, + updateConfigs, +} from "@/services/api"; +import { getClashInfo, patchClashConfig } from "@/services/cmds"; + +export const useClash = () => { + const { data: clash, mutate: mutateClash } = useSWR( + "getClashConfig", + getClashConfig + ); + + const { data: versionData, mutate: mutateVersion } = useSWR( + "getVersion", + getVersion + ); + + const patchClash = useLockFn(async (patch: Partial<IConfigData>) => { + await updateConfigs(patch); + await patchClashConfig(patch); + mutateClash(); + }); + + const version = versionData?.premium + ? `${versionData.version} Premium` + : versionData?.meta + ? `${versionData.version} Meta` + : versionData?.version || "-"; + + return { + clash, + version, + mutateClash, + mutateVersion, + patchClash, + }; +}; + +export const useClashInfo = () => { + const { data: clashInfo, mutate: mutateInfo } = useSWR( + "getClashInfo", + getClashInfo + ); + + const patchInfo = async ( + patch: Partial< + Pick<IConfigData, "mixed-port" | "external-controller" | "secret"> + > + ) => { + const hasInfo = + patch["mixed-port"] != null || + patch["external-controller"] != null || + patch.secret != null; + + if (!hasInfo) return; + + if (patch["mixed-port"]) { + const port = patch["mixed-port"]; + if (port < 1000) { + throw new Error("The port should not < 1000"); + } + if (port > 65536) { + throw new Error("The port should not > 65536"); + } + } + + await patchClashConfig(patch); + mutateInfo(); + mutate("getClashConfig"); + // åˆ·æ–°æŽ¥å£ + getAxios(true); + }; + + return { + clashInfo, + mutateInfo, + patchInfo, + }; +}; diff --git a/src/pages/connections.tsx b/src/pages/connections.tsx index eefaa6a..7ea1444 100644 --- a/src/pages/connections.tsx +++ b/src/pages/connections.tsx @@ -13,8 +13,9 @@ 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 { closeAllConnections } from "@/services/api"; import { atomConnectionSetting } from "@/services/states"; +import { useClashInfo } from "@/hooks/use-clash"; import { BaseEmpty, BasePage } from "@/components/base"; import ConnectionItem from "@/components/connection/connection-item"; import ConnectionTable from "@/components/connection/connection-table"; @@ -25,6 +26,7 @@ type OrderFunc = (list: IConnectionsItem[]) => IConnectionsItem[]; const ConnectionsPage = () => { const { t, i18n } = useTranslation(); + const { clashInfo } = useClashInfo(); const [filterText, setFilterText] = useState(""); const [curOrderOpt, setOrderOpt] = useState("Default"); @@ -52,51 +54,49 @@ const ConnectionsPage = () => { }, [connData, filterText, curOrderOpt]); useEffect(() => { - let ws: WebSocket | null = null; + if (!clashInfo) return; - getInformation().then((result) => { - const { server = "", secret = "" } = result; - ws = new WebSocket(`ws://${server}/connections?token=${secret}`); + const { server = "", secret = "" } = clashInfo; + const ws = new WebSocket(`ws://${server}/connections?token=${secret}`); - ws.addEventListener("message", (event) => { - const data = JSON.parse(event.data) as IConnections; + ws.addEventListener("message", (event) => { + const data = JSON.parse(event.data) as IConnections; - // 与å‰ä¸€æ¬¡connections的展示顺åºå°½é‡ä¿æŒä¸€è‡´ - setConnData((old) => { - const oldConn = old.connections; - const maxLen = data.connections.length; + // å°½é‡ä¸Žå‰ä¸€æ¬¡connections的展示顺åºä¿æŒä¸€è‡´ + setConnData((old) => { + const oldConn = old.connections; + const maxLen = data.connections.length; - const connections: typeof oldConn = []; + const connections: typeof oldConn = []; - const rest = data.connections.filter((each) => { - const index = oldConn.findIndex((o) => o.id === each.id); + 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; + 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; - } + connections[index] = each; + return false; } - - return { ...data, connections }; + 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(); - }, []); + }, [clashInfo]); const onCloseAll = useLockFn(closeAllConnections); @@ -140,6 +140,7 @@ const ConnectionsPage = () => { height: "36px", display: "flex", alignItems: "center", + userSelect: "text", }} > {!isTableLayout && ( @@ -176,7 +177,7 @@ const ConnectionsPage = () => { /> </Box> - <Box height="calc(100% - 50px)"> + <Box height="calc(100% - 50px)" sx={{ userSelect: "text" }}> {filterConn.length === 0 ? ( <BaseEmpty text="No Connections" /> ) : isTableLayout ? ( diff --git a/src/services/api.ts b/src/services/api.ts index 8ff75b7..2613a29 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -43,6 +43,7 @@ export async function getVersion() { const instance = await getAxios(); return instance.get("/version") as Promise<{ premium: boolean; + meta?: boolean; version: string; }>; } diff --git a/src/services/states.ts b/src/services/states.ts index 8ef38c2..399eb3e 100644 --- a/src/services/states.ts +++ b/src/services/states.ts @@ -5,11 +5,6 @@ export const atomThemeMode = atom<"light" | "dark">({ default: "light", }); -export const atomClashPort = atom<number>({ - key: "atomClashPort", - default: 0, -}); - export const atomLogData = atom<ILogItem[]>({ key: "atomLogData", default: [], diff --git a/src/services/types.d.ts b/src/services/types.d.ts index aea3938..17fb5bf 100644 --- a/src/services/types.d.ts +++ b/src/services/types.d.ts @@ -94,8 +94,8 @@ interface IConnections { type IProfileType = "local" | "remote" | "merge" | "script"; interface IClashInfo { - status: string; - port?: string; // clash mixed port + // status: string; + port?: number; // clash mixed port server?: string; // external-controller secret?: string; } -- GitLab