feat: allow setting priority context for creating tasks

This commit is contained in:
xeruf 2024-11-09 19:18:42 +01:00
parent dc8df51e0f
commit e9bee3c114
4 changed files with 38 additions and 13 deletions

View File

@ -156,7 +156,6 @@ Append `@TIME` to any task creation or change command to record the action with
- `<[TEXT]` - close active task and move up, with optional status description
- `!TEXT` - set status for current task from text and move up; empty: Open
- `!TIME: REASON` - defer current task to date
- TBI: `*[INT]` - set priority - can also be used in task creation, with any digit
- `,[TEXT]` - list notes or add text (activity / task description)
- TBI: `;[TEXT]` - list comments or comment on task
- TBI: show status history and creation with attribution
@ -171,9 +170,9 @@ Property Filters:
- `#TAG1 TAG2` - set tag filter
- `+TAG` - add tag filter (empty: list all used tags)
- `-TAG` - remove tag filters (by prefix)
- `?STATUS` - filter by status (type or description) - plain `?` to reset, `??` to show all
- `?STATUS` - set status filter (type or description) - plain `?` to reset, `??` to show all
- `*INT` - set priority filter
- `@[AUTHOR|TIME]` - filter by time or author (pubkey, or `@` for self, TBI: id prefix, name prefix)
- TBI: `**INT` - filter by priority
Status descriptions can be used for example for Kanban columns or review flows.
An active tag or status filter will also set that attribute for newly created tasks.

View File

@ -26,6 +26,7 @@ pub const PROP_KINDS: [Kind; 6] = [
PROCEDURE_KIND,
];
pub type Prio = u16;
pub const PRIO: &str = "priority";
// TODO: use formatting - bold / heading / italics - and generate from code
@ -95,7 +96,7 @@ pub(crate) fn extract_tags(input: &str) -> (String, Vec<Tag>) {
prio = Some(HIGH_PRIO);
return false
}
return match s[1..].parse::<u16>() {
return match s[1..].parse::<Prio>() {
Ok(num) => {
prio = Some(num * (if s.len() > 2 { 1 } else { 10 }));
false
@ -109,7 +110,7 @@ pub(crate) fn extract_tags(input: &str) -> (String, Vec<Tag>) {
let main = split.next().unwrap().join(" ");
let tags = extract_hashtags(&main)
.chain(split.flatten().map(|s| to_hashtag(&s)))
.chain(prio.map(|p| to_prio_tag(&p.to_string()))).collect();
.chain(prio.map(|p| to_prio_tag(p))).collect();
(main, tags)
}
@ -143,8 +144,8 @@ pub(crate) fn is_hashtag(tag: &Tag) -> bool {
.is_some_and(|letter| letter.character == Alphabet::T)
}
pub(crate) fn to_prio_tag(value: &str) -> Tag {
Tag::custom(TagKind::Custom(Cow::from(PRIO)), [if value.len() < 2 { format!("{value}0") } else { value.to_string() }])
pub(crate) fn to_prio_tag(value: Prio) -> Tag {
Tag::custom(TagKind::Custom(Cow::from(PRIO)), [value.to_string()])
}
#[test]

View File

@ -27,7 +27,7 @@ use tokio::time::error::Elapsed;
use tokio::time::timeout;
use crate::helpers::*;
use crate::kinds::{BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS, TRACKING_KIND};
use crate::kinds::{Prio, BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS, TRACKING_KIND};
use crate::task::{State, Task, TaskState, MARKER_DEPENDS};
use crate::tasks::{PropertyCollection, StateFilter, TasksRelay};
@ -536,8 +536,7 @@ async fn main() -> Result<()> {
match arg {
None => match tasks.get_position() {
None => {
info!("Filtering for bookmarked tasks");
tasks.set_view_bookmarks();
tasks.set_priority(None);
}
Some(pos) =>
match or_warn!(tasks.toggle_bookmark(pos)) {
@ -546,7 +545,16 @@ async fn main() -> Result<()> {
None => {}
}
},
Some(arg) => info!("Setting priority not yet implemented"),
Some(arg) => {
if arg == "*" {
info!("Showing only bookmarked tasks");
tasks.set_view_bookmarks();
} else {
tasks.set_priority(arg.parse()
.inspect_err(|e| warn!("Invalid Priority {arg}: {e}")).ok()
.map(|p: Prio| p * (if arg.len() < 2 { 10 } else { 1 })));
}
},
}
}

View File

@ -19,8 +19,8 @@ use regex::bytes::Regex;
use tokio::sync::mpsc::Sender;
use TagStandard::Hashtag;
const DEFAULT_PRIO: u16 = 25;
pub const HIGH_PRIO: u16 = 85;
const DEFAULT_PRIO: Prio = 25;
pub const HIGH_PRIO: Prio = 85;
/// Amount of seconds to treat as "now"
const MAX_OFFSET: u64 = 9;
@ -80,6 +80,8 @@ pub(crate) struct TasksRelay {
tags_excluded: BTreeSet<Tag>,
/// Current active state
state: StateFilter,
/// Current priority for filtering and new tasks
priority: Option<Prio>,
sender: EventSender,
overflow: VecDeque<Event>,
@ -171,6 +173,8 @@ impl TasksRelay {
tags: Default::default(),
tags_excluded: Default::default(),
state: Default::default(),
priority: None,
search_depth: 4,
view_depth: 0,
recurse_activities: true,
@ -356,6 +360,7 @@ impl TasksRelay {
.chain(self.tags_excluded.iter()
.map(|t| format!(" -#{}", t.content().unwrap())))
.chain(once(self.state.indicator()))
.chain(self.priority.map(|p| format!(" *{:02}", p)))
.join("")
}
@ -655,6 +660,15 @@ impl TasksRelay {
}
}
pub(crate) fn set_priority(&mut self, priority: Option<Prio>) {
self.view.clear();
match priority {
None => info!("Removing priority filter"),
Some(prio) => info!("Filtering for priority {}", prio),
}
self.priority = priority;
}
pub(crate) fn set_state_filter(&mut self, state: StateFilter) {
self.view.clear();
info!("Filtering for {}", state);
@ -858,10 +872,13 @@ impl TasksRelay {
/// Sanitizes input
pub(crate) fn make_task_with(&mut self, input: &str, tags: impl IntoIterator<Item=Tag>, set_state: bool) -> EventId {
let (input, input_tags) = extract_tags(input.trim());
let prio =
if input_tags.iter().find(|t| t.kind().to_string() == PRIO).is_some() { None } else { self.priority.map(|p| to_prio_tag(p)) };
let id = self.submit(
build_task(&input, input_tags, None)
.add_tags(self.tags.iter().cloned())
.add_tags(tags)
.add_tags(prio)
);
if set_state {
self.state.as_option().inspect(|s| self.set_state_for_with(id, s));