diff --git a/src/pages/proxies.tsx b/src/pages/proxies.tsx index f491dd4..c5deb2e 100644 --- a/src/pages/proxies.tsx +++ b/src/pages/proxies.tsx @@ -1,6 +1,8 @@ import useSWR, { useSWRConfig } from "swr"; -import { useEffect } from "react"; -import { List, Paper } from "@mui/material"; +import { useEffect, useMemo, useRef, useState } from "react"; +import { Button, ButtonGroup, List, Paper } from "@mui/material"; +import { getClashConfig, updateConfigs, updateProxy } from "../services/api"; +import { patchClashConfig } from "../services/cmds"; import { getProxies } from "../services/api"; import BasePage from "../components/base-page"; import ProxyItem from "../components/proxy-item"; @@ -9,34 +11,103 @@ import ProxyGroup from "../components/proxy-group"; const ProxyPage = () => { const { mutate } = useSWRConfig(); const { data: proxiesData } = useSWR("getProxies", getProxies); - const { groups = [], proxies = [] } = proxiesData ?? {}; + const { data: clashConfig } = useSWR("getClashConfig", getClashConfig); + const [curProxy, setCurProxy] = useState("DIRECT"); + const curMode = clashConfig?.mode.toLowerCase(); + // proxy groups + const { groups = [] } = proxiesData ?? {}; + // proxies and sorted + const filterProxies = useMemo(() => { + if (!proxiesData?.proxies) return []; + + const list = Object.values(proxiesData.proxies); + const retList = list.filter( + (p) => !p.all?.length && p.name !== "DIRECT" && p.name !== "REJECT" + ); + const direct = list.filter((p) => p.name === "DIRECT"); + const reject = list.filter((p) => p.name === "REJECT"); + + return direct.concat(retList).concat(reject); + }, [proxiesData]); + + const modeList = ["rule", "global", "direct"]; + const asGroup = curMode === "rule" || !groups.length; + + // make sure that fetch the proxies successfully useEffect(() => { - // fix the empty proxies on the first sight - // this bud only show on the build version - // call twice to avoid something unknown or the delay of the clash startup - setTimeout(() => mutate("getProxies"), 250); - setTimeout(() => mutate("getProxies"), 1000); - }, []); + if ( + (curMode === "rule" && !groups.length) || + (curMode === "global" && filterProxies.length < 4) + ) { + setTimeout(() => mutate("getProxies"), 500); + } + }, [groups, filterProxies, curMode]); + + // update the current proxy + useEffect(() => { + if (curMode === "direct") setCurProxy("DIRECT"); + if (curMode === "global") { + const globalNow = proxiesData?.proxies?.GLOBAL?.now; + setCurProxy(globalNow || "DIRECT"); + } + }, [curMode, proxiesData]); + + const changeLockRef = useRef(false); + const onChangeMode = async (mode: string) => { + if (changeLockRef.current) return; + changeLockRef.current = true; + + try { + // switch rapidly + await updateConfigs({ mode }); + await patchClashConfig({ mode }); + mutate("getClashConfig"); + } finally { + changeLockRef.current = false; + } + }; + + const onChangeProxy = async (name: string) => { + if (curMode !== "global") return; + await updateProxy("GLOBAL", name); + setCurProxy(name); + }; return ( - + + {modeList.map((mode) => ( + + ))} + + } + > - {groups.length > 0 && ( + {asGroup ? ( {groups.map((group) => ( ))} - )} - - {!groups.length && ( + ) : ( + // todo: virtual list - {Object.values(proxies).map((proxy) => ( + {filterProxies.map((proxy) => ( ))}