clash-port-viewer.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import useSWR from "swr";
  2. import { useEffect, useState } from "react";
  3. import { useSetRecoilState } from "recoil";
  4. import { useTranslation } from "react-i18next";
  5. import { useLockFn } from "ahooks";
  6. import {
  7. Button,
  8. Dialog,
  9. DialogActions,
  10. DialogContent,
  11. DialogTitle,
  12. List,
  13. ListItem,
  14. ListItemText,
  15. TextField,
  16. } from "@mui/material";
  17. import { atomClashPort } from "@/services/states";
  18. import { getClashConfig } from "@/services/api";
  19. import { patchClashConfig } from "@/services/cmds";
  20. import { ModalHandler } from "@/hooks/use-modal-handler";
  21. import Notice from "@/components/base/base-notice";
  22. interface Props {
  23. handler: ModalHandler;
  24. }
  25. const ClashPortViewer = ({ handler }: Props) => {
  26. const { t } = useTranslation();
  27. const { data: config, mutate: mutateClash } = useSWR(
  28. "getClashConfig",
  29. getClashConfig
  30. );
  31. const [open, setOpen] = useState(false);
  32. const [port, setPort] = useState(config?.["mixed-port"] ?? 9090);
  33. const setGlobalClashPort = useSetRecoilState(atomClashPort);
  34. if (handler) {
  35. handler.current = {
  36. open: () => setOpen(true),
  37. close: () => setOpen(false),
  38. };
  39. }
  40. useEffect(() => {
  41. if (open && config?.["mixed-port"]) {
  42. setPort(config["mixed-port"]);
  43. }
  44. }, [open, config?.["mixed-port"]]);
  45. const onSave = useLockFn(async () => {
  46. if (port < 1000) {
  47. return Notice.error("The port should not < 1000");
  48. }
  49. if (port > 65536) {
  50. return Notice.error("The port should not > 65536");
  51. }
  52. setOpen(false);
  53. if (port === config?.["mixed-port"]) return;
  54. await patchClashConfig({ "mixed-port": port });
  55. setGlobalClashPort(port);
  56. Notice.success("Change Clash port successfully!", 1000);
  57. mutateClash();
  58. });
  59. return (
  60. <Dialog open={open} onClose={() => setOpen(false)}>
  61. <DialogTitle>{t("Clash Port")}</DialogTitle>
  62. <DialogContent sx={{ width: 300 }}>
  63. <List>
  64. <ListItem sx={{ padding: "5px 2px" }}>
  65. <ListItemText primary="Mixed Port" />
  66. <TextField
  67. size="small"
  68. autoComplete="off"
  69. sx={{ width: 135 }}
  70. value={port}
  71. onChange={(e) =>
  72. setPort(+e.target.value?.replace(/\D+/, "").slice(0, 5))
  73. }
  74. />
  75. </ListItem>
  76. </List>
  77. </DialogContent>
  78. <DialogActions>
  79. <Button variant="outlined" onClick={() => setOpen(false)}>
  80. {t("Cancel")}
  81. </Button>
  82. <Button onClick={onSave} variant="contained">
  83. {t("Save")}
  84. </Button>
  85. </DialogActions>
  86. </Dialog>
  87. );
  88. };
  89. export default ClashPortViewer;