From 0cfd718d8a178b17cd97f33c77bcd01c00772b76 Mon Sep 17 00:00:00 2001 From: GyDi <zzzgydi@gmail.com> Date: Thu, 10 Nov 2022 01:27:05 +0800 Subject: [PATCH] feat: auto close connection when proxy changed --- src-tauri/src/data/verge.rs | 9 ++ src/components/proxy/proxy-group.tsx | 19 +++- src/components/setting/mods/misc-viewer.tsx | 107 ++++++++++++++++++++ src/components/setting/setting-clash.tsx | 2 +- src/components/setting/setting-verge.tsx | 14 +++ src/hooks/use-verge-config.ts | 16 +++ src/locales/zh.json | 3 +- src/services/api.ts | 6 ++ src/services/types.d.ts | 2 + 9 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 src/components/setting/mods/misc-viewer.tsx create mode 100644 src/hooks/use-verge-config.ts diff --git a/src-tauri/src/data/verge.rs b/src-tauri/src/data/verge.rs index 90aa5b7..9a5519e 100644 --- a/src-tauri/src/data/verge.rs +++ b/src-tauri/src/data/verge.rs @@ -60,6 +60,12 @@ pub struct Verge { /// hotkey map /// format: {func},{key} pub hotkeys: Option<Vec<String>>, + + /// 切æ¢ä»£ç†æ—¶è‡ªåŠ¨å…³é—连接 + pub auto_close_connection: Option<bool>, + + /// 默认的延迟测试连接 + pub default_latency_test: Option<String>, } #[derive(Default, Debug, Clone, Deserialize, Serialize)] @@ -122,6 +128,9 @@ impl Verge { patch!(clash_core); patch!(hotkeys); + patch!(auto_close_connection); + patch!(default_latency_test); + self.save_file() } } diff --git a/src/components/proxy/proxy-group.tsx b/src/components/proxy/proxy-group.tsx index 95d8adb..d25e395 100644 --- a/src/components/proxy/proxy-group.tsx +++ b/src/components/proxy/proxy-group.tsx @@ -15,8 +15,14 @@ import { ExpandLessRounded, ExpandMoreRounded, } from "@mui/icons-material"; -import { providerHealthCheck, updateProxy } from "@/services/api"; +import { + getConnections, + providerHealthCheck, + updateProxy, + deleteConnection, +} from "@/services/api"; import { getProfiles, patchProfile } from "@/services/cmds"; +import { useVergeConfig } from "@/hooks/use-verge-config"; import delayManager from "@/services/delay"; import useHeadState from "./use-head-state"; import useFilterSort from "./use-filter-sort"; @@ -42,6 +48,7 @@ const ProxyGroup = ({ group }: Props) => { ); const { data: profiles } = useSWR("getProfiles", getProfiles); + const { data: vergeConfig } = useVergeConfig(); const onChangeProxy = useLockFn(async (name: string) => { // Todo: support another proxy group type @@ -51,6 +58,16 @@ const ProxyGroup = ({ group }: Props) => { try { setNow(name); await updateProxy(group.name, name); + + if (vergeConfig?.auto_close_connection) { + getConnections().then((snapshot) => { + snapshot.connections.forEach((conn) => { + if (conn.chains.includes(oldValue!)) { + deleteConnection(conn.id); + } + }); + }); + } } catch { setNow(oldValue); return; // do not update profile diff --git a/src/components/setting/mods/misc-viewer.tsx b/src/components/setting/mods/misc-viewer.tsx new file mode 100644 index 0000000..5ab3abf --- /dev/null +++ b/src/components/setting/mods/misc-viewer.tsx @@ -0,0 +1,107 @@ +import { useEffect, useState } from "react"; +import { useLockFn } from "ahooks"; +import { useTranslation } from "react-i18next"; +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + List, + ListItem, + ListItemText, + Switch, + TextField, +} from "@mui/material"; +import { ModalHandler } from "@/hooks/use-modal-handler"; +import { useVergeConfig } from "@/hooks/use-verge-config"; +import Notice from "@/components/base/base-notice"; + +interface Props { + handler: ModalHandler; +} + +const MiscViewer = ({ handler }: Props) => { + const { t } = useTranslation(); + const { data, patchVerge } = useVergeConfig(); + + const [open, setOpen] = useState(false); + const [values, setValues] = useState({ + autoCloseConnection: false, + defaultLatencyTest: "", + }); + + if (handler) { + handler.current = { + open: () => setOpen(true), + close: () => setOpen(false), + }; + } + + useEffect(() => { + if (open) { + setValues({ + autoCloseConnection: data?.auto_close_connection || false, + defaultLatencyTest: data?.default_latency_test || "", + }); + } + }, [open, data]); + + const onSave = useLockFn(async () => { + try { + await patchVerge({ + auto_close_connection: values.autoCloseConnection, + default_latency_test: values.defaultLatencyTest, + }); + setOpen(false); + } catch (err: any) { + Notice.error(err.message || err.toString()); + } + }); + + return ( + <Dialog open={open} onClose={() => setOpen(false)}> + <DialogTitle>{t("Miscellaneous")}</DialogTitle> + + <DialogContent sx={{ width: 420 }}> + <List> + <ListItem sx={{ padding: "5px 2px" }}> + <ListItemText primary="Auto Close Connections" /> + <Switch + edge="end" + checked={values.autoCloseConnection} + onChange={(_, c) => + setValues((v) => ({ ...v, autoCloseConnection: c })) + } + /> + </ListItem> + + <ListItem sx={{ padding: "5px 2px" }}> + <ListItemText primary="Default Latency Test" /> + <TextField + size="small" + autoComplete="off" + sx={{ width: 200 }} + value={values.defaultLatencyTest} + placeholder="http://www.gstatic.com/generate_204" + onChange={(e) => + setValues((v) => ({ ...v, defaultLatencyTest: e.target.value })) + } + /> + </ListItem> + </List> + </DialogContent> + + <DialogActions> + <Button variant="outlined" onClick={() => setOpen(false)}> + {t("Cancel")} + </Button> + <Button onClick={onSave} variant="contained"> + {t("Save")} + </Button> + </DialogActions> + </Dialog> + ); +}; + +export default MiscViewer; diff --git a/src/components/setting/setting-clash.tsx b/src/components/setting/setting-clash.tsx index 8ddad18..fdc7a38 100644 --- a/src/components/setting/setting-clash.tsx +++ b/src/components/setting/setting-clash.tsx @@ -124,7 +124,7 @@ const SettingClash = ({ onError }: Props) => { /> </SettingItem> - <SettingItem label={t("External Controller")}> + <SettingItem label={t("External")}> <IconButton color="inherit" size="small" diff --git a/src/components/setting/setting-verge.tsx b/src/components/setting/setting-verge.tsx index 8eaff19..ad05cf7 100644 --- a/src/components/setting/setting-verge.tsx +++ b/src/components/setting/setting-verge.tsx @@ -22,6 +22,7 @@ import ThemeModeSwitch from "./mods/theme-mode-switch"; import ConfigViewer from "./mods/config-viewer"; import HotkeyViewer from "./mods/hotkey-viewer"; import GuardState from "./mods/guard-state"; +import MiscViewer from "./mods/misc-viewer"; import SettingTheme from "./setting-theme"; interface Props { @@ -45,11 +46,13 @@ const SettingVerge = ({ onError }: Props) => { mutateVerge({ ...vergeConfig, ...patch }, false); }; + const miscHandler = useModalHandler(); const hotkeyHandler = useModalHandler(); return ( <SettingList title={t("Verge Setting")}> <HotkeyViewer handler={hotkeyHandler} /> + <MiscViewer handler={miscHandler} /> <SettingItem label={t("Language")}> <GuardState @@ -103,6 +106,17 @@ const SettingVerge = ({ onError }: Props) => { </GuardState> </SettingItem> + <SettingItem label={t("Miscellaneous")}> + <IconButton + color="inherit" + size="small" + sx={{ my: "2px" }} + onClick={() => miscHandler.current.open()} + > + <ArrowForward /> + </IconButton> + </SettingItem> + <SettingItem label={t("Theme Setting")}> <IconButton color="inherit" diff --git a/src/hooks/use-verge-config.ts b/src/hooks/use-verge-config.ts new file mode 100644 index 0000000..0de7a80 --- /dev/null +++ b/src/hooks/use-verge-config.ts @@ -0,0 +1,16 @@ +import useSWR from "swr"; +import { getVergeConfig, patchVergeConfig } from "@/services/cmds"; + +export const useVergeConfig = () => { + const { data, mutate } = useSWR("getVergeConfig", getVergeConfig); + + const patchVerge = async (value: Partial<CmdType.VergeConfig>) => { + await patchVergeConfig(value); + mutate(); + }; + + return { + data, + patchVerge, + }; +}; diff --git a/src/locales/zh.json b/src/locales/zh.json index a1fac5e..eb9d627 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -60,7 +60,7 @@ "IPv6": "IPv6", "Log Level": "日志ç‰çº§", "Mixed Port": "端å£è®¾ç½®", - "External Controller": "外部控制", + "External": "外部控制", "Clash Core": "Clash å†…æ ¸", "Tun Mode": "Tun 模å¼", "Service Mode": "æœåŠ¡æ¨¡å¼", @@ -74,6 +74,7 @@ "Current System Proxy": "当å‰ç³»ç»Ÿä»£ç†", "Theme Mode": "主题模å¼", "Theme Blur": "背景模糊", + "Miscellaneous": "æ‚项设置", "Theme Setting": "主题设置", "Hotkey Setting": "çƒé”®è®¾ç½®", "Traffic Graph": "æµé‡å›¾æ˜¾", diff --git a/src/services/api.ts b/src/services/api.ts index 65594d5..625f96e 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -176,6 +176,12 @@ export async function providerHealthCheck(name: string) { ); } +export async function getConnections() { + const instance = await getAxios(); + const result = await instance.get("/connections"); + return result as any as ApiType.Connections; +} + // Close specific connection export async function deleteConnection(id: string) { const instance = await getAxios(); diff --git a/src/services/types.d.ts b/src/services/types.d.ts index 804d343..e84a281 100644 --- a/src/services/types.d.ts +++ b/src/services/types.d.ts @@ -165,6 +165,8 @@ declare namespace CmdType { font_family?: string; css_injection?: string; }; + auto_close_connection?: boolean; + default_latency_test?: string; } type ClashConfigValue = any; -- GitLab