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;
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>,
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);
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() {
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);
}
struct App {
backend: BackendHandle,
+
+ notebooks: Vec<Node>,
}
create_toolbar(app);
- create_window(app);
+ let window = create_window(app);
+ window.window_data(|data| init_window_data(data, self));
}
}
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]
}
}
-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| {
obj.sourcelist(|b|{
b.fill(true);
b.value(&data.notebooks);
+ b.getvalue(|elm,_row,item|{
+ item.label = Some(elm.name.clone());
+ });
});
});
});
});
});
- })
+ });
});
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();
+}
+use std::collections::HashMap;
use sea_orm::entity::prelude::*;
+use crate::collection;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "collection")]
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)]
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
+}
.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(