My personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: Priority Editing

+181 -59
+7 -1
src/tui/app.rs
··· 11 11 use crate::{ 12 12 config::Config, 13 13 tui::{Event, Tui, components::Viewport}, 14 - types::{KastenHandle, ZettelId}, 14 + types::{KastenHandle, TodoTree, ZettelId}, 15 15 }; 16 16 17 17 pub use crate::tui::components::TodoRegion; ··· 241 241 Signal::SwitchTo { page } => { 242 242 info!("Switched page to {page:#?}"); 243 243 self.page = page; 244 + } 245 + 246 + Signal::Refresh => { 247 + let mut kt = self.kh.write().await; 248 + // fuck it we just fully rebuild the tree, how computationally expensive could it even be 249 + kt.todo_tree = TodoTree::construct(&kt.db).await.expect("Must not error"); 244 250 } 245 251 246 252 Signal::Suspend => self.should_suspend = true,
+75 -9
src/tui/components/todo/inspector/mod.rs
··· 15 15 Signal, 16 16 components::{Component, DEFAULT_NAME}, 17 17 }, 18 - types::{Group, KastenHandle, Task, TodoNode, TodoNodeKind}, 18 + types::{Group, KastenHandle, Priority, Task, TodoNode, TodoNodeKind}, 19 19 }; 20 20 21 21 mod rootview; ··· 134 134 } 135 135 } 136 136 } 137 + 138 + async fn refresh(&mut self) { 139 + // cheaper to clone this than the node 140 + let kh = self.kh.clone(); 141 + let kt = kh.read().await; 142 + 143 + let Some(ref inspecting) = self.inspecting else { 144 + return; 145 + }; 146 + let node = kt.todo_tree.get_node_by_nano_id(inspecting).data(); 147 + self.inspect(node); 148 + 149 + drop(kt); 150 + } 137 151 } 138 152 139 153 pub enum RenderData<'text> { ··· 194 208 195 209 priority.set_cursor_style(Style::default().reversed()); 196 210 priority.set_cursor_line_style(Style::default().underlined()); 211 + priority.move_cursor(CursorMove::WordBack); 212 + priority.delete_line_by_end(); 197 213 198 214 self.editing = Some(Edit::Priority); 199 215 return Ok(Some(Signal::EnterRawText)); 200 216 } 201 217 218 + Signal::Refresh => { 219 + self.refresh().await; 220 + } 221 + 202 222 _ => {} 203 223 } 204 224 Ok(None) 205 225 } 206 226 227 + #[expect(clippy::too_many_lines)] 207 228 async fn handle_key_event(&mut self, key: KeyEvent) -> color_eyre::Result<Option<Signal>> { 208 229 let signal_tx = self 209 230 .signal_tx ··· 262 283 RenderData::Group { widget } => &mut widget.priority, 263 284 }; 264 285 265 - if key.code == KeyCode::Enter { 266 - priority.set_cursor_style(Style::reset()); 267 - priority.set_cursor_line_style(Style::reset()); 286 + // we dont want them entering into this 287 + if key.code != KeyCode::Enter { 288 + priority.input_without_shortcuts(key); 289 + } 268 290 291 + let priority_str = priority.lines()[0].as_str(); 292 + 293 + if let Ok(prio) = Priority::try_from(priority_str) { 269 294 priority.set_block( 270 295 priority 271 296 .block() 272 297 .cloned() 273 298 .expect("All of them should have blocks") 274 - .border_style(Style::default().fg(Color::Reset)), 299 + .border_style(Style::default().fg(Color::Green)), 275 300 ); 276 301 277 - self.editing = None; 278 - Ok(Some(Signal::ExitRawText)) 302 + if key.code == KeyCode::Enter { 303 + self.editing = None; 304 + signal_tx.send(Signal::ExitRawText)?; 305 + 306 + priority.set_cursor_style(Style::reset()); 307 + priority.set_cursor_line_style(Style::reset()); 308 + 309 + priority.set_block( 310 + priority 311 + .block() 312 + .cloned() 313 + .expect("All of them should have blocks") 314 + .border_style(Style::default().fg(Color::Reset)), 315 + ); 316 + 317 + let id = self 318 + .inspecting 319 + .clone() 320 + .expect("Invariant Broken, this must be some id"); 321 + 322 + let kt = self.kh.read().await; 323 + 324 + match &self.render_data { 325 + RenderData::Task { .. } => { 326 + Task::alter_priority(id.clone(), prio, &kt).await?; 327 + } 328 + RenderData::Group { .. } => { 329 + Group::alter_priority(id.clone(), prio, &kt).await?; 330 + } 331 + RenderData::Root { .. } => unreachable!("Already returned above"), 332 + } 333 + 334 + drop(kt); 335 + 336 + return Ok(Some(Signal::Refresh)); 337 + } 279 338 } else { 280 - priority.input_without_shortcuts(key); 281 - Ok(None) 339 + priority.set_block( 340 + priority 341 + .block() 342 + .cloned() 343 + .expect("All of them should have blocks") 344 + .border_style(Style::default().fg(Color::Red)), 345 + ); 282 346 } 347 + 348 + Ok(None) 283 349 } 284 350 285 351 None => return Ok(None),
+2 -6
src/tui/components/todo/mod.rs
··· 13 13 14 14 use crate::{ 15 15 tui::{Page, Signal, components::Component}, 16 - types::{Group, KastenHandle, Priority, Task, TodoTree}, 16 + types::{Group, KastenHandle, Priority, Task}, 17 17 }; 18 18 19 19 mod explorer; ··· 83 83 .selected() 84 84 .and_then(|idx| task_list.id_list.get(idx)); 85 85 86 - let mut kt = self.kh.write().await; 87 - 88 - // fuck it we just fully rebuild the tree, how computationally expensive could it even be 89 - kt.todo_tree = TodoTree::construct(&kt.db).await.expect("Must not error"); 86 + let kt = self.kh.read().await; 90 87 91 88 let tree = &kt.todo_tree; 92 89 ··· 131 128 132 129 self.explorer = Some(explorer); 133 130 self.task_list = Some(task_list); 134 - self.update_inspector_from_selection().await; 135 131 } 136 132 137 133 async fn update_inspector_from_selection(&mut self) {
+57 -43
src/types/group.rs
··· 28 28 } 29 29 30 30 impl Group { 31 - pub fn created_at(&self) -> String { 32 - self.created_at 33 - .format(frontmatter::DATE_FMT_STR) 34 - .to_string() 35 - } 36 - pub fn modified_at(&self) -> String { 37 - self.modified_at 38 - .format(frontmatter::DATE_FMT_STR) 39 - .to_string() 40 - } 41 - 42 - pub async fn alter_name( 43 - id: NanoId, 44 - new_name: impl Into<String>, 45 - kt: &mut Kasten, 46 - ) -> Result<()> { 47 - let new_name = new_name.into(); 48 - 49 - let g = GroupEntity::load() 50 - .filter_by_nano_id(id.clone()) 51 - .with(TagEntity) 52 - .with((ZettelEntity, TagEntity)) 53 - .one(&kt.db) 54 - .await? 55 - .expect("Invariant Broken: Must exist"); 56 - 57 - let tag_id = g.tag.as_ref().expect("Must be loaded").nano_id.clone(); 58 - 59 - let zettel_id = g.zettel_id.clone(); 60 - 61 - let _ = g 62 - .into_active_model() 63 - .set_name(new_name.as_str()) 64 - .update(&kt.db) 65 - .await?; 66 - 67 - Tag::alter_name(tag_id, &new_name, kt).await?; 68 - 69 - Zettel::alter_name(zettel_id.into(), &new_name, kt).await?; 70 - 71 - Ok(()) 72 - } 73 - 74 31 pub async fn new( 75 32 name: impl Into<String>, 76 33 parent_id: Option<NanoId>, ··· 124 81 125 82 kt.todo_tree.insert_group(&group); 126 83 Ok(group) 84 + } 85 + 86 + pub async fn alter_name( 87 + id: NanoId, 88 + new_name: impl Into<String>, 89 + kt: &mut Kasten, 90 + ) -> Result<()> { 91 + let new_name = new_name.into(); 92 + 93 + let g = GroupEntity::load() 94 + .filter_by_nano_id(id.clone()) 95 + .with(TagEntity) 96 + .with((ZettelEntity, TagEntity)) 97 + .one(&kt.db) 98 + .await? 99 + .expect("Invariant Broken: Must exist"); 100 + 101 + let tag_id = g.tag.as_ref().expect("Must be loaded").nano_id.clone(); 102 + 103 + let zettel_id = g.zettel_id.clone(); 104 + 105 + let _ = g 106 + .into_active_model() 107 + .set_name(new_name.as_str()) 108 + .update(&kt.db) 109 + .await?; 110 + 111 + Tag::alter_name(tag_id, &new_name, kt).await?; 112 + 113 + Zettel::alter_name(zettel_id.into(), &new_name, kt).await?; 114 + 115 + Ok(()) 116 + } 117 + 118 + pub async fn alter_priority(id: NanoId, new_prio: Priority, kt: &Kasten) -> Result<()> { 119 + GroupEntity::load() 120 + .filter_by_nano_id(id) 121 + .one(&kt.db) 122 + .await? 123 + .expect("Must exist") 124 + .into_active_model() 125 + .set_priority(new_prio) 126 + .update(&kt.db) 127 + .await?; 128 + 129 + Ok(()) 130 + } 131 + 132 + pub fn created_at(&self) -> String { 133 + self.created_at 134 + .format(frontmatter::DATE_FMT_STR) 135 + .to_string() 136 + } 137 + pub fn modified_at(&self) -> String { 138 + self.modified_at 139 + .format(frontmatter::DATE_FMT_STR) 140 + .to_string() 127 141 } 128 142 } 129 143
+26
src/types/priority.rs
··· 1 1 use std::fmt::Display; 2 2 3 + use color_eyre::eyre::eyre; 3 4 use dto::PriorityDTO; 4 5 5 6 /// An Enum for the various `Priority` levels ··· 26 27 write!(f, "{}", self.field1) 27 28 } 28 29 } 30 + 31 + impl TryFrom<&str> for Priority { 32 + type Error = color_eyre::Report; 33 + 34 + fn try_from(value: &str) -> Result<Self, Self::Error> { 35 + match value.to_ascii_lowercase().chars().next() { 36 + Some('a') => Ok(Self { 37 + field1: PriorityDTO::Asap, 38 + }), 39 + Some('h') => Ok(Self { 40 + field1: PriorityDTO::High, 41 + }), 42 + Some('m') => Ok(Self { 43 + field1: PriorityDTO::Medium, 44 + }), 45 + Some('l') => Ok(Self { 46 + field1: PriorityDTO::Low, 47 + }), 48 + Some('f') => Ok(Self { 49 + field1: PriorityDTO::Far, 50 + }), 51 + _ => Err(eyre!("Invalid Priority!")), 52 + } 53 + } 54 + }
+14
src/types/task.rs
··· 115 115 Ok(()) 116 116 } 117 117 118 + pub async fn alter_priority(id: NanoId, new_prio: Priority, kt: &Kasten) -> Result<()> { 119 + TaskEntity::load() 120 + .filter_by_nano_id(id) 121 + .one(&kt.db) 122 + .await? 123 + .expect("Must exist") 124 + .into_active_model() 125 + .set_priority(new_prio) 126 + .update(&kt.db) 127 + .await?; 128 + 129 + Ok(()) 130 + } 131 + 118 132 pub fn due(&self) -> Option<String> { 119 133 self.due 120 134 .map(|due| due.format(frontmatter::DATE_FMT_STR).to_string())