_layout.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import { useEffect, useMemo, useState } from "react";
  2. import useSWR, { SWRConfig } from "swr";
  3. import { Route, Routes } from "react-router-dom";
  4. import { useRecoilState } from "recoil";
  5. import {
  6. alpha,
  7. Button,
  8. createTheme,
  9. IconButton,
  10. List,
  11. Paper,
  12. ThemeProvider,
  13. } from "@mui/material";
  14. import { HorizontalRuleRounded, CloseRounded } from "@mui/icons-material";
  15. import { checkUpdate } from "@tauri-apps/api/updater";
  16. import { atomPaletteMode, atomThemeBlur } from "../states/setting";
  17. import { getVergeConfig, windowDrag, windowHide } from "../services/cmds";
  18. import LogoSvg from "../assets/image/logo.svg";
  19. import LogPage from "./log";
  20. import ProfilePage from "./profile";
  21. import ProxyPage from "./proxy";
  22. import SettingPage from "./setting";
  23. import ConnectionsPage from "./connections";
  24. import LayoutItem from "../components/layout-item";
  25. import Traffic from "../components/traffic";
  26. import UpdateDialog from "../components/update-dialog";
  27. const routers = [
  28. {
  29. label: "Proxy",
  30. link: "/",
  31. ele: ProxyPage,
  32. },
  33. {
  34. label: "Profile",
  35. link: "/profile",
  36. ele: ProfilePage,
  37. },
  38. {
  39. label: "Connections",
  40. link: "/connections",
  41. ele: ConnectionsPage,
  42. },
  43. {
  44. label: "Log",
  45. link: "/log",
  46. ele: LogPage,
  47. },
  48. {
  49. label: "Setting",
  50. link: "/setting",
  51. ele: SettingPage,
  52. },
  53. ];
  54. const Layout = () => {
  55. const [mode, setMode] = useRecoilState(atomPaletteMode);
  56. const [blur, setBlur] = useRecoilState(atomThemeBlur);
  57. const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig);
  58. const { data: updateInfo } = useSWR("checkUpdate", checkUpdate, {
  59. errorRetryCount: 2,
  60. revalidateIfStale: false,
  61. focusThrottleInterval: 36e5, // 1 hour
  62. });
  63. const [dialogOpen, setDialogOpen] = useState(false);
  64. useEffect(() => {
  65. window.addEventListener("keydown", (e) => {
  66. if (e.key === "Escape") windowHide();
  67. });
  68. }, []);
  69. useEffect(() => {
  70. if (!vergeConfig) return;
  71. setBlur(!!vergeConfig.theme_blur);
  72. setMode(vergeConfig.theme_mode ?? "light");
  73. }, [vergeConfig]);
  74. const theme = useMemo(() => {
  75. // const background = mode === "light" ? "#f5f5f5" : "#000";
  76. const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5";
  77. const rootEle = document.documentElement;
  78. rootEle.style.background = "transparent";
  79. rootEle.style.setProperty("--selection-color", selectColor);
  80. return createTheme({
  81. breakpoints: {
  82. values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 },
  83. },
  84. palette: {
  85. mode,
  86. primary: { main: "#5b5c9d" },
  87. text: { primary: "#637381", secondary: "#909399" },
  88. },
  89. });
  90. }, [mode]);
  91. return (
  92. <SWRConfig value={{}}>
  93. <ThemeProvider theme={theme}>
  94. <Paper
  95. square
  96. elevation={0}
  97. className="layout"
  98. sx={[
  99. (theme) => ({
  100. bgcolor: alpha(theme.palette.background.paper, blur ? 0.85 : 1),
  101. }),
  102. ]}
  103. >
  104. <div className="layout__left">
  105. <div className="the-logo">
  106. <img
  107. src={LogoSvg}
  108. width="100%"
  109. alt=""
  110. onPointerDown={(e) => {
  111. windowDrag();
  112. e.preventDefault();
  113. }}
  114. />
  115. {updateInfo?.shouldUpdate && (
  116. <Button
  117. color="error"
  118. variant="contained"
  119. size="small"
  120. className="the-newbtn"
  121. onClick={() => setDialogOpen(true)}
  122. >
  123. New
  124. </Button>
  125. )}
  126. </div>
  127. <List className="the-menu">
  128. {routers.map((router) => (
  129. <LayoutItem key={router.label} to={router.link}>
  130. {router.label}
  131. </LayoutItem>
  132. ))}
  133. </List>
  134. <div className="the-traffic">
  135. <Traffic />
  136. </div>
  137. </div>
  138. <div className="layout__right">
  139. <div
  140. className="the-bar"
  141. onPointerDown={(e) =>
  142. e.target === e.currentTarget && windowDrag()
  143. }
  144. >
  145. {/* todo: onClick = windowMini */}
  146. <IconButton size="small" sx={{ mx: 1 }} onClick={windowHide}>
  147. <HorizontalRuleRounded fontSize="inherit" />
  148. </IconButton>
  149. <IconButton size="small" onClick={windowHide}>
  150. <CloseRounded fontSize="inherit" />
  151. </IconButton>
  152. </div>
  153. <div className="the-content">
  154. <Routes>
  155. {routers.map(({ label, link, ele: Ele }) => (
  156. <Route key={label} path={link} element={<Ele />} />
  157. ))}
  158. </Routes>
  159. </div>
  160. </div>
  161. </Paper>
  162. <UpdateDialog open={dialogOpen} onClose={() => setDialogOpen(false)} />
  163. </ThemeProvider>
  164. </SWRConfig>
  165. );
  166. };
  167. export default Layout;