feat: enable change mixed port

This commit is contained in:
GyDi 2022-01-21 02:32:23 +08:00
parent d49fd37656
commit d0b87fd7c3
No known key found for this signature in database
GPG Key ID: 1C95E0D3467B3084
4 changed files with 52 additions and 10 deletions

View File

@ -7,7 +7,7 @@ interface Props<Value> {
onChangeProps?: string; onChangeProps?: string;
onChange?: (value: Value) => void; onChange?: (value: Value) => void;
onFormat?: (...args: any[]) => Value; onFormat?: (...args: any[]) => Value;
onGuard?: (value: Value) => Promise<void>; onGuard?: (value: Value, oldValue: Value) => Promise<void>;
onCatch?: (error: Error) => void; onCatch?: (error: Error) => void;
children: ReactNode; children: ReactNode;
} }
@ -41,7 +41,7 @@ function GuardState<T>(props: Props<T>) {
const newValue = (onFormat as any)(...args); const newValue = (onFormat as any)(...args);
// 先在ui上响应操作 // 先在ui上响应操作
onChange(newValue); onChange(newValue);
await onGuard(newValue); await onGuard(newValue, oldValue!);
} catch (err: any) { } catch (err: any) {
// 状态回退 // 状态回退
onChange(oldValue!); onChange(oldValue!);

View File

@ -1,3 +1,6 @@
import { useEffect, useState } from "react";
import { useDebounceFn } from "ahooks";
import { useSetRecoilState } from "recoil";
import useSWR, { useSWRConfig } from "swr"; import useSWR, { useSWRConfig } from "swr";
import { import {
ListItemText, ListItemText,
@ -9,11 +12,13 @@ import {
import { getClashConfig, updateConfigs } from "../../services/api"; import { getClashConfig, updateConfigs } from "../../services/api";
import { SettingList, SettingItem } from "./setting"; import { SettingList, SettingItem } from "./setting";
import { patchClashConfig } from "../../services/cmds"; import { patchClashConfig } from "../../services/cmds";
import { atomClashPort } from "../../states/setting";
import { ApiType } from "../../services/types"; import { ApiType } from "../../services/types";
import GuardState from "./guard-state"; import GuardState from "./guard-state";
import Notice from "../notice";
interface Props { interface Props {
onError?: (err: Error) => void; onError: (err: Error) => void;
} }
const SettingClash = ({ onError }: Props) => { const SettingClash = ({ onError }: Props) => {
@ -24,9 +29,14 @@ const SettingClash = ({ onError }: Props) => {
ipv6 = false, ipv6 = false,
"allow-lan": allowLan = false, "allow-lan": allowLan = false,
"log-level": logLevel = "silent", "log-level": logLevel = "silent",
"mixed-port": mixedPort = 7890, "mixed-port": thePort = 0,
} = clashConfig ?? {}; } = clashConfig ?? {};
const setPort = useSetRecoilState(atomClashPort);
const [mixedPort, setMixedPort] = useState(thePort);
useEffect(() => setMixedPort(thePort), [thePort]);
const onSwitchFormat = (_e: any, value: boolean) => value; const onSwitchFormat = (_e: any, value: boolean) => value;
const onChangeData = (patch: Partial<ApiType.ConfigData>) => { const onChangeData = (patch: Partial<ApiType.ConfigData>) => {
mutate("getClashConfig", { ...clashConfig, ...patch }, false); mutate("getClashConfig", { ...clashConfig, ...patch }, false);
@ -36,6 +46,28 @@ const SettingClash = ({ onError }: Props) => {
await patchClashConfig(patch); await patchClashConfig(patch);
}; };
// restart clash when port is changed
const { run: onUpdatePort } = useDebounceFn(
async (port: number) => {
(async () => {
if (port < 1000) {
throw new Error("The port should not < 1000");
}
if (port > 65536) {
throw new Error("The port should not > 65536");
}
await patchClashConfig({ "mixed-port": port });
onChangeData({ "mixed-port": port });
setPort(port);
Notice.success("Change Clash port successfully!");
})().catch((err: any) => {
setMixedPort(thePort); // back to old port value
Notice.error(err.message ?? err.toString());
});
},
{ wait: 1000 }
);
return ( return (
<SettingList title="Clash Setting"> <SettingList title="Clash Setting">
<SettingItem> <SettingItem>
@ -87,12 +119,14 @@ const SettingClash = ({ onError }: Props) => {
<SettingItem> <SettingItem>
<ListItemText primary="Mixed Port" /> <ListItemText primary="Mixed Port" />
<TextField <GuardState
size="small"
value={mixedPort!} value={mixedPort!}
sx={{ width: 120 }} onFormat={(e: any) => +e.target.value?.replace(/\D+/, "")}
disabled onChange={setMixedPort}
/> onGuard={onUpdatePort}
>
<TextField autoComplete="off" size="small" sx={{ width: 120 }} />
</GuardState>
</SettingItem> </SettingItem>
</SettingList> </SettingList>
); );

View File

@ -1,11 +1,14 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useRecoilValue } from "recoil";
import { Box, Typography } from "@mui/material"; import { Box, Typography } from "@mui/material";
import { ArrowDownward, ArrowUpward } from "@mui/icons-material"; import { ArrowDownward, ArrowUpward } from "@mui/icons-material";
import { getInfomation } from "../services/api"; import { getInfomation } from "../services/api";
import { ApiType } from "../services/types"; import { ApiType } from "../services/types";
import { atomClashPort } from "../states/setting";
import parseTraffic from "../utils/parse-traffic"; import parseTraffic from "../utils/parse-traffic";
const Traffic = () => { const Traffic = () => {
const portValue = useRecoilValue(atomClashPort);
const [traffic, setTraffic] = useState({ up: 0, down: 0 }); const [traffic, setTraffic] = useState({ up: 0, down: 0 });
useEffect(() => { useEffect(() => {
@ -21,7 +24,7 @@ const Traffic = () => {
}); });
return () => ws?.close(); return () => ws?.close();
}, []); }, [portValue]);
const [up, upUnit] = parseTraffic(traffic.up); const [up, upUnit] = parseTraffic(traffic.up);
const [down, downUnit] = parseTraffic(traffic.down); const [down, downUnit] = parseTraffic(traffic.down);

View File

@ -9,3 +9,8 @@ export const atomThemeBlur = atom<boolean>({
key: "atomThemeBlur", key: "atomThemeBlur",
default: false, default: false,
}); });
export const atomClashPort = atom<number>({
key: "atomClashPort",
default: 0,
});