use-render-list.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import useSWR from "swr";
  2. import { useEffect, useMemo } from "react";
  3. import { getProxies } from "@/services/api";
  4. import { useVerge } from "@/hooks/use-verge";
  5. import { filterSort } from "./use-filter-sort";
  6. import { useWindowWidth } from "./use-window-width";
  7. import {
  8. useHeadStateNew,
  9. DEFAULT_STATE,
  10. type HeadState,
  11. } from "./use-head-state";
  12. export interface IRenderItem {
  13. // 组 | head | item | empty | item col
  14. type: 0 | 1 | 2 | 3 | 4;
  15. key: string;
  16. group: IProxyGroupItem;
  17. proxy?: IProxyItem;
  18. col?: number;
  19. proxyCol?: IProxyItem[];
  20. headState?: HeadState;
  21. }
  22. export const useRenderList = (mode: string) => {
  23. const { data: proxiesData, mutate: mutateProxies } = useSWR(
  24. "getProxies",
  25. getProxies,
  26. { refreshInterval: 45000 }
  27. );
  28. const { verge } = useVerge();
  29. const { width } = useWindowWidth();
  30. let col = Math.floor(verge?.proxy_layout_column || 6);
  31. // 自适应
  32. if (col >= 6 || col <= 0) {
  33. if (width > 1450) col = 4;
  34. else if (width > 1024) col = 3;
  35. else if (width > 900) col = 2;
  36. else if (width >= 600) col = 2;
  37. else col = 1;
  38. }
  39. const [headStates, setHeadState] = useHeadStateNew();
  40. // make sure that fetch the proxies successfully
  41. useEffect(() => {
  42. if (!proxiesData) return;
  43. const { groups, proxies } = proxiesData;
  44. if (
  45. (mode === "rule" && !groups.length) ||
  46. (mode === "global" && proxies.length < 2)
  47. ) {
  48. setTimeout(() => mutateProxies(), 500);
  49. }
  50. }, [proxiesData, mode]);
  51. const renderList: IRenderItem[] = useMemo(() => {
  52. if (!proxiesData) return [];
  53. // global 和 direct 使用展开的样式
  54. const useRule = mode === "rule" || mode === "script";
  55. const renderGroups =
  56. (useRule && proxiesData.groups.length
  57. ? proxiesData.groups
  58. : [proxiesData.global!]) || [];
  59. const retList = renderGroups.flatMap((group) => {
  60. const headState = headStates[group.name] || DEFAULT_STATE;
  61. const ret: IRenderItem[] = [
  62. { type: 0, key: group.name, group, headState },
  63. ];
  64. if (headState?.open || !useRule) {
  65. const proxies = filterSort(
  66. group.all,
  67. group.name,
  68. headState.filterText,
  69. headState.sortType
  70. );
  71. ret.push({ type: 1, key: `head-${group.name}`, group, headState });
  72. if (!proxies.length) {
  73. ret.push({ type: 3, key: `empty-${group.name}`, group, headState });
  74. }
  75. // 支持多列布局
  76. if (col > 1) {
  77. return ret.concat(
  78. groupList(proxies, col).map((proxyCol) => ({
  79. type: 4,
  80. key: `col-${group.name}-${proxyCol[0].name}`,
  81. group,
  82. headState,
  83. col,
  84. proxyCol,
  85. }))
  86. );
  87. }
  88. return ret.concat(
  89. proxies.map((proxy) => ({
  90. type: 2,
  91. key: `${group.name}-${proxy!.name}`,
  92. group,
  93. proxy,
  94. headState,
  95. }))
  96. );
  97. }
  98. return ret;
  99. });
  100. if (!useRule) return retList.slice(1);
  101. return retList;
  102. }, [headStates, proxiesData, mode, col]);
  103. return {
  104. renderList,
  105. onProxies: mutateProxies,
  106. onHeadState: setHeadState,
  107. };
  108. };
  109. function groupList<T = any>(list: T[], size: number): T[][] {
  110. return list.reduce((p, n) => {
  111. if (!p.length) return [[n]];
  112. const i = p.length - 1;
  113. if (p[i].length < size) {
  114. p[i].push(n);
  115. return p;
  116. }
  117. p.push([n]);
  118. return p;
  119. }, [] as T[][]);
  120. }