Skip to content

Commit

Permalink
feature: add dark mode and serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
stickmy committed Mar 18, 2024
1 parent 4a7d640 commit ff90537
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 19 deletions.
13 changes: 13 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ regex = "1.8.1"
log = "0.4"
simplelog = { version = "0.12.1", features = ["paris"] }
home = "0.5.5"
config-file = { version = "0.2.3", features = ["json"] }

[features]
default = ["http2"]
Expand Down
64 changes: 64 additions & 0 deletions src-tauri/src/app_conf/app_setting.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use crate::error::{
self,
configuration_error::{AppConfIoError, ConfigurationErrorKind},
ConfigurationError, Error,
};
use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use std::{fs, path::Path};

#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct AppSetting {
theme: String,
}

impl Default for AppSetting {
fn default() -> Self {
Self {
theme: String::from("dark"),
}
}
}

pub(super) fn read_app_setting<P: AsRef<Path>>(path: P) -> Result<AppSetting, error::Error> {
let content_raw = fs::read(path)
.context(AppConfIoError {})
.context(ConfigurationError {
scenario: "read file",
})?;

let content_str = String::from_utf8(content_raw).map_err(|err| Error::Configuration {
scenario: "read as utf8",
source: ConfigurationErrorKind::AppSettingFmt {
msg: format!("{err}"),
},
})?;

let conf: AppSetting =
serde_json::from_str(content_str.as_str()).map_err(|err| Error::Configuration {
scenario: "deserialize with serde_json",
source: ConfigurationErrorKind::AppSettingFmt {
msg: format!("{err}"),
},
})?;

Ok(conf)
}

pub(super) fn write_app_setting<P: AsRef<Path>>(
path: P,
conf: AppSetting,
) -> Result<(), error::Error> {
let str = serde_json::to_string(&conf).map_err(|err| Error::Configuration {
scenario: "serialized as string",
source: ConfigurationErrorKind::AppSettingFmt {
msg: format!("{err}"),
},
})?;

fs::write(path, str)
.context(AppConfIoError {})
.context(ConfigurationError {
scenario: "write to disk",
})
}
28 changes: 24 additions & 4 deletions src-tauri/src/app_conf/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
use snafu::ResultExt;
use std::{fs, path::PathBuf};

use crate::error::{self, configuration_error::AppConfError, ConfigurationError};
use crate::error::{self, configuration_error::AppConfIoError, ConfigurationError};

use self::app_setting::{read_app_setting, write_app_setting};

pub(crate) use self::app_setting::AppSetting;

mod app_setting;

