]> uap-core.de Git - note.git/commitdiff
remove OpenNoteHandle, share note updates via broadcast main
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Tue, 16 Jun 2026 20:28:46 +0000 (22:28 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Tue, 16 Jun 2026 20:28:46 +0000 (22:28 +0200)
application/src/backend.rs
application/src/lockmanager.rs
application/src/note.rs
application/src/notebook.rs

index ce601ecfb194885bf6df1a8b7383f117ef1633e9..da08f473ee98b2602b322ab94b3c37982f754b40 100644 (file)
@@ -80,7 +80,7 @@ impl Clone for BackendHandle {
 pub enum BroadcastMessage {
     NotebookStructureUpdate(Vec<Node>),
     NoteTitleUpdate(NoteTitleUpdate),
-    InsertNote(NoteUpdate)
+    NoteUpdate(NoteUpdate),
 }
 
 #[derive(Clone, PartialEq, Eq)]
@@ -110,6 +110,7 @@ pub struct NoteTitleUpdate {
 
 #[derive(Clone)]
 pub struct NoteUpdate {
+    pub from: u64,
     pub id: NoteId,
     pub model: note::Model
 }
@@ -381,7 +382,7 @@ impl BackendHandle {
         let _ = self.tx.send(cmd);
     }
     
-    pub fn save_note<F>(&self, id: NoteId, note: note::ActiveModel, content: Option<notecontent::ActiveModel>, callback: F)
+    pub fn save_note<F>(&self, initiator: u64, id: NoteId, note: note::ActiveModel, content: Option<notecontent::ActiveModel>, callback: F)
     where F: FnOnce(SaveNoteResult) + Send + 'static {
         let bhandle = self.clone();
         let cmd = Box::pin(async move {
@@ -461,10 +462,11 @@ impl BackendHandle {
                     if result.is_ok() {
                         // broadcast new note
                         let update = NoteUpdate {
+                            from: initiator,
                             id: id.clone(),
                             model: note
                         };
-                        _ = bhandle.send_broadcast(BroadcastMessage::InsertNote(update));
+                        _ = bhandle.send_broadcast(BroadcastMessage::NoteUpdate(update));
                     }
                     callback(result);
                 },
index 9e7e4a9a322b829c8c554948764d7fa4d6931596..77466644e9a7b707386fd8f602006995e54c7db0 100644 (file)
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */
-use std::collections::{HashMap, HashSet};
+use std::collections::{HashSet};
 use std::sync::Arc;
-use std::sync::atomic::{AtomicU64, Ordering};
 
 
-static NHANDLE_ID: AtomicU64 = AtomicU64::new(1);
-
 pub struct LockManager {
     locks: Arc<std::sync::Mutex<HashSet<i32>>>,
-    versions: Arc<std::sync::Mutex<HashMap<i32, NoteHandleList>>>,
 }
 
 impl LockManager {
     pub fn new() -> Self {
         LockManager {
             locks: Arc::new(std::sync::Mutex::new(HashSet::new())),
-            versions: Arc::new(std::sync::Mutex::new(HashMap::new())),
         }
     }
 
@@ -54,15 +49,6 @@ impl LockManager {
         })
     }
 
-    fn versions(&self) -> std::sync::MutexGuard<'_, HashMap<i32, NoteHandleList>> {
-        self.versions.lock().unwrap_or_else(|e| {
-            // Very unlikely that the mutex gets poisoned (probably only by OOM), and if it does, who cares
-            println!("Error: LockManager revisions poisoned");
-            self.versions.clear_poison();
-            e.into_inner()
-        })
-    }
-
     pub fn lock_note(&self, note_id: i32) -> Option<NoteLock> {
         let mut locks = self.locks();
         if locks.contains(&note_id) {
@@ -78,61 +64,8 @@ impl LockManager {
         };
         Some(note_lock)
     }
-
-    pub fn try_open_note(&self, note_id: i32, version: i64) -> Option<OpenNoteHandle> {
-        let mut versions = self.versions();
-        let handles = versions.get_mut(&note_id);
-        match handles {
-            Some(hl) => {
-                // find the highest version in all open handles for this note
-                if hl.version > version {
-                    // version is outdated
-                    return None;
-                } else if version > hl.version {
-                    hl.version = version;
-                }
-                // ok
-                let id = NHANDLE_ID.fetch_add(1, Ordering::Relaxed);
-                let handle = OpenNoteHandle {
-                    versions: self.versions.clone(),
-                    note_id: note_id,
-                    handle_id: id
-                };
-                hl.handles.push(id);
-                Some(handle)
-            },
-            None => {
-                let mut v = Vec::new();
-                let id = NHANDLE_ID.fetch_add(1, Ordering::Relaxed);
-                let handle = OpenNoteHandle {
-                    versions: self.versions.clone(),
-                    note_id: note_id,
-                    handle_id: id
-                };
-                v.push(id);
-                let hl = NoteHandleList {
-                    version: version,
-                    handles: v
-                };
-                versions.insert(note_id, hl);
-                Some(handle)
-            }
-        }
-    }
-    
-    pub fn update_note_version(&self, note_id: i32, version: i64) {
-        let mut versions = self.versions();
-        let handles = versions.get_mut(&note_id);
-        if let Some(hl) = handles {
-            hl.version = version;
-        }
-    }
 }
 
-struct NoteHandleList {
-    handles: Vec<u64>,
-    version: i64
-}
 
 pub struct NoteLock {
     locks: Arc<std::sync::Mutex<HashSet<i32>>>,
@@ -147,25 +80,3 @@ impl Drop for NoteLock {
     }
 }
 
-
-pub struct OpenNoteHandle {
-    versions: Arc<std::sync::Mutex<HashMap<i32, NoteHandleList>>>,
-    note_id: i32,
-    handle_id: u64,
-}
-
-impl Drop for OpenNoteHandle {
-    fn drop(&mut self) {
-        let mut versions = self.versions.lock().unwrap_or_else(|e|{ e.into_inner()});
-        let handles = versions.get_mut(&self.note_id);
-        if let Some(hl) = handles {
-            // find this OpenNote id in the handle list
-            if let Some(pos) = hl.handles.iter().position(|n| *n == self.handle_id) {
-                hl.handles.remove(pos);
-            }
-            if hl.handles.len() == 0 {
-                versions.remove(&self.note_id);
-            }
-        } // else: should not happen
-    }
-}
index 51aa015d5b4310579aa396765edfbca30be4855e..4ed00bd37a654058c440ed0ab42dd10514ad34dd 100644 (file)
@@ -35,7 +35,7 @@ use ui_rs::{action, ui_actions, UiModel};
 use ui_rs::ui::*;
 use crate::AppStates;
 use crate::backend::{BackendHandle, BroadcastMessage, NoteId, NoteTitleUpdate, SaveNoteResult};
-use crate::lockmanager::{NoteLock, OpenNoteHandle};
+use crate::lockmanager::{NoteLock};
 use crate::window::NoteTypeTabView;
 
 static TMP_ID: AtomicU64 = AtomicU64::new(1);
@@ -44,10 +44,10 @@ static TMP_ID: AtomicU64 = AtomicU64::new(1);
 pub struct Note {
     pub doc: UiDocRef<Note>,
     pub backend: Rc<BackendHandle>,
+    pub notebook_instance_id: u64,
 
     pub id: NoteId,
     pub lock: Option<NoteLock>,
-    pub vhandle: Option<OpenNoteHandle>,
 
     pub collection_id: i32,
     pub content_id: i32,
@@ -73,15 +73,15 @@ pub struct Note {
 
 #[ui_actions]
 impl Note {
-    pub fn new(id: NoteId, collection_id: i32, backend: Rc<BackendHandle>) -> Self {
+    pub fn new(notebook_instance_id: u64, id: NoteId, collection_id: i32, backend: Rc<BackendHandle>) -> Self {
         Note {
             doc: Default::default(),
-            backend: backend,
-            collection_id: collection_id,
+            backend,
+            notebook_instance_id,
+            collection_id,
 
-            id: id,
+            id,
             lock: None,
-            vhandle: None,
             content_id: 0,
             version: 0,
             kind: NoteType::PlainTextNote,
@@ -104,7 +104,7 @@ impl Note {
                 d.ctx.suppress_state(AppStates::NoteEnableNew as i32);
             } else {
                 n.init_from_model(model);
-                n.load_content(d);
+                n.load_content();
             }
             // make sure we receive an event, when this note is attached or detached
             d.ctx.on_attachment_status_change_action("attachment_status_changed");
@@ -133,17 +133,6 @@ impl Note {
                 } else {
                     doc.ctx.unset_state(AppStates::NoteShowInfo as i32);
                 }
-
-                // also try to get an OpenNoteHandle, which checks the current note version
-                let h = self.backend.backend.lockmgr.try_open_note(note_id, self.version);
-                if h.is_some() {
-                    // ok
-                    self.vhandle = h;
-                } else {
-                    println!("note: {} version {} is outdated", note_id, self.version);
-
-                    // TOOD: reload note
-                }
             }
         } else {
             // clean self.lock, however don't clean self.vhandle
@@ -175,11 +164,15 @@ impl Note {
         self.update_title(content.content.as_str(), false);
     }
 
-    pub fn load_content(&mut self, doc: &UiDoc<Note>) {
+    pub fn load_content(&mut self) {
         let NoteId::Id(note_id) = self.id else {
             return;
         };
 
+        let Some(doc) = self.doc.get_doc() else {
+            return;
+        };
+
         let proxy = doc.doc_proxy();
         self.backend.get_note_content(note_id, move|result|{
             proxy.call_mainthread(move |_doc, note|{
@@ -264,7 +257,7 @@ impl Note {
         };
 
         let proxy = doc.doc_proxy();
-        self.backend.save_note(self.id.clone(), note, Some(notecontent), |result|{
+        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)) => {
@@ -273,7 +266,6 @@ impl Note {
                         note.version = notemodel.version;
                         println!("Note saved: note_id: {}, content_id: {}, version: {}", notemodel.note_id, content_id, note.version);
 
-                        note.backend.backend.lockmgr.update_note_version(notemodel.note_id, note.version);
                         // make sure the "new note" button is always enabled within this note
                         doc.ctx.unsuppress_state(AppStates::NoteEnableNew as i32);
                     },
@@ -290,6 +282,18 @@ impl Note {
         self.modified = false;
     }
 
+    #[action(u64)]
+    pub fn changed_by(&mut self, _event: &ActionEvent, _from: &u64) {
+        // The from id is currently unused, however it may be important when it is possible
+        // to bind multiple text views to the same text buffer. Then this textview already
+        // has the updated version, because it shares the text buffer with the save initiator.
+        // The from value can probably be used to check if we share the buffer with the
+        // save initiator or if this change came from outside.
+        println!("note changed by {}", _from);
+
+        self.load_content();
+    }
+
     /// Event before the text is changed: detect if the change affects the title
     #[action]
     pub fn note_text_change(&mut self, event: &ActionEvent) {
index 8a9f36e134db77b1a13652d0e70f6545bf38c088..9c5ca4b9bba051eb43246025c4989d0c73b19509 100644 (file)
@@ -28,6 +28,7 @@
 
 use std::any::Any;
 use std::rc::Rc;
+use std::sync::atomic::AtomicU64;
 use ui_rs::{action, ui_actions, UiModel};
 use ui_rs::ui::*;
 
@@ -36,11 +37,16 @@ use crate::backend::{BackendHandle, BroadcastMessage, NoteId, NoteTitleUpdate, N
 use crate::note::new_note_id;
 use crate::window::NavigationItem;
 
+/// Each Notebook view model gets a unique instance id, that is mostly used to identify,
+/// who initiated a broadcast message.
+static INSTANCE_ID: AtomicU64 = AtomicU64::new(1);
+
 /// UI model for a notebook
 #[derive(UiModel)]
 pub struct Notebook {
     pub doc_ref: UiDocRef<Notebook>,
     pub backend: Rc<BackendHandle>,
+    pub instance_id: u64,
     pub broadcast_rx: tokio::sync::broadcast::Receiver<BroadcastMessage>,
     pub collection_id: i32,
 
@@ -56,6 +62,7 @@ impl Notebook {
         Notebook {
             doc_ref: Default::default(),
             backend: Rc::new(backend.clone()),
+            instance_id: INSTANCE_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
             broadcast_rx: backend.btx.subscribe(),
             collection_id: id,
             selected_note: None,
@@ -151,7 +158,7 @@ impl Notebook {
             doc.clone()
         } else {
             // Create the new note
-            let note_data = crate::note::Note::new(note.id.clone(), self.collection_id, self.backend.clone());
+            let note_data = crate::note::Note::new(self.instance_id, note.id.clone(), self.collection_id, self.backend.clone());
             note_data.into_doc(&note.data)
         };
         note.model = Some(doc.clone());
@@ -259,12 +266,28 @@ impl Notebook {
         }
     }
 
-    pub fn insert_note(&mut self, update: NoteUpdate) {
+    pub fn update_note(&mut self, update: NoteUpdate) {
         if let Some(index) = self.find_note(&update.id) {
             // update existing item
             if let Some(elm) = self.notes.data_mut().get_mut(index) {
                 elm.data = update.model;
                 elm.id = NoteId::Id(elm.data.note_id); // id no longer temporary
+
+                if update.from != self.instance_id {
+                    // The note was updated from another view model
+                    // If this note was already opened, it needs a refresh
+                    if let Some(selection) = &self.selected_note && selection.index == index {
+                        // The note is also currently attached
+                        // Send a message to the note, that it needs an update
+                        let arg = Box::new(update.from);
+                        selection.doc.ctx.call_action_with_parameter("changed_by", arg);
+                    } else {
+                        // Note view model is not attached, we can just discard it and the next
+                        // time the note is opened, it is recreated and the content reloaded
+                        elm.model = None;
+                    }
+                }
+
                 self.notes.update_row(index);
             }
         } else {
@@ -289,9 +312,9 @@ impl Notebook {
                         self.update_note_title(&update);
                     }
                 },
-                BroadcastMessage::InsertNote(update) => {
+                BroadcastMessage::NoteUpdate(update) => {
                     if update.model.collection_id == self.collection_id {
-                        self.insert_note(update);
+                        self.update_note(update);
                     }
                 },
                 _ => {