fix: windows title bar and refactor old code (#4988)
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user