diff --git a/.config/config.ron b/.config/config.ron index 88153cd..52270bc 100644 --- a/.config/config.ron +++ b/.config/config.ron @@ -1,73 +1,74 @@ ( directory: "/Users/suri/dev/projects/filaments/ZettelKasten", global_key_binds: { - "ctrl-z": Suspend, "up": MoveUp, - "down": MoveDown, "ctrl-c": Quit, + "ctrl-z": Suspend, + "down": MoveDown, }, zk: ( keybinds: { + "ctrl-n": NewZettel, + "enter": OpenZettel, "tab": SwitchTo( page: Todo(Explorer), ), - "ctrl-n": NewZettel, - "enter": OpenZettel, }, ), todo: ( explorer: ( keybinds: { + "g": NewSubGroup, "tab": SwitchTo( page: Zk, ), - "3": SwitchTo( - page: Todo(TaskList), - ), - "g": NewSubGroup, - "k": MoveUp, "2": SwitchTo( page: Todo(Inspector), ), - "j": MoveDown, - "shift-g": NewGroup, + "3": SwitchTo( + page: Todo(TaskList), + ), "1": SwitchTo( page: Todo(Explorer), ), + "k": MoveUp, + "t": NewTask, + "shift-g": NewGroup, + "j": MoveDown, }, ), inspector: ( keybinds: { - "3": SwitchTo( - page: Todo(TaskList), + "tab": SwitchTo( + page: Zk, ), "1": SwitchTo( page: Todo(Explorer), ), + "3": SwitchTo( + page: Todo(TaskList), + ), "2": SwitchTo( page: Todo(Inspector), ), - "tab": SwitchTo( - page: Zk, - ), }, ), tasklist: ( keybinds: { - "k": MoveUp, "1": SwitchTo( page: Todo(Explorer), ), + "tab": SwitchTo( + page: Zk, + ), + "j": MoveDown, + "k": MoveUp, "2": SwitchTo( page: Todo(Inspector), ), "3": SwitchTo( page: Todo(TaskList), ), - "j": MoveDown, - "tab": SwitchTo( - page: Zk, - ), }, ), ), diff --git a/.config/default_config.ron b/.config/default_config.ron index 23f4fbe..0f8904b 100644 --- a/.config/default_config.ron +++ b/.config/default_config.ron @@ -24,6 +24,7 @@ "k": MoveUp, "shift-g": NewGroup, "g": NewSubGroup, + "t": NewTask, }, ), inspector: ( diff --git a/crates/tree/src/node.rs b/crates/tree/src/node.rs index 074174a..3921de6 100644 --- a/crates/tree/src/node.rs +++ b/crates/tree/src/node.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; + use serde::{Deserialize, Serialize}; use crate::NodeId; @@ -110,6 +112,14 @@ impl Node { &self.children } + /// Are able to sort the children of this `Node` + pub fn sort_children_by(&mut self, mut compare: F) + where + F: FnMut(&NodeId, &NodeId) -> Ordering, + { + self.children.sort_by(|a, b| compare(a, b)); + } + pub(crate) const fn children_mut(&mut self) -> &mut Vec { &mut self.children } diff --git a/flake.lock b/flake.lock index a4ff068..8f1e759 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "crane": { "locked": { - "lastModified": 1775839657, - "narHash": "sha256-SPm9ck7jh3Un9nwPuMGbRU04UroFmOHjLP56T10MOeM=", + "lastModified": 1776533550, + "narHash": "sha256-8mTHsQ8cB0jGlXE4WWKqpQFQPM/VotDnr2uzfrOGNKI=", "owner": "ipetkov", "repo": "crane", - "rev": "7cf72d978629469c4bd4206b95c402514c1f6000", + "rev": "e24d86e91348e3d44014974fa24c9a22cfd663b5", "type": "github" }, "original": { @@ -23,11 +23,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1776326782, - "narHash": "sha256-QzTHb5vhPVensbkL7+WhemxFYXINcdZB1SQ5EMjG2AU=", + "lastModified": 1776497206, + "narHash": "sha256-Em+RSdFnwyyKPGUBFtQYtVjm+1UvIc9gOR91Y22zlzg=", "owner": "nix-community", "repo": "fenix", - "rev": "47ece0146691d625f10a3d2ec4a2c04fca29a35b", + "rev": "df2295365fb081fe0745449762a771290782c22d", "type": "github" }, "original": { @@ -60,11 +60,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1776280158, - "narHash": "sha256-0uGgwgFVPQ28cVx7onaB1iTCHGi20MNx54e6KZbYnMs=", + "lastModified": 1776441750, + "narHash": "sha256-1rVfG+mj8R4ze+lSYCa4iAv7FzrB03Cprtxmd1MfZak=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "94c2f68935467a6381a4f3504ae6c709b5fd3c61", + "rev": "251df518d73abb5c5d573c4d5d266a3edae9ca5a", "type": "github" }, "original": { diff --git a/src/tui/components/todo/explorer.rs b/src/tui/components/todo/explorer.rs index f952c43..f7d111b 100644 --- a/src/tui/components/todo/explorer.rs +++ b/src/tui/components/todo/explorer.rs @@ -8,6 +8,7 @@ use tree::NodeId; use crate::types::{Group, TodoNode, TodoNodeKind, TodoTree}; +#[derive(Debug)] pub struct Explorer<'text> { pub render_list: ratatui::widgets::List<'text>, #[allow(dead_code)] diff --git a/src/tui/components/todo/mod.rs b/src/tui/components/todo/mod.rs index 5a87650..3ddd570 100644 --- a/src/tui/components/todo/mod.rs +++ b/src/tui/components/todo/mod.rs @@ -12,7 +12,7 @@ use tracing::debug; use crate::{ tui::{Page, Signal, components::Component}, - types::{Group, KastenHandle}, + types::{Group, KastenHandle, Priority, Task}, }; mod explorer; @@ -83,6 +83,7 @@ impl Todo<'_> { let kt = self.kh.read().await; let tree = &kt.todo_tree; + debug!("tree after refresh {tree:#?}"); let splits = self .layouts .split(Rect::new(0, 0, self.area.width, self.area.height)); @@ -93,6 +94,8 @@ impl Todo<'_> { let mut explorer = Explorer::new(tree, &tree.root_id, l_state, splits.explorer.width); let mut task_list = TaskList::new(tree, &tree.root_id, l_state, splits.task_list.width); + debug!("explorer constructed after refresh {explorer:#?}"); + drop(kt); let explorer_selection_idx = @@ -242,6 +245,7 @@ impl Component for Todo<'_> { Ok(()) } + #[allow(clippy::too_many_lines)] async fn update(&mut self, signal: Signal) -> color_eyre::Result> { let explorer = self .explorer @@ -317,10 +321,26 @@ impl Component for Todo<'_> { return Ok(None); } - todo!() + let mut kt = self.kh.write().await; + let Some(parent) = explorer + .group_of_current_selection(&kt.todo_tree) + // .cloned() + .map(|parent| parent.id.clone()) + else { + return Ok(None); + }; + let task = Task::new( + NanoId::default().to_string(), + parent, + &mut kt, + None, + Priority::default(), + ) + .await?; - // let ancestors = kt.todo_tree.tree.ancestors(node_id) = todo!(); - // let task = Task::new("wahoo"); + drop(kt); + debug!("created task: {task:#?}"); + return Ok(Some(Signal::Refresh)); } Signal::NewSubGroup => { diff --git a/src/tui/components/zk/mod.rs b/src/tui/components/zk/mod.rs index 6751d02..0e119c7 100644 --- a/src/tui/components/zk/mod.rs +++ b/src/tui/components/zk/mod.rs @@ -320,17 +320,13 @@ impl Component for Zk<'_> { self.refresh().await?; } - Signal::MoveDown => { - if self.active { - zettel_list.state.select_next(); - self.update_views_from_zettel_list_selection().await?; - } + Signal::MoveDown if self.active => { + zettel_list.state.select_next(); + self.update_views_from_zettel_list_selection().await?; } - Signal::MoveUp => { - if self.active { - zettel_list.state.select_previous(); - self.update_views_from_zettel_list_selection().await?; - } + Signal::MoveUp if self.active => { + zettel_list.state.select_previous(); + self.update_views_from_zettel_list_selection().await?; } Signal::OpenZettel => { diff --git a/src/tui/components/zk/search.rs b/src/tui/components/zk/search.rs index fa05b95..bf098aa 100644 --- a/src/tui/components/zk/search.rs +++ b/src/tui/components/zk/search.rs @@ -107,7 +107,7 @@ impl Search<'_> { }) .collect(); - results.sort_by(|a, b| b.1.cmp(&a.1)); + results.sort_by_key(|b| std::cmp::Reverse(b.1)); results.into_iter().map(|(i, _)| i).collect() } diff --git a/src/types/group.rs b/src/types/group.rs index e52f4ca..f73a972 100644 --- a/src/types/group.rs +++ b/src/types/group.rs @@ -3,9 +3,8 @@ use dto::{ DateTime, GroupActiveModel, GroupEntity, GroupModelEx, IntoActiveModel as _, NanoId, TagActiveModel, TagEntity, ZettelEntity, }; -use tree::Node; -use crate::types::{Kasten, Priority, Tag, TodoNode, Zettel, frontmatter}; +use crate::types::{Kasten, Priority, Tag, Zettel, frontmatter}; /// A `Group` which contains tasks! #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -91,42 +90,7 @@ impl Group { .expect("We just inserted it") .into(); - // we should also insert the group into the kasten - - let parent_node_id = group - .parent_id - .clone() - .and_then(|id| kt.todo_tree.nanoid_to_nodeid.get(&id)) - .unwrap_or(&kt.todo_tree.root_id); - - let my_depth = if *parent_node_id == kt.todo_tree.root_id { - 0 - } else { - kt.todo_tree - .tree - .get(parent_node_id) - .expect("Must exist inside tree") - .data() - .depth - + 1 - }; - - let inserted_node_id = kt - .todo_tree - .tree - .insert( - Node::new(TodoNode::new( - super::TodoNodeKind::Group(Box::new(group.clone())), - my_depth, - )), - tree::InsertBehavior::UnderNode(parent_node_id), - ) - .expect("Insertion of group should not error!"); - - kt.todo_tree - .nanoid_to_nodeid - .insert(group.id.clone(), inserted_node_id); - + kt.todo_tree.insert_group(&group); Ok(group) } } diff --git a/src/types/kasten/index/mod.rs b/src/types/kasten/index/mod.rs index 086bc7c..f080b6c 100644 --- a/src/types/kasten/index/mod.rs +++ b/src/types/kasten/index/mod.rs @@ -41,7 +41,7 @@ impl Index { .par_bridge() .flatten() .filter(|entry| { - entry.file_type().map(|ft| ft.is_file()).unwrap_or(false) + entry.file_type().is_ok_and(|ft| ft.is_file()) && entry .path() .extension() diff --git a/src/types/kasten/todo_tree.rs b/src/types/kasten/todo_tree.rs index 858b02d..1bab96d 100644 --- a/src/types/kasten/todo_tree.rs +++ b/src/types/kasten/todo_tree.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{cmp::Ordering, collections::HashMap}; use color_eyre::eyre::{Context, Result}; use dto::{ @@ -137,4 +137,101 @@ impl TodoTree { Ok(()) } + + pub fn insert_group(&mut self, group: &Group) { + let parent_node_id = group + .parent_id + .clone() + .and_then(|id| self.nanoid_to_nodeid.get(&id)) + .unwrap_or(&self.root_id) + .clone(); + + let my_depth = if parent_node_id == self.root_id { + 0 + } else { + self.tree + .get(&parent_node_id) + .expect("Must exist inside tree") + .data() + .depth + + 1 + }; + + let inserted_node_id = self + .tree + .insert( + Node::new(TodoNode::new( + super::TodoNodeKind::Group(Box::new(group.clone())), + my_depth, + )), + tree::InsertBehavior::UnderNode(&parent_node_id), + ) + .expect("Insertion of group should not error!"); + + self.reorder_chidren(&parent_node_id); + + self.nanoid_to_nodeid + .insert(group.id.clone(), inserted_node_id); + } + + pub fn insert_task(&mut self, task: &Task) { + let parent_node_id = self + .nanoid_to_nodeid + .get(&task.group_id) + .expect("The group must already be in the lookup hashmap") + .clone(); + + let my_depth = self + .tree + .get(&parent_node_id) + .expect("Must exist inside tree") + .data() + .depth + + 1; + + let inserted_node_id = self + .tree + .insert( + Node::new(TodoNode::new( + super::TodoNodeKind::Task(Box::new(task.clone())), + my_depth, + )), + tree::InsertBehavior::UnderNode(&parent_node_id), + ) + .expect("Insertion of Task should not error!"); + + self.reorder_chidren(&parent_node_id); + + self.nanoid_to_nodeid + .insert(task.id.clone(), inserted_node_id); + } + + fn reorder_chidren(&mut self, parent_node_id: &NodeId) { + let children = self + .tree + .children(parent_node_id) + .expect("Must be valid") + .zip( + self.tree + .children_ids(parent_node_id) + .expect("Must be valid"), + ) + .map(|(a, b)| (b.clone(), matches!(a.data().kind, TodoNodeKind::Task(_)))) + .collect::>(); + + let parent = self + .tree + .get_mut(parent_node_id) + .expect("parent must exist"); + + parent.sort_children_by(|a, _| { + let a = children.get(a).expect("must exist"); + + if *a { + return Ordering::Less; + } + + Ordering::Equal + }); + } } diff --git a/src/types/task.rs b/src/types/task.rs index 062a566..05d5cc9 100644 --- a/src/types/task.rs +++ b/src/types/task.rs @@ -64,10 +64,6 @@ impl Task { .into_active_model(), ) .set_due(due) - // .set_due(Some(DateTime::new( - // Date::from_ymd_opt(2026, 1, 31).unwrap(), - // Time::from_hms_opt(10, 10, 10).unwrap(), - // ))) .insert(&kt.db) .await?; @@ -79,20 +75,20 @@ impl Task { .await? .expect("We just inserted it"); - let mut task = TaskEntity::load() + let mut task_am = TaskEntity::load() .with((ZettelEntity, TagEntity)) .filter_by_nano_id(inserted.nano_id) .one(&kt.db) .await? .expect("We just inserted it"); - task.group = HasOne::Loaded(Box::new(group)); + task_am.group = HasOne::Loaded(Box::new(group)); - println!("task: {task:#?}"); + let task: Self = task_am.into(); - // Ok(task.into()) + kt.todo_tree.insert_task(&task); - todo!() + Ok(task) } pub fn due(&self) -> Option {