forked from janek/mostr
feat: allow setting priority context for creating tasks
This commit is contained in:
parent
dc8df51e0f
commit
e9bee3c114
|
@ -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.
|
||||
|
|
|
@ -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]
|
||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -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 })));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
21
src/tasks.rs
21
src/tasks.rs
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue