feat: implement task stopping shortcut
This commit is contained in:
parent
06bfe8e18a
commit
9fbe3e27cb
|
@ -104,12 +104,15 @@ To stop time-tracking completely, simply move to the root of all tasks.
|
||||||
|
|
||||||
Dots and slashes can be repeated to move to parent tasks.
|
Dots and slashes can be repeated to move to parent tasks.
|
||||||
|
|
||||||
- `:[IND][COL]` - add property column COL at IND or end, if it already exists remove property column COL or IND (1-indexed)
|
- `:[IND][PROP]` - add property column PROP at IND or end, if it already exists remove property column PROP or IND (1-indexed)
|
||||||
- `*[TIME]` - add timetracking with the specified offset (empty: list tracked times)
|
- `::[PROP]` - Sort by property PROP
|
||||||
|
- `([TIME]` - insert timetracking with the specified offset in minutes (empty: list tracked times)
|
||||||
|
- `)[TIME]` - stop timetracking with the specified offset in minutes - convenience helper to move to root (empty: stop now)
|
||||||
- `>[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` - set state for current task from text
|
- `!TEXT` - set state for current task from text
|
||||||
- `,TEXT` - add text note (comment / description)
|
- `,TEXT` - add text note (comment / description)
|
||||||
|
- TBI: `*[INT]` - set priority - can also be used in task, with any digit
|
||||||
- `@` - undoes last action (moving in place or upwards or waiting a minute confirms pending actions)
|
- `@` - undoes last action (moving in place or upwards or waiting a minute confirms pending actions)
|
||||||
- `wss://...` - switch or subscribe to relay (prefix with space to forcibly add a new one)
|
- `wss://...` - switch or subscribe to relay (prefix with space to forcibly add a new one)
|
||||||
|
|
||||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -378,22 +378,27 @@ async fn main() {
|
||||||
None => tasks.clear_filter()
|
None => tasks.clear_filter()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some('*') =>
|
Some('(') =>
|
||||||
match arg {
|
match arg {
|
||||||
Some(arg) => {
|
Some(arg) =>
|
||||||
if let Ok(num) = arg.parse::<i64>() {
|
if !tasks.track_from(arg) {
|
||||||
tasks.track_at(Timestamp::from(Timestamp::now().as_u64().saturating_add_signed(num)));
|
continue;
|
||||||
} else if let Ok(date) = DateTime::parse_from_rfc3339(arg) {
|
|
||||||
tasks.track_at(Timestamp::from(date.to_utc().timestamp() as u64));
|
|
||||||
} else {
|
|
||||||
warn!("Cannot parse {arg}");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
println!("{}", tasks.times_tracked());
|
println!("{}", tasks.times_tracked());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(')') => {
|
||||||
|
tasks.move_to(None);
|
||||||
|
if let Some(arg) = arg {
|
||||||
|
if !tasks.track_from(arg) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some('.') => {
|
Some('.') => {
|
||||||
let mut dots = 1;
|
let mut dots = 1;
|
||||||
|
|
31
src/tasks.rs
31
src/tasks.rs
|
@ -7,7 +7,7 @@ use std::str::FromStr;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use chrono::{Local, TimeZone};
|
use chrono::{DateTime, Local, TimeZone};
|
||||||
use chrono::LocalResult::Single;
|
use chrono::LocalResult::Single;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -633,10 +633,25 @@ impl Tasks {
|
||||||
self.tasks.get(id).map_or(id.to_string(), |t| t.get_title())
|
self.tasks.get(id).map_or(id.to_string(), |t| t.get_title())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse string and set tracking
|
||||||
|
/// Returns false if parsing failed
|
||||||
|
pub(crate) fn track_from(&mut self, str: &str) -> bool {
|
||||||
|
if let Ok(num) = str.parse::<i64>() {
|
||||||
|
self.track_at(Timestamp::from(Timestamp::now().as_u64().saturating_add_signed(num * 60)));
|
||||||
|
} else if let Ok(date) = DateTime::parse_from_rfc3339(str) {
|
||||||
|
self.track_at(Timestamp::from(date.to_utc().timestamp() as u64));
|
||||||
|
} else {
|
||||||
|
warn!("Cannot parse {str}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn track_at(&mut self, time: Timestamp) -> EventId {
|
pub(crate) fn track_at(&mut self, time: Timestamp) -> EventId {
|
||||||
info!("Tracking \"{:?}\" from {}", self.position.map(|id| self.get_task_title(&id)), time.to_human_datetime());
|
info!("{} from {}", self.position.map_or(String::from("Stopping time-tracking"), |id| format!("Tracking \"{}\"", self.get_task_title(&id))), time.to_human_datetime()); // TODO omit seconds
|
||||||
let pos = self.get_position();
|
let pos = self.get_position();
|
||||||
let tracking = build_tracking(pos);
|
let tracking = build_tracking(pos);
|
||||||
|
// TODO this can lead to funny deletions
|
||||||
self.get_own_history().map(|events| {
|
self.get_own_history().map(|events| {
|
||||||
if let Some(event) = events.pop_last() {
|
if let Some(event) = events.pop_last() {
|
||||||
if event.kind.as_u16() == TRACKING_KIND &&
|
if event.kind.as_u16() == TRACKING_KIND &&
|
||||||
|
@ -914,6 +929,18 @@ mod tasks_test {
|
||||||
// TODO test received events
|
// TODO test received events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_timestamps() {
|
||||||
|
let mut tasks = stub_tasks();
|
||||||
|
let zero = EventId::all_zeros();
|
||||||
|
tasks.move_to(Some(zero));
|
||||||
|
tasks.track_at(Timestamp::from(Timestamp::now().as_u64() + 100));
|
||||||
|
assert_eq!(timestamps(tasks.history.values().nth(0).unwrap().into_iter(), &vec![zero]).collect_vec().len(), 2)
|
||||||
|
// TODO Does not show both future and current tracking properly, need to split by now
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_depth() {
|
fn test_depth() {
|
||||||
let mut tasks = stub_tasks();
|
let mut tasks = stub_tasks();
|
||||||
|
|
Loading…
Reference in New Issue