Feature: Switch Proxy Profile from Tray Menu (#2644)

This commit is contained in:
Tunglies 2025-02-05 08:52:47 +08:00 committed by GitHub
parent bae606bc9d
commit f66fa08b2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 109 additions and 4 deletions

View File

@ -22,6 +22,7 @@ pub fn copy_clash_env() -> CmdResult {
#[tauri::command] #[tauri::command]
pub fn get_profiles() -> CmdResult<IProfiles> { pub fn get_profiles() -> CmdResult<IProfiles> {
let _ = tray::Tray::global().update_menu();
Ok(Config::profiles().data().clone()) Ok(Config::profiles().data().clone())
} }
@ -62,7 +63,6 @@ pub async fn delete_profile(index: String) -> CmdResult {
wrap_err!(CoreManager::global().update_config().await)?; wrap_err!(CoreManager::global().update_config().await)?;
handle::Handle::refresh_clash(); handle::Handle::refresh_clash();
} }
Ok(()) Ok(())
} }
@ -70,7 +70,6 @@ pub async fn delete_profile(index: String) -> CmdResult {
#[tauri::command] #[tauri::command]
pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult { pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult {
wrap_err!({ Config::profiles().draft().patch_config(profiles) })?; wrap_err!({ Config::profiles().draft().patch_config(profiles) })?;
match CoreManager::global().update_config().await { match CoreManager::global().update_config().await {
Ok(_) => { Ok(_) => {
handle::Handle::refresh_clash(); handle::Handle::refresh_clash();
@ -87,6 +86,14 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult {
} }
} }
/// 根据profile name修改profiles
#[tauri::command]
pub async fn patch_profiles_config_by_profile_name(profile_name: String) -> CmdResult {
let profile_id = Config::profiles().data().get_profile_uid(&profile_name);
let profiles = IProfiles{current: profile_id, items: None};
patch_profiles_config(profiles).await
}
/// 修改某个profile item的 /// 修改某个profile item的
#[tauri::command] #[tauri::command]
pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult { pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult {

View File

@ -71,7 +71,6 @@ impl IProfiles {
if let Some(current) = patch.current { if let Some(current) = patch.current {
let items = self.items.as_ref().unwrap(); let items = self.items.as_ref().unwrap();
let some_uid = Some(current); let some_uid = Some(current);
if items.iter().any(|e| e.uid == some_uid) { if items.iter().any(|e| e.uid == some_uid) {
self.current = some_uid; self.current = some_uid;
} }
@ -465,4 +464,50 @@ impl IProfiles {
_ => None, _ => None,
} }
} }
/// 获取current指向的profile名称
pub fn current_profile_name(&self) -> Option<String> {
match (self.current.as_ref(), self.items.as_ref()) {
(Some(current), Some(items)) => {
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
return item.name.clone();
}
None
}
_ => None,
}
}
/// 判断profile是否是current指向的
pub fn is_current_profile(&self, profile_name: &str) -> bool {
match self.current_profile_name() {
Some(current_profile_name) => current_profile_name == profile_name,
None => false,
}
}
/// 根据profile名称获取uid
pub fn get_profile_uid(&self, profile_name: &str) -> Option<String> {
match self.items.as_ref() {
Some(items) => {
for item in items.iter() {
if item.name.as_ref() == Some(&profile_name.to_string()) {
return item.uid.clone();
}
}
None
}
None => None,
}
}
/// 获取所有的profiles称
pub fn all_profile_names(&self) -> Option<Vec<String>> {
log::info!(target: "app", "{:?}", self.get_items());
match self.items.as_ref() {
Some(items) => Some(items.iter().filter_map(|e| e.name.clone()).collect()),
None => None,
}
}
} }

View File

@ -21,7 +21,7 @@ use parking_lot::RwLock;
pub use speed_rate::{SpeedRate, Traffic}; pub use speed_rate::{SpeedRate, Traffic};
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use std::sync::Arc; use std::sync::Arc;
use tauri::menu::CheckMenuItem; use tauri::menu::{CheckMenuItem, IsMenuItem};
use tauri::AppHandle; use tauri::AppHandle;
use tauri::{ use tauri::{
menu::{MenuEvent, MenuItem, PredefinedMenuItem, Submenu}, menu::{MenuEvent, MenuItem, PredefinedMenuItem, Submenu},
@ -124,6 +124,10 @@ impl Tray {
.unwrap_or("rule") .unwrap_or("rule")
.to_owned() .to_owned()
}; };
let profile_names = Config::profiles()
.data()
.all_profile_names()
.unwrap_or(Vec::new());
let tray = app_handle.tray_by_id("main").unwrap(); let tray = app_handle.tray_by_id("main").unwrap();
let _ = tray.set_menu(Some(create_tray_menu( let _ = tray.set_menu(Some(create_tray_menu(
@ -131,6 +135,7 @@ impl Tray {
Some(mode.as_str()), Some(mode.as_str()),
*system_proxy, *system_proxy,
*tun_mode, *tun_mode,
profile_names,
)?)); )?));
Ok(()) Ok(())
} }
@ -363,6 +368,7 @@ fn create_tray_menu(
mode: Option<&str>, mode: Option<&str>,
system_proxy_enabled: bool, system_proxy_enabled: bool,
tun_mode_enabled: bool, tun_mode_enabled: bool,
profile_names: Vec<String>,
) -> Result<tauri::menu::Menu<Wry>> { ) -> Result<tauri::menu::Menu<Wry>> {
let mode = mode.unwrap_or(""); let mode = mode.unwrap_or("");
let version = VERSION.get().unwrap(); let version = VERSION.get().unwrap();
@ -422,6 +428,33 @@ fn create_tray_menu(
) )
.unwrap(); .unwrap();
let profile_menu_items: Vec<CheckMenuItem<Wry>> = profile_names
.iter()
.map(|item| {
let is_current_profile = Config::profiles().data().is_current_profile(item);
CheckMenuItem::with_id(
app_handle,
&format!("profiles_{}", item),
t(&item),
true,
is_current_profile,
None::<&str>,
)
.unwrap()
}).collect();
let profile_menu_items: Vec<&dyn IsMenuItem<Wry>> = profile_menu_items
.iter()
.map(|item| item as &dyn IsMenuItem<Wry>)
.collect();
let profiles = &Submenu::with_id_and_items(
app_handle,
"profiles",
t("Profiles"),
true,
&profile_menu_items,
).unwrap();
let system_proxy = &CheckMenuItem::with_id( let system_proxy = &CheckMenuItem::with_id(
app_handle, app_handle,
"system_proxy", "system_proxy",
@ -530,6 +563,8 @@ fn create_tray_menu(
global_mode, global_mode,
direct_mode, direct_mode,
separator, separator,
profiles,
separator,
system_proxy, system_proxy,
tun_mode, tun_mode,
copy_env, copy_env,
@ -562,6 +597,10 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
"quit" => { "quit" => {
println!("quit"); println!("quit");
feat::quit(Some(0)); feat::quit(Some(0));
},
id if id.starts_with("profiles_") => {
let profile_name = &id["profiles_".len()..];
feat::toggle_proxy_profile(profile_name.into());
} }
_ => {} _ => {}
} }

View File

@ -4,6 +4,7 @@
//! - timer 定时器 //! - timer 定时器
//! - cmds 页面调用 //! - cmds 页面调用
//! //!
use crate::cmds;
use crate::config::*; use crate::config::*;
use crate::core::*; use crate::core::*;
use crate::log_err; use crate::log_err;
@ -103,6 +104,19 @@ pub fn toggle_system_proxy() {
}); });
} }
// 切换代理文件
pub fn toggle_proxy_profile(profile_name: String) {
tauri::async_runtime::spawn(async move {
match cmds::patch_profiles_config_by_profile_name(profile_name).await {
Ok(_) => {
let _ = tray::Tray::global().update_menu();
handle::Handle::refresh_verge();
},
Err(err) => log::error!(target: "app", "{err}"),
}
});
}
// 切换tun模式 // 切换tun模式
pub fn toggle_tun_mode() { pub fn toggle_tun_mode() {
let enable = Config::verge().data().enable_tun_mode; let enable = Config::verge().data().enable_tun_mode;