pub fn init() -> Result<(), error::Error> {
ensure_app_dir()
Expand All @@ -15,13 +21,27 @@ pub fn app_rule_dir() -> PathBuf {
get_app_path("rule")
}

fn app_setting_file() -> PathBuf {
get_app_path("settings.json")
}

pub fn get_app_setting() -> AppSetting {
read_app_setting(app_setting_file()).unwrap_or(AppSetting::default())
}

pub fn save_app_setting(conf: AppSetting) -> () {
if let Err(err) = write_app_setting(app_setting_file(), conf) {
log::error!("save app setting error: {}", err);
}
}

fn get_app_path(name: &str) -> PathBuf {
let mut path = app_dir();
path.push(name);
path
}

fn app_dir() -> PathBuf {
pub fn app_dir() -> PathBuf {
const APP_DOT: &str = ".proxyman";

let mut app_dir = home::home_dir().unwrap();
Expand All @@ -37,7 +57,7 @@ fn ensure_app_dir() -> Result<(), error::Error> {
Ok(meta) => {
if !meta.is_dir() {
fs::create_dir(app_dir)
.context(AppConfError {})
.context(AppConfIoError {})
.context(ConfigurationError {
scenario: "Ensure app dir",
})
Expand All @@ -46,7 +66,7 @@ fn ensure_app_dir() -> Result<(), error::Error> {
}
}
Err(_) => fs::create_dir(app_dir)
.context(AppConfError {})
.context(AppConfIoError {})
.context(ConfigurationError {
scenario: "Ensure app dir",
}),
Expand Down
13 changes: 13 additions & 0 deletions src-tauri/src/commands/app_setting.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use crate::app_conf;

#[tauri::command]
pub async fn set_app_setting(setting: app_conf::AppSetting) -> () {
log::trace!("set_app_setting, {:?}", setting);

app_conf::save_app_setting(setting);
}

#[tauri::command]
pub async fn get_app_setting() -> app_conf::AppSetting {
app_conf::get_app_setting()
}
3 changes: 2 additions & 1 deletion src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod processor;
pub mod ca;
pub mod global_proxy;
pub mod global_proxy;
pub mod app_setting;
12 changes: 9 additions & 3 deletions src-tauri/src/error/configuration_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use snafu::Snafu;
#[snafu(visibility(pub(crate)), context(suffix(Error)))]
pub enum ConfigurationErrorKind {
Ssl { source: openssl::error::ErrorStack },
AppConf { source: std::io::Error },
AppConfIo { source: std::io::Error },
AppSettingFmt { msg: String },
Cert { scenario: &'static str },
Unknown {},
}
Expand All @@ -21,11 +22,16 @@ impl Serialize for ConfigurationErrorKind {
state.serialize_field("message", source.to_string().as_str())?;
state.end()
}
Self::AppConf { source } => {
let mut state = serializer.serialize_struct("AppConf", 1)?;
Self::AppConfIo { source } => {
let mut state = serializer.serialize_struct("AppConfIo", 1)?;
state.serialize_field("message", source.to_string().as_str())?;
state.end()
}
Self::AppSettingFmt { msg } => {
let mut state = serializer.serialize_struct("AppSettingFmt", 1)?;
state.serialize_field("message", msg)?;
state.end()
}
Self::Cert { scenario } => {
let mut state = serializer.serialize_struct("Certificate", 1)?;
state.serialize_field("message", scenario)?;
Expand Down
4 changes: 3 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use sys_events::handle_sys_events;
use tauri::{Manager};
use tauri::Manager;

mod app_conf;
mod ca;
Expand All @@ -19,6 +19,8 @@ mod window;
fn main() {
let app = tauri::Builder::default()
.plugin(proxy::init())
.menu(window::initial_menu())
.on_menu_event(window::register_menu_events)
.setup(|app| {
let win = app.get_window("main").unwrap();

Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/proxy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
commands::processor::remove_response_mapping,
commands::global_proxy::turn_on_global_proxy,
commands::global_proxy::turn_off_global_proxy,
commands::app_setting::set_app_setting,
commands::app_setting::get_app_setting,
])
.build()
}
36 changes: 28 additions & 8 deletions src-tauri/src/window/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
use tauri::{LogicalSize, Size, Window};
use std::process::Command;

use tauri::{CustomMenuItem, LogicalSize, Menu, Size, Submenu, Window, WindowMenuEvent};

use crate::app_conf::{self};

pub fn initial_window(win: Window) -> () {
win.set_size(Size::Logical(LogicalSize {
width: 1200.0,
height: 800.0,
}))
.unwrap();

win.center().unwrap();
}

pub fn initial_menu() -> Menu {
Menu::new()
.add_submenu(Submenu::new("proxyman", Menu::new()))
.add_submenu(Submenu::new(
"帮助",
Menu::new().add_item(CustomMenuItem::new("openlog", "打开日志文件")),
))
}

win
.set_size(Size::Logical(LogicalSize {
width: 1200.0,
height: 800.0,
}))
.unwrap();
}
pub fn register_menu_events(event: WindowMenuEvent) {
match event.menu_item_id() {
"openlog" => {
Command::new("open").arg(app_conf::app_dir().as_os_str()).output().unwrap();
},
_ => {}
};
}
15 changes: 15 additions & 0 deletions src/Commands/Commands.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ThemeType } from "@/Components/TopBar/useTheme";
import { invoke, InvokeArgs } from "@tauri-apps/api/tauri";

export const checkTlsCertInstalled = async () => {
Expand Down Expand Up @@ -49,6 +50,20 @@ export const removeResponseMapping = async (req: string) => {
});
};

export interface AppSetting {
theme: ThemeType;
}

export const setAppSetting = async (setting: AppSetting) => {
return invokeWithLogging("plugin:proxy|set_app_setting", {
setting
});
}

export const getAppSetting = async () => {
return invokeWithLogging<AppSetting>("plugin:proxy|get_app_setting");
}

const invokeWithLogging = async <T>(
cmd: string,
args?: InvokeArgs
Expand Down
17 changes: 15 additions & 2 deletions src/Components/TopBar/useTheme.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { getAppSetting, setAppSetting } from "@/Commands/Commands";
import { create } from "zustand";

export type ThemeType = "light" | "dark";

const getSystemPreferColorScheme = (): ThemeType => {
const getSystemPreferColorScheme = (delayUpdator: (theme: ThemeType) => void): ThemeType => {
// 初始化返回系统偏好, 随后采用用户设置中的主题配置
getAppSetting().then(setting => {
delayUpdator(setting.theme);
});

return window.matchMedia("screen and (prefers-color-scheme: light)").matches
? "light"
: "dark";
Expand All @@ -12,9 +18,16 @@ export const useTheme = create<{
theme: ThemeType;
setTheme: (theme: ThemeType) => void;
}>((set) => ({
theme: getSystemPreferColorScheme(),
theme: getSystemPreferColorScheme(theme => {
document.body.setAttribute("arco-theme", theme);
set({ theme });
}),
setTheme: (theme: ThemeType) => {
document.body.setAttribute("arco-theme", theme);
set({ theme });

getAppSetting().then(setting => {
setAppSetting({ ...setting, theme });
});
},
}));

0 comments on commit ff90537

Please sign in to comment.