From 8a53ab99499499cbda64401f27f12c657c0accec Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Sat, 18 Jul 2026 11:45:08 +0200 Subject: [PATCH] check lastmodified when a Note view model is re-attached --- application/backend/src/backend.rs | 48 ++++++++++++++++++++++++++---- application/backend/src/storage.rs | 2 +- application/note/src/note.rs | 44 +++++++++++++++++++++++---- 3 files changed, 81 insertions(+), 13 deletions(-) diff --git a/application/backend/src/backend.rs b/application/backend/src/backend.rs index 4f0661a..d7aec62 100644 --- a/application/backend/src/backend.rs +++ b/application/backend/src/backend.rs @@ -25,7 +25,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ - +use std::fs::Metadata; use std::future::Future; use std::io::Error; use std::path::{Path, PathBuf}; @@ -48,7 +48,7 @@ use entity::notecontent::{Entity as NoteContent}; use migration::prelude::Utc; use crate::lockmanager::LockManager; use crate::note::{create_nodename, randomize_nodename}; -use crate::storage::{get_collection_storage_path, get_file_content, get_last_modified, get_note_storage_path, write_file, write_tmp_file}; +use crate::storage::{*}; pub struct Backend { rt: Arc, @@ -562,7 +562,14 @@ impl BackendHandle { let fs_result = write_file(path.as_path(), note_content.as_ref()).await; match fs_result { Ok(_) => { - result = SaveNoteResult::Ok((note.clone(), 0)); + let lastmodified = get_last_modified(path.as_path()).await; + let ret = SaveNoteRet { + model: note.clone(), + content_id: 0, + local_path: Some(path), + local_lastmodified: lastmodified + }; + result = SaveNoteResult::Ok(ret); }, Err(e) => { result = SaveNoteResult::FileError(e); @@ -579,7 +586,13 @@ impl BackendHandle { match result2 { Ok(ctn) => { - result = SaveNoteResult::Ok((note.clone(), ctn.id)); + let ret = SaveNoteRet { + model: note.clone(), + content_id: ctn.id, + local_path: None, + local_lastmodified: None + }; + result = SaveNoteResult::Ok(ret); } Err(e) => { result = SaveNoteResult::Error(e); @@ -587,7 +600,13 @@ impl BackendHandle { } } } else { - result = SaveNoteResult::Ok((note.clone(), 0)); + let ret = SaveNoteRet { + model: note.clone(), + content_id: 0, + local_path: None, + local_lastmodified: None + }; + result = SaveNoteResult::Ok(ret); } if result.is_ok() { @@ -609,6 +628,16 @@ impl BackendHandle { let _ = self.tx.send(cmd); } + pub fn get_file_metadata(&self, path: &Path, callback: F) + where F: FnOnce(std::io::Result) + Send + 'static { + let p = PathBuf::from(path); + let cmd = Box::pin(async move { + let result = tokio::fs::metadata(p.as_path()).await; + callback(result); + }); + let _ = self.tx.send(cmd); + } + pub fn open_extern(&self, note_id: i32, callback: F) where F: FnOnce(OpenExternResult) + Send + 'static { let backend = self.backend.clone(); @@ -666,7 +695,7 @@ pub struct NoteContentRet { pub enum SaveNoteResult { - Ok((entity::note::Model, i32)), + Ok(SaveNoteRet), VersionConflict, FileAlreadyExists(PathBuf), Error(DbErr), @@ -674,6 +703,13 @@ pub enum SaveNoteResult { ValidationError(&'static str) } +pub struct SaveNoteRet { + pub model: entity::note::Model, + pub content_id: i32, + pub local_path: Option, + pub local_lastmodified: Option +} + pub enum OpenExternResult { Ok((PathBuf, bool)), // path, istmp DbErr(DbErr), diff --git a/application/backend/src/storage.rs b/application/backend/src/storage.rs index 6271822..a91ab75 100644 --- a/application/backend/src/storage.rs +++ b/application/backend/src/storage.rs @@ -25,7 +25,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -use std::fs::metadata; + use std::io::ErrorKind; use std::path::Path; use std::time::SystemTime; diff --git a/application/note/src/note.rs b/application/note/src/note.rs index 93b1713..b2a933e 100644 --- a/application/note/src/note.rs +++ b/application/note/src/note.rs @@ -129,7 +129,7 @@ impl Note { /// Handles any attachment status change, which includes if this note is /// directly attached or detached, but also if any parent attachment status changes #[action] - pub fn attachment_status_changed(&mut self, _event: &mut ActionEvent) -> Option<()>{ + pub fn attachment_status_changed(&mut self, _event: &mut ActionEvent) -> Option<()> { let doc = self.doc.get_doc()?; if doc.ctx.is_attached_to_obj() { @@ -149,6 +149,8 @@ impl Note { doc.ctx.unset_state(AppStates::NoteShowInfo as i32); } } + + self.check_lastmodified(); } else { self.lock = None; self.text.set_readonly(false); @@ -157,6 +159,34 @@ impl Note { Some(()) } + fn check_lastmodified(&mut self) { + let Some(doc) = self.doc.get_doc() else { + return; + }; + + 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| { + match result { + Ok(metadata) => { + if let Ok(lm) = metadata.modified() && let Some(lastmodified) = note.local_lastmodified { + if lastmodified != lm { + println!("note was changed"); + } + } else { + println!("no last_modified available"); + } + }, + Err(e) => { + println!("cannot get metadata: {}", e); + } + } + }); + }); + } + } + pub fn init_from_model(&mut self, model: &entity::note::Model) { self.id = NoteId::Id(model.note_id); self.nodename = Some(model.nodename.clone()); @@ -365,11 +395,13 @@ impl NoteViewModel for Note { self.backend.save_note(self.notebook_instance_id, self.id.clone(), note, Some(notecontent), |result|{ proxy.call_mainthread(move |doc, note|{ match result { - SaveNoteResult::Ok((notemodel, content_id)) => { - note.id = NoteId::Id(notemodel.note_id); - note.content_id = content_id; - note.version = notemodel.version; - println!("Note saved: note_id: {}, content_id: {}, version: {}", notemodel.note_id, content_id, note.version); + SaveNoteResult::Ok(ret) => { + note.id = NoteId::Id(ret.model.note_id); + note.content_id = ret.content_id; + note.version = ret.model.version; + note.local_path = ret.local_path; + note.local_lastmodified = ret.local_lastmodified; + println!("Note saved: note_id: {}, content_id: {}, version: {}", ret.model.note_id, ret.content_id, note.version); // make sure the "new note" button is always enabled within this note doc.ctx.unsuppress_state(AppStates::NoteEnableNew as i32); -- 2.52.0