- {/* 代理组导航栏 */}
+ {hydrationChip && (
+
+ {hydrationChip}
+
+ )}
{mode === "rule" && (
{
)}
/>
+ {showHydrationOverlay && overlayMessage && (
+
+
+
+ {overlayMessage}
+
+
+
+ )}
);
};
diff --git a/src/components/proxy/use-render-list.ts b/src/components/proxy/use-render-list.ts
index 7a5949ae3..1e6e0fd6a 100644
--- a/src/components/proxy/use-render-list.ts
+++ b/src/components/proxy/use-render-list.ts
@@ -14,50 +14,13 @@ import {
} from "./use-head-state";
import { useWindowWidth } from "./use-window-width";
-// 定义代理项接口
-interface IProxyItem {
- name: string;
- type: string;
- udp: boolean;
- xudp: boolean;
- tfo: boolean;
- mptcp: boolean;
- smux: boolean;
- history: {
- time: string;
- delay: number;
- }[];
- provider?: string;
- testUrl?: string;
- [key: string]: any; // 添加索引签名以适应其他可能的属性
-}
-
-// 代理组类型
-type ProxyGroup = {
- name: string;
- type: string;
- udp: boolean;
- xudp: boolean;
- tfo: boolean;
- mptcp: boolean;
- smux: boolean;
- history: {
- time: string;
- delay: number;
- }[];
- now: string;
- all: IProxyItem[];
- hidden?: boolean;
- icon?: string;
- testUrl?: string;
- provider?: string;
-};
+type RenderGroup = IProxyGroupItem;
export interface IRenderItem {
// 组 | head | item | empty | item col
type: 0 | 1 | 2 | 3 | 4;
key: string;
- group: ProxyGroup;
+ group: RenderGroup;
proxy?: IProxyItem;
col?: number;
proxyCol?: IProxyItem[];
@@ -99,7 +62,7 @@ export const useRenderList = (
selectedGroup?: string | null,
) => {
// 使用全局数据提供者
- const { proxies: proxiesData, refreshProxy } = useAppData();
+ const { proxies: proxiesData, proxyHydration, refreshProxy } = useAppData();
const { verge } = useVerge();
const { width } = useWindowWidth();
const [headStates, setHeadState] = useHeadStateNew();
@@ -123,17 +86,29 @@ export const useRenderList = (
// 确保代理数据加载
useEffect(() => {
- if (!proxiesData) return;
+ if (!proxiesData || proxyHydration !== "live") return;
const { groups, proxies } = proxiesData;
if (
(mode === "rule" && !groups.length) ||
(mode === "global" && proxies.length < 2)
) {
- const handle = setTimeout(() => refreshProxy(), 500);
+ const handle = setTimeout(() => {
+ void refreshProxy().catch(() => {});
+ }, 500);
return () => clearTimeout(handle);
}
- }, [proxiesData, mode, refreshProxy]);
+ }, [proxiesData, proxyHydration, mode, refreshProxy]);
+
+ useEffect(() => {
+ if (proxyHydration !== "snapshot") return;
+
+ const handle = setTimeout(() => {
+ void refreshProxy().catch(() => {});
+ }, 1800);
+
+ return () => clearTimeout(handle);
+ }, [proxyHydration, refreshProxy]);
// 链式代理模式节点自动计算延迟
useEffect(() => {
@@ -147,7 +122,7 @@ export const useRenderList = (
// 设置组监听器,当有延迟更新时自动刷新
const groupListener = () => {
console.log("[ChainMode] 延迟更新,刷新UI");
- refreshProxy();
+ void refreshProxy().catch(() => {});
};
delayManager.setGroupListener("chain-mode", groupListener);
@@ -188,9 +163,12 @@ export const useRenderList = (
// 链式代理模式下,显示代理组和其节点
if (isChainMode && runtimeConfig && mode === "rule") {
// 使用正常的规则模式代理组
- const allGroups = proxiesData.groups.length
- ? proxiesData.groups
- : [proxiesData.global!];
+ const chainGroups = proxiesData.groups ?? [];
+ const allGroups = chainGroups.length
+ ? chainGroups
+ : proxiesData.global
+ ? [proxiesData.global]
+ : [];
// 如果选择了特定代理组,只显示该组的节点
if (selectedGroup) {
@@ -282,7 +260,7 @@ export const useRenderList = (
});
// 创建一个虚拟的组来容纳所有节点
- const virtualGroup: ProxyGroup = {
+ const virtualGroup: RenderGroup = {
name: "All Proxies",
type: "Selector",
udp: false,
@@ -340,7 +318,7 @@ export const useRenderList = (
});
// 创建一个虚拟的组来容纳所有节点
- const virtualGroup: ProxyGroup = {
+ const virtualGroup: RenderGroup = {
name: "All Proxies",
type: "Selector",
udp: false,
@@ -380,12 +358,15 @@ export const useRenderList = (
// 正常模式的渲染逻辑
const useRule = mode === "rule" || mode === "script";
- const renderGroups =
- useRule && proxiesData.groups.length
- ? proxiesData.groups
- : [proxiesData.global!];
+ const renderGroups = (() => {
+ const groups = proxiesData.groups ?? [];
+ if (useRule && groups.length) {
+ return groups;
+ }
+ return proxiesData.global ? [proxiesData.global] : groups;
+ })();
- const retList = renderGroups.flatMap((group: ProxyGroup) => {
+ const retList = renderGroups.flatMap((group: RenderGroup) => {
const headState = headStates[group.name] || DEFAULT_STATE;
const ret: IRenderItem[] = [
{
diff --git a/src/hooks/use-current-proxy.ts b/src/hooks/use-current-proxy.ts
index 7d3523269..0c7108ffb 100644
--- a/src/hooks/use-current-proxy.ts
+++ b/src/hooks/use-current-proxy.ts
@@ -2,12 +2,6 @@ import { useMemo } from "react";
import { useAppData } from "@/providers/app-data-context";
-// 定义代理组类型
-interface ProxyGroup {
- name: string;
- now: string;
-}
-
// 获取当前代理节点信息的自定义Hook
export const useCurrentProxy = () => {
// 从AppDataProvider获取数据
@@ -37,15 +31,15 @@ export const useCurrentProxy = () => {
"自动选择",
];
const primaryGroup =
- groups.find((group: ProxyGroup) =>
+ groups.find((group) =>
primaryKeywords.some((keyword) =>
group.name.toLowerCase().includes(keyword.toLowerCase()),
),
- ) || groups.filter((g: ProxyGroup) => g.name !== "GLOBAL")[0];
+ ) || groups.find((group) => group.name !== "GLOBAL");
if (primaryGroup) {
primaryGroupName = primaryGroup.name;
- currentName = primaryGroup.now;
+ currentName = primaryGroup.now ?? currentName;
}
}
diff --git a/src/hooks/use-profiles.ts b/src/hooks/use-profiles.ts
index fdb734627..c412ec307 100644
--- a/src/hooks/use-profiles.ts
+++ b/src/hooks/use-profiles.ts
@@ -5,33 +5,54 @@ import {
getProfiles,
patchProfile,
patchProfilesConfig,
+ calcuProxies,
} from "@/services/cmds";
-import { calcuProxies } from "@/services/cmds";
+import {
+ useProfileStore,
+ selectEffectiveProfiles,
+ selectIsHydrating,
+ selectLastResult,
+} from "@/stores/profile-store";
export const useProfiles = () => {
+ const profilesFromStore = useProfileStore(selectEffectiveProfiles);
+ const storeHydrating = useProfileStore(selectIsHydrating);
+ const lastResult = useProfileStore(selectLastResult);
+ const commitProfileSnapshot = useProfileStore(
+ (state) => state.commitHydrated,
+ );
+
const {
- data: profiles,
+ data: swrProfiles,
mutate: mutateProfiles,
error,
isValidating,
} = useSWR("getProfiles", getProfiles, {
revalidateOnFocus: false,
revalidateOnReconnect: false,
- dedupingInterval: 500, // 减少去重时间,提高响应性
+ dedupingInterval: 500,
errorRetryCount: 3,
errorRetryInterval: 1000,
- refreshInterval: 0, // 完全由手动控制
- onError: (error) => {
- console.error("[useProfiles] SWR错误:", error);
+ refreshInterval: 0,
+ onError: (err) => {
+ console.error("[useProfiles] SWR错误:", err);
},
onSuccess: (data) => {
+ commitProfileSnapshot(data);
console.log(
- "[useProfiles] 配置数据更新成功,配置数量:",
+ "[useProfiles] 配置数据更新成功,配置数量",
data?.items?.length || 0,
);
},
});
+ const rawProfiles = profilesFromStore ?? swrProfiles;
+ const profiles = (rawProfiles ?? {
+ current: null,
+ items: [],
+ }) as IProfilesConfig;
+ const hasProfiles = rawProfiles != null;
+
const patchProfiles = async (
value: Partial