Compare commits

...

2 Commits

6 changed files with 57 additions and 81 deletions

View File

@@ -211,28 +211,18 @@ impl Hotkey {
let is_quit = matches!(function, HotkeyFunction::Quit); let is_quit = matches!(function, HotkeyFunction::Quit);
manager.on_shortcut(hotkey, move |_app_handle, hotkey_event, event| { manager.on_shortcut(hotkey, move |_app_handle, hotkey_event, event| {
let hotkey_event_owned = *hotkey_event; if event.state == ShortcutState::Pressed {
let event_owned = event; logging!(debug, Type::Hotkey, "Hotkey pressed: {:?}", hotkey_event);
let function_owned = function; let hotkey = hotkey_event.key;
let is_quit_owned = is_quit; if hotkey == Code::KeyQ && is_quit {
if let Some(window) = handle::Handle::get_window()
AsyncHandler::spawn(move || async move { && window.is_focused().unwrap_or(false)
if event_owned.state == ShortcutState::Pressed { {
logging!( logging!(debug, Type::Hotkey, "Executing quit function");
debug, Self::execute_function(function);
Type::Hotkey, }
"Hotkey pressed: {:?}", } else {
hotkey_event_owned AsyncHandler::spawn(move || async move {
);
if hotkey_event_owned.key == Code::KeyQ && is_quit_owned {
if let Some(window) = handle::Handle::get_window()
&& window.is_focused().unwrap_or(false)
{
logging!(debug, Type::Hotkey, "Executing quit function");
Self::execute_function(function_owned);
}
} else {
logging!(debug, Type::Hotkey, "Executing function directly"); logging!(debug, Type::Hotkey, "Executing function directly");
let is_enable_global_hotkey = Config::verge() let is_enable_global_hotkey = Config::verge()
@@ -242,19 +232,19 @@ impl Hotkey {
.unwrap_or(true); .unwrap_or(true);
if is_enable_global_hotkey { if is_enable_global_hotkey {
Self::execute_function(function_owned); Self::execute_function(function);
} else { } else {
use crate::utils::window_manager::WindowManager; use crate::utils::window_manager::WindowManager;
let is_visible = WindowManager::is_main_window_visible(); let is_visible = WindowManager::is_main_window_visible();
let is_focused = WindowManager::is_main_window_focused(); let is_focused = WindowManager::is_main_window_focused();
if is_focused && is_visible { if is_focused && is_visible {
Self::execute_function(function_owned); Self::execute_function(function);
} }
} }
} });
} }
}); }
})?; })?;
logging!( logging!(

View File

@@ -12,7 +12,7 @@ use std::{
mpsc, mpsc,
}, },
thread, thread,
time::Instant, time::{Duration, Instant},
}; };
use tauri::{Emitter, WebviewWindow}; use tauri::{Emitter, WebviewWindow};
@@ -92,12 +92,15 @@ impl NotificationSystem {
} }
fn worker_loop(rx: mpsc::Receiver<FrontendEvent>) { fn worker_loop(rx: mpsc::Receiver<FrontendEvent>) {
let handle = Handle::global(); loop {
while !handle.is_exiting() { let handle = Handle::global();
match rx.try_recv() { if handle.is_exiting() {
break;
}
match rx.recv_timeout(Duration::from_millis(1_000)) {
Ok(event) => Self::process_event(handle, event), Ok(event) => Self::process_event(handle, event),
Err(mpsc::TryRecvError::Disconnected) => break, Err(mpsc::RecvTimeoutError::Timeout) => (),
Err(mpsc::TryRecvError::Empty) => break, Err(mpsc::RecvTimeoutError::Disconnected) => break,
} }
} }
} }

View File

