setting-clash.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import { useEffect, useState } from "react";
  2. import { useDebounceFn } from "ahooks";
  3. import { useSetRecoilState } from "recoil";
  4. import useSWR, { useSWRConfig } from "swr";
  5. import {
  6. ListItemText,
  7. TextField,
  8. Switch,
  9. Select,
  10. MenuItem,
  11. Typography,
  12. } from "@mui/material";
  13. import { ApiType } from "../../services/types";
  14. import { atomClashPort } from "../../states/setting";
  15. import { patchClashConfig } from "../../services/cmds";
  16. import { SettingList, SettingItem } from "./setting";
  17. import { getClashConfig, getVersion, updateConfigs } from "../../services/api";
  18. import GuardState from "./guard-state";
  19. import Notice from "../notice";
  20. interface Props {
  21. onError: (err: Error) => void;
  22. }
  23. const SettingClash = ({ onError }: Props) => {
  24. const { mutate } = useSWRConfig();
  25. const { data: clashConfig } = useSWR("getClashConfig", getClashConfig);
  26. const {
  27. ipv6 = false,
  28. "allow-lan": allowLan = false,
  29. "log-level": logLevel = "silent",
  30. "mixed-port": thePort = 0,
  31. } = clashConfig ?? {};
  32. const setPort = useSetRecoilState(atomClashPort);
  33. const [mixedPort, setMixedPort] = useState(thePort);
  34. useEffect(() => setMixedPort(thePort), [thePort]);
  35. const onSwitchFormat = (_e: any, value: boolean) => value;
  36. const onChangeData = (patch: Partial<ApiType.ConfigData>) => {
  37. mutate("getClashConfig", { ...clashConfig, ...patch }, false);
  38. };
  39. const onUpdateData = async (patch: Partial<ApiType.ConfigData>) => {
  40. await updateConfigs(patch);
  41. await patchClashConfig(patch);
  42. };
  43. // restart clash when port is changed
  44. const { run: onUpdatePort } = useDebounceFn(
  45. async (port: number) => {
  46. try {
  47. if (port < 1000) {
  48. throw new Error("The port should not < 1000");
  49. }
  50. if (port > 65536) {
  51. throw new Error("The port should not > 65536");
  52. }
  53. await patchClashConfig({ "mixed-port": port });
  54. onChangeData({ "mixed-port": port });
  55. setPort(port);
  56. Notice.success("Change Clash port successfully!");
  57. } catch (err: any) {
  58. setMixedPort(thePort); // back to old port value
  59. Notice.error(err.message ?? err.toString());
  60. }
  61. },
  62. { wait: 1000 }
  63. );
  64. // get clash core version
  65. const [clashVer, setClashVer] = useState("");
  66. useEffect(() => {
  67. getVersion().then(({ premium, version }) => {
  68. setClashVer(premium ? `${version} Premium` : version);
  69. });
  70. }, []);
  71. return (
  72. <SettingList title="Clash Setting">
  73. <SettingItem>
  74. <ListItemText primary="Allow Lan" />
  75. <GuardState
  76. value={allowLan}
  77. valueProps="checked"
  78. onCatch={onError}
  79. onFormat={onSwitchFormat}
  80. onChange={(e) => onChangeData({ "allow-lan": e })}
  81. onGuard={(e) => onUpdateData({ "allow-lan": e })}
  82. >
  83. <Switch edge="end" />
  84. </GuardState>
  85. </SettingItem>
  86. <SettingItem>
  87. <ListItemText primary="IPv6" />
  88. <GuardState
  89. value={ipv6}
  90. valueProps="checked"
  91. onCatch={onError}
  92. onFormat={onSwitchFormat}
  93. onChange={(e) => onChangeData({ ipv6: e })}
  94. onGuard={(e) => onUpdateData({ ipv6: e })}
  95. >
  96. <Switch edge="end" />
  97. </GuardState>
  98. </SettingItem>
  99. <SettingItem>
  100. <ListItemText primary="Log Level" />
  101. <GuardState
  102. value={logLevel}
  103. onCatch={onError}
  104. onFormat={(e: any) => e.target.value}
  105. onChange={(e) => onChangeData({ "log-level": e })}
  106. onGuard={(e) => onUpdateData({ "log-level": e })}
  107. >
  108. <Select size="small" sx={{ width: 120 }}>
  109. <MenuItem value="debug">Debug</MenuItem>
  110. <MenuItem value="info">Info</MenuItem>
  111. <MenuItem value="warning">Warning</MenuItem>
  112. <MenuItem value="error">Error</MenuItem>
  113. <MenuItem value="silent">Silent</MenuItem>
  114. </Select>
  115. </GuardState>
  116. </SettingItem>
  117. <SettingItem>
  118. <ListItemText primary="Mixed Port" />
  119. <GuardState
  120. value={mixedPort!}
  121. onFormat={(e: any) => +e.target.value?.replace(/\D+/, "")}
  122. onChange={setMixedPort}
  123. onGuard={onUpdatePort}
  124. >
  125. <TextField autoComplete="off" size="small" sx={{ width: 120 }} />
  126. </GuardState>
  127. </SettingItem>
  128. <SettingItem>
  129. <ListItemText primary="Clash core" />
  130. <Typography sx={{ py: 1 }}>{clashVer}</Typography>
  131. </SettingItem>
  132. </SettingList>
  133. );
  134. };
  135. export default SettingClash;