|
@@ -1,13 +1,25 @@
|
|
-import { useState, useMemo, useRef } from "react";
|
|
|
|
|
|
+import { useState, useMemo, useRef, useEffect } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Virtuoso } from "react-virtuoso";
|
|
import { Virtuoso } from "react-virtuoso";
|
|
-import { getRules, getClashConfig, closeAllConnections } from "@/services/api";
|
|
|
|
|
|
+import {
|
|
|
|
+ getRules,
|
|
|
|
+ getClashConfig,
|
|
|
|
+ closeAllConnections,
|
|
|
|
+ updateConfigs,
|
|
|
|
+} from "@/services/api";
|
|
import { BaseEmpty, BasePage, Notice } from "@/components/base";
|
|
import { BaseEmpty, BasePage, Notice } from "@/components/base";
|
|
import RuleItem from "@/components/rule/rule-item";
|
|
import RuleItem from "@/components/rule/rule-item";
|
|
import { ProviderButton } from "@/components/rule/provider-button";
|
|
import { ProviderButton } from "@/components/rule/provider-button";
|
|
import { useCustomTheme } from "@/components/layout/use-custom-theme";
|
|
import { useCustomTheme } from "@/components/layout/use-custom-theme";
|
|
import { BaseSearchBox } from "@/components/base/base-search-box";
|
|
import { BaseSearchBox } from "@/components/base/base-search-box";
|
|
-import { Box, Button, Grid, IconButton, Stack, Divider } from "@mui/material";
|
|
|
|
|
|
+import {
|
|
|
|
+ Box,
|
|
|
|
+ Button,
|
|
|
|
+ Grid,
|
|
|
|
+ IconButton,
|
|
|
|
+ Stack,
|
|
|
|
+ ButtonGroup,
|
|
|
|
+} from "@mui/material";
|
|
import { BaseStyledTextField } from "@/components/base/base-styled-text-field";
|
|
import { BaseStyledTextField } from "@/components/base/base-styled-text-field";
|
|
import { readText } from "@tauri-apps/api/clipboard";
|
|
import { readText } from "@tauri-apps/api/clipboard";
|
|
import {
|
|
import {
|
|
@@ -42,6 +54,7 @@ import {
|
|
installService,
|
|
installService,
|
|
uninstallService,
|
|
uninstallService,
|
|
checkService,
|
|
checkService,
|
|
|
|
+ patchClashConfig,
|
|
} from "@/services/cmds";
|
|
} from "@/services/cmds";
|
|
import {
|
|
import {
|
|
DndContext,
|
|
DndContext,
|
|
@@ -158,6 +171,8 @@ const QuickPage = () => {
|
|
return profileItems;
|
|
return profileItems;
|
|
}, [profiles]);
|
|
}, [profiles]);
|
|
|
|
|
|
|
|
+ const isEmpty = profileItems.length === 0;
|
|
|
|
+
|
|
const currentActivatings = () => {
|
|
const currentActivatings = () => {
|
|
return [...new Set([profiles.current ?? ""])].filter(Boolean);
|
|
return [...new Set([profiles.current ?? ""])].filter(Boolean);
|
|
};
|
|
};
|
|
@@ -233,9 +248,10 @@ const QuickPage = () => {
|
|
);
|
|
);
|
|
|
|
|
|
const isWindows = getSystem() === "windows";
|
|
const isWindows = getSystem() === "windows";
|
|
- const isActive = status === "active";
|
|
|
|
- const isInstalled = status === "installed";
|
|
|
|
- const isUninstall = status === "uninstall" || status === "unknown";
|
|
|
|
|
|
+ const isActive = serviceStatus === "active";
|
|
|
|
+ const isInstalled = serviceStatus === "installed";
|
|
|
|
+ const isUninstall =
|
|
|
|
+ serviceStatus === "uninstall" || serviceStatus === "unknown";
|
|
const [serviceLoading, setServiceLoading] = useState(false);
|
|
const [serviceLoading, setServiceLoading] = useState(false);
|
|
const [openInstall, setOpenInstall] = useState(false);
|
|
const [openInstall, setOpenInstall] = useState(false);
|
|
const [openUninstall, setOpenUninstall] = useState(false);
|
|
const [openUninstall, setOpenUninstall] = useState(false);
|
|
@@ -317,31 +333,63 @@ const QuickPage = () => {
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
- const {
|
|
|
|
- enable_tun_mode,
|
|
|
|
- enable_auto_launch,
|
|
|
|
- enable_silent_start,
|
|
|
|
- enable_system_proxy,
|
|
|
|
- enable_service_mode,
|
|
|
|
- } = verge ?? {};
|
|
|
|
-
|
|
|
|
- const link = () => {
|
|
|
|
- onInstallOrEnableService();
|
|
|
|
- onChangeData({ enable_tun_mode: true });
|
|
|
|
- patchVerge({ enable_tun_mode: true });
|
|
|
|
- onChangeData({ enable_system_proxy: true });
|
|
|
|
- patchVerge({ enable_system_proxy: true });
|
|
|
|
|
|
+ const [result, setResult] = useState(false);
|
|
|
|
+
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ if (data) {
|
|
|
|
+ const { enable_tun_mode, enable_system_proxy, enable_service_mode } =
|
|
|
|
+ verge ?? {};
|
|
|
|
+
|
|
|
|
+ // 当 enable_tun_mode, enable_system_proxy, enable_service_mode 同为 true 时,result 为 true
|
|
|
|
+ // 否则,result 为 false
|
|
|
|
+ // setResult(enable_tun_mode && enable_system_proxy && enable_service_mode);
|
|
|
|
+ setResult(
|
|
|
|
+ !!(enable_tun_mode && enable_system_proxy && enable_service_mode)
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ }, [data]);
|
|
|
|
+
|
|
|
|
+ const link = async () => {
|
|
|
|
+ if (!isEmpty) {
|
|
|
|
+ onInstallOrEnableService();
|
|
|
|
+ await patchVerge({ enable_service_mode: true });
|
|
|
|
+ onChangeData({ enable_service_mode: true });
|
|
|
|
+ onChangeData({ enable_tun_mode: true });
|
|
|
|
+ patchVerge({ enable_tun_mode: true });
|
|
|
|
+ onChangeData({ enable_system_proxy: true });
|
|
|
|
+ patchVerge({ enable_system_proxy: true });
|
|
|
|
+ setResult(true);
|
|
|
|
+ } else {
|
|
|
|
+ Notice.error(t("Profiles Null"));
|
|
|
|
+ }
|
|
};
|
|
};
|
|
|
|
|
|
const cancelink = async () => {
|
|
const cancelink = async () => {
|
|
- await patchVerge({ enable_service_mode: !isActive });
|
|
|
|
- onChangeData({ enable_service_mode: !isActive });
|
|
|
|
|
|
+ await patchVerge({ enable_service_mode: false });
|
|
|
|
+ onChangeData({ enable_service_mode: false });
|
|
onChangeData({ enable_tun_mode: false });
|
|
onChangeData({ enable_tun_mode: false });
|
|
patchVerge({ enable_tun_mode: false });
|
|
patchVerge({ enable_tun_mode: false });
|
|
onChangeData({ enable_system_proxy: false });
|
|
onChangeData({ enable_system_proxy: false });
|
|
patchVerge({ enable_system_proxy: false });
|
|
patchVerge({ enable_system_proxy: false });
|
|
|
|
+ setResult(false);
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ const modeList = ["rule", "global", "direct"];
|
|
|
|
+ const onChangeMode = useLockFn(async (mode: string) => {
|
|
|
|
+ // 断开连接
|
|
|
|
+ if (mode !== curMode && verge?.auto_close_connection) {
|
|
|
|
+ closeAllConnections();
|
|
|
|
+ }
|
|
|
|
+ await updateConfigs({ mode });
|
|
|
|
+ await patchClashConfig({ mode });
|
|
|
|
+ mutateClash();
|
|
|
|
+ });
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ if (curMode && !modeList.includes(curMode)) {
|
|
|
|
+ onChangeMode("rule");
|
|
|
|
+ }
|
|
|
|
+ }, [curMode]);
|
|
|
|
+
|
|
return (
|
|
return (
|
|
<BasePage
|
|
<BasePage
|
|
title={t("Quick")}
|
|
title={t("Quick")}
|
|
@@ -349,15 +397,28 @@ const QuickPage = () => {
|
|
header={
|
|
header={
|
|
<Box display="flex" alignItems="center" gap={1}>
|
|
<Box display="flex" alignItems="center" gap={1}>
|
|
<ProviderButton />
|
|
<ProviderButton />
|
|
|
|
+
|
|
|
|
+ <ButtonGroup size="small">
|
|
|
|
+ {modeList.map((mode) => (
|
|
|
|
+ <Button
|
|
|
|
+ key={mode}
|
|
|
|
+ variant={mode === curMode ? "contained" : "outlined"}
|
|
|
|
+ onClick={() => onChangeMode(mode)}
|
|
|
|
+ sx={{ textTransform: "capitalize" }}
|
|
|
|
+ >
|
|
|
|
+ {t(mode)}
|
|
|
|
+ </Button>
|
|
|
|
+ ))}
|
|
|
|
+ </ButtonGroup>
|
|
</Box>
|
|
</Box>
|
|
}
|
|
}
|
|
>
|
|
>
|
|
<div className="quickCon">
|
|
<div className="quickCon">
|
|
- {enable_system_proxy && enable_tun_mode && enable_service_mode ? (
|
|
|
|
|
|
+ {result ? (
|
|
<div className="aquickCon1">
|
|
<div className="aquickCon1">
|
|
<div className="aquickCon2">
|
|
<div className="aquickCon2">
|
|
<div className="aquick" onClick={cancelink}>
|
|
<div className="aquick" onClick={cancelink}>
|
|
- 取消连接
|
|
|
|
|
|
+ {t("Close Connection")}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@@ -365,7 +426,7 @@ const QuickPage = () => {
|
|
<div className="quickCon1">
|
|
<div className="quickCon1">
|
|
<div className="quickCon2">
|
|
<div className="quickCon2">
|
|
<div className="quick" onClick={link}>
|
|
<div className="quick" onClick={link}>
|
|
- 一键连接
|
|
|
|
|
|
+ {t("Quick Connection")}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@@ -422,14 +483,6 @@ const QuickPage = () => {
|
|
>
|
|
>
|
|
{t("Import")}
|
|
{t("Import")}
|
|
</LoadingButton>
|
|
</LoadingButton>
|
|
- <Button
|
|
|
|
- variant="contained"
|
|
|
|
- size="small"
|
|
|
|
- sx={{ borderRadius: "6px" }}
|
|
|
|
- onClick={() => viewerRef.current?.create()}
|
|
|
|
- >
|
|
|
|
- {t("New")}
|
|
|
|
- </Button>
|
|
|
|
</Stack>
|
|
</Stack>
|
|
<Box
|
|
<Box
|
|
sx={{
|
|
sx={{
|