setting-clash.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import useSWR, { useSWRConfig } from "swr";
  2. import { useSetRecoilState } from "recoil";
  3. import { useTranslation } from "react-i18next";
  4. import {
  5. TextField,
  6. Switch,
  7. Select,
  8. MenuItem,
  9. Typography,
  10. IconButton,
  11. } from "@mui/material";
  12. import { atomClashPort } from "@/services/states";
  13. import { ArrowForward } from "@mui/icons-material";
  14. import { patchClashConfig } from "@/services/cmds";
  15. import { SettingList, SettingItem } from "./setting";
  16. import { getClashConfig, getVersion, updateConfigs } from "@/services/api";
  17. import useModalHandler from "@/hooks/use-modal-handler";
  18. import Notice from "../base/base-notice";
  19. import GuardState from "./mods/guard-state";
  20. import CoreSwitch from "./mods/core-switch";
  21. import WebUIViewer from "./mods/web-ui-viewer";
  22. interface Props {
  23. onError: (err: Error) => void;
  24. }
  25. const SettingClash = ({ onError }: Props) => {
  26. const { t } = useTranslation();
  27. const { mutate } = useSWRConfig();
  28. const { data: clashConfig } = useSWR("getClashConfig", getClashConfig);
  29. const { data: versionData } = useSWR("getVersion", getVersion);
  30. const {
  31. ipv6,
  32. "allow-lan": allowLan,
  33. "log-level": logLevel,
  34. "mixed-port": mixedPort,
  35. } = clashConfig ?? {};
  36. const setGlobalClashPort = useSetRecoilState(atomClashPort);
  37. const webUIHandler = useModalHandler();
  38. const onSwitchFormat = (_e: any, value: boolean) => value;
  39. const onChangeData = (patch: Partial<ApiType.ConfigData>) => {
  40. mutate("getClashConfig", { ...clashConfig, ...patch }, false);
  41. };
  42. const onUpdateData = async (patch: Partial<ApiType.ConfigData>) => {
  43. await updateConfigs(patch);
  44. await patchClashConfig(patch);
  45. };
  46. const onUpdatePort = async (port: number) => {
  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. setGlobalClashPort(port);
  55. Notice.success("Change Clash port successfully!", 1000);
  56. // update the config
  57. mutate("getClashConfig");
  58. };
  59. // get clash core version
  60. const clashVer = versionData?.premium
  61. ? `${versionData.version} Premium`
  62. : versionData?.version || "-";
  63. return (
  64. <SettingList title={t("Clash Setting")}>
  65. <WebUIViewer handler={webUIHandler} onError={onError} />
  66. <SettingItem label={t("Allow Lan")}>
  67. <GuardState
  68. value={allowLan ?? false}
  69. valueProps="checked"
  70. onCatch={onError}
  71. onFormat={onSwitchFormat}
  72. onChange={(e) => onChangeData({ "allow-lan": e })}
  73. onGuard={(e) => onUpdateData({ "allow-lan": e })}
  74. >
  75. <Switch edge="end" />
  76. </GuardState>
  77. </SettingItem>
  78. <SettingItem label={t("IPv6")}>
  79. <GuardState
  80. value={ipv6 ?? false}
  81. valueProps="checked"
  82. onCatch={onError}
  83. onFormat={onSwitchFormat}
  84. onChange={(e) => onChangeData({ ipv6: e })}
  85. onGuard={(e) => onUpdateData({ ipv6: e })}
  86. >
  87. <Switch edge="end" />
  88. </GuardState>
  89. </SettingItem>
  90. <SettingItem label={t("Web UI")}>
  91. <IconButton
  92. color="inherit"
  93. size="small"
  94. sx={{ my: "2px" }}
  95. onClick={() => webUIHandler.current.open()}
  96. >
  97. <ArrowForward />
  98. </IconButton>
  99. </SettingItem>
  100. <SettingItem label={t("Log Level")}>
  101. <GuardState
  102. value={logLevel ?? "info"}
  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, "> div": { py: "7.5px" } }}>
  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 label={t("Mixed Port")}>
  118. <GuardState
  119. value={mixedPort ?? 0}
  120. onCatch={onError}
  121. onFormat={(e: any) => +e.target.value?.replace(/\D+/, "")}
  122. onChange={(e) => onChangeData({ "mixed-port": e })}
  123. onGuard={onUpdatePort}
  124. waitTime={1000}
  125. >
  126. <TextField
  127. autoComplete="off"
  128. size="small"
  129. sx={{ width: 120, input: { py: "7.5px" } }}
  130. />
  131. </GuardState>
  132. </SettingItem>
  133. <SettingItem label={t("Clash Core")} extra={<CoreSwitch />}>
  134. <Typography sx={{ py: "7px" }}>{clashVer}</Typography>
  135. </SettingItem>
  136. </SettingList>
  137. );
  138. };
  139. export default SettingClash;