diff --git a/src/components/setting/mods/sysproxy-tooltip.tsx b/src/components/setting/mods/sysproxy-tooltip.tsx deleted file mode 100644 index 5641fa0e8e7af9f37ece16abe97fec351dcffc79..0000000000000000000000000000000000000000 --- a/src/components/setting/mods/sysproxy-tooltip.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { useEffect, useState } from "react"; -import { InfoRounded } from "@mui/icons-material"; -import { ClickAwayListener, Tooltip } from "@mui/material"; -import { getSystemProxy } from "@/services/cmds"; - -const SysproxyTooltip = () => { - const [open, setOpen] = useState(false); - const [info, setInfo] = useState<any>({}); - - const onShow = async () => { - const data = await getSystemProxy(); - setInfo(data ?? {}); - setOpen(true); - }; - - useEffect(() => { - if (!open) return; - const timer = setTimeout(() => setOpen(false), 2000); - return () => clearTimeout(timer); - }, [open]); - - // todo: add error info - const showTitle = ( - <div> - <div>Enable: {(!!info.enable).toString()}</div> - <div>Server: {info.server}</div> - <div>Bypass: {info.bypass}</div> - </div> - ); - - return ( - <ClickAwayListener onClickAway={() => setOpen(false)}> - <Tooltip - PopperProps={{ - disablePortal: true, - }} - onClose={() => setOpen(false)} - open={open} - disableFocusListener - disableHoverListener - disableTouchListener - placement="top" - title={showTitle} - arrow - > - <InfoRounded - fontSize="small" - style={{ cursor: "pointer", opacity: 0.75 }} - onClick={onShow} - /> - </Tooltip> - </ClickAwayListener> - ); -}; - -export default SysproxyTooltip; diff --git a/src/components/setting/mods/sysproxy-viewer.tsx b/src/components/setting/mods/sysproxy-viewer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ec7939e6b0f6a65eed1f8eeb343628ad613cbf12 --- /dev/null +++ b/src/components/setting/mods/sysproxy-viewer.tsx @@ -0,0 +1,204 @@ +import useSWR from "swr"; +import { useEffect, useState } from "react"; +import { useLockFn } from "ahooks"; +import { useTranslation } from "react-i18next"; +import { + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + InputAdornment, + List, + ListItem, + ListItemText, + styled, + Switch, + TextField, + Typography, +} from "@mui/material"; +import { + getSystemProxy, + getVergeConfig, + patchVergeConfig, +} from "@/services/cmds"; +import { ModalHandler } from "@/hooks/use-modal-handler"; +import Notice from "@/components/base/base-notice"; + +interface Props { + handler: ModalHandler; +} + +const FlexBox = styled("div")` + display: flex; + margin-top: 4px; + + .label { + flex: none; + width: 80px; + } +`; + +const SysproxyViewer = ({ handler }: Props) => { + const { t } = useTranslation(); + + const [open, setOpen] = useState(false); + + if (handler) { + handler.current = { + open: () => setOpen(true), + close: () => setOpen(false), + }; + } + + const { data: vergeConfig, mutate: mutateVerge } = useSWR( + "getVergeConfig", + getVergeConfig + ); + + const { + enable_system_proxy: enabled, + enable_proxy_guard, + system_proxy_bypass, + proxy_guard_duration, + } = vergeConfig ?? {}; + + const { data: sysproxy } = useSWR( + open ? "getSystemProxy" : null, + getSystemProxy + ); + + const [value, setValue] = useState({ + guard: enable_proxy_guard, + bypass: system_proxy_bypass, + duration: proxy_guard_duration ?? 10, + }); + + useEffect(() => { + setValue({ + guard: enable_proxy_guard, + bypass: system_proxy_bypass, + duration: proxy_guard_duration ?? 10, + }); + }, [vergeConfig]); + + const onSave = useLockFn(async () => { + if (value.duration < 5) { + Notice.error("Proxy guard duration at least 5 seconds"); + return; + } + + const patch: Partial<CmdType.VergeConfig> = {}; + + if (value.guard !== enable_proxy_guard) { + patch.enable_proxy_guard = value.guard; + } + if (value.duration !== proxy_guard_duration) { + patch.proxy_guard_duration = value.duration; + } + if (value.bypass !== system_proxy_bypass) { + patch.system_proxy_bypass = value.bypass; + } + + try { + await patchVergeConfig(patch); + mutateVerge(); + setOpen(false); + } catch (err: any) { + Notice.error(err.message || err.toString()); + } + }); + + return ( + <Dialog open={open} onClose={() => setOpen(false)}> + <DialogTitle>{t("System Proxy Setting")}</DialogTitle> + + <DialogContent sx={{ width: 450, maxHeight: 300 }}> + <List> + <ListItem sx={{ padding: "5px 2px" }}> + <ListItemText primary={t("Proxy Guard")} /> + <Switch + edge="end" + disabled={!enabled} + checked={value.guard} + onChange={(_, e) => setValue((v) => ({ ...v, guard: e }))} + /> + </ListItem> + + <ListItem sx={{ padding: "5px 2px" }}> + <ListItemText primary={t("Guard Duration")} /> + <TextField + disabled={!enabled} + size="small" + value={value.duration} + sx={{ width: 100 }} + InputProps={{ + endAdornment: <InputAdornment position="end">s</InputAdornment>, + }} + onChange={(e) => { + setValue((v) => ({ + ...v, + duration: +e.target.value.replace(/\D/, ""), + })); + }} + /> + </ListItem> + + <ListItem sx={{ padding: "5px 2px", alignItems: "start" }}> + <ListItemText + primary={t("Proxy Bypass")} + sx={{ padding: "3px 0" }} + /> + <TextField + disabled={!enabled} + size="small" + autoComplete="off" + multiline + rows={3} + sx={{ width: 280 }} + value={value.bypass} + onChange={(e) => + setValue((v) => ({ ...v, bypass: e.target.value })) + } + /> + </ListItem> + </List> + + <Box sx={{ mt: 2.5 }}> + <Typography variant="body1" sx={{ fontSize: "18px", mb: 1 }}> + {t("Current System Proxy")} + </Typography> + + <FlexBox> + <Typography className="label">Enable:</Typography> + <Typography className="value"> + {(!!sysproxy?.enable).toString()} + </Typography> + </FlexBox> + + <FlexBox> + <Typography className="label">Server:</Typography> + <Typography className="value">{sysproxy?.server || "-"}</Typography> + </FlexBox> + + <FlexBox> + <Typography className="label">Bypass:</Typography> + <Typography className="value">{sysproxy?.bypass || "-"}</Typography> + </FlexBox> + </Box> + </DialogContent> + + <DialogActions> + <Button variant="outlined" onClick={() => setOpen(false)}> + {t("Cancel")} + </Button> + <Button onClick={onSave} variant="contained"> + {t("Save")} + </Button> + </DialogActions> + </Dialog> + ); +}; + +export default SysproxyViewer; diff --git a/src/components/setting/setting-system.tsx b/src/components/setting/setting-system.tsx index 7ff8aba41b3ad33b7acb6031f3190b25f897a8d5..1d15bee3c5ff45cde8ff59da8c368042f325b863 100644 --- a/src/components/setting/setting-system.tsx +++ b/src/components/setting/setting-system.tsx @@ -1,18 +1,19 @@ -import useSWR, { useSWRConfig } from "swr"; +import useSWR from "swr"; import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { IconButton, Switch, TextField } from "@mui/material"; -import { ArrowForward, PrivacyTipRounded } from "@mui/icons-material"; +import { IconButton, Switch } from "@mui/material"; +import { ArrowForward, PrivacyTipRounded, Settings } from "@mui/icons-material"; import { checkService, getVergeConfig, patchVergeConfig, } from "@/services/cmds"; import { SettingList, SettingItem } from "./setting"; +import useModalHandler from "@/hooks/use-modal-handler"; import getSystem from "@/utils/get-system"; import GuardState from "./mods/guard-state"; import ServiceMode from "./mods/service-mode"; -import SysproxyTooltip from "./mods/sysproxy-tooltip"; +import SysproxyViewer from "./mods/sysproxy-viewer"; interface Props { onError?: (err: Error) => void; @@ -22,8 +23,11 @@ const isWIN = getSystem() === "windows"; const SettingSystem = ({ onError }: Props) => { const { t } = useTranslation(); - const { mutate } = useSWRConfig(); - const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig); + + const { data: vergeConfig, mutate: mutateVerge } = useSWR( + "getVergeConfig", + getVergeConfig + ); // service mode const [serviceOpen, setServiceOpen] = useState(false); @@ -39,17 +43,19 @@ const SettingSystem = ({ onError }: Props) => { enable_service_mode, enable_silent_start, enable_system_proxy, - system_proxy_bypass, - enable_proxy_guard, } = vergeConfig ?? {}; const onSwitchFormat = (_e: any, value: boolean) => value; const onChangeData = (patch: Partial<CmdType.VergeConfig>) => { - mutate("getVergeConfig", { ...vergeConfig, ...patch }, false); + mutateVerge({ ...vergeConfig, ...patch }, false); }; + const sysproxyHandler = useModalHandler(); + return ( <SettingList title={t("System Setting")}> + <SysproxyViewer handler={sysproxyHandler} /> + <SettingItem label={t("Tun Mode")}> <GuardState value={enable_tun_mode ?? false} @@ -108,81 +114,56 @@ const SettingSystem = ({ onError }: Props) => { /> )} - <SettingItem label={t("Auto Launch")}> + <SettingItem + label={t("System Proxy")} + extra={ + <Settings + fontSize="small" + style={{ cursor: "pointer", opacity: 0.75 }} + onClick={() => sysproxyHandler.current.open()} + /> + } + > <GuardState - value={enable_auto_launch ?? false} + value={enable_system_proxy ?? false} valueProps="checked" onCatch={onError} onFormat={onSwitchFormat} - onChange={(e) => onChangeData({ enable_auto_launch: e })} - onGuard={(e) => patchVergeConfig({ enable_auto_launch: e })} + onChange={(e) => onChangeData({ enable_system_proxy: e })} + onGuard={async (e) => { + await patchVergeConfig({ enable_system_proxy: e }); + mutateVerge(); // update bypass value + }} > <Switch edge="end" /> </GuardState> </SettingItem> - <SettingItem label={t("Silent Start")}> + <SettingItem label={t("Auto Launch")}> <GuardState - value={enable_silent_start ?? false} + value={enable_auto_launch ?? false} valueProps="checked" onCatch={onError} onFormat={onSwitchFormat} - onChange={(e) => onChangeData({ enable_silent_start: e })} - onGuard={(e) => patchVergeConfig({ enable_silent_start: e })} + onChange={(e) => onChangeData({ enable_auto_launch: e })} + onGuard={(e) => patchVergeConfig({ enable_auto_launch: e })} > <Switch edge="end" /> </GuardState> </SettingItem> - <SettingItem label={t("System Proxy")} extra={<SysproxyTooltip />}> + <SettingItem label={t("Silent Start")}> <GuardState - value={enable_system_proxy ?? false} + value={enable_silent_start ?? false} valueProps="checked" onCatch={onError} onFormat={onSwitchFormat} - onChange={(e) => onChangeData({ enable_system_proxy: e })} - onGuard={async (e) => { - await patchVergeConfig({ enable_system_proxy: e }); - mutate("getVergeConfig"); // update bypass value - }} + onChange={(e) => onChangeData({ enable_silent_start: e })} + onGuard={(e) => patchVergeConfig({ enable_silent_start: e })} > <Switch edge="end" /> </GuardState> </SettingItem> - - {enable_system_proxy && ( - <SettingItem label={t("Proxy Guard")}> - <GuardState - value={enable_proxy_guard ?? false} - valueProps="checked" - onCatch={onError} - onFormat={onSwitchFormat} - onChange={(e) => onChangeData({ enable_proxy_guard: e })} - onGuard={(e) => patchVergeConfig({ enable_proxy_guard: e })} - > - <Switch edge="end" /> - </GuardState> - </SettingItem> - )} - - {enable_system_proxy && ( - <SettingItem label={t("Proxy Bypass")}> - <GuardState - value={system_proxy_bypass ?? ""} - onCatch={onError} - onFormat={(e: any) => e.target.value} - onChange={(e) => onChangeData({ system_proxy_bypass: e })} - onGuard={(e) => patchVergeConfig({ system_proxy_bypass: e })} - waitTime={1000} - > - <TextField - autoComplete="off" - size="small" - sx={{ width: 120, input: { py: "7.5px" } }} - /> - </GuardState> - </SettingItem> - )} </SettingList> ); }; diff --git a/src/locales/en.json b/src/locales/en.json index 037e1b9ea4108c7ec25efbb754e3174dd8ff5704..cbaab6357b9efa2850efbef04144d299c2a7c1fb 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -65,8 +65,11 @@ "Auto Launch": "Auto Launch", "Silent Start": "Silent Start", "System Proxy": "System Proxy", + "System Proxy Setting": "System Proxy Setting", "Proxy Guard": "Proxy Guard", + "Guard Duration": "Guard Duration", "Proxy Bypass": "Proxy Bypass", + "Current System Proxy": "Current System Proxy", "Theme Mode": "Theme Mode", "Theme Blur": "Theme Blur", "Theme Setting": "Theme Setting", diff --git a/src/locales/zh.json b/src/locales/zh.json index 30f67f44a887583196eabefd71c9461327ce16ac..05cd83b5b1e7dbf59a301933eaa271b73e6fb56d 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -65,8 +65,11 @@ "Auto Launch": "开机自å¯", "Silent Start": "é™é»˜å¯åŠ¨", "System Proxy": "系统代ç†", + "System Proxy Setting": "系统代ç†è®¾ç½®", "Proxy Guard": "系统代ç†å®ˆå«", + "Guard Duration": "代ç†å®ˆå«é—´éš”", "Proxy Bypass": "Proxy Bypass", + "Current System Proxy": "当å‰ç³»ç»Ÿä»£ç†", "Theme Mode": "主题模å¼", "Theme Blur": "背景模糊", "Theme Setting": "主题设置", diff --git a/src/services/cmds.ts b/src/services/cmds.ts index e4f9ebf4082e76ad08140a627a58ca9466c2642c..d99c60aab18f69ef6f7b2baaa13d695c22de8a32 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -121,7 +121,11 @@ export async function patchVergeConfig(payload: CmdType.VergeConfig) { } export async function getSystemProxy() { - return invoke<any>("get_sys_proxy"); + return invoke<{ + enable: boolean; + server: string; + bypass: string; + }>("get_sys_proxy"); } export async function changeClashCore(clashCore: string) { diff --git a/src/services/types.d.ts b/src/services/types.d.ts index 37115a6d541c0a3a98e29bd238f0598d40c512f6..fa2604813d9fb444e80fe9f491a076f0360114cb 100644 --- a/src/services/types.d.ts +++ b/src/services/types.d.ts @@ -144,6 +144,7 @@ declare namespace CmdType { enable_silent_start?: boolean; enable_system_proxy?: boolean; enable_proxy_guard?: boolean; + proxy_guard_duration?: number; system_proxy_bypass?: string; web_ui_list?: string[]; theme_setting?: {