From ce7e015b02ff5288a18af6fc24cd70a4ef80614c Mon Sep 17 00:00:00 2001 From: xeruf <27jf@pm.me> Date: Thu, 25 Jul 2024 22:40:35 +0300 Subject: [PATCH] feat: activating tags --- README.md | 5 ++++- src/main.rs | 4 ++++ src/task.rs | 5 ++++- src/tasks.rs | 45 +++++++++++++++++++++++++++++++++------------ 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index dfba105..1b91b48 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,9 @@ Recommendation: Flat hierarchy, using tags for filtering (TBI) ## Reference -TASK add syntax: `NAME: TAG1 TAG2` +### Command Syntax + +TASK add syntax: `NAME: TAG1 TAG2 ...` - `TASK` - create task - `.` - clear filters and reload @@ -35,6 +37,7 @@ Dots can be repeated to move to parent tasks - `>[TEXT]` - Complete active task and move to parent, with optional state description - `<[TEXT]` - Close active task and move to parent, with optional state description - `-TEXT` - add text note (comment / description) +- `#TAG` - filter by tag State descriptions can be used for example for Kanban columns. diff --git a/src/main.rs b/src/main.rs index f0a299b..a63771b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -200,6 +200,10 @@ async fn main() { }); tasks.move_up() } + + Some('#') => { + tasks.add_tag(input[1..].to_string()); + } Some('.') => { let mut dots = 1; diff --git a/src/task.rs b/src/task.rs index 08b802b..4018ab6 100644 --- a/src/task.rs +++ b/src/task.rs @@ -7,13 +7,16 @@ pub(crate) struct Task { pub(crate) event: Event, pub(crate) children: HashSet, pub(crate) props: BTreeSet, + /// Cached sorted tags of the event + pub(crate) tags: BTreeSet, } impl Task { pub(crate) fn new(event: Event) -> Task { Task { - event, children: Default::default(), props: Default::default(), + tags: event.tags.iter().cloned().collect(), + event, } } diff --git a/src/tasks.rs b/src/tasks.rs index 751632a..b2550d9 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -1,7 +1,8 @@ -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap}; use std::iter::once; use nostr_sdk::{Event, EventBuilder, EventId, Keys, Kind, Tag}; +use nostr_sdk::Tag::Hashtag; use crate::{EventSender, TASK_KIND}; use crate::task::{State, Task}; @@ -17,8 +18,10 @@ pub(crate) struct Tasks { /// Positive: Go down the respective level pub(crate) depth: i8, - /// The task currently selected. + /// Currently active task position: Option, + /// Currently active tags + tags: BTreeSet, /// A filtered view of the current tasks view: Vec, @@ -32,6 +35,7 @@ impl Tasks { properties: vec!["id".into(), "name".into(), "state".into(), "ttime".into()], position: None, view: Default::default(), + tags: Default::default(), depth: 1, sender, } @@ -65,6 +69,12 @@ impl Tasks { pub(crate) fn taskpath(&self, id: Option) -> String { join_tasks(self.traverse_up_from(id)) + + &self + .tags + .iter() + .map(|t| format!(" #{}", t.content().unwrap())) + .collect::>() + .join("") } pub(crate) fn traverse_up_from(&self, id: Option) -> ParentIterator { @@ -132,7 +142,7 @@ impl Tasks { if res.len() > 0 { return res; } - self.position.map_or_else( + let tasks = self.position.map_or_else( || { if self.depth > 8 { self.tasks.values().collect() @@ -155,7 +165,18 @@ impl Tasks { .get(&p) .map_or(Vec::new(), |t| self.resolve_tasks(t.children.iter())) }, - ) + ); + if self.tags.is_empty() { + tasks + } else { + tasks + .into_iter() + .filter(|t| { + let mut iter = t.tags.iter(); + self.tags.iter().all(|tag| iter.any(|t| t == tag)) + }) + .collect() + } } pub(crate) fn print_current_tasks(&self) { @@ -187,6 +208,11 @@ impl Tasks { self.view = view } + pub(crate) fn add_tag(&mut self, tag: String) { + self.view.clear(); + self.tags.insert(Hashtag(tag)); + } + pub(crate) fn move_up(&mut self) { self.move_to( self.position @@ -197,6 +223,7 @@ impl Tasks { pub(crate) fn move_to(&mut self, id: Option) { self.view.clear(); + self.tags.clear(); if id == self.position { return; } @@ -220,18 +247,12 @@ impl Tasks { // Updates pub(crate) fn build_task(&self, input: &str) -> EventBuilder { - let mut tags: Vec = Vec::new(); + let mut tags: Vec = self.tags.iter().cloned().collect(); self.position.inspect(|p| tags.push(Tag::event(*p))); return match input.split_once(": ") { None => EventBuilder::new(Kind::from(TASK_KIND), input, tags), Some(s) => { - tags.append( - &mut s - .1 - .split(" ") - .map(|t| Tag::Hashtag(t.to_string())) - .collect(), - ); + tags.append(&mut s.1.split(" ").map(|t| Hashtag(t.to_string())).collect()); EventBuilder::new(Kind::from(TASK_KIND), s.0, tags) } };