fix: windows title bar and refactor old code (#4988)

This commit is contained in:
Sline
2025-10-09 10:53:20 +08:00
committed by GitHub
parent f5c2b2a23d
commit 03ab2410cc

View File

@@ -4,7 +4,13 @@ import { listen } from "@tauri-apps/api/event";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import dayjs from "dayjs"; import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from "dayjs/plugin/relativeTime";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import React, {
useCallback,
useEffect,
useMemo,
useReducer,
useRef,
} from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useLocation, useNavigate, useRoutes } from "react-router-dom"; import { useLocation, useNavigate, useRoutes } from "react-router-dom";
import { SWRConfig, mutate } from "swr"; import { SWRConfig, mutate } from "swr";
@@ -13,6 +19,7 @@ import iconDark from "@/assets/image/icon_dark.svg?react";
import iconLight from "@/assets/image/icon_light.svg?react"; import iconLight from "@/assets/image/icon_light.svg?react";
import LogoSvg from "@/assets/image/logo.svg?react"; import LogoSvg from "@/assets/image/logo.svg?react";
import { NoticeManager } from "@/components/base/NoticeManager"; import { NoticeManager } from "@/components/base/NoticeManager";
import { WindowControls } from "@/components/controller/window-controller";
import { LayoutItem } from "@/components/layout/layout-item"; import { LayoutItem } from "@/components/layout/layout-item";
import { LayoutTraffic } from "@/components/layout/layout-traffic"; import { LayoutTraffic } from "@/components/layout/layout-traffic";
import { UpdateButton } from "@/components/layout/update-button"; import { UpdateButton } from "@/components/layout/update-button";
@@ -36,9 +43,6 @@ import { routers } from "./_routers";
import "dayjs/locale/ru"; import "dayjs/locale/ru";
import "dayjs/locale/zh-cn"; import "dayjs/locale/zh-cn";
import { WindowControls } from "@/components/controller/window-controller";
// 删除重复导入
const appWindow = getCurrentWebviewWindow(); const appWindow = getCurrentWebviewWindow();
export const portableFlag = false; export const portableFlag = false;
@@ -160,15 +164,12 @@ const Layout = () => {
useConnectionData(); useConnectionData();
useLogData(); useLogData();
const mode = useThemeMode(); const mode = useThemeMode();
const isDark = mode === "light" ? false : true; const isDark = mode !== "light";
const { t } = useTranslation(); const { t } = useTranslation();
const { theme } = useCustomTheme(); const { theme } = useCustomTheme();
const { verge } = useVerge(); const { verge } = useVerge();
const { clashInfo } = useClashInfo(); useClashInfo();
const [clashLog] = useClashLog(); useClashLog();
const enableLog = clashLog.enable;
const logLevel = clashLog.logLevel;
// const [logLevel] = useLocalStorage<LogLevel>("log:log-level", "info");
const { language, start_page } = verge ?? {}; const { language, start_page } = verge ?? {};
const { switchLanguage } = useI18n(); const { switchLanguage } = useI18n();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -176,7 +177,7 @@ const Layout = () => {
const routersEles = useRoutes(routers); const routersEles = useRoutes(routers);
const { addListener } = useListen(); const { addListener } = useListen();
const initRef = useRef(false); const initRef = useRef(false);
const [themeReady, setThemeReady] = useState(false); const [themeReady, activateTheme] = useReducer(() => true, false);
const windowControls = useRef<any>(null); const windowControls = useRef<any>(null);
const { decorated } = useWindowDecorations(); const { decorated } = useWindowDecorations();
@@ -187,6 +188,8 @@ const Layout = () => {
decorated, decorated,
"| showing:", "| showing:",
!decorated, !decorated,
"| theme mode:",
mode,
); );
if (!decorated) { if (!decorated) {
return ( return (
@@ -196,22 +199,22 @@ const Layout = () => {
); );
} }
return null; return null;
}, [decorated]); }, [decorated, mode]);
useEffect(() => { useEffect(() => {
setThemeReady(true); activateTheme();
}, [theme]); }, [theme]);
const handleNotice = useCallback( const handleNotice = useCallback(
(payload: [string, string]) => { (payload: [string, string]) => {
const [status, msg] = payload; const [status, msg] = payload;
setTimeout(() => { queueMicrotask(() => {
try { try {
handleNoticeMessage(status, msg, t, navigate); handleNoticeMessage(status, msg, t, navigate);
} catch (error) { } catch (error) {
console.error("[Layout] 处理通知消息失败:", error); console.error("[Layout] 处理通知消息失败:", error);
} }
}, 0); });
}, },
[t, navigate], [t, navigate],
); );
@@ -260,10 +263,10 @@ const Layout = () => {
}; };
}; };
const cleanupWindow = setupWindowListeners(); const cleanupWindowPromise = setupWindowListeners();
return () => { return () => {
setTimeout(() => { queueMicrotask(() => {
listeners.forEach((listener) => { listeners.forEach((listener) => {
if (typeof listener.then === "function") { if (typeof listener.then === "function") {
listener listener
@@ -280,7 +283,7 @@ const Layout = () => {
} }
}); });
cleanupWindow cleanupWindowPromise
.then((cleanup) => { .then((cleanup) => {
try { try {
cleanup(); cleanup();
@@ -291,9 +294,9 @@ const Layout = () => {
.catch((error) => { .catch((error) => {
console.error("[Layout] 获取cleanup函数失败:", error); console.error("[Layout] 获取cleanup函数失败:", error);
}); });
}, 0); });
}; };
}, [handleNotice]); }, [addListener, handleNotice]);
useEffect(() => { useEffect(() => {
if (initRef.current) { if (initRef.current) {
@@ -303,6 +306,16 @@ const Layout = () => {
console.log("[Layout] 开始执行初始化代码"); console.log("[Layout] 开始执行初始化代码");
initRef.current = true; initRef.current = true;
const timers = new Set<number>();
const scheduleTimeout = (callback: () => void, delay = 0) => {
const timeoutId = window.setTimeout(() => {
timers.delete(timeoutId);
callback();
}, delay);
timers.add(timeoutId);
return timeoutId;
};
let isInitialized = false; let isInitialized = false;
let initializationAttempts = 0; let initializationAttempts = 0;
const maxAttempts = 3; const maxAttempts = 3;
@@ -326,7 +339,7 @@ const Layout = () => {
if (initialOverlay) { if (initialOverlay) {
console.log("[Layout] 移除加载指示器"); console.log("[Layout] 移除加载指示器");
initialOverlay.style.opacity = "0"; initialOverlay.style.opacity = "0";
setTimeout(() => { scheduleTimeout(() => {
try { try {
initialOverlay.remove(); initialOverlay.remove();
} catch { } catch {
@@ -357,13 +370,13 @@ const Layout = () => {
console.log("[Layout] React组件已挂载"); console.log("[Layout] React组件已挂载");
resolve(); resolve();
} else { } else {
setTimeout(checkReactMount, 50); scheduleTimeout(checkReactMount, 50);
} }
}; };
checkReactMount(); checkReactMount();
setTimeout(() => { scheduleTimeout(() => {
console.log("[Layout] React组件挂载检查超时继续执行"); console.log("[Layout] React组件挂载检查超时继续执行");
resolve(); resolve();
}, 2000); }, 2000);
@@ -391,7 +404,7 @@ const Layout = () => {
console.log( console.log(
`[Layout] 将在500ms后进行第 ${initializationAttempts + 1} 次重试`, `[Layout] 将在500ms后进行第 ${initializationAttempts + 1} 次重试`,
); );
setTimeout(performInitialization, 500); scheduleTimeout(performInitialization, 500);
} else { } else {
console.error("[Layout] 所有初始化尝试都失败,执行紧急初始化"); console.error("[Layout] 所有初始化尝试都失败,执行紧急初始化");
@@ -408,15 +421,6 @@ const Layout = () => {
let hasEventTriggered = false; let hasEventTriggered = false;
const setupEventListener = async () => {
try {
console.log("[Layout] 开始监听启动完成事件");
} catch (err) {
console.error("[Layout] 监听启动完成事件失败:", err);
return () => { };
}
};
const checkImmediateInitialization = async () => { const checkImmediateInitialization = async () => {
try { try {
console.log("[Layout] 检查后端是否已就绪"); console.log("[Layout] 检查后端是否已就绪");
@@ -432,7 +436,7 @@ const Layout = () => {
} }
}; };
const backupInitialization = setTimeout(() => { const backupInitialization = scheduleTimeout(() => {
if (!hasEventTriggered && !isInitialized) { if (!hasEventTriggered && !isInitialized) {
console.warn("[Layout] 备用初始化触发1.5秒内未开始初始化"); console.warn("[Layout] 备用初始化触发1.5秒内未开始初始化");
hasEventTriggered = true; hasEventTriggered = true;
@@ -440,20 +444,28 @@ const Layout = () => {
} }
}, 1500); }, 1500);
const emergencyInitialization = setTimeout(() => { const emergencyInitialization = scheduleTimeout(() => {
if (!isInitialized) { if (!isInitialized) {
console.error("[Layout] 紧急初始化触发5秒内未完成初始化"); console.error("[Layout] 紧急初始化触发5秒内未完成初始化");
removeLoadingOverlay(); removeLoadingOverlay();
notifyBackend("UI就绪").catch(() => { }); notifyBackend("UI就绪").catch(() => {});
isInitialized = true; isInitialized = true;
} }
}, 5000); }, 5000);
setTimeout(checkImmediateInitialization, 100); const immediateInitialization = scheduleTimeout(
checkImmediateInitialization,
100,
);
return () => { return () => {
clearTimeout(backupInitialization); window.clearTimeout(backupInitialization);
clearTimeout(emergencyInitialization); window.clearTimeout(emergencyInitialization);
window.clearTimeout(immediateInitialization);
timers.forEach((timeoutId) => {
window.clearTimeout(timeoutId);
});
timers.clear();
}; };
}, []); }, []);
@@ -469,7 +481,7 @@ const Layout = () => {
if (start_page) { if (start_page) {
navigate(start_page, { replace: true }); navigate(start_page, { replace: true });
} }
}, [start_page]); }, [navigate, start_page]);
if (!themeReady) { if (!themeReady) {
return ( return (
@@ -543,27 +555,27 @@ const Layout = () => {
borderTopLeftRadius: "0px", borderTopLeftRadius: "0px",
borderTopRightRadius: "0px", borderTopRightRadius: "0px",
}} }}
onContextMenu={(e) => { onContextMenu={() => {
// TODO: 禁止右键菜单 // TODO: 禁止右键菜单
// if ( // if (
// OS === "windows" && // OS === "windows" &&
// !["input", "textarea"].includes( // !["input", "textarea"].includes(
// e.currentTarget.tagName.toLowerCase(), // event.currentTarget.tagName.toLowerCase(),
// ) && // ) &&
// !e.currentTarget.isContentEditable // !event.currentTarget.isContentEditable
// ) { // ) {
// e.preventDefault(); // event.preventDefault();
// } // }
}} }}
sx={[ sx={[
({ palette }) => ({ bgcolor: palette.background.paper }), ({ palette }) => ({ bgcolor: palette.background.paper }),
OS === "linux" OS === "linux"
? { ? {
borderRadius: "8px", borderRadius: "8px",
border: "1px solid var(--divider-color)", border: "1px solid var(--divider-color)",
width: "100vw", width: "100vw",
height: "100vh", height: "100vh",
} }
: {}, : {},
]} ]}
> >
@@ -617,7 +629,11 @@ const Layout = () => {
<div className="layout-content__right"> <div className="layout-content__right">
<div className="the-bar"></div> <div className="the-bar"></div>
<div className="the-content"> <div className="the-content">
{React.cloneElement(routersEles, { key: location.pathname })} {routersEles ? (
<React.Fragment key={location.pathname}>
{routersEles}
</React.Fragment>
) : null}
</div> </div>
</div> </div>
</div> </div>