refactor: invock mihomo api by use tauri-plugin-mihomo (#4926)
* feat: add tauri-plugin-mihomo * refactor: invock mihomo api by use tauri-plugin-mihomo * chore: todo * chore: update * chore: update * chore: update * chore: update * fix: incorrect delay status and update pretty config * chore: update * chore: remove cache * chore: update * chore: update * fix: app freezed when change group proxy * chore: update * chore: update * chore: add rustfmt.toml to tauri-plugin-mihomo * chore: happy clippy * refactor: connect mihomo websocket * chore: update * chore: update * fix: parse bigint to number * chore: update * Revert "fix: parse bigint to number" This reverts commit 74c006522e23aa52cf8979a8fb47d2b1ae0bb043. * chore: use number instead of bigint * chore: cleanup * fix: rule data not refresh when switch profile * chore: update * chore: cleanup * chore: update * fix: traffic graph data display * feat: add ipc connection pool * chore: update * chore: clippy * fix: incorrect delay status * fix: typo * fix: empty proxies tray menu * chore: clippy * chore: import tauri-plugin-mihomo by using git repo * chore: cleanup * fix: mihomo api * fix: incorrect delay status * chore: update tauri-plugin-mihomo dep chore: update
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
use crate::AsyncHandler;
|
||||
use crate::core::logger::Logger;
|
||||
use crate::{
|
||||
config::*,
|
||||
core::{
|
||||
handle,
|
||||
service::{self, SERVICE_MANAGER, ServiceStatus},
|
||||
},
|
||||
ipc::IpcManager,
|
||||
logging, logging_error, singleton_lazy,
|
||||
utils::{
|
||||
dirs,
|
||||
@@ -25,6 +25,10 @@ use std::{
|
||||
};
|
||||
use tauri_plugin_shell::{ShellExt, process::CommandChild};
|
||||
|
||||
// TODO:
|
||||
// - 重构,提升模式切换速度
|
||||
// - 内核启动添加启动 IPC 启动参数, `-ext-ctl-unix` / `-ext-ctl-pipe`, 运行时配置需要删除相关配置项
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CoreManager {
|
||||
running: Arc<Mutex<RunningMode>>,
|
||||
@@ -250,11 +254,7 @@ impl CoreManager {
|
||||
let clash_core = Config::verge().await.latest_ref().get_valid_clash_core();
|
||||
logging!(info, Type::Config, true, "使用内核: {}", clash_core);
|
||||
|
||||
let app_handle = handle::Handle::global().app_handle().ok_or_else(|| {
|
||||
let msg = "Failed to get app handle";
|
||||
logging!(error, Type::Core, true, "{}", msg);
|
||||
anyhow::anyhow!(msg)
|
||||
})?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let app_dir = dirs::app_home_dir()?;
|
||||
let app_dir_str = dirs::path_to_str(&app_dir)?;
|
||||
logging!(info, Type::Config, true, "验证目录: {}", app_dir_str);
|
||||
@@ -414,7 +414,11 @@ impl CoreManager {
|
||||
logging_error!(Type::Core, true, "{}", msg);
|
||||
msg
|
||||
});
|
||||
match IpcManager::global().put_configs_force(run_path_str?).await {
|
||||
match handle::Handle::mihomo()
|
||||
.await
|
||||
.reload_config(true, run_path_str?)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
Config::runtime().await.apply();
|
||||
logging!(info, Type::Core, true, "Configuration updated successfully");
|
||||
@@ -733,9 +737,7 @@ impl CoreManager {
|
||||
logging!(info, Type::Core, true, "Running core by sidecar");
|
||||
|
||||
let config_file = &Config::generate_file(ConfigType::Run).await?;
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or(anyhow::anyhow!("failed to get app handle"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let clash_core = Config::verge().await.latest_ref().get_valid_clash_core();
|
||||
let config_dir = dirs::app_home_dir()?;
|
||||
|
||||
@@ -774,12 +776,16 @@ impl CoreManager {
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
tauri_plugin_shell::process::CommandEvent::Stdout(line) => {
|
||||
if let Err(e) = writeln!(log_file, "{}", String::from_utf8_lossy(&line)) {
|
||||
let line = String::from_utf8_lossy(&line);
|
||||
Logger::global().append_log(line.to_string());
|
||||
if let Err(e) = writeln!(log_file, "{}", line) {
|
||||
eprintln!("[Sidecar] write stdout failed: {e}");
|
||||
}
|
||||
}
|
||||
tauri_plugin_shell::process::CommandEvent::Stderr(line) => {
|
||||
let _ = writeln!(log_file, "[stderr] {}", String::from_utf8_lossy(&line));
|
||||
let line = String::from_utf8_lossy(&line);
|
||||
Logger::global().append_log(line.to_string());
|
||||
let _ = writeln!(log_file, "[stderr] {}", line);
|
||||
}
|
||||
tauri_plugin_shell::process::CommandEvent::Terminated(term) => {
|
||||
let _ = writeln!(log_file, "[terminated] {:?}", term);
|
||||
@@ -900,6 +906,7 @@ impl CoreManager {
|
||||
|
||||
/// 停止核心运行
|
||||
pub async fn stop_core(&self) -> Result<()> {
|
||||
Logger::global().clear_logs();
|
||||
match self.get_running_mode() {
|
||||
RunningMode::Service => self.stop_core_by_service().await,
|
||||
RunningMode::Sidecar => self.stop_core_by_sidecar(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::singleton;
|
||||
use crate::{APP_HANDLE, singleton};
|
||||
use parking_lot::RwLock;
|
||||
use std::{
|
||||
sync::{
|
||||
@@ -10,6 +10,8 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tauri::{AppHandle, Emitter, Manager, WebviewWindow};
|
||||
use tauri_plugin_mihomo::{Mihomo, MihomoExt};
|
||||
use tokio::sync::{RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use crate::{logging, utils::logging::Type};
|
||||
|
||||
@@ -107,7 +109,7 @@ impl NotificationSystem {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(window) = handle.get_window() {
|
||||
if let Some(window) = Handle::get_window() {
|
||||
*system.last_emit_time.write() = Instant::now();
|
||||
|
||||
let (event_name_str, payload_result) = match event {
|
||||
@@ -249,7 +251,6 @@ impl NotificationSystem {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Handle {
|
||||
pub app_handle: Arc<RwLock<Option<AppHandle>>>,
|
||||
pub is_exiting: Arc<RwLock<bool>>,
|
||||
startup_errors: Arc<RwLock<Vec<ErrorMessage>>>,
|
||||
startup_completed: Arc<RwLock<bool>>,
|
||||
@@ -259,7 +260,6 @@ pub struct Handle {
|
||||
impl Default for Handle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
app_handle: Arc::new(RwLock::new(None)),
|
||||
is_exiting: Arc::new(RwLock::new(false)),
|
||||
startup_errors: Arc::new(RwLock::new(Vec::new())),
|
||||
startup_completed: Arc::new(RwLock::new(false)),
|
||||
@@ -276,18 +276,13 @@ impl Handle {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn init(&self, app_handle: AppHandle) {
|
||||
pub fn init(&self) {
|
||||
// 如果正在退出,不要重新初始化
|
||||
if self.is_exiting() {
|
||||
log::debug!("Handle::init called while exiting, skipping initialization");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
let mut handle = self.app_handle.write();
|
||||
*handle = Some(app_handle);
|
||||
}
|
||||
|
||||
let mut system_opt = self.notification_system.write();
|
||||
if let Some(system) = system_opt.as_mut() {
|
||||
// 只在未运行时启动
|
||||
@@ -300,12 +295,22 @@ impl Handle {
|
||||
}
|
||||
|
||||
/// 获取 AppHandle
|
||||
pub fn app_handle(&self) -> Option<AppHandle> {
|
||||
self.app_handle.read().clone()
|
||||
#[allow(clippy::expect_used)]
|
||||
pub fn app_handle() -> &'static AppHandle {
|
||||
APP_HANDLE.get().expect("failed to get global app handle")
|
||||
}
|
||||
|
||||
pub fn get_window(&self) -> Option<WebviewWindow> {
|
||||
let app_handle = self.app_handle()?;
|
||||
pub async fn mihomo() -> RwLockReadGuard<'static, Mihomo> {
|
||||
Self::app_handle().mihomo().read().await
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub async fn mihomo_mut() -> RwLockWriteGuard<'static, Mihomo> {
|
||||
Self::app_handle().mihomo().write().await
|
||||
}
|
||||
|
||||
pub fn get_window() -> Option<WebviewWindow> {
|
||||
let app_handle = Self::app_handle();
|
||||
let window: Option<WebviewWindow> = app_handle.get_webview_window("main");
|
||||
if window.is_none() {
|
||||
log::debug!(target:"app", "main window not found");
|
||||
@@ -520,14 +525,10 @@ impl Handle {
|
||||
#[cfg(target_os = "macos")]
|
||||
impl Handle {
|
||||
pub fn set_activation_policy(&self, policy: tauri::ActivationPolicy) -> Result<(), String> {
|
||||
let app_handle = self.app_handle();
|
||||
if let Some(app_handle) = app_handle.as_ref() {
|
||||
app_handle
|
||||
.set_activation_policy(policy)
|
||||
.map_err(|e| e.to_string())
|
||||
} else {
|
||||
Err("AppHandle not initialized".to_string())
|
||||
}
|
||||
let app_handle = Self::app_handle();
|
||||
app_handle
|
||||
.set_activation_policy(policy)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub fn set_activation_policy_regular(&self) {
|
||||
|
||||
@@ -200,9 +200,7 @@ impl Hotkey {
|
||||
hotkey: &str,
|
||||
function: HotkeyFunction,
|
||||
) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let manager = app_handle.global_shortcut();
|
||||
|
||||
logging!(
|
||||
@@ -375,9 +373,7 @@ impl Hotkey {
|
||||
}
|
||||
|
||||
pub fn reset(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let manager = app_handle.global_shortcut();
|
||||
manager.unregister_all()?;
|
||||
Ok(())
|
||||
@@ -390,9 +386,7 @@ impl Hotkey {
|
||||
}
|
||||
|
||||
pub fn unregister(&self, hotkey: &str) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let manager = app_handle.global_shortcut();
|
||||
manager.unregister(hotkey)?;
|
||||
logging!(debug, Type::Hotkey, "Unregister hotkey {}", hotkey);
|
||||
@@ -468,17 +462,7 @@ impl Hotkey {
|
||||
|
||||
impl Drop for Hotkey {
|
||||
fn drop(&mut self) {
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Hotkey,
|
||||
"Failed to get app handle during hotkey cleanup"
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
if let Err(e) = app_handle.global_shortcut().unregister_all() {
|
||||
logging!(
|
||||
error,
|
||||
|
||||
37
src-tauri/src/core/logger.rs
Normal file
37
src-tauri/src/core/logger.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use std::{collections::VecDeque, sync::Arc};
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::{RwLock, RwLockReadGuard};
|
||||
|
||||
const LOGS_QUEUE_LEN: usize = 100;
|
||||
|
||||
pub struct Logger {
|
||||
logs: Arc<RwLock<VecDeque<String>>>,
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
pub fn global() -> &'static Logger {
|
||||
static LOGGER: OnceCell<Logger> = OnceCell::new();
|
||||
|
||||
LOGGER.get_or_init(|| Logger {
|
||||
logs: Arc::new(RwLock::new(VecDeque::with_capacity(LOGS_QUEUE_LEN + 10))),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_logs(&self) -> RwLockReadGuard<'_, VecDeque<String>> {
|
||||
self.logs.read()
|
||||
}
|
||||
|
||||
pub fn append_log(&self, text: String) {
|
||||
let mut logs = self.logs.write();
|
||||
if logs.len() > LOGS_QUEUE_LEN {
|
||||
logs.pop_front();
|
||||
}
|
||||
logs.push_back(text);
|
||||
}
|
||||
|
||||
pub fn clear_logs(&self) {
|
||||
let mut logs = self.logs.write();
|
||||
logs.clear();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ mod core;
|
||||
pub mod event_driven_proxy;
|
||||
pub mod handle;
|
||||
pub mod hotkey;
|
||||
pub mod logger;
|
||||
pub mod service;
|
||||
pub mod service_ipc;
|
||||
pub mod sysopt;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
cache::{CacheService, SHORT_TERM_TTL},
|
||||
config::Config,
|
||||
core::service_ipc::{IpcCommand, send_ipc_request},
|
||||
logging, logging_error,
|
||||
@@ -333,28 +332,24 @@ pub async fn force_reinstall_service() -> Result<()> {
|
||||
|
||||
/// 检查服务版本 - 使用IPC通信
|
||||
async fn check_service_version() -> Result<String> {
|
||||
let cache = CacheService::global();
|
||||
let key = CacheService::make_key("service", "version");
|
||||
let version_arc = cache
|
||||
.get_or_fetch(key, SHORT_TERM_TTL, || async {
|
||||
logging!(info, Type::Service, true, "开始检查服务版本 (IPC)");
|
||||
let payload = serde_json::json!({});
|
||||
let response = send_ipc_request(IpcCommand::GetVersion, payload).await?;
|
||||
let version_arc: Result<String> = {
|
||||
logging!(info, Type::Service, true, "开始检查服务版本 (IPC)");
|
||||
let payload = serde_json::json!({});
|
||||
let response = send_ipc_request(IpcCommand::GetVersion, payload).await?;
|
||||
|
||||
let data = response
|
||||
.data
|
||||
.ok_or_else(|| anyhow::anyhow!("服务版本响应中没有数据"))?;
|
||||
|
||||
if let Some(nested_data) = data.get("data")
|
||||
&& let Some(version) = nested_data.get("version").and_then(|v| v.as_str())
|
||||
{
|
||||
// logging!(info, Type::Service, true, "获取到服务版本: {}", version);
|
||||
return Ok(version.to_string());
|
||||
}
|
||||
let data = response
|
||||
.data
|
||||
.ok_or_else(|| anyhow::anyhow!("服务版本响应中没有数据"))?;
|
||||
|
||||
if let Some(nested_data) = data.get("data")
|
||||
&& let Some(version) = nested_data.get("version").and_then(|v| v.as_str())
|
||||
{
|
||||
// logging!(info, Type::Service, true, "获取到服务版本: {}", version);
|
||||
Ok(version.to_string())
|
||||
} else {
|
||||
Ok("unknown".to_string())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
};
|
||||
|
||||
match version_arc.as_ref() {
|
||||
Ok(v) => Ok(v.clone()),
|
||||
|
||||
@@ -262,10 +262,7 @@ impl Sysopt {
|
||||
|
||||
/// 尝试使用原来的自启动方法
|
||||
fn try_original_autostart_method(&self, is_enable: bool) {
|
||||
let Some(app_handle) = Handle::global().app_handle() else {
|
||||
log::error!(target: "app", "App handle not available for autostart");
|
||||
return;
|
||||
};
|
||||
let app_handle = Handle::app_handle();
|
||||
let autostart_manager = app_handle.autolaunch();
|
||||
|
||||
if is_enable {
|
||||
@@ -292,9 +289,7 @@ impl Sysopt {
|
||||
}
|
||||
|
||||
// 回退到原来的方法
|
||||
let app_handle = Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("App handle not available"))?;
|
||||
let app_handle = Handle::app_handle();
|
||||
let autostart_manager = app_handle.autolaunch();
|
||||
|
||||
match autostart_manager.is_enabled() {
|
||||
|
||||
@@ -139,6 +139,27 @@ impl Timer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 每 3 秒更新系统托盘菜单,总共执行 3 次
|
||||
pub fn add_update_tray_menu_task(&self) -> Result<()> {
|
||||
let tid = self.timer_count.fetch_add(1, Ordering::SeqCst);
|
||||
let delay_timer = self.delay_timer.write();
|
||||
let task = TaskBuilder::default()
|
||||
.set_task_id(tid)
|
||||
.set_maximum_parallel_runnable_num(1)
|
||||
.set_frequency_count_down_by_seconds(3, 3)
|
||||
.spawn_async_routine(|| async move {
|
||||
logging!(info, Type::Timer, "Updating tray menu");
|
||||
crate::core::tray::Tray::global()
|
||||
.update_tray_display()
|
||||
.await
|
||||
})
|
||||
.context("failed to create update tray menu timer task")?;
|
||||
delay_timer
|
||||
.add_task(task)
|
||||
.context("failed to add update tray menu timer task")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Refresh timer tasks with better error handling
|
||||
pub async fn refresh(&self) -> Result<()> {
|
||||
// Generate diff outside of lock to minimize lock contention
|
||||
|
||||
@@ -3,16 +3,13 @@ use tauri::Emitter;
|
||||
use tauri::tray::TrayIconBuilder;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod speed_rate;
|
||||
use crate::ipc::Rate;
|
||||
use crate::module::lightweight;
|
||||
use crate::process::AsyncHandler;
|
||||
use crate::utils::window_manager::WindowManager;
|
||||
use crate::{
|
||||
Type, cmd,
|
||||
config::Config,
|
||||
feat,
|
||||
ipc::IpcManager,
|
||||
logging,
|
||||
feat, logging,
|
||||
module::lightweight::is_in_lightweight_mode,
|
||||
singleton_lazy,
|
||||
utils::{dirs::find_target_icons, i18n::t},
|
||||
@@ -34,6 +31,8 @@ use tauri::{
|
||||
tray::{MouseButton, MouseButtonState, TrayIconEvent},
|
||||
};
|
||||
|
||||
// TODO: 是否需要将可变菜单抽离存储起来,后续直接更新对应菜单实例,无需重新创建菜单(待考虑)
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TrayState {}
|
||||
|
||||
@@ -54,7 +53,7 @@ fn should_handle_tray_click() -> bool {
|
||||
*last_click = now;
|
||||
true
|
||||
} else {
|
||||
log::debug!(target: "app", "托盘点击被防抖机制忽略,距离上次点击 {:?}ms",
|
||||
log::debug!(target: "app", "托盘点击被防抖机制忽略,距离上次点击 {:?}ms",
|
||||
now.duration_since(*last_click).as_millis());
|
||||
false
|
||||
}
|
||||
@@ -189,28 +188,25 @@ singleton_lazy!(Tray, TRAY, Tray::default);
|
||||
|
||||
impl Tray {
|
||||
pub async fn init(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray initialization"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
match self.create_tray_from_handle(&app_handle).await {
|
||||
match self.create_tray_from_handle(app_handle).await {
|
||||
Ok(_) => {
|
||||
log::info!(target: "app", "System tray created successfully");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!(target: "app", "System tray creation failed: {}, Application will continue running without tray icon", e);
|
||||
// Don't return error, let application continue running without tray
|
||||
Ok(())
|
||||
log::warn!(target: "app", "System tray creation failed: {}, Application will continue running without tray icon", e);
|
||||
}
|
||||
}
|
||||
// TODO: 初始化时,暂时使用此方法更新系统托盘菜单,有效避免代理节点菜单空白
|
||||
crate::core::timer::Timer::global().add_update_tray_menu_task()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 更新托盘点击行为
|
||||
pub async fn update_click_behavior(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray update"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let tray_event = { Config::verge().await.latest_ref().tray_event.clone() };
|
||||
let tray_event: String = tray_event.unwrap_or("main_window".into());
|
||||
let tray = app_handle
|
||||
@@ -250,18 +246,12 @@ impl Tray {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::warn!(target: "app", "更新托盘菜单失败: app_handle不存在");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
// 设置更新状态
|
||||
self.menu_updating.store(true, Ordering::Release);
|
||||
|
||||
let result = self.update_menu_internal(&app_handle).await;
|
||||
let result = self.update_menu_internal(app_handle).await;
|
||||
|
||||
{
|
||||
let mut last_update = self.last_menu_update.lock();
|
||||
@@ -318,14 +308,8 @@ impl Tray {
|
||||
|
||||
/// 更新托盘图标
|
||||
#[cfg(target_os = "macos")]
|
||||
pub async fn update_icon(&self, _rate: Option<Rate>) -> Result<()> {
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::warn!(target: "app", "更新托盘图标失败: app_handle不存在");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
pub async fn update_icon(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
let tray = match app_handle.tray_by_id("main") {
|
||||
Some(tray) => tray,
|
||||
@@ -355,14 +339,8 @@ impl Tray {
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub async fn update_icon(&self, _rate: Option<Rate>) -> Result<()> {
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::warn!(target: "app", "更新托盘图标失败: app_handle不存在");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
pub async fn update_icon(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
let tray = match app_handle.tray_by_id("main") {
|
||||
Some(tray) => tray,
|
||||
@@ -389,9 +367,7 @@ impl Tray {
|
||||
|
||||
/// 更新托盘显示状态的函数
|
||||
pub async fn update_tray_display(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray update"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let _tray = app_handle
|
||||
.tray_by_id("main")
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?;
|
||||
@@ -404,13 +380,7 @@ impl Tray {
|
||||
|
||||
/// 更新托盘提示
|
||||
pub async fn update_tooltip(&self) -> Result<()> {
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::warn!(target: "app", "更新托盘提示失败: app_handle不存在");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
let verge = Config::verge().await.latest_ref().clone();
|
||||
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
|
||||
@@ -464,7 +434,7 @@ impl Tray {
|
||||
// self.update_menu().await?;
|
||||
// 更新轻量模式显示状态
|
||||
self.update_tray_display().await?;
|
||||
self.update_icon(None).await?;
|
||||
self.update_icon().await?;
|
||||
self.update_tooltip().await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -550,7 +520,7 @@ impl Tray {
|
||||
// 确保所有状态更新完成
|
||||
self.update_tray_display().await?;
|
||||
// self.update_menu().await?;
|
||||
self.update_icon(None).await?;
|
||||
self.update_icon().await?;
|
||||
self.update_tooltip().await?;
|
||||
|
||||
Ok(())
|
||||
@@ -578,14 +548,7 @@ async fn create_tray_menu(
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
let proxy_nodes_data = cmd::get_proxies().await.unwrap_or_else(|e| {
|
||||
logging!(
|
||||
error,
|
||||
Type::Cmd,
|
||||
"Failed to fetch proxies for tray menu: {e}"
|
||||
);
|
||||
serde_json::Value::Object(serde_json::Map::new())
|
||||
});
|
||||
let proxy_nodes_data = handle::Handle::mihomo().await.get_proxies().await;
|
||||
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
|
||||
@@ -638,46 +601,43 @@ async fn create_tray_menu(
|
||||
let mut submenus = Vec::new();
|
||||
let mut group_name_submenus_hash = HashMap::new();
|
||||
|
||||
if let Some(proxies) = proxy_nodes_data.get("proxies").and_then(|v| v.as_object()) {
|
||||
for (group_name, group_data) in proxies.iter() {
|
||||
// TODO: 应用启动时,内核还未启动完全,无法获取代理节点信息
|
||||
if let Ok(proxy_nodes_data) = proxy_nodes_data {
|
||||
for (group_name, group_data) in proxy_nodes_data.proxies.iter() {
|
||||
// Filter groups based on mode
|
||||
let should_show = match mode {
|
||||
"global" => group_name == "GLOBAL",
|
||||
_ => group_name != "GLOBAL",
|
||||
} &&
|
||||
// Check if the group is hidden
|
||||
!group_data.get("hidden").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
!group_data.hidden.unwrap_or_default();
|
||||
|
||||
if !should_show {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(all_proxies) = group_data.get("all").and_then(|v| v.as_array()) else {
|
||||
let Some(all_proxies) = group_data.all.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let now_proxy = group_data.get("now").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let now_proxy = group_data.now.as_deref().unwrap_or_default();
|
||||
|
||||
// Create proxy items
|
||||
let group_items: Vec<CheckMenuItem<Wry>> = all_proxies
|
||||
.iter()
|
||||
.filter_map(|proxy_name| proxy_name.as_str())
|
||||
.filter_map(|proxy_str| {
|
||||
let is_selected = proxy_str == now_proxy;
|
||||
let is_selected = *proxy_str == now_proxy;
|
||||
let item_id = format!("proxy_{}_{}", group_name, proxy_str);
|
||||
|
||||
// Get delay for display
|
||||
let delay_text = proxies
|
||||
let delay_text = proxy_nodes_data
|
||||
.proxies
|
||||
.get(proxy_str)
|
||||
.and_then(|p| p.get("history"))
|
||||
.and_then(|h| h.as_array())
|
||||
.and_then(|h| h.last())
|
||||
.and_then(|r| r.get("delay"))
|
||||
.and_then(|d| d.as_i64())
|
||||
.map(|delay| match delay {
|
||||
-1 => "-ms".to_string(),
|
||||
.and_then(|h| h.history.last())
|
||||
.map(|h| match h.delay {
|
||||
0 => "-ms".to_string(),
|
||||
delay if delay >= 10000 => "-ms".to_string(),
|
||||
_ => format!("{}ms", delay),
|
||||
_ => format!("{}ms", h.delay),
|
||||
})
|
||||
.unwrap_or_else(|| "-ms".to_string());
|
||||
|
||||
@@ -1066,29 +1026,30 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
|
||||
let group_name = parts[1];
|
||||
let proxy_name = parts[2];
|
||||
|
||||
match cmd::proxy::update_proxy_and_sync(
|
||||
group_name.to_string(),
|
||||
proxy_name.to_string(),
|
||||
)
|
||||
.await
|
||||
match handle::Handle::mihomo()
|
||||
.await
|
||||
.select_node_for_group(group_name, proxy_name)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
log::info!(target: "app", "切换代理成功: {} -> {}", group_name, proxy_name);
|
||||
let _ = handle::Handle::app_handle()
|
||||
.emit("verge://refresh-proxy-config", ());
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(target: "app", "切换代理失败: {} -> {}, 错误: {:?}", group_name, proxy_name, e);
|
||||
|
||||
// Fallback to IPC update
|
||||
if (IpcManager::global()
|
||||
.update_proxy(group_name, proxy_name)
|
||||
if (handle::Handle::mihomo()
|
||||
.await
|
||||
.select_node_for_group(group_name, proxy_name)
|
||||
.await)
|
||||
.is_ok()
|
||||
{
|
||||
log::info!(target: "app", "代理切换回退成功: {} -> {}", group_name, proxy_name);
|
||||
|
||||
if let Some(app_handle) = handle::Handle::global().app_handle() {
|
||||
let _ = app_handle.emit("verge://force-refresh-proxies", ());
|
||||
}
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let _ = app_handle.emit("verge://force-refresh-proxies", ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user