From d4bca1c26f260193e353bca81d2c339ed1a93119 Mon Sep 17 00:00:00 2001 From: xeruf <27jf@pm.me> Date: Mon, 23 Sep 2024 08:50:12 +0200 Subject: [PATCH] feat: deferred state updates --- README.md | 1 + src/main.rs | 51 ++++++++++++++++++++++++++++++++------------------- src/tasks.rs | 5 ++++- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 724ae84..0af6e6a 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ Append `@TIME` to any task creation or change command to record the action with - `>[TEXT]` - complete active task and move up, with optional status description - `<[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 note (stateless task / task description) - TBI: `;[TEXT]` - list comments or comment on task diff --git a/src/main.rs b/src/main.rs index d9f5567..159d47e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ use xdg::BaseDirectories; use crate::helpers::*; use crate::kinds::{BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS, TRACKING_KIND}; -use crate::task::{State, MARKER_DEPENDS}; +use crate::task::{State, Task, TaskState, MARKER_DEPENDS}; use crate::tasks::{PropertyCollection, StateFilter, TasksRelay}; mod helpers; @@ -262,7 +262,7 @@ async fn main() -> Result<()> { or_warn!(client.set_metadata(meta).await, "Unable to set metadata"); } - loop { + 'repl: loop { let result_received = timeout(Duration::from_secs(INACTVITY_DELAY), rx.recv()).await; match result_received { Ok(Some(MostrMessage::NewRelay(url))) => { @@ -300,7 +300,7 @@ async fn main() -> Result<()> { } Ok(None) => { debug!("Finalizing nostr communication thread because communication channel was closed"); - break; + break 'repl; } } } @@ -325,7 +325,7 @@ async fn main() -> Result<()> { } } - loop { + 'repl: loop { println!(); let tasks = relays.get(&selected_relay).unwrap(); let prompt = format!( @@ -370,7 +370,7 @@ async fn main() -> Result<()> { debug!("Flushing Tasks because of empty command"); tasks.flush(); or_warn!(tasks.print_tasks()); - continue; + continue 'repl; } Some('@') => {} Some(_) => { @@ -413,7 +413,7 @@ async fn main() -> Result<()> { tasks.get_columns().add_or_remove(arg.to_string()); } else { println!("{}", PROPERTY_COLUMNS); - continue; + continue 'repl; } } @@ -424,12 +424,12 @@ async fn main() -> Result<()> { || info!("With a task selected, use ,NOTE to attach NOTE and , to list all its notes"), |task| println!("{}", task.description_events().map(|e| format!("{} {}", format_timestamp_local(&e.created_at), e.content)).join("\n")), ); - continue; + continue 'repl; } Some(arg) => { if arg.len() < CHARACTER_THRESHOLD { warn!("Note needs at least {CHARACTER_THRESHOLD} characters!"); - continue; + continue 'repl; } tasks.make_note(arg) } @@ -437,12 +437,12 @@ async fn main() -> Result<()> { Some('>') => { tasks.update_state(arg_default, State::Done); - tasks.move_up(); + if tasks.custom_time.is_none() { tasks.move_up(); } } Some('<') => { tasks.update_state(arg_default, State::Closed); - tasks.move_up(); + if tasks.custom_time.is_none() { tasks.move_up(); } } Some('&') => { @@ -455,7 +455,7 @@ async fn main() -> Result<()> { _ => { if !tasks.move_back_to(text) { warn!("Did not find a match in history for \"{text}\""); - continue; + continue 'repl; } } } @@ -489,7 +489,7 @@ async fn main() -> Result<()> { } }; if !success { - continue; + continue 'repl; } } @@ -559,9 +559,22 @@ async fn main() -> Result<()> { tasks.set_state_for(id, right, state); break 'block; } + if let Some(time) = parse_hour(left, 20) + .map(|dt| dt.to_utc()) + .or_else(|| parse_date(left)) { + let stamp = time.to_timestamp(); + let state = tasks.get_by_id(&id).and_then(Task::state); + tasks.set_state_for(id, right, State::Pending); + tasks.custom_time = Some(stamp); + tasks.set_state_for(id, + &state.as_ref().map(TaskState::get_label).unwrap_or_default(), + state.map(|ts| ts.state).unwrap_or(State::Open)); + break 'block; + } } tasks.set_state_for_with(id, arg_default); } + tasks.custom_time = None; tasks.move_up(); } } @@ -577,7 +590,7 @@ async fn main() -> Result<()> { if tasks.has_tag_filter() { println!("Use # to remove tag filters and . to remove all filters.") } - continue; + continue 'repl; } } @@ -599,7 +612,7 @@ async fn main() -> Result<()> { let (label, mut times) = tasks.times_tracked(); println!("{}\n{}", label.italic(), times.join("\n")); } - continue; + continue 'repl; } Some(')') => { @@ -612,7 +625,7 @@ async fn main() -> Result<()> { times.rev().take(15).collect_vec().iter().rev().join("\n")); } // So the error message is not covered up - continue; + continue 'repl; } } } @@ -677,7 +690,7 @@ async fn main() -> Result<()> { if let Some((url, tasks)) = relays.iter().find(|(key, _)| key.as_ref().is_some_and(|url| url.as_str().starts_with(&command))) { selected_relay.clone_from(url); or_warn!(tasks.print_tasks()); - continue; + continue 'repl; } or_warn!(Url::parse(&command), "Failed to parse url {}", command).map(|url| { match tx.try_send(MostrMessage::NewRelay(url.clone())) { @@ -689,7 +702,7 @@ async fn main() -> Result<()> { } } }); - continue; + continue 'repl; } else if command.contains('\n') { command.split('\n').for_each(|line| { if !line.trim().is_empty() { @@ -703,8 +716,8 @@ async fn main() -> Result<()> { tasks.custom_time = None; or_warn!(tasks.print_tasks()); } - Err(ReadlineError::Eof) => break, - Err(ReadlineError::Interrupted) => break, // TODO exit if prompt was empty, or clear + Err(ReadlineError::Eof) => break 'repl, + Err(ReadlineError::Interrupted) => break 'repl, // TODO exit if prompt was empty, or clear Err(e) => warn!("{}", e), } } diff --git a/src/tasks.rs b/src/tasks.rs index 4ed8289..54cfe97 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -1052,7 +1052,10 @@ impl TasksRelay { comment, id, ); - info!("Task status {} set for \"{}\"", TaskState::get_label_for(&state, comment), self.get_task_title(&id)); + info!("Task status {} set for \"{}\"{}", + TaskState::get_label_for(&state, comment), + self.get_task_title(&id), + self.custom_time.map(|ts| format!(" at {}", format_timestamp_relative(&ts))).unwrap_or_default()); self.submit(prop) }