From 6b1313ee3d9518038b4099bfedbbec43a30b8c18 Mon Sep 17 00:00:00 2001 From: Mice7R Date: Tue, 31 Jan 2023 16:56:01 +0100 Subject: [PATCH 01/15] timewarrior block working prototype pad minutes Add some basic doc for functions wip stop_continue Redirect timew output to nulll states changes based on minutes Fix state change when stop Fix for new set_widget API --- src/blocks.rs | 1 + src/blocks/timewarrior.rs | 209 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 src/blocks/timewarrior.rs diff --git a/src/blocks.rs b/src/blocks.rs index a3e8acb0e9..a0ba6036ae 100644 --- a/src/blocks.rs +++ b/src/blocks.rs @@ -193,6 +193,7 @@ define_blocks!( taskwarrior, temperature, time, + timewarrior, tea_timer, toggle, uptime, diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs new file mode 100644 index 0000000000..b5b14f5cb6 --- /dev/null +++ b/src/blocks/timewarrior.rs @@ -0,0 +1,209 @@ +use super::prelude::*; +use tokio::process::Command; +use chrono::DateTime; + +#[derive(Deserialize, Debug, SmartDefault)] +#[serde(default)] +pub struct Config { + #[default(30.into())] + interval : Seconds, + format: FormatConfig, + + info: Option, + good: Option, + warning: Option, + critical: Option, +} + +pub async fn run(config: Config, mut api: CommonApi) -> Result<()> { + + api.set_default_actions(&[ + (MouseButton::Left, None, "stop_continue"), + ]) + .await?; + + let widget = Widget::new().with_format( + config + .format + .with_default(" $icon {$elapsed|}")?, + ); + + + loop { + let mut values = map! { + "icon" => Value::icon(api.get_icon("tasks")?), + }; + let mut state = State::Idle; + let mut widget = widget.clone(); + + let data = process_timewarrior_data(&call_timewarrior().await?); + if let Some(tw) = data { + if tw.end.is_none() { + // only show active tasks + let elapsed = chrono::Utc::now() - tw.start; + + // calculate state + for (level, st) in [ + (&config.critical, State::Critical), + (&config.warning, State::Warning), + (&config.good, State::Good), + (&config.info, State::Info), + + ] { + if let Some(value) = level { + if (elapsed.num_minutes() as u64) >= *value { + state = st; + break; + } + } + } + + values.insert("tags".into(), Value::text(tw.tags.join(" "))); + + let elapsedstr = format!("{}:{:0>2}", + elapsed.num_hours(), + elapsed.num_minutes()%60); + values.insert("elapsed".into(), Value::text(elapsedstr)); + + if let Some(annotation) = tw.annotation { + values.insert("annotation".into(), Value::text(annotation)); + } + } + } + + widget.state = state; + widget.set_values(values); + api.set_widget(widget).await?; + + select! { + _ = sleep(config.interval.0) => (), + event = api.event() => match event { + UpdateRequest => (), + Action(a) => { + if a == "stop_continue" { + stop_continue().await?; + } + }, + } + } + } +} + +/// Raw output from timew +#[derive(Deserialize, Debug)] +struct TimewarriorRAW { + pub id : u32, + pub start : String, + pub tags : Vec, + pub annotation : Option, + pub end : Option, +} + +/// TimeWarrior entry +#[derive(Debug, PartialEq)] +struct TimewarriorData { + pub id : u32, + pub start : DateTime, + pub tags : Vec, + pub annotation : Option, + pub end : Option>, +} + +impl From for TimewarriorData { + fn from(item:TimewarriorRAW) -> Self { + Self { + id: item.id, + tags: item.tags, + annotation: item.annotation, + start : DateTime::from_utc( + chrono::NaiveDateTime::parse_from_str(&item.start, "%Y%m%dT%H%M%SZ") + .unwrap(), + chrono::Utc), + end : item.end.map(|v| DateTime::from_utc( + chrono::NaiveDateTime::parse_from_str(&v, "%Y%m%dT%H%M%SZ") + .unwrap(), + chrono::Utc)), + } + } +} + +/// Format a DateTime given a format string +#[allow(dead_code)] +fn format_datetime(date:&DateTime, format:&str) -> String { + date.format(format).to_string() +} + +/// Execute "timew export now" and return the result +async fn call_timewarrior() -> Result { + let out = Command::new("timew") + .args(["export", "now"]) + .output() + .await + .error("failed to run timewarrior")? + .stdout; + let output = std::str::from_utf8(&out) + .error("failed to read output from timewarrior (invalid UTF-8)")? + .trim(); + Ok(output.to_string()) +} + +/// Stop or continue a task +async fn stop_continue() -> Result<()> { + let mut execute_continue:bool = true; + if let Some(tw) = process_timewarrior_data(&call_timewarrior().await?) { + // we only execute continue if the current task is stopped + // i.e. has an end defined + execute_continue = tw.end.is_some(); + } + + // is there a more rust way of doing this? + let args = match execute_continue { + true => "continue", + false => "stop", + }; + + Command::new("timew") + .args(&[args]) + .stdout(std::process::Stdio::null()) + .spawn() + .error("Error spawing timew")? + .wait() + .await + .error("Error executing stop/continue") + .map(|_| ()) +} + + +/// Process the output from "timew export" and return the first entry +fn process_timewarrior_data(input:&str) -> Option { + let t : Vec = serde_json::from_str(input).unwrap_or_default(); + match t.into_iter().next() { + Some(t) => Some(TimewarriorData::from(t)), + None => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_process_timewarrior_data() { + assert_eq!( + process_timewarrior_data(""), + None, + ); + + assert_eq!( + process_timewarrior_data("[]"), + None, + ); + + let a = process_timewarrior_data("[{\"id\":1,\"start\":\"20230131T175754Z\",\"tags\":[\"i3status\"],\"annotation\":\"timewarrior plugin\"}]"); + assert_eq!(a.is_some(), true); + if let Some(b) = a { + assert_eq!(b.id, 1); + assert_eq!(b.end, None); + } + } +} From fbba1f8b511821f24d44c2ed929663006f574d2f Mon Sep 17 00:00:00 2001 From: Mice7R Date: Tue, 25 Apr 2023 21:36:52 +0200 Subject: [PATCH 02/15] Add doc --- src/blocks/timewarrior.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index b5b14f5cb6..f09bec5b2a 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -1,3 +1,35 @@ +//! Time and information of the current timewarrior task +//! +//! Clicking left mouse stops or resumes the task +//! +//! # Configuration +//! +//! Key | Values | Default +//! ----|--------|-------- +//! `interval` | Update interval in seconds | `30` +//! `format` | A string to customise the output of the block. See placeholders. | `" $icon {$elapsed\|}"` +//! `info` | The threshold of minutes the task turns into a info state | - +//! `good` | The threshold of minutes the task turns into a good state | - +//! `warning` | The threshold of minutes the task turns into a warning state | - +//! `critical` | The threshold of minutes the task turns into a critical state | - +//! +//! Placeholder | Value | Type | Unit +//! ------------|-------|------|------ +//! `icon` | A static icon | Icon | - +//! `elapsed`| Elapsed time in format H:MM (Only present if task is active) | Text | - +//! `tags` | Tags of the active task separated by space (Only present if task is active) | Text | - +//! `annotation` | Annotation of the active task (Only present if task is active) | Text | - +//! +//! # Example +//! ```toml +//! [[block]] +//! block = "timewarrior" +//! format = " $icon {$tags.rot-str(8,1) $elapsed|}" +//! ``` +//! +//! # Icons Used +//! - `tasks` + use super::prelude::*; use tokio::process::Command; use chrono::DateTime; From 29326bd10b3ab902918ff8e3a23a99eb20c4faf3 Mon Sep 17 00:00:00 2001 From: Mice7R Date: Tue, 25 Apr 2023 22:37:02 +0200 Subject: [PATCH 03/15] Format code --- src/blocks/timewarrior.rs | 94 ++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 56 deletions(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index f09bec5b2a..0ec8ea523d 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -31,14 +31,14 @@ //! - `tasks` use super::prelude::*; -use tokio::process::Command; use chrono::DateTime; +use tokio::process::Command; #[derive(Deserialize, Debug, SmartDefault)] #[serde(default)] pub struct Config { #[default(30.into())] - interval : Seconds, + interval: Seconds, format: FormatConfig, info: Option, @@ -48,18 +48,10 @@ pub struct Config { } pub async fn run(config: Config, mut api: CommonApi) -> Result<()> { + api.set_default_actions(&[(MouseButton::Left, None, "stop_continue")]) + .await?; - api.set_default_actions(&[ - (MouseButton::Left, None, "stop_continue"), - ]) - .await?; - - let widget = Widget::new().with_format( - config - .format - .with_default(" $icon {$elapsed|}")?, - ); - + let widget = Widget::new().with_format(config.format.with_default(" $icon {$elapsed|}")?); loop { let mut values = map! { @@ -80,7 +72,6 @@ pub async fn run(config: Config, mut api: CommonApi) -> Result<()> { (&config.warning, State::Warning), (&config.good, State::Good), (&config.info, State::Info), - ] { if let Some(value) = level { if (elapsed.num_minutes() as u64) >= *value { @@ -92,9 +83,8 @@ pub async fn run(config: Config, mut api: CommonApi) -> Result<()> { values.insert("tags".into(), Value::text(tw.tags.join(" "))); - let elapsedstr = format!("{}:{:0>2}", - elapsed.num_hours(), - elapsed.num_minutes()%60); + let elapsedstr = + format!("{}:{:0>2}", elapsed.num_hours(), elapsed.num_minutes() % 60); values.insert("elapsed".into(), Value::text(elapsedstr)); if let Some(annotation) = tw.annotation { @@ -124,44 +114,46 @@ pub async fn run(config: Config, mut api: CommonApi) -> Result<()> { /// Raw output from timew #[derive(Deserialize, Debug)] struct TimewarriorRAW { - pub id : u32, - pub start : String, - pub tags : Vec, - pub annotation : Option, - pub end : Option, + pub id: u32, + pub start: String, + pub tags: Vec, + pub annotation: Option, + pub end: Option, } /// TimeWarrior entry #[derive(Debug, PartialEq)] struct TimewarriorData { - pub id : u32, - pub start : DateTime, - pub tags : Vec, - pub annotation : Option, - pub end : Option>, + pub id: u32, + pub start: DateTime, + pub tags: Vec, + pub annotation: Option, + pub end: Option>, } impl From for TimewarriorData { - fn from(item:TimewarriorRAW) -> Self { + fn from(item: TimewarriorRAW) -> Self { Self { id: item.id, tags: item.tags, annotation: item.annotation, - start : DateTime::from_utc( - chrono::NaiveDateTime::parse_from_str(&item.start, "%Y%m%dT%H%M%SZ") - .unwrap(), - chrono::Utc), - end : item.end.map(|v| DateTime::from_utc( - chrono::NaiveDateTime::parse_from_str(&v, "%Y%m%dT%H%M%SZ") - .unwrap(), - chrono::Utc)), + start: DateTime::from_utc( + chrono::NaiveDateTime::parse_from_str(&item.start, "%Y%m%dT%H%M%SZ").unwrap(), + chrono::Utc, + ), + end: item.end.map(|v| { + DateTime::from_utc( + chrono::NaiveDateTime::parse_from_str(&v, "%Y%m%dT%H%M%SZ").unwrap(), + chrono::Utc, + ) + }), } } } /// Format a DateTime given a format string #[allow(dead_code)] -fn format_datetime(date:&DateTime, format:&str) -> String { +fn format_datetime(date: &DateTime, format: &str) -> String { date.format(format).to_string() } @@ -181,7 +173,7 @@ async fn call_timewarrior() -> Result { /// Stop or continue a task async fn stop_continue() -> Result<()> { - let mut execute_continue:bool = true; + let mut execute_continue: bool = true; if let Some(tw) = process_timewarrior_data(&call_timewarrior().await?) { // we only execute continue if the current task is stopped // i.e. has an end defined @@ -195,24 +187,20 @@ async fn stop_continue() -> Result<()> { }; Command::new("timew") - .args(&[args]) + .args([args]) .stdout(std::process::Stdio::null()) .spawn() - .error("Error spawing timew")? + .error("Error spawning timew")? .wait() .await .error("Error executing stop/continue") .map(|_| ()) } - /// Process the output from "timew export" and return the first entry -fn process_timewarrior_data(input:&str) -> Option { - let t : Vec = serde_json::from_str(input).unwrap_or_default(); - match t.into_iter().next() { - Some(t) => Some(TimewarriorData::from(t)), - None => None, - } +fn process_timewarrior_data(input: &str) -> Option { + let t: Vec = serde_json::from_str(input).unwrap_or_default(); + t.into_iter().next().map(TimewarriorData::from) } #[cfg(test)] @@ -221,15 +209,9 @@ mod tests { #[test] fn test_process_timewarrior_data() { - assert_eq!( - process_timewarrior_data(""), - None, - ); - - assert_eq!( - process_timewarrior_data("[]"), - None, - ); + assert_eq!(process_timewarrior_data(""), None,); + + assert_eq!(process_timewarrior_data("[]"), None,); let a = process_timewarrior_data("[{\"id\":1,\"start\":\"20230131T175754Z\",\"tags\":[\"i3status\"],\"annotation\":\"timewarrior plugin\"}]"); assert_eq!(a.is_some(), true); From 2641bc76c5d7d3be13aa62317a13e56737c6b4d1 Mon Sep 17 00:00:00 2001 From: Mice7R Date: Tue, 25 Apr 2023 23:41:21 +0200 Subject: [PATCH 04/15] Fix example to use new formatter arguments --- src/blocks/timewarrior.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index 0ec8ea523d..62fb29899d 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -24,7 +24,7 @@ //! ```toml //! [[block]] //! block = "timewarrior" -//! format = " $icon {$tags.rot-str(8,1) $elapsed|}" +//! format = " $icon {$tags.str(w:8,max_w:8,rot_interval:4) $elapsed|}" //! ``` //! //! # Icons Used From 7aeac2571a4af4604343f2c4e76845bc3ad22ba3 Mon Sep 17 00:00:00 2001 From: Mice7R Date: Tue, 25 Apr 2023 23:48:33 +0200 Subject: [PATCH 05/15] Add missing Action documentation --- src/blocks/timewarrior.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index 62fb29899d..9958ce5412 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -20,6 +20,10 @@ //! `tags` | Tags of the active task separated by space (Only present if task is active) | Text | - //! `annotation` | Annotation of the active task (Only present if task is active) | Text | - //! +//! Action | Default button +//! ----------------|---------------- +//! `stop_continue` | Left +//! //! # Example //! ```toml //! [[block]] From feceb0c6b8497007ae477a3968fa8acc9fe6bc9c Mon Sep 17 00:00:00 2001 From: Mice7R Date: Tue, 25 Apr 2023 23:51:46 +0200 Subject: [PATCH 06/15] Add timew word in cspell.yml --- cspell.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/cspell.yaml b/cspell.yaml index 2be42c67f7..81ae7845b4 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -154,6 +154,7 @@ words: - sysfs - tebi - tera + - timew - tzname - tzset - udev From d27797127d2883daf74add59094d28127ea2af2a Mon Sep 17 00:00:00 2001 From: Mice7R Date: Thu, 27 Apr 2023 22:53:35 +0200 Subject: [PATCH 07/15] Add deny_unknown_fields --- src/blocks/timewarrior.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index 9958ce5412..0c62fe39f8 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -39,7 +39,7 @@ use chrono::DateTime; use tokio::process::Command; #[derive(Deserialize, Debug, SmartDefault)] -#[serde(default)] +#[serde(deny_unknown_fields, default)] pub struct Config { #[default(30.into())] interval: Seconds, From 6812cc1b3381f359c2291b66f904b8f79e95e260 Mon Sep 17 00:00:00 2001 From: Mice7R Date: Thu, 27 Apr 2023 23:21:31 +0200 Subject: [PATCH 08/15] Simplify get_current_timew_task --- src/blocks/timewarrior.rs | 44 +++++++++------------------------------ 1 file changed, 10 insertions(+), 34 deletions(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index 0c62fe39f8..68f0e4bf85 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -64,7 +64,7 @@ pub async fn run(config: Config, mut api: CommonApi) -> Result<()> { let mut state = State::Idle; let mut widget = widget.clone(); - let data = process_timewarrior_data(&call_timewarrior().await?); + let data = get_current_timewarrior_task().await?; if let Some(tw) = data { if tw.end.is_none() { // only show active tasks @@ -126,7 +126,8 @@ struct TimewarriorRAW { } /// TimeWarrior entry -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Deserialize)] +#[serde(from = "TimewarriorRAW")] struct TimewarriorData { pub id: u32, pub start: DateTime, @@ -161,24 +162,24 @@ fn format_datetime(date: &DateTime, format: &str) -> String { date.format(format).to_string() } -/// Execute "timew export now" and return the result -async fn call_timewarrior() -> Result { +/// Execute "timew export now" and return the current task (if any) +async fn get_current_timewarrior_task() -> Result> { let out = Command::new("timew") .args(["export", "now"]) .output() .await .error("failed to run timewarrior")? .stdout; - let output = std::str::from_utf8(&out) - .error("failed to read output from timewarrior (invalid UTF-8)")? - .trim(); - Ok(output.to_string()) + Ok(serde_json::from_slice::>(&out) + .unwrap_or_default() + .into_iter() + .next()) } /// Stop or continue a task async fn stop_continue() -> Result<()> { let mut execute_continue: bool = true; - if let Some(tw) = process_timewarrior_data(&call_timewarrior().await?) { + if let Some(tw) = get_current_timewarrior_task().await? { // we only execute continue if the current task is stopped // i.e. has an end defined execute_continue = tw.end.is_some(); @@ -200,28 +201,3 @@ async fn stop_continue() -> Result<()> { .error("Error executing stop/continue") .map(|_| ()) } - -/// Process the output from "timew export" and return the first entry -fn process_timewarrior_data(input: &str) -> Option { - let t: Vec = serde_json::from_str(input).unwrap_or_default(); - t.into_iter().next().map(TimewarriorData::from) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_process_timewarrior_data() { - assert_eq!(process_timewarrior_data(""), None,); - - assert_eq!(process_timewarrior_data("[]"), None,); - - let a = process_timewarrior_data("[{\"id\":1,\"start\":\"20230131T175754Z\",\"tags\":[\"i3status\"],\"annotation\":\"timewarrior plugin\"}]"); - assert_eq!(a.is_some(), true); - if let Some(b) = a { - assert_eq!(b.id, 1); - assert_eq!(b.end, None); - } - } -} From 62bc9ce99cb37de4bdded6ce8361c0fff28d6345 Mon Sep 17 00:00:00 2001 From: Mice7R Date: Fri, 28 Apr 2023 00:05:04 +0200 Subject: [PATCH 09/15] Check timew stop/continue exit code --- src/blocks/timewarrior.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index 68f0e4bf85..c6a41d6591 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -198,6 +198,8 @@ async fn stop_continue() -> Result<()> { .error("Error spawning timew")? .wait() .await - .error("Error executing stop/continue") - .map(|_| ()) + .error("Error executing stop/continue")? + .success() + .then_some(()) + .error("timew exited with non-zero value when attempting to stop/contniue") } From 1cec90771c9361066836ed5ecd0c61a7ee1b2b71 Mon Sep 17 00:00:00 2001 From: Mice7R Date: Mon, 1 May 2023 12:44:09 +0200 Subject: [PATCH 10/15] Fix small issues in doc Co-authored-by: Max Verevkin <34583604+MaxVerevkin@users.noreply.github.com> --- src/blocks/timewarrior.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index c6a41d6591..95e8be641a 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -7,7 +7,7 @@ //! Key | Values | Default //! ----|--------|-------- //! `interval` | Update interval in seconds | `30` -//! `format` | A string to customise the output of the block. See placeholders. | `" $icon {$elapsed\|}"` +//! `format` | A string to customise the output of the block. See placeholders. | " $icon {$elapsed |}" //! `info` | The threshold of minutes the task turns into a info state | - //! `good` | The threshold of minutes the task turns into a good state | - //! `warning` | The threshold of minutes the task turns into a warning state | - @@ -28,7 +28,7 @@ //! ```toml //! [[block]] //! block = "timewarrior" -//! format = " $icon {$tags.str(w:8,max_w:8,rot_interval:4) $elapsed|}" +//! format = " $icon {$tags.str(w:8,rot_interval:4) $elapsed|}" //! ``` //! //! # Icons Used From d0b70d00c31af5e65cb80e893b74ce7d4a9c4883 Mon Sep 17 00:00:00 2001 From: Mice7R Date: Mon, 12 Feb 2024 22:15:37 +0100 Subject: [PATCH 11/15] refactor some code to make it more rusty --- src/blocks/timewarrior.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index 95e8be641a..6ad967a626 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -178,19 +178,10 @@ async fn get_current_timewarrior_task() -> Result> { /// Stop or continue a task async fn stop_continue() -> Result<()> { - let mut execute_continue: bool = true; - if let Some(tw) = get_current_timewarrior_task().await? { - // we only execute continue if the current task is stopped - // i.e. has an end defined - execute_continue = tw.end.is_some(); - } - - // is there a more rust way of doing this? - let args = match execute_continue { - true => "continue", - false => "stop", - }; - + let is_stopped = get_current_timewarrior_task() + .await? + .map_or(false, |tw| tw.end.is_some()); + let args = if is_stopped { "continue" } else { "stop" }; Command::new("timew") .args([args]) .stdout(std::process::Stdio::null()) From 1a32ebbf6df48e1b0e0b5279577e1a841bcc5acf Mon Sep 17 00:00:00 2001 From: Mice7R Date: Mon, 12 Feb 2024 22:52:44 +0100 Subject: [PATCH 12/15] Update block to 0.32.3 --- src/blocks/timewarrior.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index 6ad967a626..1bc017046e 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -51,15 +51,15 @@ pub struct Config { critical: Option, } -pub async fn run(config: Config, mut api: CommonApi) -> Result<()> { - api.set_default_actions(&[(MouseButton::Left, None, "stop_continue")]) - .await?; +pub async fn run(config: &Config, api: &CommonApi) -> Result<()> { + let mut actions = api.get_actions()?; + api.set_default_actions(&[(MouseButton::Left, None, "stop_continue")])?; let widget = Widget::new().with_format(config.format.with_default(" $icon {$elapsed|}")?); loop { let mut values = map! { - "icon" => Value::icon(api.get_icon("tasks")?), + "icon" => Value::icon("tasks"), }; let mut state = State::Idle; let mut widget = widget.clone(); @@ -99,17 +99,14 @@ pub async fn run(config: Config, mut api: CommonApi) -> Result<()> { widget.state = state; widget.set_values(values); - api.set_widget(widget).await?; + api.set_widget(widget)?; select! { _ = sleep(config.interval.0) => (), - event = api.event() => match event { - UpdateRequest => (), - Action(a) => { - if a == "stop_continue" { - stop_continue().await?; - } - }, + _ = api.wait_for_update_request() => (), + Some(action) = actions.recv() => match action.as_ref() { + "stop_continue" => { stop_continue().await?; } + _ => (), } } } From 5a4e8426b6150ee3fce2a5d8d52075059f2656da Mon Sep 17 00:00:00 2001 From: Mice7R Date: Mon, 12 Feb 2024 23:10:36 +0100 Subject: [PATCH 13/15] Remove deprecated DateTime::from_utc --- src/blocks/timewarrior.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index 1bc017046e..823480e867 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -139,14 +139,14 @@ impl From for TimewarriorData { id: item.id, tags: item.tags, annotation: item.annotation, - start: DateTime::from_utc( - chrono::NaiveDateTime::parse_from_str(&item.start, "%Y%m%dT%H%M%SZ").unwrap(), - chrono::Utc, + start: chrono::TimeZone::from_utc_datetime( + &chrono::Utc, + &chrono::NaiveDateTime::parse_from_str(&item.start, "%Y%m%dT%H%M%SZ").unwrap() ), end: item.end.map(|v| { - DateTime::from_utc( - chrono::NaiveDateTime::parse_from_str(&v, "%Y%m%dT%H%M%SZ").unwrap(), - chrono::Utc, + chrono::TimeZone::from_utc_datetime( + &chrono::Utc, + &chrono::NaiveDateTime::parse_from_str(&v, "%Y%m%dT%H%M%SZ").unwrap() ) }), } From 3402a53c3ddfe02dd58c617aef8c6be173b19b24 Mon Sep 17 00:00:00 2001 From: Mice7R Date: Mon, 12 Feb 2024 23:25:06 +0100 Subject: [PATCH 14/15] Fix non-zero exit when continue --- src/blocks/timewarrior.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index 823480e867..500ca2c1eb 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -177,7 +177,7 @@ async fn get_current_timewarrior_task() -> Result> { async fn stop_continue() -> Result<()> { let is_stopped = get_current_timewarrior_task() .await? - .map_or(false, |tw| tw.end.is_some()); + .map_or(true, |tw| tw.end.is_some()); let args = if is_stopped { "continue" } else { "stop" }; Command::new("timew") .args([args]) From cfcd9c54b99cfa7432afc5b7cbdf809f173e88e5 Mon Sep 17 00:00:00 2001 From: Mice7R Date: Mon, 12 Feb 2024 23:25:28 +0100 Subject: [PATCH 15/15] Fix typo --- src/blocks/timewarrior.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/timewarrior.rs b/src/blocks/timewarrior.rs index 500ca2c1eb..26d4a67cf3 100644 --- a/src/blocks/timewarrior.rs +++ b/src/blocks/timewarrior.rs @@ -189,5 +189,5 @@ async fn stop_continue() -> Result<()> { .error("Error executing stop/continue")? .success() .then_some(()) - .error("timew exited with non-zero value when attempting to stop/contniue") + .error("timew exited with non-zero value when attempting to stop/continue") }