feat: enable state filtering

This commit is contained in:
xeruf 2024-07-26 11:07:47 +03:00
parent 960a5210c6
commit 79c3174f4f
3 changed files with 56 additions and 23 deletions

View File

@ -36,10 +36,12 @@ Dots can be repeated to move to parent tasks
- `:[IND][COL]` - add / remove property column COL to IND or end - `:[IND][COL]` - add / remove property column COL to IND or end
- `>[TEXT]` - Complete active task and move to parent, with optional state description - `>[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]` - Close active task and move to parent, with optional state description
- `-TEXT` - add text note (comment / description)
- `#TAG` - filter by tag - `#TAG` - filter by tag
- `?TAG` - filter by state (type or description)
- `-TEXT` - add text note (comment / description)
State descriptions can be used for example for Kanban columns. State descriptions can be used for example for Kanban columns.
An active tag or state filter will also create new tasks with those corresponding attributes.
### Available Columns ### Available Columns

View File

@ -139,23 +139,24 @@ async fn main() {
println!(); println!();
loop { loop {
while let Ok(notification) = notifications.try_recv() {
if let RelayPoolNotification::Event {
subscription_id,
event,
..
} = notification
{
print_event(&event);
tasks.add(*event);
}
}
tasks.print_current_tasks(); tasks.print_current_tasks();
print!(" {}) ", tasks.taskpath(tasks.get_position())); print!(" {}) ", tasks.taskpath(tasks.get_position()));
stdout().flush().unwrap(); stdout().flush().unwrap();
match stdin().lines().next() { match stdin().lines().next() {
Some(Ok(input)) => { Some(Ok(input)) => {
while let Ok(notification) = notifications.try_recv() {
if let RelayPoolNotification::Event {
subscription_id,
event,
..
} = notification
{
print_event(&event);
tasks.add(*event);
}
}
let mut iter = input.chars(); let mut iter = input.chars();
let op = iter.next(); let op = iter.next();
match op { match op {
@ -188,6 +189,25 @@ async fn main() {
} }
}, },
Some('?') => {
let arg = &input[1..];
tasks.move_to(tasks.get_position());
tasks.set_filter(
tasks
.current_tasks()
.into_iter()
.filter(|t| {
if arg.is_empty() {
t.pure_state() == State::Open
} else {
t.state().is_some_and(|s| s.get_label() == arg)
}
})
.map(|t| t.event.id)
.collect(),
);
}
Some('-') => tasks.add_note(&input[1..]), Some('-') => tasks.add_note(&input[1..]),
Some('>') | Some('<') => { Some('>') | Some('<') => {
@ -200,7 +220,7 @@ async fn main() {
}); });
tasks.move_up() tasks.move_up()
} }
Some('#') => { Some('#') => {
tasks.add_tag(input[1..].to_string()); tasks.add_tag(input[1..].to_string());
} }

View File

@ -42,14 +42,7 @@ impl Task {
fn states(&self) -> impl Iterator<Item = TaskState> + '_ { fn states(&self) -> impl Iterator<Item = TaskState> + '_ {
self.props.iter().filter_map(|event| { self.props.iter().filter_map(|event| {
match event.kind.as_u32() { event.kind.try_into().ok().map(|s| TaskState {
1630 => Some(State::Open),
1631 => Some(State::Done),
1632 => Some(State::Closed),
1633 => Some(State::Active),
_ => None,
}
.map(|s| TaskState {
name: if event.content.is_empty() { name: if event.content.is_empty() {
None None
} else { } else {
@ -61,7 +54,7 @@ impl Task {
}) })
} }
fn state(&self) -> Option<TaskState> { pub(crate) fn state(&self) -> Option<TaskState> {
self.states().max_by_key(|t| t.time) self.states().max_by_key(|t| t.time)
} }
@ -130,11 +123,16 @@ impl Task {
} }
} }
struct TaskState { pub(crate) struct TaskState {
name: Option<String>, name: Option<String>,
state: State, state: State,
time: Timestamp, time: Timestamp,
} }
impl TaskState {
pub(crate) fn get_label(&self) -> String {
self.name.clone().unwrap_or_else(|| self.state.to_string())
}
}
impl fmt::Display for TaskState { impl fmt::Display for TaskState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
@ -155,6 +153,19 @@ pub(crate) enum State {
Active, Active,
Done, Done,
} }
impl TryFrom<Kind> for State {
type Error = ();
fn try_from(value: Kind) -> Result<Self, Self::Error> {
match value.as_u32() {
1630 => Ok(State::Open),
1631 => Ok(State::Done),
1632 => Ok(State::Closed),
1633 => Ok(State::Active),
_ => Err(()),
}
}
}
impl State { impl State {
pub(crate) fn kind(&self) -> Kind { pub(crate) fn kind(&self) -> Kind {
match self { match self {