use-custom-theme.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import { useEffect, useMemo } from "react";
  2. import { useRecoilState } from "recoil";
  3. import { createTheme, Theme } from "@mui/material";
  4. import { appWindow } from "@tauri-apps/api/window";
  5. import { atomThemeMode } from "@/services/states";
  6. import { defaultTheme, defaultDarkTheme } from "@/pages/_theme";
  7. import { useVerge } from "@/hooks/use-verge";
  8. /**
  9. * custom theme
  10. */
  11. export const useCustomTheme = () => {
  12. const { verge } = useVerge();
  13. const { theme_mode, theme_setting } = verge ?? {};
  14. const [mode, setMode] = useRecoilState(atomThemeMode);
  15. useEffect(() => {
  16. const themeMode = ["light", "dark", "system"].includes(theme_mode!)
  17. ? theme_mode!
  18. : "light";
  19. if (themeMode !== "system") {
  20. setMode(themeMode);
  21. return;
  22. }
  23. appWindow.theme().then((m) => m && setMode(m));
  24. const unlisten = appWindow.onThemeChanged((e) => setMode(e.payload));
  25. return () => {
  26. unlisten.then((fn) => fn());
  27. };
  28. }, [theme_mode]);
  29. const theme = useMemo(() => {
  30. const setting = theme_setting || {};
  31. const dt = mode === "light" ? defaultTheme : defaultDarkTheme;
  32. let theme: Theme;
  33. try {
  34. theme = createTheme({
  35. breakpoints: {
  36. values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 },
  37. },
  38. palette: {
  39. mode,
  40. primary: { main: setting.primary_color || dt.primary_color },
  41. secondary: { main: setting.secondary_color || dt.secondary_color },
  42. info: { main: setting.info_color || dt.info_color },
  43. error: { main: setting.error_color || dt.error_color },
  44. warning: { main: setting.warning_color || dt.warning_color },
  45. success: { main: setting.success_color || dt.success_color },
  46. text: {
  47. primary: setting.primary_text || dt.primary_text,
  48. secondary: setting.secondary_text || dt.secondary_text,
  49. },
  50. },
  51. typography: {
  52. // todo
  53. fontFamily: setting.font_family
  54. ? `${setting.font_family}, ${dt.font_family}`
  55. : dt.font_family,
  56. },
  57. });
  58. } catch {
  59. // fix #294
  60. theme = createTheme({
  61. breakpoints: {
  62. values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 },
  63. },
  64. palette: {
  65. mode,
  66. primary: { main: dt.primary_color },
  67. secondary: { main: dt.secondary_color },
  68. info: { main: dt.info_color },
  69. error: { main: dt.error_color },
  70. warning: { main: dt.warning_color },
  71. success: { main: dt.success_color },
  72. text: { primary: dt.primary_text, secondary: dt.secondary_text },
  73. },
  74. typography: { fontFamily: dt.font_family },
  75. });
  76. }
  77. // css
  78. const backgroundColor = mode === "light" ? "#ffffff" : "#121212";
  79. const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5";
  80. const scrollColor = mode === "light" ? "#90939980" : "#54545480";
  81. const rootEle = document.documentElement;
  82. rootEle.style.setProperty("--background-color", backgroundColor);
  83. rootEle.style.setProperty("--selection-color", selectColor);
  84. rootEle.style.setProperty("--scroller-color", scrollColor);
  85. rootEle.style.setProperty("--primary-main", theme.palette.primary.main);
  86. // inject css
  87. let style = document.querySelector("style#verge-theme");
  88. if (!style) {
  89. style = document.createElement("style");
  90. style.id = "verge-theme";
  91. document.head.appendChild(style!);
  92. }
  93. if (style) {
  94. style.innerHTML = setting.css_injection || "";
  95. }
  96. // update svg icon
  97. const { palette } = theme;
  98. setTimeout(() => {
  99. const dom = document.querySelector("#Gradient2");
  100. if (dom) {
  101. dom.innerHTML = `
  102. <stop offset="0%" stop-color="${palette.primary.main}" />
  103. <stop offset="80%" stop-color="${palette.primary.dark}" />
  104. <stop offset="100%" stop-color="${palette.primary.dark}" />
  105. `;
  106. }
  107. }, 0);
  108. return theme;
  109. }, [mode, theme_setting]);
  110. return { theme };
  111. };