import dayjs from "dayjs"; import i18next from "i18next"; import relativeTime from "dayjs/plugin/relativeTime"; import { SWRConfig, mutate } from "swr"; import { useEffect, useCallback } from "react"; import { useTranslation } from "react-i18next"; import { useLocation, useRoutes, useNavigate } from "react-router-dom"; import { List, Paper, ThemeProvider, SvgIcon } from "@mui/material"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; import { routers } from "./_routers"; import { getAxios } from "@/services/api"; import { useVerge } from "@/hooks/use-verge"; import LogoSvg from "@/assets/image/logo.svg?react"; import iconLight from "@/assets/image/icon_light.svg?react"; import iconDark from "@/assets/image/icon_dark.svg?react"; import { useThemeMode } from "@/services/states"; import { Notice } from "@/components/base"; import { LayoutItem } from "@/components/layout/layout-item"; import { LayoutControl } from "@/components/layout/layout-control"; import { LayoutTraffic } from "@/components/layout/layout-traffic"; import { UpdateButton } from "@/components/layout/update-button"; import { useCustomTheme } from "@/components/layout/use-custom-theme"; import getSystem from "@/utils/get-system"; import "dayjs/locale/ru"; import "dayjs/locale/zh-cn"; import { getPortableFlag } from "@/services/cmds"; import React from "react"; import { TransitionGroup, CSSTransition } from "react-transition-group"; import { useListen } from "@/hooks/use-listen"; import { listen } from "@tauri-apps/api/event"; const appWindow = getCurrentWebviewWindow(); export let portableFlag = false; dayjs.extend(relativeTime); const OS = getSystem(); // 通知处理函数 const handleNoticeMessage = ( status: string, msg: string, t: (key: string) => string, navigate: (path: string, options?: any) => void, ) => { console.log("[通知监听] 收到消息:", status, msg); switch (status) { case "import_sub_url::ok": navigate("/profile", { state: { current: msg } }); Notice.success(t("Import Subscription Successful")); break; case "import_sub_url::error": navigate("/profile"); Notice.error(msg); break; case "set_config::error": Notice.error(msg); break; case "config_validate::boot_error": Notice.error(`${t("Boot Config Validation Failed")} ${msg}`); break; case "config_validate::core_change": Notice.error(`${t("Core Change Config Validation Failed")} ${msg}`); break; case "config_validate::error": Notice.error(`${t("Config Validation Failed")} ${msg}`); break; case "config_validate::process_terminated": Notice.error(t("Config Validation Process Terminated")); break; case "config_validate::stdout_error": Notice.error(`${t("Config Validation Failed")} ${msg}`); break; case "config_validate::script_error": Notice.error(`${t("Script File Error")} ${msg}`); break; case "config_validate::script_syntax_error": Notice.error(`${t("Script Syntax Error")} ${msg}`); break; case "config_validate::script_missing_main": Notice.error(`${t("Script Missing Main")} ${msg}`); break; case "config_validate::file_not_found": Notice.error(`${t("File Not Found")} ${msg}`); break; case "config_core::change_success": Notice.success(`${t("Core Changed Successfully")}: ${msg}`); break; case "config_core::change_error": Notice.error(`${t("Failed to Change Core")}: ${msg}`); break; } }; const Layout = () => { const mode = useThemeMode(); const isDark = mode === "light" ? false : true; const { t } = useTranslation(); const { theme } = useCustomTheme(); const { verge } = useVerge(); const { language, start_page } = verge ?? {}; const navigate = useNavigate(); const location = useLocation(); const routersEles = useRoutes(routers); const { addListener, setupCloseListener } = useListen(); const handleNotice = useCallback( (payload: [string, string]) => { const [status, msg] = payload; handleNoticeMessage(status, msg, t, navigate); }, [t, navigate], ); // 设置监听器 useEffect(() => { const listeners = [ // 配置更新监听 addListener("verge://refresh-clash-config", async () => { await getAxios(true); mutate("getProxies"); mutate("getVersion"); mutate("getClashConfig"); mutate("getProxyProviders"); }), // verge 配置更新监听 addListener("verge://refresh-verge-config", () => mutate("getVergeConfig"), ), // 通知消息监听 addListener("verge://notice-message", ({ payload }) => handleNotice(payload as [string, string]), ), ]; // 设置窗口显示/隐藏监听 const setupWindowListeners = async () => { const [hideUnlisten, showUnlisten] = await Promise.all([ listen("verge://hide-window", () => appWindow.hide()), listen("verge://show-window", () => appWindow.show()), ]); return () => { hideUnlisten(); showUnlisten(); }; }; // 初始化 setupCloseListener(); const cleanupWindow = setupWindowListeners(); // 清理函数 return () => { // 清理主要监听器 listeners.forEach((listener) => { if (typeof listener.then === "function") { listener.then((unlisten) => unlisten()); } }); // 清理窗口监听器 cleanupWindow.then((cleanup) => cleanup()); }; }, [handleNotice]); // 语言和起始页设置 useEffect(() => { if (language) { dayjs.locale(language === "zh" ? "zh-cn" : language); i18next.changeLanguage(language); } }, [language]); useEffect(() => { if (start_page) { navigate(start_page, { replace: true }); } }, [start_page]); if (!routersEles) return null; return ( { if ( OS === "windows" && !["input", "textarea"].includes( e.currentTarget.tagName.toLowerCase(), ) && !e.currentTarget.isContentEditable ) { e.preventDefault(); } }} sx={[ ({ palette }) => ({ bgcolor: palette.background.paper }), { borderRadius: "8px", border: "0px solid var(--divider-color)", width: "calc(100vw - 1px)", height: "calc(100vh - 1px)", }, ]} >
{routers.map((router) => ( {t(router.label)} ))}
{OS !== "macos" && }
{React.cloneElement(routersEles, { key: location.pathname })}
); }; export default Layout;