feat: add rustfmt configuration and CI workflow for code formatting
refactor: streamline formatting workflow by removing unused taplo steps and clarifying directory change refactor: remove unnecessary directory change step in formatting workflow
This commit is contained in:
@@ -82,21 +82,21 @@ export const BackupConfigViewer = memo(
|
||||
|
||||
if (!url) {
|
||||
urlRef.current?.focus();
|
||||
showNotice('error', t("WebDAV URL Required"));
|
||||
showNotice("error", t("WebDAV URL Required"));
|
||||
throw new Error(t("WebDAV URL Required"));
|
||||
} else if (!isValidUrl(url)) {
|
||||
urlRef.current?.focus();
|
||||
showNotice('error', t("Invalid WebDAV URL"));
|
||||
showNotice("error", t("Invalid WebDAV URL"));
|
||||
throw new Error(t("Invalid WebDAV URL"));
|
||||
}
|
||||
if (!username) {
|
||||
usernameRef.current?.focus();
|
||||
showNotice('error', t("WebDAV URL Required"));
|
||||
showNotice("error", t("WebDAV URL Required"));
|
||||
throw new Error(t("Username Required"));
|
||||
}
|
||||
if (!password) {
|
||||
passwordRef.current?.focus();
|
||||
showNotice('error', t("WebDAV URL Required"));
|
||||
showNotice("error", t("WebDAV URL Required"));
|
||||
throw new Error(t("Password Required"));
|
||||
}
|
||||
};
|
||||
@@ -110,11 +110,11 @@ export const BackupConfigViewer = memo(
|
||||
data.username.trim(),
|
||||
data.password,
|
||||
).then(() => {
|
||||
showNotice('success', t("WebDAV Config Saved"));
|
||||
showNotice("success", t("WebDAV Config Saved"));
|
||||
onSaveSuccess();
|
||||
});
|
||||
} catch (error) {
|
||||
showNotice('error', t("WebDAV Config Save Failed", { error }), 3000);
|
||||
showNotice("error", t("WebDAV Config Save Failed", { error }), 3000);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -125,11 +125,11 @@ export const BackupConfigViewer = memo(
|
||||
try {
|
||||
setLoading(true);
|
||||
await createWebdavBackup().then(async () => {
|
||||
showNotice('success', t("Backup Created"));
|
||||
showNotice("success", t("Backup Created"));
|
||||
await onBackupSuccess();
|
||||
});
|
||||
} catch (error) {
|
||||
showNotice('error', t("Backup Failed", { error }));
|
||||
showNotice("error", t("Backup Failed", { error }));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export const BackupTableViewer = memo(
|
||||
|
||||
const handleRestore = useLockFn(async (filename: string) => {
|
||||
await restoreWebDavBackup(filename).then(() => {
|
||||
showNotice('success', t("Restore Success, App will restart in 1s"));
|
||||
showNotice("success", t("Restore Success, App will restart in 1s"));
|
||||
});
|
||||
await restartApp();
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
const errorMsg = await changeClashCore(core);
|
||||
|
||||
if (errorMsg) {
|
||||
showNotice('error', errorMsg);
|
||||
showNotice("error", errorMsg);
|
||||
setChangingCore(null);
|
||||
return;
|
||||
}
|
||||
@@ -65,7 +65,7 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
}, 500);
|
||||
} catch (err: any) {
|
||||
setChangingCore(null);
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -73,11 +73,11 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
try {
|
||||
setRestarting(true);
|
||||
await restartCore();
|
||||
showNotice('success', t(`Clash Core Restarted`));
|
||||
showNotice("success", t(`Clash Core Restarted`));
|
||||
setRestarting(false);
|
||||
} catch (err: any) {
|
||||
setRestarting(false);
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -86,14 +86,14 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
setUpgrading(true);
|
||||
await upgradeCore();
|
||||
setUpgrading(false);
|
||||
showNotice('success', t(`Core Version Updated`));
|
||||
showNotice("success", t(`Core Version Updated`));
|
||||
} catch (err: any) {
|
||||
setUpgrading(false);
|
||||
const errMsg = err.response?.data?.message || err.toString();
|
||||
const showMsg = errMsg.includes("already using latest version")
|
||||
? "Already Using Latest Core Version"
|
||||
: errMsg;
|
||||
showNotice('error', t(showMsg));
|
||||
showNotice("error", t(showMsg));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
ListItem,
|
||||
ListItemText,
|
||||
Stack,
|
||||
TextField
|
||||
TextField,
|
||||
} from "@mui/material";
|
||||
import { useLockFn, useRequest } from "ahooks";
|
||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
||||
@@ -26,127 +26,136 @@ interface ClashPortViewerRef {
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
const generateRandomPort = () => Math.floor(Math.random() * (65535 - 1025 + 1)) + 1025;
|
||||
const generateRandomPort = () =>
|
||||
Math.floor(Math.random() * (65535 - 1025 + 1)) + 1025;
|
||||
|
||||
export const ClashPortViewer = forwardRef<ClashPortViewerRef, ClashPortViewerProps>(
|
||||
(props, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const { clashInfo, patchInfo } = useClashInfo();
|
||||
const { verge, patchVerge } = useVerge();
|
||||
const [open, setOpen] = useState(false);
|
||||
export const ClashPortViewer = forwardRef<
|
||||
ClashPortViewerRef,
|
||||
ClashPortViewerProps
|
||||
>((props, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const { clashInfo, patchInfo } = useClashInfo();
|
||||
const { verge, patchVerge } = useVerge();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// Mixed Port
|
||||
const [mixedPort, setMixedPort] = useState(
|
||||
verge?.verge_mixed_port ?? clashInfo?.mixed_port ?? 7897
|
||||
);
|
||||
// Mixed Port
|
||||
const [mixedPort, setMixedPort] = useState(
|
||||
verge?.verge_mixed_port ?? clashInfo?.mixed_port ?? 7897,
|
||||
);
|
||||
|
||||
// 其他端口状态
|
||||
const [socksPort, setSocksPort] = useState(verge?.verge_socks_port ?? 7898);
|
||||
const [socksEnabled, setSocksEnabled] = useState(verge?.verge_socks_enabled ?? false);
|
||||
const [httpPort, setHttpPort] = useState(verge?.verge_port ?? 7899);
|
||||
const [httpEnabled, setHttpEnabled] = useState(verge?.verge_http_enabled ?? false);
|
||||
const [redirPort, setRedirPort] = useState(verge?.verge_redir_port ?? 7895);
|
||||
const [redirEnabled, setRedirEnabled] = useState(verge?.verge_redir_enabled ?? false);
|
||||
const [tproxyPort, setTproxyPort] = useState(verge?.verge_tproxy_port ?? 7896);
|
||||
const [tproxyEnabled, setTproxyEnabled] = useState(verge?.verge_tproxy_enabled ?? false);
|
||||
// 其他端口状态
|
||||
const [socksPort, setSocksPort] = useState(verge?.verge_socks_port ?? 7898);
|
||||
const [socksEnabled, setSocksEnabled] = useState(
|
||||
verge?.verge_socks_enabled ?? false,
|
||||
);
|
||||
const [httpPort, setHttpPort] = useState(verge?.verge_port ?? 7899);
|
||||
const [httpEnabled, setHttpEnabled] = useState(
|
||||
verge?.verge_http_enabled ?? false,
|
||||
);
|
||||
const [redirPort, setRedirPort] = useState(verge?.verge_redir_port ?? 7895);
|
||||
const [redirEnabled, setRedirEnabled] = useState(
|
||||
verge?.verge_redir_enabled ?? false,
|
||||
);
|
||||
const [tproxyPort, setTproxyPort] = useState(
|
||||
verge?.verge_tproxy_port ?? 7896,
|
||||
);
|
||||
const [tproxyEnabled, setTproxyEnabled] = useState(
|
||||
verge?.verge_tproxy_enabled ?? false,
|
||||
);
|
||||
|
||||
// 添加保存请求,防止GUI卡死
|
||||
const { loading, run: saveSettings } = useRequest(
|
||||
async (params: {
|
||||
clashConfig: any;
|
||||
vergeConfig: any;
|
||||
}) => {
|
||||
const { clashConfig, vergeConfig } = params;
|
||||
await Promise.all([
|
||||
patchInfo(clashConfig),
|
||||
patchVerge(vergeConfig)
|
||||
]);
|
||||
// 添加保存请求,防止GUI卡死
|
||||
const { loading, run: saveSettings } = useRequest(
|
||||
async (params: { clashConfig: any; vergeConfig: any }) => {
|
||||
const { clashConfig, vergeConfig } = params;
|
||||
await Promise.all([patchInfo(clashConfig), patchVerge(vergeConfig)]);
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: () => {
|
||||
setOpen(false);
|
||||
showNotice("success", t("Port settings saved")); // 调用提示函数
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: () => {
|
||||
setOpen(false);
|
||||
showNotice("success", t("Port settings saved")); // 调用提示函数
|
||||
},
|
||||
onError: () => {
|
||||
showNotice("error", t("Failed to save settings")); // 调用提示函数
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
open: () => {
|
||||
setMixedPort(verge?.verge_mixed_port ?? clashInfo?.mixed_port ?? 7897);
|
||||
setSocksPort(verge?.verge_socks_port ?? 7898);
|
||||
setSocksEnabled(verge?.verge_socks_enabled ?? false);
|
||||
setHttpPort(verge?.verge_port ?? 7899);
|
||||
setHttpEnabled(verge?.verge_http_enabled ?? false);
|
||||
setRedirPort(verge?.verge_redir_port ?? 7895);
|
||||
setRedirEnabled(verge?.verge_redir_enabled ?? false);
|
||||
setTproxyPort(verge?.verge_tproxy_port ?? 7896);
|
||||
setTproxyEnabled(verge?.verge_tproxy_enabled ?? false);
|
||||
setOpen( true);
|
||||
onError: () => {
|
||||
showNotice("error", t("Failed to save settings")); // 调用提示函数
|
||||
},
|
||||
close: () => setOpen(false),
|
||||
}));
|
||||
},
|
||||
);
|
||||
|
||||
const onSave = useLockFn(async () => {
|
||||
// 端口冲突检测
|
||||
const portList = [
|
||||
mixedPort,
|
||||
socksEnabled ? socksPort : -1,
|
||||
httpEnabled ? httpPort : -1,
|
||||
redirEnabled ? redirPort : -1,
|
||||
tproxyEnabled ? tproxyPort : -1
|
||||
].filter(p => p !== -1);
|
||||
useImperativeHandle(ref, () => ({
|
||||
open: () => {
|
||||
setMixedPort(verge?.verge_mixed_port ?? clashInfo?.mixed_port ?? 7897);
|
||||
setSocksPort(verge?.verge_socks_port ?? 7898);
|
||||
setSocksEnabled(verge?.verge_socks_enabled ?? false);
|
||||
setHttpPort(verge?.verge_port ?? 7899);
|
||||
setHttpEnabled(verge?.verge_http_enabled ?? false);
|
||||
setRedirPort(verge?.verge_redir_port ?? 7895);
|
||||
setRedirEnabled(verge?.verge_redir_enabled ?? false);
|
||||
setTproxyPort(verge?.verge_tproxy_port ?? 7896);
|
||||
setTproxyEnabled(verge?.verge_tproxy_enabled ?? false);
|
||||
setOpen(true);
|
||||
},
|
||||
close: () => setOpen(false),
|
||||
}));
|
||||
|
||||
if (new Set(portList).size !== portList.length) {
|
||||
return;
|
||||
}
|
||||
const onSave = useLockFn(async () => {
|
||||
// 端口冲突检测
|
||||
const portList = [
|
||||
mixedPort,
|
||||
socksEnabled ? socksPort : -1,
|
||||
httpEnabled ? httpPort : -1,
|
||||
redirEnabled ? redirPort : -1,
|
||||
tproxyEnabled ? tproxyPort : -1,
|
||||
].filter((p) => p !== -1);
|
||||
|
||||
// 验证端口范围
|
||||
const isValidPort = (port: number) => port >= 1 && port <= 65535;
|
||||
const allPortsValid = [
|
||||
mixedPort,
|
||||
socksEnabled ? socksPort : 0,
|
||||
httpEnabled ? httpPort : 0,
|
||||
redirEnabled ? redirPort : 0,
|
||||
tproxyEnabled ? tproxyPort : 0
|
||||
].every(port => port === 0 || isValidPort(port));
|
||||
if (new Set(portList).size !== portList.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allPortsValid) {
|
||||
return;
|
||||
}
|
||||
// 验证端口范围
|
||||
const isValidPort = (port: number) => port >= 1 && port <= 65535;
|
||||
const allPortsValid = [
|
||||
mixedPort,
|
||||
socksEnabled ? socksPort : 0,
|
||||
httpEnabled ? httpPort : 0,
|
||||
redirEnabled ? redirPort : 0,
|
||||
tproxyEnabled ? tproxyPort : 0,
|
||||
].every((port) => port === 0 || isValidPort(port));
|
||||
|
||||
// 准备配置数据
|
||||
const clashConfig = {
|
||||
"mixed-port": mixedPort,
|
||||
"socks-port": socksPort,
|
||||
port: httpPort,
|
||||
"redir-port": redirPort,
|
||||
"tproxy-port": tproxyPort
|
||||
};
|
||||
if (!allPortsValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const vergeConfig = {
|
||||
verge_mixed_port: mixedPort,
|
||||
verge_socks_port: socksPort,
|
||||
verge_socks_enabled: socksEnabled,
|
||||
verge_port: httpPort,
|
||||
verge_http_enabled: httpEnabled,
|
||||
verge_redir_port: redirPort,
|
||||
verge_redir_enabled: redirEnabled,
|
||||
verge_tproxy_port: tproxyPort,
|
||||
verge_tproxy_enabled: tproxyEnabled
|
||||
};
|
||||
// 准备配置数据
|
||||
const clashConfig = {
|
||||
"mixed-port": mixedPort,
|
||||
"socks-port": socksPort,
|
||||
port: httpPort,
|
||||
"redir-port": redirPort,
|
||||
"tproxy-port": tproxyPort,
|
||||
};
|
||||
|
||||
// 提交保存请求
|
||||
await saveSettings({ clashConfig, vergeConfig });
|
||||
});
|
||||
const vergeConfig = {
|
||||
verge_mixed_port: mixedPort,
|
||||
verge_socks_port: socksPort,
|
||||
verge_socks_enabled: socksEnabled,
|
||||
verge_port: httpPort,
|
||||
verge_http_enabled: httpEnabled,
|
||||
verge_redir_port: redirPort,
|
||||
verge_redir_enabled: redirEnabled,
|
||||
verge_tproxy_port: tproxyPort,
|
||||
verge_tproxy_enabled: tproxyEnabled,
|
||||
};
|
||||
|
||||
// 优化的数字输入处理
|
||||
const handleNumericChange = (setter: (value: number) => void) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value.replace(/\D+/, '');
|
||||
if (value === '') {
|
||||
// 提交保存请求
|
||||
await saveSettings({ clashConfig, vergeConfig });
|
||||
});
|
||||
|
||||
// 优化的数字输入处理
|
||||
const handleNumericChange =
|
||||
(setter: (value: number) => void) =>
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value.replace(/\D+/, "");
|
||||
if (value === "") {
|
||||
setter(0);
|
||||
return;
|
||||
}
|
||||
@@ -157,190 +166,201 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef, ClashPortViewerPro
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("Port Configuration")}
|
||||
contentSx={{
|
||||
width: 400
|
||||
}}
|
||||
okBtn={
|
||||
loading ? (
|
||||
<Stack direction="row" alignItems="center" spacing={1}>
|
||||
<CircularProgress size={20} />
|
||||
{t("Saving...")}
|
||||
</Stack>
|
||||
) : t("Save")
|
||||
}
|
||||
cancelBtn={t("Cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
>
|
||||
<List sx={{ width: "100%" }}>
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Mixed Port")}
|
||||
primaryTypographyProps={{ fontSize: 12 }}
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("Port Configuration")}
|
||||
contentSx={{
|
||||
width: 400,
|
||||
}}
|
||||
okBtn={
|
||||
loading ? (
|
||||
<Stack direction="row" alignItems="center" spacing={1}>
|
||||
<CircularProgress size={20} />
|
||||
{t("Saving...")}
|
||||
</Stack>
|
||||
) : (
|
||||
t("Save")
|
||||
)
|
||||
}
|
||||
cancelBtn={t("Cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
>
|
||||
<List sx={{ width: "100%" }}>
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Mixed Port")}
|
||||
primaryTypographyProps={{ fontSize: 12 }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<TextField
|
||||
size="small"
|
||||
sx={{ width: 80, mr: 0.5, fontSize: 12 }}
|
||||
value={mixedPort}
|
||||
onChange={(e) =>
|
||||
setMixedPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))
|
||||
}
|
||||
inputProps={{ style: { fontSize: 12 } }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<TextField
|
||||
size="small"
|
||||
sx={{ width: 80, mr: 0.5, fontSize: 12 }}
|
||||
value={mixedPort}
|
||||
onChange={(e) => setMixedPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))}
|
||||
inputProps={{ style: { fontSize: 12 } }}
|
||||
/>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setMixedPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
<Shuffle fontSize="small" />
|
||||
</IconButton>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={true}
|
||||
disabled={true}
|
||||
sx={{ ml: 0.5, opacity: 0.7 }}
|
||||
/>
|
||||
</div>
|
||||
</ListItem>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setMixedPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
<Shuffle fontSize="small" />
|
||||
</IconButton>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={true}
|
||||
disabled={true}
|
||||
sx={{ ml: 0.5, opacity: 0.7 }}
|
||||
/>
|
||||
</div>
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Socks Port")}
|
||||
primaryTypographyProps={{ fontSize: 12 }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<TextField
|
||||
size="small"
|
||||
sx={{ width: 80, mr: 0.5, fontSize: 12 }}
|
||||
value={socksPort}
|
||||
onChange={(e) =>
|
||||
setSocksPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))
|
||||
}
|
||||
disabled={!socksEnabled}
|
||||
inputProps={{ style: { fontSize: 12 } }}
|
||||
/>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setSocksPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
disabled={!socksEnabled}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
<Shuffle fontSize="small" />
|
||||
</IconButton>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={socksEnabled}
|
||||
onChange={(_, c) => setSocksEnabled(c)}
|
||||
sx={{ ml: 0.5 }}
|
||||
/>
|
||||
</div>
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("HTTP Port")}
|
||||
primaryTypographyProps={{ fontSize: 12 }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<TextField
|
||||
size="small"
|
||||
sx={{ width: 80, mr: 0.5, fontSize: 12 }}
|
||||
value={httpPort}
|
||||
onChange={(e) =>
|
||||
setHttpPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))
|
||||
}
|
||||
disabled={!httpEnabled}
|
||||
inputProps={{ style: { fontSize: 12 } }}
|
||||
/>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setHttpPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
disabled={!httpEnabled}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
<Shuffle fontSize="small" />
|
||||
</IconButton>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={httpEnabled}
|
||||
onChange={(_, c) => setHttpEnabled(c)}
|
||||
sx={{ ml: 0.5 }}
|
||||
/>
|
||||
</div>
|
||||
</ListItem>
|
||||
|
||||
{OS !== "windows" && (
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Socks Port")}
|
||||
primary={t("Redir Port")}
|
||||
primaryTypographyProps={{ fontSize: 12 }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<TextField
|
||||
size="small"
|
||||
sx={{ width: 80, mr: 0.5, fontSize: 12 }}
|
||||
value={socksPort}
|
||||
onChange={(e) => setSocksPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))}
|
||||
disabled={!socksEnabled}
|
||||
value={redirPort}
|
||||
onChange={(e) =>
|
||||
setRedirPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))
|
||||
}
|
||||
disabled={!redirEnabled}
|
||||
inputProps={{ style: { fontSize: 12 } }}
|
||||
/>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setSocksPort(generateRandomPort())}
|
||||
onClick={() => setRedirPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
disabled={!socksEnabled}
|
||||
disabled={!redirEnabled}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
<Shuffle fontSize="small" />
|
||||
</IconButton>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={socksEnabled}
|
||||
onChange={(_, c) => setSocksEnabled(c)}
|
||||
checked={redirEnabled}
|
||||
onChange={(_, c) => setRedirEnabled(c)}
|
||||
sx={{ ml: 0.5 }}
|
||||
/>
|
||||
</div>
|
||||
</ListItem>
|
||||
)}
|
||||
|
||||
{OS === "linux" && (
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("HTTP Port")}
|
||||
primary={t("Tproxy Port")}
|
||||
primaryTypographyProps={{ fontSize: 12 }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<TextField
|
||||
size="small"
|
||||
sx={{ width: 80, mr: 0.5, fontSize: 12 }}
|
||||
value={httpPort}
|
||||
onChange={(e) => setHttpPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))}
|
||||
disabled={!httpEnabled}
|
||||
value={tproxyPort}
|
||||
onChange={(e) =>
|
||||
setTproxyPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))
|
||||
}
|
||||
disabled={!tproxyEnabled}
|
||||
inputProps={{ style: { fontSize: 12 } }}
|
||||
/>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setHttpPort(generateRandomPort())}
|
||||
onClick={() => setTproxyPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
disabled={!httpEnabled}
|
||||
disabled={!tproxyEnabled}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
<Shuffle fontSize="small" />
|
||||
</IconButton>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={httpEnabled}
|
||||
onChange={(_, c) => setHttpEnabled(c)}
|
||||
checked={tproxyEnabled}
|
||||
onChange={(_, c) => setTproxyEnabled(c)}
|
||||
sx={{ ml: 0.5 }}
|
||||
/>
|
||||
</div>
|
||||
</ListItem>
|
||||
|
||||
{OS !== "windows" && (
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Redir Port")}
|
||||
primaryTypographyProps={{ fontSize: 12 }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<TextField
|
||||
size="small"
|
||||
sx={{ width: 80, mr: 0.5, fontSize: 12 }}
|
||||
value={redirPort}
|
||||
onChange={(e) => setRedirPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))}
|
||||
disabled={!redirEnabled}
|
||||
inputProps={{ style: { fontSize: 12 } }}
|
||||
/>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setRedirPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
disabled={!redirEnabled}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
<Shuffle fontSize="small" />
|
||||
</IconButton>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={redirEnabled}
|
||||
onChange={(_, c) => setRedirEnabled(c)}
|
||||
sx={{ ml: 0.5 }}
|
||||
/>
|
||||
</div>
|
||||
</ListItem>
|
||||
)}
|
||||
|
||||
{OS === "linux" && (
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Tproxy Port")}
|
||||
primaryTypographyProps={{ fontSize: 12 }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<TextField
|
||||
size="small"
|
||||
sx={{ width: 80, mr: 0.5, fontSize: 12 }}
|
||||
value={tproxyPort}
|
||||
onChange={(e) => setTproxyPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))}
|
||||
disabled={!tproxyEnabled}
|
||||
inputProps={{ style: { fontSize: 12 } }}
|
||||
/>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setTproxyPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
disabled={!tproxyEnabled}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
<Shuffle fontSize="small" />
|
||||
</IconButton>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={tproxyEnabled}
|
||||
onChange={(_, c) => setTproxyEnabled(c)}
|
||||
sx={{ ml: 0.5 }}
|
||||
/>
|
||||
</div>
|
||||
</ListItem>
|
||||
)}
|
||||
</List>
|
||||
</BaseDialog>
|
||||
);
|
||||
}
|
||||
);
|
||||
)}
|
||||
</List>
|
||||
</BaseDialog>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
ListItemText,
|
||||
Snackbar,
|
||||
TextField,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
|
||||
@@ -42,58 +42,72 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
// 保存配置
|
||||
const onSave = useLockFn(async () => {
|
||||
if (!controller.trim()) {
|
||||
showNotice('error', t("Controller address cannot be empty"), 3000);
|
||||
showNotice("error", t("Controller address cannot be empty"), 3000);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!secret.trim()) {
|
||||
showNotice('error', t("Secret cannot be empty"), 3000);
|
||||
showNotice("error", t("Secret cannot be empty"), 3000);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSaving(true);
|
||||
await patchInfo({ "external-controller": controller, secret });
|
||||
showNotice('success', t("Configuration saved successfully"), 2000);
|
||||
showNotice("success", t("Configuration saved successfully"), 2000);
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || t("Failed to save configuration"), 4000);
|
||||
showNotice(
|
||||
"error",
|
||||
err.message || t("Failed to save configuration"),
|
||||
4000,
|
||||
);
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
});
|
||||
|
||||
// 复制到剪贴板
|
||||
const handleCopyToClipboard = useLockFn(async (text: string, type: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setCopySuccess(type);
|
||||
setTimeout(() => setCopySuccess(null), 2000);
|
||||
} catch (err) {
|
||||
showNotice('error', t("Failed to copy"), 2000);
|
||||
}
|
||||
});
|
||||
const handleCopyToClipboard = useLockFn(
|
||||
async (text: string, type: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setCopySuccess(type);
|
||||
setTimeout(() => setCopySuccess(null), 2000);
|
||||
} catch (err) {
|
||||
showNotice("error", t("Failed to copy"), 2000);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("External Controller")}
|
||||
contentSx={{ width: 400 }}
|
||||
okBtn={isSaving ? (
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<CircularProgress size={16} color="inherit" />
|
||||
{t("Saving...")}
|
||||
</Box>
|
||||
) : (
|
||||
t("Save")
|
||||
)}
|
||||
okBtn={
|
||||
isSaving ? (
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<CircularProgress size={16} color="inherit" />
|
||||
{t("Saving...")}
|
||||
</Box>
|
||||
) : (
|
||||
t("Save")
|
||||
)
|
||||
}
|
||||
cancelBtn={t("Cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
>
|
||||
<List>
|
||||
<ListItem sx={{ padding: "5px 2px", display: "flex", justifyContent: "space-between" }}>
|
||||
<ListItem
|
||||
sx={{
|
||||
padding: "5px 2px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t("External Controller")} />
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<TextField
|
||||
@@ -101,11 +115,11 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
sx={{
|
||||
width: 175,
|
||||
opacity: 1,
|
||||
pointerEvents: 'auto'
|
||||
pointerEvents: "auto",
|
||||
}}
|
||||
value={controller}
|
||||
placeholder="Required"
|
||||
onChange={e => setController(e.target.value)}
|
||||
onChange={(e) => setController(e.target.value)}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
<Tooltip title={t("Copy to clipboard")}>
|
||||
@@ -121,7 +135,13 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
</Box>
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px", display: "flex", justifyContent: "space-between" }}>
|
||||
<ListItem
|
||||
sx={{
|
||||
padding: "5px 2px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t("Core Secret")} />
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<TextField
|
||||
@@ -129,11 +149,11 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
sx={{
|
||||
width: 175,
|
||||
opacity: 1,
|
||||
pointerEvents: 'auto'
|
||||
pointerEvents: "auto",
|
||||
}}
|
||||
value={secret}
|
||||
placeholder={t("Recommended")}
|
||||
onChange={e => setSecret(e.target.value)}
|
||||
onChange={(e) => setSecret(e.target.value)}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
<Tooltip title={t("Copy to clipboard")}>
|
||||
@@ -153,13 +173,12 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
<Snackbar
|
||||
open={copySuccess !== null}
|
||||
autoHideDuration={2000}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
||||
>
|
||||
<Alert severity="success">
|
||||
{copySuccess === "controller"
|
||||
? t("Controller address copied to clipboard")
|
||||
: t("Secret copied to clipboard")
|
||||
}
|
||||
: t("Secret copied to clipboard")}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -59,7 +59,13 @@ const DEFAULT_DNS_CONFIG = {
|
||||
"*.msftncsi.com",
|
||||
"www.msftconnecttest.com",
|
||||
],
|
||||
"default-nameserver": ["system", "223.6.6.6", "8.8.8.8", "2400:3200::1", "2001:4860:4860::8888"],
|
||||
"default-nameserver": [
|
||||
"system",
|
||||
"223.6.6.6",
|
||||
"8.8.8.8",
|
||||
"2400:3200::1",
|
||||
"2001:4860:4860::8888",
|
||||
],
|
||||
nameserver: [
|
||||
"8.8.8.8",
|
||||
"https://doh.pub/dns-query",
|
||||
@@ -70,7 +76,7 @@ const DEFAULT_DNS_CONFIG = {
|
||||
"proxy-server-nameserver": [
|
||||
"https://doh.pub/dns-query",
|
||||
"https://dns.alidns.com/dns-query",
|
||||
"tls://223.5.5.5"
|
||||
"tls://223.5.5.5",
|
||||
],
|
||||
"direct-nameserver": [],
|
||||
"direct-nameserver-follow-policy": false,
|
||||
@@ -219,8 +225,7 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
dnsConfig["respect-rules"] ?? DEFAULT_DNS_CONFIG["respect-rules"],
|
||||
useHosts: dnsConfig["use-hosts"] ?? DEFAULT_DNS_CONFIG["use-hosts"],
|
||||
useSystemHosts:
|
||||
dnsConfig["use-system-hosts"] ??
|
||||
DEFAULT_DNS_CONFIG["use-system-hosts"],
|
||||
dnsConfig["use-system-hosts"] ?? DEFAULT_DNS_CONFIG["use-system-hosts"],
|
||||
ipv6: dnsConfig.ipv6 ?? DEFAULT_DNS_CONFIG.ipv6,
|
||||
fakeIpFilter:
|
||||
dnsConfig["fake-ip-filter"]?.join(", ") ??
|
||||
@@ -229,7 +234,8 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
dnsConfig.nameserver?.join(", ") ??
|
||||
DEFAULT_DNS_CONFIG.nameserver.join(", "),
|
||||
fallback:
|
||||
dnsConfig.fallback?.join(", ") ?? DEFAULT_DNS_CONFIG.fallback.join(", "),
|
||||
dnsConfig.fallback?.join(", ") ??
|
||||
DEFAULT_DNS_CONFIG.fallback.join(", "),
|
||||
defaultNameserver:
|
||||
dnsConfig["default-nameserver"]?.join(", ") ??
|
||||
DEFAULT_DNS_CONFIG["default-nameserver"].join(", "),
|
||||
@@ -299,9 +305,8 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
// 从表单值更新YAML内容
|
||||
const updateYamlFromValues = () => {
|
||||
|
||||
const config: Record<string, any> = {};
|
||||
|
||||
|
||||
const dnsConfig = generateDnsConfig();
|
||||
if (Object.keys(dnsConfig).length > 0) {
|
||||
config.dns = dnsConfig;
|
||||
@@ -311,7 +316,7 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
if (Object.keys(hosts).length > 0) {
|
||||
config.hosts = hosts;
|
||||
}
|
||||
|
||||
|
||||
setYamlContent(yaml.dump(config, { forceQuotes: true }));
|
||||
};
|
||||
|
||||
@@ -320,10 +325,10 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
try {
|
||||
const parsedYaml = yaml.load(yamlContent) as any;
|
||||
if (!parsedYaml) return;
|
||||
|
||||
|
||||
updateValuesFromConfig(parsedYaml);
|
||||
} catch (err: any) {
|
||||
showNotice('error', t("Invalid YAML format"));
|
||||
showNotice("error", t("Invalid YAML format"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -505,7 +510,7 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
if (Object.keys(dnsConfig).length > 0) {
|
||||
config.dns = dnsConfig;
|
||||
}
|
||||
|
||||
|
||||
const hosts = parseHosts(values.hosts);
|
||||
if (Object.keys(hosts).length > 0) {
|
||||
config.hosts = hosts;
|
||||
@@ -521,30 +526,41 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
// 保存配置
|
||||
await invoke("save_dns_config", { dnsConfig: config });
|
||||
|
||||
|
||||
// 验证配置
|
||||
const [isValid, errorMsg] = await invoke<[boolean, string]>("validate_dns_config", {});
|
||||
|
||||
const [isValid, errorMsg] = await invoke<[boolean, string]>(
|
||||
"validate_dns_config",
|
||||
{},
|
||||
);
|
||||
|
||||
if (!isValid) {
|
||||
let cleanErrorMsg = errorMsg;
|
||||
|
||||
|
||||
// 提取关键错误信息
|
||||
if (errorMsg.includes("level=error")) {
|
||||
const errorLines = errorMsg.split('\n').filter(line =>
|
||||
line.includes("level=error") ||
|
||||
line.includes("level=fatal") ||
|
||||
line.includes("failed")
|
||||
);
|
||||
|
||||
const errorLines = errorMsg
|
||||
.split("\n")
|
||||
.filter(
|
||||
(line) =>
|
||||
line.includes("level=error") ||
|
||||
line.includes("level=fatal") ||
|
||||
line.includes("failed"),
|
||||
);
|
||||
|
||||
if (errorLines.length > 0) {
|
||||
cleanErrorMsg = errorLines.map(line => {
|
||||
const msgMatch = line.match(/msg="([^"]+)"/);
|
||||
return msgMatch ? msgMatch[1] : line;
|
||||
}).join(", ");
|
||||
cleanErrorMsg = errorLines
|
||||
.map((line) => {
|
||||
const msgMatch = line.match(/msg="([^"]+)"/);
|
||||
return msgMatch ? msgMatch[1] : line;
|
||||
})
|
||||
.join(", ");
|
||||
}
|
||||
}
|
||||
|
||||
showNotice('error', t("DNS configuration error") + ": " + cleanErrorMsg);
|
||||
|
||||
showNotice(
|
||||
"error",
|
||||
t("DNS configuration error") + ": " + cleanErrorMsg,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -555,9 +571,9 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
showNotice('success', t("DNS settings saved"));
|
||||
showNotice("success", t("DNS settings saved"));
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
});
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.toString());
|
||||
showNotice("error", err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
const onSwitchFormat = (_e: any, value: boolean) => value;
|
||||
const onError = (err: any) => {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
};
|
||||
const onChangeData = (patch: Partial<IVergeConfig>) => {
|
||||
mutateVerge({ ...verge, ...patch }, false);
|
||||
|
||||
@@ -44,7 +44,7 @@ export const LiteModeViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
});
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
});
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.toString());
|
||||
showNotice("error", err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -246,7 +246,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">{t("millis")}</InputAdornment>
|
||||
),
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -129,7 +129,7 @@ const AddressDisplay = (props: { label: string; content: string }) => {
|
||||
size="small"
|
||||
onClick={async () => {
|
||||
await writeText(props.content);
|
||||
showNotice('success', t("Copy Success"));
|
||||
showNotice("success", t("Copy Success"));
|
||||
}}
|
||||
>
|
||||
<ContentCopyRounded sx={{ fontSize: "18px" }} />
|
||||
|
||||
@@ -202,11 +202,14 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
const onSave = useLockFn(async () => {
|
||||
if (value.duration < 1) {
|
||||
showNotice('error', t("Proxy Daemon Duration Cannot be Less than 1 Second"));
|
||||
showNotice(
|
||||
"error",
|
||||
t("Proxy Daemon Duration Cannot be Less than 1 Second"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (value.bypass && !validReg.test(value.bypass)) {
|
||||
showNotice('error', t("Invalid Bypass Format"));
|
||||
showNotice("error", t("Invalid Bypass Format"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -223,7 +226,7 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
!ipv6Regex.test(value.proxy_host) &&
|
||||
!hostnameRegex.test(value.proxy_host)
|
||||
) {
|
||||
showNotice('error', t("Invalid Proxy Host Format"));
|
||||
showNotice("error", t("Invalid Proxy Host Format"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -271,41 +274,44 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
}
|
||||
|
||||
// 判断是否需要重置系统代理
|
||||
const needResetProxy =
|
||||
value.pac !== proxy_auto_config ||
|
||||
proxyHost !== proxy_host ||
|
||||
pacContent !== pac_file_content ||
|
||||
const needResetProxy =
|
||||
value.pac !== proxy_auto_config ||
|
||||
proxyHost !== proxy_host ||
|
||||
pacContent !== pac_file_content ||
|
||||
value.bypass !== system_proxy_bypass ||
|
||||
value.use_default !== use_default_bypass;
|
||||
|
||||
await patchVerge(patch);
|
||||
|
||||
|
||||
// 更新系统代理状态,以便UI立即反映变化
|
||||
await Promise.all([mutate("getSystemProxy"), mutate("getAutotemProxy")]);
|
||||
|
||||
|
||||
// 只有在修改了影响系统代理的配置且系统代理当前启用时,才重置系统代理
|
||||
if (needResetProxy) {
|
||||
const currentSysProxy = await getSystemProxy();
|
||||
const currentAutoProxy = await getAutotemProxy();
|
||||
|
||||
|
||||
if (value.pac ? currentAutoProxy?.enable : currentSysProxy?.enable) {
|
||||
// 临时关闭系统代理
|
||||
await patchVergeConfig({ enable_system_proxy: false });
|
||||
|
||||
|
||||
// 减少等待时间
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
|
||||
// 重新开启系统代理
|
||||
await patchVergeConfig({ enable_system_proxy: true });
|
||||
|
||||
|
||||
// 更新UI状态
|
||||
await Promise.all([mutate("getSystemProxy"), mutate("getAutotemProxy")]);
|
||||
await Promise.all([
|
||||
mutate("getSystemProxy"),
|
||||
mutate("getAutotemProxy"),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.toString());
|
||||
showNotice("error", err.toString());
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -415,7 +421,7 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: <InputAdornment position="end">s</InputAdornment>,
|
||||
}
|
||||
},
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setValue((v) => ({
|
||||
@@ -432,12 +438,14 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
edge="end"
|
||||
disabled={!enabled}
|
||||
checked={value.use_default}
|
||||
onChange={(_, e) => setValue((v) => ({
|
||||
...v,
|
||||
use_default: e,
|
||||
// 当取消选择use_default且当前bypass为空时,填充默认值
|
||||
bypass: (!e && !v.bypass) ? defaultBypass() : v.bypass
|
||||
}))}
|
||||
onChange={(_, e) =>
|
||||
setValue((v) => ({
|
||||
...v,
|
||||
use_default: e,
|
||||
// 当取消选择use_default且当前bypass为空时,填充默认值
|
||||
bypass: !e && !v.bypass ? defaultBypass() : v.bypass,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
|
||||
@@ -49,7 +49,7 @@ export const ThemeViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
await patchVerge({ theme_setting: theme });
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.toString());
|
||||
showNotice("error", err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -77,13 +77,13 @@ export const TunViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
);
|
||||
try {
|
||||
await enhanceProfiles();
|
||||
showNotice('success', t("Settings Applied"));
|
||||
showNotice("success", t("Settings Applied"));
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import useSWR from "swr";
|
||||
import { forwardRef, useImperativeHandle, useState, useMemo, useEffect } from "react";
|
||||
import {
|
||||
forwardRef,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
useMemo,
|
||||
useEffect,
|
||||
} from "react";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { Box, LinearProgress, Button } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -18,7 +24,8 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [currentProgressListener, setCurrentProgressListener] = useState<UnlistenFn | null>(null);
|
||||
const [currentProgressListener, setCurrentProgressListener] =
|
||||
useState<UnlistenFn | null>(null);
|
||||
|
||||
const updateState = useUpdateState();
|
||||
const setUpdateState = useSetUpdateState();
|
||||
@@ -55,12 +62,12 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
const onUpdate = useLockFn(async () => {
|
||||
if (portableFlag) {
|
||||
showNotice('error', t("Portable Updater Error"));
|
||||
showNotice("error", t("Portable Updater Error"));
|
||||
return;
|
||||
}
|
||||
if (!updateInfo?.body) return;
|
||||
if (breakChangeFlag) {
|
||||
showNotice('error', t("Break Change Update Error"));
|
||||
showNotice("error", t("Break Change Update Error"));
|
||||
return;
|
||||
}
|
||||
if (updateState) return;
|
||||
@@ -86,7 +93,7 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
await updateInfo.downloadAndInstall();
|
||||
await relaunch();
|
||||
} catch (err: any) {
|
||||
showNotice('error', err?.message || err.toString());
|
||||
showNotice("error", err?.message || err.toString());
|
||||
} finally {
|
||||
setUpdateState(false);
|
||||
if (progressListener) {
|
||||
|
||||
Reference in New Issue
Block a user