Skip to content

Commit 9778432

Browse files
committed
feat: integrate with aw-notify-rs for notifications
1 parent dd644b0 commit 9778432

2 files changed

Lines changed: 154 additions & 5 deletions

File tree

‎src-tauri/src/lib.rs‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,8 @@ pub fn run() {
533533
let menu =
534534
Menu::with_items(app, &[&open, &quit]).expect("failed to create tray menu");
535535

536-
let mut tray_builder = TrayIconBuilder::new()
536+
#[cfg(not(target_os = "windows"))]
537+
let tray_builder = TrayIconBuilder::new()
537538
.icon(
538539
app.default_window_icon()
539540
.expect("failed to get window icon")
@@ -543,10 +544,15 @@ pub fn run() {
543544
.show_menu_on_left_click(true);
544545

545546
#[cfg(target_os = "windows")]
546-
{
547-
tray_builder = tray_builder.tooltip("ActivityWatch");
548-
}
549-
547+
let tray_builder = TrayIconBuilder::new()
548+
.icon(
549+
app.default_window_icon()
550+
.expect("failed to get window icon")
551+
.clone(),
552+
)
553+
.menu(&menu)
554+
.show_menu_on_left_click(true)
555+
.tooltip("ActivityWatch");
550556
let tray = tray_builder.build(app).expect("failed to create tray");
551557

552558
init_tray_id(tray.id().clone());

‎src-tauri/src/manager.rs‎

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ use tauri::menu::{CheckMenuItem, Menu, MenuItem, PredefinedMenuItem, SubmenuBuil
3838
use tauri_plugin_dialog::{DialogExt, MessageDialogKind};
3939

4040
use crate::{get_app_handle, get_config, get_tray_id, HANDLE_CONDVAR};
41+
use std::io::{BufRead, BufReader, Read};
42+
use tauri_plugin_notification::NotificationExt;
4143

4244
#[derive(Debug)]
4345
pub enum ModuleMessage {
@@ -358,6 +360,13 @@ fn start_module_thread(
358360
custom_args: Option<Vec<String>>,
359361
tx: Sender<ModuleMessage>,
360362
) {
363+
// Special handling for aw-notify module
364+
if name == "aw-notify" {
365+
info!("Using special aw-notify handler for module: {name}");
366+
start_notify_module_thread(name, path, custom_args, tx);
367+
return;
368+
}
369+
361370
thread::spawn(move || {
362371
// Start the child process
363372
let port_string = get_config().port.to_string();
@@ -404,6 +413,136 @@ fn start_module_thread(
404413
});
405414
}
406415

416+
fn start_notify_module_thread(
417+
name: String,
418+
path: PathBuf,
419+
custom_args: Option<Vec<String>>,
420+
tx: Sender<ModuleMessage>,
421+
) {
422+
thread::spawn(move || {
423+
// Start the child process with --output-only flag
424+
let port_string = get_config().port.to_string();
425+
let mut command = Command::new(&path);
426+
427+
// Always add --output-only flag for aw-notify
428+
let mut args = vec!["--output-only".to_string()];
429+
430+
// Add any custom args
431+
if let Some(ref custom) = custom_args {
432+
args.extend_from_slice(custom);
433+
}
434+
435+
command.args(&args);
436+
437+
// Set creation flags on Windows to hide console window
438+
#[cfg(windows)]
439+
command.creation_flags(CREATE_NO_WINDOW);
440+
441+
let mut child = match command
442+
.stdout(std::process::Stdio::piped())
443+
.stderr(std::process::Stdio::piped())
444+
.spawn()
445+
{
446+
Ok(child) => child,
447+
Err(e) => {
448+
let error_msg = e.to_string();
449+
if error_msg.contains("No such option: --output-only") {
450+
info!("aw-notify module doesn't support --output-only, falling back to default behavior");
451+
// Fallback to default module handler
452+
start_module_thread(name, path, custom_args, tx);
453+
return;
454+
} else {
455+
error!("Failed to start module {name}: {e}");
456+
return;
457+
}
458+
}
459+
};
460+
461+
// Send a message to the manager that the module has started
462+
tx.send(ModuleMessage::Started {
463+
name: name.to_string(),
464+
pid: child.id(),
465+
args: Some(args),
466+
})
467+
.unwrap();
468+
469+
// Read output continuously and parse notifications
470+
let stdout = child.stdout.take().expect("Failed to get stdout");
471+
let reader = BufReader::new(stdout);
472+
473+
let mut in_notification = false;
474+
let mut notification_content = Vec::new();
475+
476+
for line in reader.lines() {
477+
match line {
478+
Ok(line_content) => {
479+
// Check for notification boundaries (exactly 50 dashes)
480+
if line_content == "-".repeat(50) {
481+
if in_notification {
482+
// End of notification - send it
483+
if !notification_content.is_empty() {
484+
let content = notification_content.join("\n");
485+
send_notification(&content);
486+
notification_content.clear();
487+
}
488+
in_notification = false;
489+
} else {
490+
// Start of notification
491+
in_notification = true;
492+
}
493+
} else if in_notification && !line_content.trim().is_empty() {
494+
// Collect notification content
495+
notification_content.push(line_content.clone());
496+
}
497+
// Log all output for debugging
498+
debug!("aw-notify output: {}", line_content);
499+
}
500+
Err(e) => {
501+
error!("Error reading aw-notify output: {}", e);
502+
break;
503+
}
504+
}
505+
}
506+
507+
// Wait for the child to exit
508+
let output = child.wait_with_output().expect("failed to wait on child");
509+
510+
// Send the process output to the manager
511+
tx.send(ModuleMessage::Stopped {
512+
name: name.to_string(),
513+
output,
514+
})
515+
.unwrap();
516+
});
517+
}
518+
519+
fn send_notification(content: &str) {
520+
// Get app handle and send notification
521+
if let Ok(app_handle_guard) = get_app_handle().lock() {
522+
let app_handle = &*app_handle_guard;
523+
let result = app_handle
524+
.notification()
525+
.builder()
526+
.title("ActivityWatch")
527+
.body(content)
528+
.show();
529+
530+
match result {
531+
Ok(_) => {
532+
info!(
533+
"Sent notification: {}",
534+
content.lines().next().unwrap_or("")
535+
);
536+
}
537+
Err(e) => {
538+
error!("Failed to send notification: {}", e);
539+
}
540+
}
541+
} else {
542+
error!("Failed to get app handle lock for notification");
543+
}
544+
}
545+
407546
#[cfg(unix)]
408547
fn discover_modules() -> BTreeMap<String, PathBuf> {
409548
let excluded = [
@@ -481,6 +620,10 @@ fn discover_modules() -> BTreeMap<String, PathBuf> {
481620
}
482621
}
483622

623+
debug!(
624+
"Discovered modules: {:?}",
625+
found_modules.keys().collect::<Vec<_>>()
626+
);
484627
found_modules
485628
}
486629

0 commit comments

Comments
 (0)