mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2024-11-16 11:42:21 +08:00
feat: theme mode support follows system
This commit is contained in:
parent
55cc83a5d4
commit
8bce2ce040
|
@ -1,7 +1,10 @@
|
|||
import useSWR from "swr";
|
||||
import { useMemo } from "react";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { useRecoilState } from "recoil";
|
||||
import { createTheme } from "@mui/material";
|
||||
import { appWindow } from "@tauri-apps/api/window";
|
||||
import { getVergeConfig } from "../../services/cmds";
|
||||
import { atomThemeMode } from "../../services/states";
|
||||
import { defaultTheme, defaultDarkTheme } from "../../pages/_theme";
|
||||
|
||||
/**
|
||||
|
@ -10,10 +13,23 @@ import { defaultTheme, defaultDarkTheme } from "../../pages/_theme";
|
|||
export default function useCustomTheme() {
|
||||
const { data } = useSWR("getVergeConfig", getVergeConfig);
|
||||
const { theme_mode, theme_setting } = data ?? {};
|
||||
const [mode, setMode] = useRecoilState(atomThemeMode);
|
||||
|
||||
useEffect(() => {
|
||||
if (theme_mode !== "system") {
|
||||
setMode(theme_mode ?? "light");
|
||||
return;
|
||||
}
|
||||
|
||||
appWindow.theme().then((m) => m && setMode(m));
|
||||
const unlisten = appWindow.onThemeChanged((e) => setMode(e.payload));
|
||||
|
||||
return () => {
|
||||
unlisten.then((fn) => fn());
|
||||
};
|
||||
}, [theme_mode]);
|
||||
|
||||
const theme = useMemo(() => {
|
||||
const mode = theme_mode ?? "light";
|
||||
|
||||
const setting = theme_setting || {};
|
||||
const dt = mode === "light" ? defaultTheme : defaultDarkTheme;
|
||||
|
||||
|
@ -78,7 +94,7 @@ export default function useCustomTheme() {
|
|||
}, 0);
|
||||
|
||||
return theme;
|
||||
}, [theme_mode, theme_setting]);
|
||||
}, [mode, theme_setting]);
|
||||
|
||||
return { theme };
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import useSWR from "swr";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { useRecoilValue } from "recoil";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Button,
|
||||
|
@ -9,11 +9,8 @@ import {
|
|||
DialogContent,
|
||||
DialogTitle,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
getVergeConfig,
|
||||
readProfileFile,
|
||||
saveProfileFile,
|
||||
} from "../../services/cmds";
|
||||
import { atomThemeMode } from "../../services/states";
|
||||
import { readProfileFile, saveProfileFile } from "../../services/cmds";
|
||||
import Notice from "../base/base-notice";
|
||||
|
||||
import "monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js";
|
||||
|
@ -35,8 +32,7 @@ const FileEditor = (props: Props) => {
|
|||
const { t } = useTranslation();
|
||||
const editorRef = useRef<any>();
|
||||
const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null);
|
||||
const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig);
|
||||
const { theme_mode } = vergeConfig ?? {};
|
||||
const themeMode = useRecoilValue(atomThemeMode);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
|
@ -50,7 +46,7 @@ const FileEditor = (props: Props) => {
|
|||
instanceRef.current = editor.create(editorRef.current, {
|
||||
value: data,
|
||||
language: mode,
|
||||
theme: theme_mode === "light" ? "vs" : "vs-dark",
|
||||
theme: themeMode === "light" ? "vs" : "vs-dark",
|
||||
minimap: { enabled: false },
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { styled, Switch } from "@mui/material";
|
||||
|
||||
// todo: deprecated
|
||||
// From: https://mui.com/components/switches/
|
||||
const PaletteSwitch = styled(Switch)(({ theme }) => ({
|
||||
width: 62,
|
||||
|
|
|
@ -19,7 +19,7 @@ import { ArrowForward } from "@mui/icons-material";
|
|||
import { SettingList, SettingItem } from "./setting";
|
||||
import { CmdType } from "../../services/types";
|
||||
import { version } from "../../../package.json";
|
||||
import PaletteSwitch from "./palette-switch";
|
||||
import ThemeModeSwitch from "./theme-mode-switch";
|
||||
import GuardState from "./guard-state";
|
||||
import SettingTheme from "./setting-theme";
|
||||
|
||||
|
@ -43,19 +43,31 @@ const SettingVerge = ({ onError }: Props) => {
|
|||
|
||||
return (
|
||||
<SettingList title={t("Verge Setting")}>
|
||||
<SettingItem>
|
||||
<ListItemText primary={t("Language")} />
|
||||
<GuardState
|
||||
value={language ?? "en"}
|
||||
onCatch={onError}
|
||||
onFormat={(e: any) => e.target.value}
|
||||
onChange={(e) => onChangeData({ language: e })}
|
||||
onGuard={(e) => patchVergeConfig({ language: e })}
|
||||
>
|
||||
<Select size="small" sx={{ width: 100 }}>
|
||||
<MenuItem value="zh">中文</MenuItem>
|
||||
<MenuItem value="en">English</MenuItem>
|
||||
</Select>
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem>
|
||||
<ListItemText primary={t("Theme Mode")} />
|
||||
<GuardState
|
||||
value={theme_mode === "dark"}
|
||||
valueProps="checked"
|
||||
value={theme_mode}
|
||||
onCatch={onError}
|
||||
onFormat={onSwitchFormat}
|
||||
onChange={(e) => onChangeData({ theme_mode: e ? "dark" : "light" })}
|
||||
onGuard={(e) =>
|
||||
patchVergeConfig({ theme_mode: e ? "dark" : "light" })
|
||||
}
|
||||
onChange={(e) => onChangeData({ theme_mode: e })}
|
||||
onGuard={(e) => patchVergeConfig({ theme_mode: e })}
|
||||
>
|
||||
<PaletteSwitch edge="end" />
|
||||
<ThemeModeSwitch />
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
|
@ -87,22 +99,6 @@ const SettingVerge = ({ onError }: Props) => {
|
|||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem>
|
||||
<ListItemText primary={t("Language")} />
|
||||
<GuardState
|
||||
value={language ?? "en"}
|
||||
onCatch={onError}
|
||||
onFormat={(e: any) => e.target.value}
|
||||
onChange={(e) => onChangeData({ language: e })}
|
||||
onGuard={(e) => patchVergeConfig({ language: e })}
|
||||
>
|
||||
<Select size="small" sx={{ width: 100 }}>
|
||||
<MenuItem value="zh">中文</MenuItem>
|
||||
<MenuItem value="en">English</MenuItem>
|
||||
</Select>
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem>
|
||||
<ListItemText primary={t("Theme Setting")} />
|
||||
<IconButton
|
||||
|
@ -129,7 +125,7 @@ const SettingVerge = ({ onError }: Props) => {
|
|||
</SettingItem>
|
||||
|
||||
<SettingItem>
|
||||
<ListItemText primary={t("Version")} />
|
||||
<ListItemText primary={t("Verge Version")} />
|
||||
<Typography sx={{ py: "6px" }}>v{version}</Typography>
|
||||
</SettingItem>
|
||||
|
||||
|
|
34
src/components/setting/theme-mode-switch.tsx
Normal file
34
src/components/setting/theme-mode-switch.tsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { useTranslation } from "react-i18next";
|
||||
import { Button, ButtonGroup } from "@mui/material";
|
||||
import { CmdType } from "../../services/types";
|
||||
|
||||
type ThemeValue = CmdType.VergeConfig["theme_mode"];
|
||||
|
||||
interface Props {
|
||||
value?: ThemeValue;
|
||||
onChange?: (value: ThemeValue) => void;
|
||||
}
|
||||
|
||||
const ThemeModeSwitch = (props: Props) => {
|
||||
const { value, onChange } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const modes = ["light", "dark", "system"] as const;
|
||||
|
||||
return (
|
||||
<ButtonGroup size="small">
|
||||
{modes.map((mode) => (
|
||||
<Button
|
||||
key={mode}
|
||||
variant={mode === value ? "contained" : "outlined"}
|
||||
onClick={() => onChange?.(mode)}
|
||||
sx={{ textTransform: "capitalize" }}
|
||||
>
|
||||
{t(`theme.${mode}`)}
|
||||
</Button>
|
||||
))}
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeModeSwitch;
|
|
@ -55,7 +55,10 @@
|
|||
"Language": "Language",
|
||||
"Open App Dir": "Open App Dir",
|
||||
"Open Logs Dir": "Open Logs Dir",
|
||||
"Version": "Version",
|
||||
"Verge Version": "Verge Version",
|
||||
"theme.light": "Light",
|
||||
"theme.dark": "Dark",
|
||||
"theme.system": "System",
|
||||
|
||||
"Save": "Save",
|
||||
"Cancel": "Cancel"
|
||||
|
|
|
@ -48,14 +48,17 @@
|
|||
"System Proxy": "系统代理",
|
||||
"Proxy Guard": "系统代理守卫",
|
||||
"Proxy Bypass": "Proxy Bypass",
|
||||
"Theme Mode": "暗夜模式",
|
||||
"Theme Mode": "主题模式",
|
||||
"Theme Blur": "背景模糊",
|
||||
"Theme Setting": "主题设置",
|
||||
"Traffic Graph": "流量图显",
|
||||
"Language": "语言设置",
|
||||
"Open App Dir": "应用目录",
|
||||
"Open Logs Dir": "日志目录",
|
||||
"Version": "版本",
|
||||
"Verge Version": "应用版本",
|
||||
"theme.light": "浅色",
|
||||
"theme.dark": "深色",
|
||||
"theme.system": "系统",
|
||||
|
||||
"Save": "保存",
|
||||
"Cancel": "取消"
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import { atom } from "recoil";
|
||||
import { ApiType } from "./types";
|
||||
|
||||
export const atomThemeMode = atom<"light" | "dark">({
|
||||
key: "atomThemeMode",
|
||||
default: "light",
|
||||
});
|
||||
|
||||
export const atomClashPort = atom<number>({
|
||||
key: "atomClashPort",
|
||||
default: 0,
|
||||
|
|
|
@ -126,7 +126,7 @@ export namespace CmdType {
|
|||
export interface VergeConfig {
|
||||
language?: string;
|
||||
clash_core?: string;
|
||||
theme_mode?: "light" | "dark";
|
||||
theme_mode?: "light" | "dark" | "system";
|
||||
theme_blur?: boolean;
|
||||
traffic_graph?: boolean;
|
||||
enable_tun_mode?: boolean;
|
||||
|
|
Loading…
Reference in New Issue
Block a user