]> uap-core.de Git - note.git/commitdiff
load notebook structure at startup
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sat, 16 May 2026 11:30:46 +0000 (13:30 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sat, 16 May 2026 11:30:46 +0000 (13:30 +0200)
application/src/backend.rs
application/src/main.rs
application/src/window.rs
entity/src/collection.rs
migration/src/m20260502_184134_create_settings.rs

index a1fc41c11b091a14c9b99288c8aebbd398a0b247..28261848051c723de2b48f6c577cf1b255914ddb 100644 (file)
@@ -1,6 +1,6 @@
 use std::future::Future;
 use std::pin::Pin;
-use sea_orm::{ActiveModelTrait, Database, DatabaseConnection, DbErr, EntityTrait, QueryFilter, ColumnTrait, Set};
+use sea_orm::{ActiveModelTrait, Database, DatabaseConnection, DbErr, EntityTrait, QueryFilter, ColumnTrait, Set, QueryOrder};
 use tokio::runtime::Runtime;
 use std::sync::{Arc};
 use tokio::sync::mpsc;
@@ -8,8 +8,9 @@ use tokio::sync::mpsc::UnboundedSender;
 use migration::{Migrator, MigratorTrait};
 use ui_rs::ui;
 
-use entity::profile;
+use entity::{collection, profile};
 use entity::profile::Entity as Profile;
+use entity::collection::Entity as Collection;
 
 pub struct Backend {
     rt: Arc<Runtime>,
@@ -115,6 +116,15 @@ impl Backend {
         Ok(profile)
     }
 
+    pub fn load_collections(&self, profile_id: i32) -> Result<Vec<collection::Model>, DbErr> {
+        self.rt.block_on(async {
+            Collection::find()
+                .filter(collection::Column::ProfileId.eq(profile_id))
+                .order_by_asc(collection::Column::Parent)
+                .all(&self.db).await
+        })
+    }
+
     pub fn start(self) -> BackendHandle {
         let backend = Arc::new(self);
 
index 1f2f184c2648d787ae95b2f4bddc02083052871b..78ae303b9ec99bf513d29f4840e4c392efb4be18 100644 (file)
@@ -30,9 +30,10 @@ mod window;
 mod backend;
 
 use std::env;
+use entity::collection::{create_notebook_hierarchy, Node};
 use ui_rs::{ui};
 use ui_rs::ui::*;
-use crate::backend::{Backend, BackendHandle, DynCmd};
+use crate::backend::{Backend, BackendHandle};
 use crate::window::*;
 
 fn main() {
@@ -45,10 +46,23 @@ fn main() {
             return;
         }
     };
+    let profile_id = backend.current_profile.as_ref().unwrap().id;
+
+    let notebooks = match backend.load_collections(profile_id) {
+        Ok(notebooks) => create_notebook_hierarchy(notebooks),
+        Err(e) => {
+            let msg = format!("Error loading collections: {:?}", e);
+            ui::app_run_startup_error("Error", msg.as_str());
+            return;
+        }
+    };
 
     let handle = backend.start();
 
-    let mut app = App { backend: handle };
+    let mut app = App {
+        backend: handle,
+        notebooks: notebooks,
+    };
     ui::app_run::<MainWindow>(&mut app);
 }
 
@@ -100,6 +114,8 @@ fn init_backend() -> Result<Backend, ErrMsg> {
 
 struct App {
     backend: BackendHandle,
+
+    notebooks: Vec<Node>,
 }
 
 
@@ -111,7 +127,8 @@ impl Application<MainWindow> for App {
 
         create_toolbar(app);
 
-        create_window(app);
+        let window = create_window(app);
+        window.window_data(|data| init_window_data(data, self));
     }
 }
 
index a83100d5303b7bda8a2408e9e8a8adc06bb766ce..2880d3ae7f56624e327bf2cd960ee8088f16769a 100644 (file)
 
 use ui_rs::{action, ui_actions, UiModel};
 use ui_rs::ui::*;
+use crate::App;
+use entity::collection::Model as Collection;
 
 #[derive(UiModel, Default)]
 pub struct MainWindow {
     #[bind]
-    notebooks: UiSourceList<String>
+    notebooks: UiSourceList<Collection>
 }
 
 #[ui_actions]
@@ -43,7 +45,7 @@ impl MainWindow {
     }
 }
 
-pub fn create_window(app: &AppContext<MainWindow>) {
+pub fn create_window(app: &AppContext<MainWindow>) -> UiObject<MainWindow> {
     let windowdata: MainWindow = Default::default();
 
     let window = app.splitview_window("note", true, windowdata, |obj, data| {
@@ -51,6 +53,9 @@ pub fn create_window(app: &AppContext<MainWindow>) {
             obj.sourcelist(|b|{
                 b.fill(true);
                 b.value(&data.notebooks);
+                b.getvalue(|elm,_row,item|{
+                    item.label = Some(elm.name.clone());
+                });
             });
         });
 
@@ -81,8 +86,24 @@ pub fn create_window(app: &AppContext<MainWindow>) {
                         });
                     });
                 });
