Implement file change watching and tray menu to trigger backup

This commit is contained in:
Robin Gottschalk 2025-01-30 23:15:01 +01:00
commit dc557ec780
5 changed files with 3085 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

2940
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "obsidian-git-backup"
version = "0.1.0"
edition = "2021"
[dependencies]
winit = "0.30.8"
notify = "8.0.0"
tray-icon = "0.19.2"
git2 = "0.20.0"

134
src/main.rs Normal file
View File

@ -0,0 +1,134 @@
use notify::{Event, RecursiveMode, Watcher};
use std::path::Path;
use std::time::{Duration, Instant};
use tray_icon::{
menu::{Menu, MenuId, MenuItem},
Icon, TrayIconBuilder,
};
use winit::event::StartCause;
use winit::{
application::ApplicationHandler,
event::WindowEvent,
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
};
enum UserEvent {
MenuEvent(tray_icon::menu::MenuEvent),
FileEvent(Event),
}
#[derive(Default)]
struct App {
tray_icon: Option<tray_icon::TrayIcon>,
menu_push_now_id: Option<MenuId>,
menu_exit_id: Option<MenuId>,
last_change_time: Option<Instant>,
duration_threshold: Duration,
}
impl ApplicationHandler<UserEvent> for App {
fn resumed(&mut self, _event_loop: &ActiveEventLoop) {
// Create and show the tray icon
let menu_item_push_now = MenuItem::new("Push Now", true, None);
let menu_item_exit = MenuItem::new("Exit", true, None);
let tray_menu = Menu::new();
tray_menu.append(&menu_item_push_now).unwrap();
tray_menu.append(&menu_item_exit).unwrap();
let icon_path = Path::new("D:/Dev/obsidian-git-backup/sync.ico");
let icon = Icon::from_path(icon_path, None).unwrap();
let tray_icon = TrayIconBuilder::new()
.with_menu(Box::new(tray_menu))
.with_tooltip("Obsidian Git Backup")
.with_icon(icon)
.build()
.unwrap();
self.tray_icon = Some(tray_icon);
self.menu_push_now_id = Some(menu_item_push_now.id().clone());
self.menu_exit_id = Some(menu_item_exit.id().clone());
// Set the duration threshold (e.g., 5 minutes)
self.last_change_time = None;
self.duration_threshold = Duration::from_secs(15);
}
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
match event {
UserEvent::MenuEvent(menu_event) => {
match menu_event.id() {
id if id == self.menu_push_now_id.as_ref().unwrap() => {
// Handle push now menu item
self.last_change_time = None;
backup_changes();
}
id if id == self.menu_exit_id.as_ref().unwrap() => {
// Handle exit menu item
event_loop.exit();
}
_ => {}
}
}
UserEvent::FileEvent(_event) => {
println!("File change detected");
self.last_change_time = Some(Instant::now());
println!("At: {:?}", self.last_change_time.unwrap());
}
}
}
fn new_events(&mut self, event_loop: &ActiveEventLoop, _cause: StartCause) {
let now = Instant::now();
// Backup changes if last file change time exceeded threshold
if let Some(last_change_time) = self.last_change_time {
if now.duration_since(last_change_time) > self.duration_threshold {
self.last_change_time = None;
backup_changes();
}
}
// Reset timer
event_loop.set_control_flow(ControlFlow::WaitUntil(now + Duration::from_secs(5)));
}
fn window_event(
&mut self,
_event_loop: &ActiveEventLoop,
_window_id: winit::window::WindowId,
_event: WindowEvent,
) {
}
}
fn backup_changes() {
println!("Backup changes started");
println!("Checking for changes...");
}
fn main() {
let event_loop = EventLoop::<UserEvent>::with_user_event().build().unwrap();
event_loop.set_control_flow(ControlFlow::Wait);
// Set up menu event handler
let proxy = event_loop.create_proxy();
tray_icon::menu::MenuEvent::set_event_handler(Some(move |event| {
let _ = proxy.send_event(UserEvent::MenuEvent(event));
}));
// Set up file change event handler
let proxy = event_loop.create_proxy();
let repo_path = Path::new("D:/test/obsidian-vault-test");
let mut watcher = notify::recommended_watcher(move |event| {
if let Ok(event) = event {
let _ = proxy.send_event(UserEvent::FileEvent(event));
}
})
.unwrap();
watcher.watch(repo_path, RecursiveMode::Recursive).unwrap();
// Start event loop
let mut app = App::default();
let _ = event_loop.run_app(&mut app);
}

BIN
sync.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB