mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-04-17 07:50:33 +08:00
refactor: reorganize component and hook structure; remove unused modules
This commit is contained in:
parent
4f8ed63cf0
commit
3cf51de850
12
src/App.tsx
12
src/App.tsx
@ -1,12 +0,0 @@
|
|||||||
import Layout from "./pages/_layout";
|
|
||||||
import { AppDataProvider } from "./providers/app-data-provider";
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
return (
|
|
||||||
<AppDataProvider>
|
|
||||||
<Layout />
|
|
||||||
</AppDataProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import { Box, BoxProps } from "@mui/material";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
interface CenterProps extends BoxProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Center: React.FC<CenterProps> = ({ children, ...props }) => {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
display="flex"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -3,14 +3,14 @@ import { Divider, Stack, Typography } from "@mui/material";
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { useClash } from "@/hooks/use-clash";
|
||||||
import {
|
import {
|
||||||
useAppUptime,
|
useAppUptime,
|
||||||
useClashConfig,
|
useClashConfig,
|
||||||
useRulesData,
|
useRulesData,
|
||||||
useSystemProxyAddress,
|
useSystemProxyAddress,
|
||||||
useSystemProxyData,
|
useSystemProxyData,
|
||||||
} from "@/hooks/app-data";
|
} from "@/hooks/use-clash-data";
|
||||||
import { useClash } from "@/hooks/use-clash";
|
|
||||||
|
|
||||||
import { EnhancedCard } from "./enhanced-card";
|
import { EnhancedCard } from "./enhanced-card";
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { useMemo } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { closeAllConnections } from "tauri-plugin-mihomo-api";
|
import { closeAllConnections } from "tauri-plugin-mihomo-api";
|
||||||
|
|
||||||
import { useClashConfig } from "@/hooks/app-data";
|
import { useClashConfig } from "@/hooks/use-clash-data";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { patchClashMode } from "@/services/cmds";
|
import { patchClashMode } from "@/services/cmds";
|
||||||
import type { TranslationKey } from "@/types/generated/i18n-keys";
|
import type { TranslationKey } from "@/types/generated/i18n-keys";
|
||||||
|
|||||||
@ -34,7 +34,11 @@ import { useNavigate } from "react-router";
|
|||||||
import { delayGroup, healthcheckProxyProvider } from "tauri-plugin-mihomo-api";
|
import { delayGroup, healthcheckProxyProvider } from "tauri-plugin-mihomo-api";
|
||||||
|
|
||||||
import { EnhancedCard } from "@/components/home/enhanced-card";
|
import { EnhancedCard } from "@/components/home/enhanced-card";
|
||||||
import { useClashConfig, useProxiesData, useRulesData } from "@/hooks/app-data";
|
import {
|
||||||
|
useClashConfig,
|
||||||
|
useProxiesData,
|
||||||
|
useRulesData,
|
||||||
|
} from "@/hooks/use-clash-data";
|
||||||
import { useProfiles } from "@/hooks/use-profiles";
|
import { useProfiles } from "@/hooks/use-profiles";
|
||||||
import { useProxySelection } from "@/hooks/use-proxy-selection";
|
import { useProxySelection } from "@/hooks/use-proxy-selection";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import { useCallback, useMemo, useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
import { useRefreshAll } from "@/hooks/app-data";
|
import { useRefreshAll } from "@/hooks/use-clash-data";
|
||||||
import { openWebUrl, updateProfile } from "@/services/cmds";
|
import { openWebUrl, updateProfile } from "@/services/cmds";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
import parseTraffic from "@/utils/parse-traffic";
|
import parseTraffic from "@/utils/parse-traffic";
|
||||||
|
|||||||
@ -20,9 +20,9 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
|
import { useServiceInstaller } from "@/hooks/use-service-installer";
|
||||||
import { useSystemState } from "@/hooks/use-system-state";
|
import { useSystemState } from "@/hooks/use-system-state";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { useServiceInstaller } from "@/hooks/useServiceInstaller";
|
|
||||||
import { getSystemInfo } from "@/services/cmds";
|
import { getSystemInfo } from "@/services/cmds";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
import { checkUpdateSafe as checkUpdate } from "@/services/update";
|
import { checkUpdateSafe as checkUpdate } from "@/services/update";
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { updateProxyProvider } from "tauri-plugin-mihomo-api";
|
import { updateProxyProvider } from "tauri-plugin-mihomo-api";
|
||||||
|
|
||||||
import { useProxiesData, useProxyProvidersData } from "@/hooks/app-data";
|
import { useProxiesData, useProxyProvidersData } from "@/hooks/use-clash-data";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
import parseTraffic from "@/utils/parse-traffic";
|
import parseTraffic from "@/utils/parse-traffic";
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,7 @@ import {
|
|||||||
selectNodeForGroup,
|
selectNodeForGroup,
|
||||||
} from "tauri-plugin-mihomo-api";
|
} from "tauri-plugin-mihomo-api";
|
||||||
|
|
||||||
import { useProxiesData } from "@/hooks/app-data";
|
import { useProxiesData } from "@/hooks/use-clash-data";
|
||||||
import { calcuProxies, updateProxyChainConfigInRuntime } from "@/services/cmds";
|
import { calcuProxies, updateProxyChainConfigInRuntime } from "@/services/cmds";
|
||||||
import { debugLog } from "@/utils/debug";
|
import { debugLog } from "@/utils/debug";
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Virtuoso, type VirtuosoHandle } from "react-virtuoso";
|
import { Virtuoso, type VirtuosoHandle } from "react-virtuoso";
|
||||||
import { delayGroup, healthcheckProxyProvider } from "tauri-plugin-mihomo-api";
|
import { delayGroup, healthcheckProxyProvider } from "tauri-plugin-mihomo-api";
|
||||||
|
|
||||||
import { useProxiesData } from "@/hooks/app-data";
|
import { useProxiesData } from "@/hooks/use-clash-data";
|
||||||
import { useProxySelection } from "@/hooks/use-proxy-selection";
|
import { useProxySelection } from "@/hooks/use-proxy-selection";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { updateProxyChainConfigInRuntime } from "@/services/cmds";
|
import { updateProxyChainConfigInRuntime } from "@/services/cmds";
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
import { useProxiesData } from "@/hooks/app-data";
|
import { useProxiesData } from "@/hooks/use-clash-data";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { getRuntimeConfig } from "@/services/cmds";
|
import { getRuntimeConfig } from "@/services/cmds";
|
||||||
import delayManager from "@/services/delay";
|
import delayManager from "@/services/delay";
|
||||||
|
|||||||
@ -21,7 +21,10 @@ import { useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { updateRuleProvider } from "tauri-plugin-mihomo-api";
|
import { updateRuleProvider } from "tauri-plugin-mihomo-api";
|
||||||
|
|
||||||
import type { useRuleProvidersData, useRulesData } from "@/hooks/app-data";
|
import type {
|
||||||
|
useRuleProvidersData,
|
||||||
|
useRulesData,
|
||||||
|
} from "@/hooks/use-clash-data";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
|
|
||||||
// 辅助组件 - 类型框
|
// 辅助组件 - 类型框
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import {
|
|||||||
useClashConfig,
|
useClashConfig,
|
||||||
useSystemProxyAddress,
|
useSystemProxyAddress,
|
||||||
useSystemProxyData,
|
useSystemProxyData,
|
||||||
} from "@/hooks/app-data";
|
} from "@/hooks/use-clash-data";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import {
|
import {
|
||||||
getAutotemProxy,
|
getAutotemProxy,
|
||||||
|
|||||||
@ -16,11 +16,11 @@ import { TooltipIcon } from "@/components/base/base-tooltip-icon";
|
|||||||
import { GuardState } from "@/components/setting/mods/guard-state";
|
import { GuardState } from "@/components/setting/mods/guard-state";
|
||||||
import { SysproxyViewer } from "@/components/setting/mods/sysproxy-viewer";
|
import { SysproxyViewer } from "@/components/setting/mods/sysproxy-viewer";
|
||||||
import { TunViewer } from "@/components/setting/mods/tun-viewer";
|
import { TunViewer } from "@/components/setting/mods/tun-viewer";
|
||||||
|
import { useServiceInstaller } from "@/hooks/use-service-installer";
|
||||||
|
import { useServiceUninstaller } from "@/hooks/use-service-uninstaller";
|
||||||
import { useSystemProxyState } from "@/hooks/use-system-proxy-state";
|
import { useSystemProxyState } from "@/hooks/use-system-proxy-state";
|
||||||
import { useSystemState } from "@/hooks/use-system-state";
|
import { useSystemState } from "@/hooks/use-system-state";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { useServiceInstaller } from "@/hooks/useServiceInstaller";
|
|
||||||
import { useServiceUninstaller } from "@/hooks/useServiceUninstaller";
|
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
|
|
||||||
interface ProxySwitchProps {
|
interface ProxySwitchProps {
|
||||||
|
|||||||
@ -1,35 +0,0 @@
|
|||||||
import { useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 资源清理 Hook
|
|
||||||
* 用于在组件卸载或窗口关闭时统一清理资源
|
|
||||||
*/
|
|
||||||
export const useCleanup = () => {
|
|
||||||
const cleanupFnsRef = useRef<Set<() => void>>(new Set());
|
|
||||||
|
|
||||||
const registerCleanup = (fn: () => void) => {
|
|
||||||
cleanupFnsRef.current.add(fn);
|
|
||||||
return () => {
|
|
||||||
cleanupFnsRef.current.delete(fn);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const cleanup = () => {
|
|
||||||
cleanupFnsRef.current.forEach((fn) => {
|
|
||||||
try {
|
|
||||||
fn();
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[资源清理] 清理失败:", error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cleanupFnsRef.current.clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
cleanup();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { registerCleanup, cleanup };
|
|
||||||
};
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
import { useMemo } from "react";
|
|
||||||
|
|
||||||
import { useClashConfig, useProxiesData } from "@/hooks/app-data";
|
|
||||||
|
|
||||||
// 获取当前代理节点信息的自定义Hook
|
|
||||||
export const useCurrentProxy = () => {
|
|
||||||
const { proxies, refreshProxy } = useProxiesData();
|
|
||||||
const { clashConfig } = useClashConfig();
|
|
||||||
|
|
||||||
// 获取当前模式
|
|
||||||
const currentMode = clashConfig?.mode?.toLowerCase() || "rule";
|
|
||||||
|
|
||||||
// 获取当前代理节点信息
|
|
||||||
const currentProxyInfo = useMemo(() => {
|
|
||||||
if (!proxies) return { currentProxy: null, primaryGroupName: null };
|
|
||||||
|
|
||||||
const globalGroup = proxies.global as IProxyGroupItem | undefined;
|
|
||||||
const groups: IProxyGroupItem[] = Array.isArray(proxies.groups)
|
|
||||||
? (proxies.groups as IProxyGroupItem[])
|
|
||||||
: [];
|
|
||||||
const records = (proxies.records || {}) as Record<string, IProxyItem>;
|
|
||||||
|
|
||||||
// 默认信息
|
|
||||||
let primaryGroupName = "GLOBAL";
|
|
||||||
let currentName = globalGroup?.now;
|
|
||||||
|
|
||||||
// 在规则模式下,寻找主要代理组(通常是第一个或者名字包含特定关键词的组)
|
|
||||||
if (currentMode === "rule" && groups.length > 0) {
|
|
||||||
// 查找主要的代理组(优先级:包含关键词 > 第一个非GLOBAL组)
|
|
||||||
const primaryKeywords = [
|
|
||||||
"auto",
|
|
||||||
"select",
|
|
||||||
"proxy",
|
|
||||||
"节点选择",
|
|
||||||
"自动选择",
|
|
||||||
];
|
|
||||||
const primaryGroup =
|
|
||||||
groups.find((group) =>
|
|
||||||
primaryKeywords.some((keyword) =>
|
|
||||||
group.name.toLowerCase().includes(keyword.toLowerCase()),
|
|
||||||
),
|
|
||||||
) || groups.filter((g) => g.name !== "GLOBAL")[0];
|
|
||||||
|
|
||||||
if (primaryGroup) {
|
|
||||||
primaryGroupName = primaryGroup.name;
|
|
||||||
currentName = primaryGroup.now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果找不到当前节点,返回null
|
|
||||||
if (!currentName) return { currentProxy: null, primaryGroupName };
|
|
||||||
|
|
||||||
// 获取完整的节点信息
|
|
||||||
const currentProxy = records[currentName] || {
|
|
||||||
name: currentName,
|
|
||||||
type: "Unknown",
|
|
||||||
udp: false,
|
|
||||||
xudp: false,
|
|
||||||
tfo: false,
|
|
||||||
mptcp: false,
|
|
||||||
smux: false,
|
|
||||||
history: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
return { currentProxy, primaryGroupName };
|
|
||||||
}, [proxies, currentMode]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
currentProxy: currentProxyInfo.currentProxy,
|
|
||||||
primaryGroupName: currentProxyInfo.primaryGroupName,
|
|
||||||
mode: currentMode,
|
|
||||||
refreshProxy,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -2,7 +2,7 @@ import { useLockFn } from "ahooks";
|
|||||||
import useSWR, { mutate } from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
import { closeAllConnections } from "tauri-plugin-mihomo-api";
|
import { closeAllConnections } from "tauri-plugin-mihomo-api";
|
||||||
|
|
||||||
import { useSystemProxyData } from "@/hooks/app-data";
|
import { useSystemProxyData } from "@/hooks/use-clash-data";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { getAutotemProxy } from "@/services/cmds";
|
import { getAutotemProxy } from "@/services/cmds";
|
||||||
|
|
||||||
|
|||||||
@ -54,10 +54,12 @@ import { useWindowDecorations } from "@/hooks/use-window";
|
|||||||
import { useThemeMode } from "@/services/states";
|
import { useThemeMode } from "@/services/states";
|
||||||
import getSystem from "@/utils/get-system";
|
import getSystem from "@/utils/get-system";
|
||||||
|
|
||||||
import { handleNoticeMessage } from "./_layout/notificationHandlers";
|
import {
|
||||||
import { useAppInitialization } from "./_layout/useAppInitialization";
|
useAppInitialization,
|
||||||
import { useLayoutEvents } from "./_layout/useLayoutEvents";
|
useLayoutEvents,
|
||||||
import { useLoadingOverlay } from "./_layout/useLoadingOverlay";
|
useLoadingOverlay,
|
||||||
|
} from "./_layout/hooks";
|
||||||
|
import { handleNoticeMessage } from "./_layout/utils";
|
||||||
import { navItems } from "./_routers";
|
import { navItems } from "./_routers";
|
||||||
|
|
||||||
import "dayjs/locale/ru";
|
import "dayjs/locale/ru";
|
||||||
|
|||||||
3
src/pages/_layout/hooks/index.ts
Normal file
3
src/pages/_layout/hooks/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export { useAppInitialization } from "./use-app-initialization";
|
||||||
|
export { useLayoutEvents } from "./use-layout-events";
|
||||||
|
export { useLoadingOverlay } from "./use-loading-overlay";
|
||||||
@ -1,6 +1,8 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
import { hideInitialOverlay } from "../utils";
|
||||||
|
|
||||||
export const useAppInitialization = () => {
|
export const useAppInitialization = () => {
|
||||||
const initRef = useRef(false);
|
const initRef = useRef(false);
|
||||||
|
|
||||||
@ -25,6 +27,8 @@ export const useAppInitialization = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const notifyBackend = async (stage?: string) => {
|
const notifyBackend = async (stage?: string) => {
|
||||||
|
if (isCancelled) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (stage) {
|
if (stage) {
|
||||||
await invoke("update_ui_stage", { stage });
|
await invoke("update_ui_stage", { stage });
|
||||||
@ -32,20 +36,16 @@ export const useAppInitialization = () => {
|
|||||||
await invoke("notify_ui_ready");
|
await invoke("notify_ui_ready");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`[初始化] 通知后端失败:`, err);
|
console.error(`[Initialization] Failed to notify backend:`, err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeLoadingOverlay = () => {
|
const removeLoadingOverlay = () => {
|
||||||
const overlay = document.getElementById("initial-loading-overlay");
|
hideInitialOverlay({ schedule: scheduleTimeout });
|
||||||
if (overlay) {
|
|
||||||
overlay.style.opacity = "0";
|
|
||||||
scheduleTimeout(() => overlay.remove(), 300);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const performInitialization = async () => {
|
const performInitialization = async () => {
|
||||||
if (isInitialized) return;
|
if (isCancelled || isInitialized) return;
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -70,14 +70,18 @@ export const useAppInitialization = () => {
|
|||||||
await notifyBackend("ResourcesLoaded");
|
await notifyBackend("ResourcesLoaded");
|
||||||
await notifyBackend();
|
await notifyBackend();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[初始化] 失败:", error);
|
if (!isCancelled) {
|
||||||
removeLoadingOverlay();
|
console.error("[Initialization] Failed:", error);
|
||||||
notifyBackend().catch(console.error);
|
removeLoadingOverlay();
|
||||||
|
notifyBackend().catch(console.error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkBackendReady = async () => {
|
const checkBackendReady = async () => {
|
||||||
try {
|
try {
|
||||||
|
if (isCancelled) return;
|
||||||
|
|
||||||
await invoke("update_ui_stage", { stage: "Loading" });
|
await invoke("update_ui_stage", { stage: "Loading" });
|
||||||
performInitialization();
|
performInitialization();
|
||||||
} catch {
|
} catch {
|
||||||
@ -99,7 +103,7 @@ export const useAppInitialization = () => {
|
|||||||
try {
|
try {
|
||||||
window.clearTimeout(id);
|
window.clearTimeout(id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("[初始化] 清理定时器失败:", error);
|
console.warn("[Initialization] Failed to clear timer:", error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
timers.clear();
|
timers.clear();
|
||||||
@ -13,6 +13,10 @@ export const useLayoutEvents = (
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unlisteners: Array<() => void> = [];
|
const unlisteners: Array<() => void> = [];
|
||||||
let disposed = false;
|
let disposed = false;
|
||||||
|
const revalidateKeys = (keys: readonly string[]) => {
|
||||||
|
const keySet = new Set(keys);
|
||||||
|
mutate((key) => typeof key === "string" && keySet.has(key));
|
||||||
|
};
|
||||||
|
|
||||||
const register = (
|
const register = (
|
||||||
maybeUnlisten: void | (() => void) | Promise<void | (() => void)>,
|
maybeUnlisten: void | (() => void) | Promise<void | (() => void)>,
|
||||||
@ -33,25 +37,31 @@ export const useLayoutEvents = (
|
|||||||
unlisteners.push(unlisten);
|
unlisteners.push(unlisten);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => console.error("[事件监听] 注册失败", error));
|
.catch((error) =>
|
||||||
|
console.error("[Event Listener] Registration failed:", error),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
register(
|
register(
|
||||||
addListener("verge://refresh-clash-config", async () => {
|
addListener("verge://refresh-clash-config", async () => {
|
||||||
mutate("getProxies");
|
revalidateKeys([
|
||||||
mutate("getVersion");
|
"getProxies",
|
||||||
mutate("getClashConfig");
|
"getVersion",
|
||||||
mutate("getProxyProviders");
|
"getClashConfig",
|
||||||
|
"getProxyProviders",
|
||||||
|
]);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
register(
|
register(
|
||||||
addListener("verge://refresh-verge-config", () => {
|
addListener("verge://refresh-verge-config", () => {
|
||||||
mutate("getVergeConfig");
|
revalidateKeys([
|
||||||
mutate("getSystemProxy");
|
"getVergeConfig",
|
||||||
mutate("getAutotemProxy");
|
"getSystemProxy",
|
||||||
mutate("getRunningMode");
|
"getAutotemProxy",
|
||||||
mutate("isServiceAvailable");
|
"getRunningMode",
|
||||||
|
"isServiceAvailable",
|
||||||
|
]);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -91,7 +101,7 @@ export const useLayoutEvents = (
|
|||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
console.error(
|
console.error(
|
||||||
`[事件监听] 清理过程中发生 ${errors.length} 个错误:`,
|
`[Event Listener] Encountered ${errors.length} errors during cleanup:`,
|
||||||
errors,
|
errors,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,13 +1,15 @@
|
|||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
import { hideInitialOverlay } from "../utils";
|
||||||
|
|
||||||
export const useLoadingOverlay = (themeReady: boolean) => {
|
export const useLoadingOverlay = (themeReady: boolean) => {
|
||||||
const overlayRemovedRef = useRef(false);
|
const overlayRemovedRef = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!themeReady || overlayRemovedRef.current) return;
|
if (!themeReady || overlayRemovedRef.current) return;
|
||||||
|
|
||||||
let fadeTimer: number | null = null;
|
let removalTimer: number | undefined;
|
||||||
let retryTimer: number | null = null;
|
let retryTimer: number | undefined;
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const maxAttempts = 50;
|
const maxAttempts = 50;
|
||||||
let stopped = false;
|
let stopped = false;
|
||||||
@ -15,19 +17,15 @@ export const useLoadingOverlay = (themeReady: boolean) => {
|
|||||||
const tryRemoveOverlay = () => {
|
const tryRemoveOverlay = () => {
|
||||||
if (stopped || overlayRemovedRef.current) return;
|
if (stopped || overlayRemovedRef.current) return;
|
||||||
|
|
||||||
const overlay = document.getElementById("initial-loading-overlay");
|
const { removed, removalTimer: timerId } = hideInitialOverlay({
|
||||||
if (overlay) {
|
assumeMissingAsRemoved: true,
|
||||||
overlayRemovedRef.current = true;
|
});
|
||||||
overlay.style.opacity = "0";
|
if (typeof timerId === "number") {
|
||||||
overlay.style.pointerEvents = "none";
|
removalTimer = timerId;
|
||||||
|
}
|
||||||
|
|
||||||
fadeTimer = window.setTimeout(() => {
|
if (removed) {
|
||||||
try {
|
overlayRemovedRef.current = true;
|
||||||
overlay.remove();
|
|
||||||
} catch (error) {
|
|
||||||
console.warn("[加载遮罩] 移除失败:", error);
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +33,7 @@ export const useLoadingOverlay = (themeReady: boolean) => {
|
|||||||
attempts += 1;
|
attempts += 1;
|
||||||
retryTimer = window.setTimeout(tryRemoveOverlay, 100);
|
retryTimer = window.setTimeout(tryRemoveOverlay, 100);
|
||||||
} else {
|
} else {
|
||||||
console.warn("[加载遮罩] 未找到元素");
|
console.warn("[Loading Overlay] Element not found");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,8 +41,8 @@ export const useLoadingOverlay = (themeReady: boolean) => {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
stopped = true;
|
stopped = true;
|
||||||
if (fadeTimer) window.clearTimeout(fadeTimer);
|
if (typeof removalTimer === "number") window.clearTimeout(removalTimer);
|
||||||
if (retryTimer) window.clearTimeout(retryTimer);
|
if (typeof retryTimer === "number") window.clearTimeout(retryTimer);
|
||||||
};
|
};
|
||||||
}, [themeReady]);
|
}, [themeReady]);
|
||||||
};
|
};
|
||||||
@ -1,25 +0,0 @@
|
|||||||
import { useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
export const useLazyDataLoad = (
|
|
||||||
callbacks: Array<() => void>,
|
|
||||||
delay: number = 1000,
|
|
||||||
) => {
|
|
||||||
const hasLoadedRef = useRef(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (hasLoadedRef.current) return;
|
|
||||||
|
|
||||||
const timer = window.setTimeout(() => {
|
|
||||||
hasLoadedRef.current = true;
|
|
||||||
callbacks.forEach((callback) => {
|
|
||||||
try {
|
|
||||||
callback();
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[延迟加载] 执行失败:", error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, delay);
|
|
||||||
|
|
||||||
return () => window.clearTimeout(timer);
|
|
||||||
}, [callbacks, delay]);
|
|
||||||
};
|
|
||||||
2
src/pages/_layout/utils/index.ts
Normal file
2
src/pages/_layout/utils/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { hideInitialOverlay } from "./initial-loading-overlay";
|
||||||
|
export { handleNoticeMessage } from "./notification-handlers";
|
||||||
45
src/pages/_layout/utils/initial-loading-overlay.ts
Normal file
45
src/pages/_layout/utils/initial-loading-overlay.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const OVERLAY_ID = "initial-loading-overlay";
|
||||||
|
const REMOVE_DELAY = 300;
|
||||||
|
|
||||||
|
let overlayRemoved = false;
|
||||||
|
|
||||||
|
type HideOverlayOptions = {
|
||||||
|
schedule?: (handler: () => void, delay: number) => number;
|
||||||
|
assumeMissingAsRemoved?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type HideOverlayResult = {
|
||||||
|
removed: boolean;
|
||||||
|
removalTimer?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hideInitialOverlay = (
|
||||||
|
options: HideOverlayOptions = {},
|
||||||
|
): HideOverlayResult => {
|
||||||
|
if (overlayRemoved) {
|
||||||
|
return { removed: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlay = document.getElementById(OVERLAY_ID);
|
||||||
|
if (!overlay) {
|
||||||
|
if (options.assumeMissingAsRemoved) {
|
||||||
|
overlayRemoved = true;
|
||||||
|
return { removed: true };
|
||||||
|
}
|
||||||
|
return { removed: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
overlayRemoved = true;
|
||||||
|
overlay.dataset.hidden = "true";
|
||||||
|
|
||||||
|
const schedule = options.schedule ?? window.setTimeout;
|
||||||
|
const removalTimer = schedule(() => {
|
||||||
|
try {
|
||||||
|
overlay.remove();
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("[Loading Overlay] Removal failed:", error);
|
||||||
|
}
|
||||||
|
}, REMOVE_DELAY);
|
||||||
|
|
||||||
|
return { removed: true, removalTimer };
|
||||||
|
};
|
||||||
@ -8,7 +8,7 @@ import { BaseSearchBox } from "@/components/base/base-search-box";
|
|||||||
import { ScrollTopButton } from "@/components/layout/scroll-top-button";
|
import { ScrollTopButton } from "@/components/layout/scroll-top-button";
|
||||||
import { ProviderButton } from "@/components/rule/provider-button";
|
import { ProviderButton } from "@/components/rule/provider-button";
|
||||||
import RuleItem from "@/components/rule/rule-item";
|
import RuleItem from "@/components/rule/rule-item";
|
||||||
import { useRuleProvidersData, useRulesData } from "@/hooks/app-data";
|
import { useRuleProvidersData, useRulesData } from "@/hooks/use-clash-data";
|
||||||
import { useVisibility } from "@/hooks/use-visibility";
|
import { useVisibility } from "@/hooks/use-visibility";
|
||||||
|
|
||||||
const RulesPage = () => {
|
const RulesPage = () => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user