-        })
+        });
     });
 
     window.show();
-}
\ No newline at end of file
+    window
+}
+
+pub fn init_window_data(window: &mut MainWindow, data: &App) {
+    for elm in data.notebooks.iter() {
+        let mut notebook = SubList::new();
+        notebook.set_header(elm.collection.name.as_str());
+
+        let sublist_data = notebook.list().data();
+        for child in elm.children.iter() {
+            sublist_data.push(child.collection.clone());
+        }
+
+        window.notebooks.push(notebook);
+    }
+    window.notebooks.update();
+}
index ecc0c1aaf97423cf9a7b7fc12d35a7a1d4b8e13d..656d1451f574f79ceba3efe90a104dc4013cd603 100644 (file)
@@ -1,4 +1,6 @@
+use std::collections::HashMap;
 use sea_orm::entity::prelude::*;
+use crate::collection;
 
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
 #[sea_orm(table_name = "collection")]
@@ -8,10 +10,15 @@ pub struct Model {
     pub profile_id: i32,
 
     pub name: String,
-    pub path: String,
+    pub parent: String,
     pub icon: String,
     pub kind: CollectionType,
+}
 
+impl Model {
+    pub fn depth(&self) -> usize {
+        self.parent.chars().filter(|c| *c == '/').count()
+    }
 }
 
 #[derive(EnumIter, DeriveActiveEnum, Clone, Debug, PartialEq)]
@@ -27,3 +34,55 @@ pub enum CollectionType {
 pub enum Relation {}
 
 impl ActiveModelBehavior for ActiveModel {}
+
+
+#[derive(Clone)]
+pub struct Node {
+    pub collection: Model,
+    pub children: Vec<Node>
+}
+
+struct Path {
+    str: String,
+    depth: usize,
+}
+
+pub fn create_notebook_hierarchy(list: Vec<Model>) -> Vec<Node> {
+    let mut tree = HashMap::new();
+    let mut order = Vec::new();
+
+    // create a map of all nodes with the full path as key
+    for elm in list {
+        let path_str = format!("{}/{}", elm.parent, elm.name);
+        let depth  = elm.depth();
+        let path = Path { str: path_str, depth:  depth };
+
+        let node = Node {
+            collection: elm,
+            children: Vec::new()
+        };
+
+        tree.insert(path.str.clone(), node);
+        order.push(path);
+    }
+
+    // sort nodes by depth, because we need to process the tree from bottom to top
+    order.sort_by(|a, b| {
+        b.depth.cmp(&a.depth)
+    });
+
+    // transform node map into a tree
+    let mut nodes = Vec::new();
+
+    for path in order {
+        if let Some(node) = tree.remove(&path.str) {
+            if let Some(parent) = tree.get_mut(&node.collection.parent) {
+                parent.children.push(node);
+            } else {
+                nodes.push(node);
+            }
+        }
+    }
+
+    nodes
+}
index 5f66a189bc674afef76f10f1216e228a26e120f7..33e01c46e892ad99d86df5985b9db2a1277cffc4 100644 (file)
@@ -27,7 +27,7 @@ impl MigrationTrait for Migration {
                     .col(pk_auto("collection_id"))
                     .col(integer("profile_id"))
                     .col(string("name"))
-                    .col(string("path"))
+                    .col(string("parent"))
                     .col(string("icon"))
                     .col(integer("kind"))
                     .foreign_key(