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
- `>[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
- `?TAG` - filter by state (type or description)
- `-TEXT` - add text note (comment / description)
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

View File

@ -139,6 +139,12 @@ async fn main() {
println!();
loop {
tasks.print_current_tasks();
print!(" {}) ", tasks.taskpath(tasks.get_position()));
stdout().flush().unwrap();
match stdin().lines().next() {
Some(Ok(input)) => {
while let Ok(notification) = notifications.try_recv() {
if let RelayPoolNotification::Event {
subscription_id,
@ -150,12 +156,7 @@ async fn main() {
tasks.add(*event);
}
}
tasks.print_current_tasks();
print!(" {}) ", tasks.taskpath(tasks.get_position()));
stdout().flush().unwrap();
match stdin().lines().next() {
Some(Ok(input)) => {
let mut iter = input.chars();
let op = iter.next();
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('>') | Some('<') => {

View File

@ -42,14 +42,7 @@ impl Task {
fn states(&self) -> impl Iterator<Item = TaskState> + '_ {
self.props.iter().filter_map(|event| {
match event.kind.as_u32() {
1630 => Some(State::Open),
1631 => Some(State::Done),
1632 => Some(State::Closed),
1633 => Some(State::Active),
_ => None,
}
.map(|s| TaskState {
event.kind.try_into().ok().map(|s| TaskState {
name: if event.content.is_empty() {
None
} 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)
}
@ -130,11 +123,16 @@ impl Task {
}
}
struct TaskState {
pub(crate) struct TaskState {
name: Option<String>,
state: State,
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 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
@ -155,6 +153,19 @@ pub(crate) enum State {
Active,
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 {
pub(crate) fn kind(&self) -> Kind {
match self {