@@ -24,8 +24,6 @@ use futures::future::join_all;
use parking_lot::Mutex; use parking_lot::Mutex;
use smartstring::alias::String; use smartstring::alias::String;
use std::collections::HashMap; use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::{ use std::{
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
@@ -550,49 +548,34 @@ impl Tray {
let tray = builder.build(app_handle)?; let tray = builder.build(app_handle)?;
tray.on_tray_icon_event(|_app_handle, event| { tray.on_tray_icon_event(|_app_handle, event| {
// 忽略移动、进入和离开等无需处理的事件,避免不必要的刷新 if let TrayIconEvent::Click {
match event { button: MouseButton::Left,
TrayIconEvent::Move { .. } button_state: MouseButtonState::Down,
| TrayIconEvent::Enter { .. } ..
| TrayIconEvent::Leave { .. } => { } = event
return; {
} AsyncHandler::spawn(|| async move {
_ => {} let tray_event = { Config::verge().await.latest_arc().tray_event.clone() };
} let tray_event: String = tray_event.unwrap_or_else(|| "main_window".into());
logging!(debug, Type::Tray, "tray event: {tray_event:?}");
AsyncHandler::spawn(|| async move {
let tray_event = { Config::verge().await.latest_arc().tray_event.clone() };
let tray_event: String = tray_event.unwrap_or_else(|| "main_window".into());
logging!(debug, Type::Tray, "tray event: {tray_event:?}");
if let TrayIconEvent::Click {
button: MouseButton::Left,
button_state: MouseButtonState::Down,
..
} = event
{
// 添加防抖检查,防止快速连击 // 添加防抖检查,防止快速连击
if !should_handle_tray_click() { if !should_handle_tray_click() {
return; return;
} }
let fut: Pin<Box<dyn Future<Output = ()> + Send>> = match tray_event.as_str() { match tray_event.as_str() {
"system_proxy" => Box::pin(async move { "system_proxy" => feat::toggle_system_proxy().await,
feat::toggle_system_proxy().await; "tun_mode" => feat::toggle_tun_mode(None).await,
}), "main_window" => {
"tun_mode" => Box::pin(async move {
feat::toggle_tun_mode(None).await;
}),
"main_window" => Box::pin(async move {
if !lightweight::exit_lightweight_mode().await { if !lightweight::exit_lightweight_mode().await {
WindowManager::show_main_window().await; WindowManager::show_main_window().await;
}; };
}), }
_ => Box::pin(async move {}), _ => {}
}; };
fut.await; });
} }
});
}); });
tray.on_menu_event(on_menu_event); tray.on_menu_event(on_menu_event);
Ok(()) Ok(())

View File

@@ -1,3 +1,5 @@
use std::time::Duration;
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use percent_encoding::percent_decode_str; use percent_encoding::percent_decode_str;
use smartstring::alias::String; use smartstring::alias::String;
@@ -73,24 +75,23 @@ pub(super) async fn resolve_scheme(param: &str) -> Result<()> {
"failed to parse profile from url: {:?}", "failed to parse profile from url: {:?}",
e e
); );
// TODO 通知系统疑似损坏,前端无法显示通知事件
handle::Handle::notice_message("import_sub_url::error", e.to_string()); handle::Handle::notice_message("import_sub_url::error", e.to_string());
return Ok(()); return Ok(());
} }
}; };
let uid = item.uid.clone().unwrap_or_default(); let uid = item.uid.clone().unwrap_or_default();
// TODO 通过 deep link 导入后需要正确调用前端刷新订阅页面,以及通知结果
match profiles::profiles_append_item_safe(&mut item).await { match profiles::profiles_append_item_safe(&mut item).await {
Ok(_) => { Ok(_) => {
Config::profiles().await.apply(); Config::profiles().await.apply();
let _ = Config::profiles().await.data_arc().save_file().await; let _ = Config::profiles().await.data_arc().save_file().await;
// TODO 通知系统疑似损坏,前端无法显示通知事件
handle::Handle::notice_message( handle::Handle::notice_message(
"import_sub_url::ok", "import_sub_url::ok",
item.uid.clone().unwrap_or_default(), "", // 空 msg 传入,我们不希望导致 后端-前端-后端 死循环,这里只做提醒。
); );
// TODO fuck me this shit is fucking broken as fucked handle::Handle::refresh_verge();
handle::Handle::notify_profile_changed(uid.clone());
tokio::time::sleep(Duration::from_millis(100)).await;
handle::Handle::notify_profile_changed(uid); handle::Handle::notify_profile_changed(uid);
} }
Err(e) => { Err(e) => {
@@ -101,14 +102,10 @@ pub(super) async fn resolve_scheme(param: &str) -> Result<()> {
e e
); );
Config::profiles().await.discard(); Config::profiles().await.discard();
// TODO 通知系统疑似损坏,前端无法显示通知事件
handle::Handle::notice_message("import_sub_url::error", e.to_string()); handle::Handle::notice_message("import_sub_url::error", e.to_string());
return Ok(()); return Ok(());
} }
} }
handle::Handle::refresh_verge();
handle::Handle::refresh_clash();
Ok(()) Ok(())
} }

View File

@@ -11,7 +11,10 @@ export const handleNoticeMessage = (
) => { ) => {
const handlers: Record<string, () => void> = { const handlers: Record<string, () => void> = {
"import_sub_url::ok": () => { "import_sub_url::ok": () => {
navigate("/profile", { state: { current: msg } }); // 空 msg 传入,我们不希望导致 后端-前端-后端 死循环,这里只做提醒。
// 未来细分事件通知时,可以考虑传入订阅 ID 或其他标识符
// navigate("/profile", { state: { current: msg } });
navigate("/profile");
showNotice("success", t("Import Subscription Successful")); showNotice("success", t("Import Subscription Successful"));
}, },
"import_sub_url::error": () => { "import_sub_url::error": () => {

View File

@@ -13,15 +13,15 @@ import {
sortableKeyboardCoordinates, sortableKeyboardCoordinates,
} from "@dnd-kit/sortable"; } from "@dnd-kit/sortable";
import { import {
CheckBoxOutlineBlankRounded,
CheckBoxRounded,
ClearRounded, ClearRounded,
ContentPasteRounded, ContentPasteRounded,
DeleteRounded,
IndeterminateCheckBoxRounded,
LocalFireDepartmentRounded, LocalFireDepartmentRounded,
RefreshRounded, RefreshRounded,
TextSnippetOutlined, TextSnippetOutlined,
CheckBoxOutlineBlankRounded,
CheckBoxRounded,
IndeterminateCheckBoxRounded,
DeleteRounded,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { LoadingButton } from "@mui/lab"; import { LoadingButton } from "@mui/lab";
import { Box, Button, Divider, Grid, IconButton, Stack } from "@mui/material"; import { Box, Button, Divider, Grid, IconButton, Stack } from "@mui/material";