proxies.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import useSWR, { useSWRConfig } from "swr";
  2. import { useEffect, useMemo, useRef, useState } from "react";
  3. import { Virtuoso } from "react-virtuoso";
  4. import { Button, ButtonGroup, List, Paper } from "@mui/material";
  5. import { getClashConfig, updateConfigs, updateProxy } from "../services/api";
  6. import { patchClashConfig } from "../services/cmds";
  7. import { getProxies } from "../services/api";
  8. import BasePage from "../components/base-page";
  9. import ProxyItem from "../components/proxy-item";
  10. import ProxyGroup from "../components/proxy-group";
  11. const ProxyPage = () => {
  12. const { mutate } = useSWRConfig();
  13. const { data: proxiesData } = useSWR("getProxies", getProxies);
  14. const { data: clashConfig } = useSWR("getClashConfig", getClashConfig);
  15. const [curProxy, setCurProxy] = useState<string>("DIRECT");
  16. const curMode = clashConfig?.mode.toLowerCase();
  17. // proxy groups
  18. const { groups = [] } = proxiesData ?? {};
  19. // proxies and sorted
  20. const filterProxies = useMemo(() => {
  21. if (!proxiesData?.proxies) return [];
  22. const list = Object.values(proxiesData.proxies);
  23. const retList = list.filter(
  24. (p) => !p.all?.length && p.name !== "DIRECT" && p.name !== "REJECT"
  25. );
  26. const direct = list.filter((p) => p.name === "DIRECT");
  27. const reject = list.filter((p) => p.name === "REJECT");
  28. return direct.concat(retList).concat(reject);
  29. }, [proxiesData]);
  30. const modeList = ["rule", "global", "direct"];
  31. const asGroup = curMode === "rule" && groups.length;
  32. // make sure that fetch the proxies successfully
  33. useEffect(() => {
  34. if (
  35. (curMode === "rule" && !groups.length) ||
  36. (curMode === "global" && filterProxies.length < 4)
  37. ) {
  38. setTimeout(() => mutate("getProxies"), 500);
  39. }
  40. }, [groups, filterProxies, curMode]);
  41. // update the current proxy
  42. useEffect(() => {
  43. if (curMode === "direct") setCurProxy("DIRECT");
  44. if (curMode === "global") {
  45. const globalNow = proxiesData?.proxies?.GLOBAL?.now;
  46. setCurProxy(globalNow || "DIRECT");
  47. }
  48. }, [curMode, proxiesData]);
  49. const changeLockRef = useRef(false);
  50. const onChangeMode = async (mode: string) => {
  51. if (changeLockRef.current) return;
  52. changeLockRef.current = true;
  53. try {
  54. // switch rapidly
  55. await updateConfigs({ mode });
  56. await patchClashConfig({ mode });
  57. mutate("getClashConfig");
  58. } finally {
  59. changeLockRef.current = false;
  60. }
  61. };
  62. const onChangeProxy = async (name: string) => {
  63. if (curMode !== "global") return;
  64. await updateProxy("GLOBAL", name);
  65. setCurProxy(name);
  66. };
  67. // difference style
  68. const pageStyle = asGroup ? {} : { height: "100%" };
  69. const paperStyle: any = asGroup
  70. ? { mb: 0.5 }
  71. : { py: 1, height: "100%", boxSizing: "border-box" };
  72. return (
  73. <BasePage
  74. contentStyle={pageStyle}
  75. title={asGroup ? "Proxy Groups" : "Proxies"}
  76. header={
  77. <ButtonGroup size="small">
  78. {modeList.map((mode) => (
  79. <Button
  80. key={mode}
  81. variant={mode === curMode ? "contained" : "outlined"}
  82. onClick={() => onChangeMode(mode)}
  83. sx={{ textTransform: "capitalize" }}
  84. >
  85. {mode}
  86. </Button>
  87. ))}
  88. </ButtonGroup>
  89. }
  90. >
  91. <Paper sx={{ borderRadius: 1, boxShadow: 2, ...paperStyle }}>
  92. {asGroup ? (
  93. <List>
  94. {groups.map((group) => (
  95. <ProxyGroup key={group.name} group={group} />
  96. ))}
  97. </List>
  98. ) : (
  99. // virtual list
  100. <Virtuoso
  101. style={{ height: "100%" }}
  102. totalCount={filterProxies.length}
  103. itemContent={(index) => (
  104. <ProxyItem
  105. proxy={filterProxies[index]}
  106. selected={filterProxies[index].name === curProxy}
  107. onClick={onChangeProxy}
  108. sx={{ py: 0, px: 2 }}
  109. />
  110. )}
  111. />
  112. )}
  113. </Paper>
  114. </BasePage>
  115. );
  116. };
  117. export default ProxyPage;