proxy-global.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import useSWR, { useSWRConfig } from "swr";
  2. import { useEffect, useRef, useState } from "react";
  3. import { useLockFn } from "ahooks";
  4. import { Virtuoso } from "react-virtuoso";
  5. import { providerHealthCheck, updateProxy } from "@/services/api";
  6. import { getProfiles, patchProfile } from "@/services/cmds";
  7. import delayManager from "@/services/delay";
  8. import useHeadState from "./use-head-state";
  9. import useFilterSort from "./use-filter-sort";
  10. import ProxyHead from "./proxy-head";
  11. import ProxyItem from "./proxy-item";
  12. interface Props {
  13. groupName: string;
  14. curProxy?: string;
  15. proxies: ApiType.ProxyItem[];
  16. }
  17. // this component will be used for DIRECT/GLOBAL
  18. const ProxyGlobal = (props: Props) => {
  19. const { groupName, curProxy, proxies } = props;
  20. const { mutate } = useSWRConfig();
  21. const [now, setNow] = useState(curProxy || "DIRECT");
  22. const [headState, setHeadState] = useHeadState(groupName);
  23. const virtuosoRef = useRef<any>();
  24. const sortedProxies = useFilterSort(
  25. proxies,
  26. groupName,
  27. headState.filterText,
  28. headState.sortType
  29. );
  30. const { data: profiles } = useSWR("getProfiles", getProfiles);
  31. const onChangeProxy = useLockFn(async (name: string) => {
  32. await updateProxy(groupName, name);
  33. setNow(name);
  34. if (groupName === "DIRECT") return;
  35. // update global selected
  36. const profile = profiles?.items?.find((p) => p.uid === profiles.current);
  37. if (!profile) return;
  38. if (!profile.selected) profile.selected = [];
  39. const index = profile.selected.findIndex((item) => item.name === groupName);
  40. if (index < 0) {
  41. profile.selected.unshift({ name: groupName, now: name });
  42. } else {
  43. profile.selected[index] = { name: groupName, now: name };
  44. }
  45. await patchProfile(profiles!.current!, { selected: profile.selected });
  46. });
  47. const onLocation = (smooth = true) => {
  48. const index = sortedProxies.findIndex((p) => p.name === now);
  49. if (index >= 0) {
  50. virtuosoRef.current?.scrollToIndex?.({
  51. index,
  52. align: "center",
  53. behavior: smooth ? "smooth" : "auto",
  54. });
  55. }
  56. };
  57. const onCheckAll = useLockFn(async () => {
  58. const providers = new Set(
  59. sortedProxies.map((p) => p.provider!).filter(Boolean)
  60. );
  61. if (providers.size) {
  62. Promise.allSettled(
  63. [...providers].map((p) => providerHealthCheck(p))
  64. ).then(() => mutate("getProxies"));
  65. }
  66. await delayManager.checkListDelay(
  67. sortedProxies.filter((p) => !p.provider).map((p) => p.name),
  68. groupName,
  69. 16
  70. );
  71. mutate("getProxies");
  72. });
  73. useEffect(() => onLocation(false), [groupName]);
  74. useEffect(() => {
  75. if (groupName === "DIRECT") setNow("DIRECT");
  76. else if (groupName === "GLOBAL") {
  77. if (profiles) {
  78. const current = profiles.current;
  79. const profile = profiles.items?.find((p) => p.uid === current);
  80. profile?.selected?.forEach((item) => {
  81. if (item.name === "GLOBAL") {
  82. if (item.now && item.now !== curProxy) {
  83. updateProxy("GLOBAL", item.now).then(() => setNow(item!.now!));
  84. mutate("getProxies");
  85. }
  86. }
  87. });
  88. }
  89. setNow(curProxy || "DIRECT");
  90. }
  91. }, [groupName, curProxy, profiles]);
  92. return (
  93. <>
  94. <ProxyHead
  95. sx={{ px: 3, my: 0.5, button: { mr: 0.5 } }}
  96. groupName={groupName}
  97. headState={headState}
  98. onLocation={onLocation}
  99. onCheckDelay={onCheckAll}
  100. onHeadState={setHeadState}
  101. />
  102. <Virtuoso
  103. ref={virtuosoRef}
  104. style={{ height: "calc(100% - 40px)" }}
  105. totalCount={sortedProxies.length}
  106. itemContent={(index) => (
  107. <ProxyItem
  108. groupName={groupName}
  109. proxy={sortedProxies[index]}
  110. selected={sortedProxies[index].name === now}
  111. showType={headState.showType}
  112. onClick={onChangeProxy}
  113. sx={{ py: 0, px: 2 }}
  114. />
  115. )}
  116. />
  117. </>
  118. );
  119. };
  120. export default ProxyGlobal;