From 670bf9f978703f326ea118907eef1e975eaf2cb8 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Sun, 19 Jul 2026 21:26:51 +0200 Subject: [PATCH] add fs watch to notebook --- application/backend/src/backend.rs | 28 +++++++++++++++++++- application/backend/src/lib.rs | 4 +-- application/backend/src/notify.rs | 41 +++++++++++++++++++++++++----- application/note/src/note.rs | 2 +- application/note/src/notebook.rs | 24 +++++++++++++++++ 5 files changed, 88 insertions(+), 11 deletions(-) diff --git a/application/backend/src/backend.rs b/application/backend/src/backend.rs index 4366330..6a3e800 100644 --- a/application/backend/src/backend.rs +++ b/application/backend/src/backend.rs @@ -49,6 +49,7 @@ use entity::notecontent::{Entity as NoteContent}; use migration::prelude::Utc; use crate::lockmanager::LockManager; use crate::note::{create_nodename, randomize_nodename}; +use crate::notify::{fs_notify, FSWatch}; use crate::storage::{*}; pub struct Backend { @@ -68,7 +69,8 @@ type CmdFuture = Pin + Send>>; pub struct BackendHandle { pub backend: Arc, pub tx: tokio::sync::mpsc::UnboundedSender, - pub btx: tokio::sync::broadcast::Sender + pub btx: tokio::sync::broadcast::Sender, + pub ntx: tokio::sync::mpsc::UnboundedSender } impl Clone for BackendHandle { @@ -77,6 +79,7 @@ impl Clone for BackendHandle { backend: self.backend.clone(), tx: self.tx.clone(), btx: self.btx.clone(), + ntx: self.ntx.clone() } } } @@ -260,6 +263,7 @@ impl Backend { /// The handle also contains a broadcast sender/receiver (btx, brx). pub fn start(self) -> (BackendHandle, JoinHandle<()>) { let (tx, mut rx) = mpsc::unbounded_channel::(); + let (ntx, nrx) = mpsc::unbounded_channel::(); let broadcast_tx = self.broadcast.clone(); let backend = Arc::new(self); @@ -304,10 +308,22 @@ impl Backend { }); }); + // start fs notification + let fs_btx = broadcast_tx.clone(); + let cmd = Box::pin(async move { + let ret = fs_notify(nrx, fs_btx).await; + if let Err(e) = ret { + println!("fs_noitify task failed {:?}", e); + } + }); + let _ = tx.send(cmd); + + // create backend handle let backend_handle = BackendHandle { backend: backend_clone, tx: tx, btx: broadcast_tx, + ntx: ntx, }; (backend_handle, join) @@ -384,6 +400,16 @@ impl BackendHandle { let _ = self.tx.send(cmd); } + pub fn get_notebook_local_path(&self, collection_id: i32, callback: F) + where F: FnOnce(Result, DbErr>) + Send + 'static { + let backend = self.backend.clone(); + let cmd = Box::pin(async move { + let result = get_collection_storage_path(&backend.db, collection_id).await; + callback(result); + }); + let _ = self.tx.send(cmd); + } + pub fn get_note_content(&self, note_id: i32, callback: F) where F: FnOnce(Result, DbErr>) + Send + 'static { let backend = self.backend.clone(); diff --git a/application/backend/src/lib.rs b/application/backend/src/lib.rs index 862d97d..4257a23 100644 --- a/application/backend/src/lib.rs +++ b/application/backend/src/lib.rs @@ -1,5 +1,5 @@ pub mod backend; pub mod lockmanager; pub mod storage; -mod note; -mod notify; \ No newline at end of file +pub mod note; +pub mod notify; \ No newline at end of file diff --git a/application/backend/src/notify.rs b/application/backend/src/notify.rs index cb9fba6..c2297b5 100644 --- a/application/backend/src/notify.rs +++ b/application/backend/src/notify.rs @@ -26,10 +26,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ use std::collections::HashMap; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; -use tokio::sync::mpsc; -use tokio::sync::mpsc::Receiver; +use tokio::sync::mpsc::{*}; use crate::backend::BroadcastMessage; use std::collections::hash_map::Entry; @@ -38,14 +37,14 @@ pub enum FSWatch { Remove(PathBuf), } -pub async fn fs_notify(mut rx: Receiver, btx: tokio::sync::broadcast::Sender) -> notify::Result<()> { +pub async fn fs_notify(mut rx: UnboundedReceiver, btx: tokio::sync::broadcast::Sender) -> notify::Result<()> { let mut path_map = HashMap::::new(); let mut watcher = RecommendedWatcher::new( move |res: Result| { match res { Ok(event) => { - btx.send(BroadcastMessage::Notify(event)); + _ = btx.send(BroadcastMessage::Notify(event)); }, Err(e) => { println!("Notify Error: {:?}", e); @@ -91,8 +90,36 @@ pub async fn fs_notify(mut rx: Receiver, btx: tokio::sync::broadcast::S } } } + + //println!("fs_notify end"); + Ok(()) +} +pub struct FSPathWatcher { + path: Option, + tx: UnboundedSender +} - println!("fs_notify end"); - Ok(()) +impl<'a> FSPathWatcher { + pub fn new(tx: UnboundedSender) -> Self { + FSPathWatcher { path: None, tx } + } + + pub fn watch(&mut self, path: &Path) { + self.clear(); + self.path = Some(path.to_path_buf()); + let _ = self.tx.send(FSWatch::Add(path.to_path_buf())); + } + + pub fn clear(&mut self) { + if let Some(path) = self.path.take() { + let _ = self.tx.send(FSWatch::Remove(path)); + } + } +} + +impl<'a> Drop for FSPathWatcher { + fn drop(&mut self) { + self.clear(); + } } \ No newline at end of file diff --git a/application/note/src/note.rs b/application/note/src/note.rs index b2a933e..d1c627e 100644 --- a/application/note/src/note.rs +++ b/application/note/src/note.rs @@ -167,7 +167,7 @@ impl Note { if let Some(path) = &self.local_path && let Some(lastmodified) = self.local_lastmodified { let proxy = doc.doc_proxy(); self.backend.get_file_metadata(path.as_path(), |result| { - proxy.call_mainthread(|doc, note| { + proxy.call_mainthread(|_doc, note| { match result { Ok(metadata) => { if let Ok(lm) = metadata.modified() && let Some(lastmodified) = note.local_lastmodified { diff --git a/application/note/src/notebook.rs b/application/note/src/notebook.rs index d07e0f5..2fd1b3d 100644 --- a/application/note/src/notebook.rs +++ b/application/note/src/notebook.rs @@ -27,9 +27,11 @@ */ use std::any::Any; +use std::path::Path; use std::rc::Rc; use std::sync::atomic::AtomicU64; use backend::backend::{BackendHandle, BroadcastMessage, NoteId, NoteTitleUpdate, NoteUpdate}; +use backend::notify::FSPathWatcher; use ui_rs::{action, ui_actions, UiModel}; use ui_rs::ui::*; @@ -46,6 +48,7 @@ static INSTANCE_ID: AtomicU64 = AtomicU64::new(1); pub struct Notebook { pub doc_ref: UiDocRef, pub backend: Rc, + pub fswatch: FSPathWatcher, pub instance_id: u64, pub broadcast_rx: tokio::sync::broadcast::Receiver, pub collection_id: i32, @@ -65,6 +68,7 @@ impl Notebook { Notebook { doc_ref: Default::default(), backend: Rc::new(backend.clone()), + fswatch: FSPathWatcher::new(backend.ntx.clone()), instance_id: INSTANCE_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed), broadcast_rx: backend.btx.subscribe(), collection_id: id, @@ -341,6 +345,9 @@ impl Notebook { self.update_note(update); } }, + BroadcastMessage::Notify(event) => { + println!("fs notify event"); + }, _ => { }, @@ -401,6 +408,23 @@ impl NotebookItem { } }); }); + + let proxy = doc.doc_proxy(); + backend.get_notebook_local_path(self.data.collection_id, |result|{ + proxy.call_mainthread(|_n, nb| { + match result { + Ok(pathstr) => { + if let Some(pathstr) = pathstr { + let path = Path::new(&pathstr); + nb.fswatch.watch(&path); + } + }, + Err(err) => { + eprintln!("get_notebook_local_path failed: {}", err); + } + } + }); + }); self.model = Some(doc.clone()); -- 2.52.0