Compare commits
No commits in common. "c836b0e57bd77c121caa86d64522d6fad7e22f91" and "a7c6cf2f59381c4780d18dd0154b6b7371c350c6" have entirely different histories.
c836b0e57b
...
a7c6cf2f59
6 changed files with 26 additions and 55 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1462,7 +1462,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mostr"
|
name = "mostr"
|
||||||
version = "0.9.3"
|
version = "0.9.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"colog",
|
"colog",
|
||||||
|
|
|
@ -5,7 +5,7 @@ repository = "https://forge.ftt.gmbh/janek/mostr"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "GPL 3.0"
|
license = "GPL 3.0"
|
||||||
authors = ["melonion"]
|
authors = ["melonion"]
|
||||||
version = "0.9.3"
|
version = "0.9.2"
|
||||||
rust-version = "1.82"
|
rust-version = "1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
default-run = "mostr"
|
default-run = "mostr"
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
// Mostr Library Crate
|
|
||||||
// Placeholder for tests to not include main.rs bulk
|
|
|
@ -460,11 +460,13 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Some('>') => {
|
Some('>') => {
|
||||||
tasks.update_state_and_up(arg_default, State::Done);
|
tasks.update_state(arg_default, State::Done);
|
||||||
|
if tasks.custom_time.is_none() { tasks.move_up(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
Some('<') => {
|
Some('<') => {
|
||||||
tasks.update_state_and_up(arg_default, State::Closed);
|
tasks.update_state(arg_default, State::Closed);
|
||||||
|
if tasks.custom_time.is_none() { tasks.move_up(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
Some('&') =>
|
Some('&') =>
|
||||||
|
@ -785,7 +787,6 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tasks.custom_time = None;
|
tasks.custom_time = None;
|
||||||
tasks.update_position();
|
|
||||||
println!("{}", tasks);
|
println!("{}", tasks);
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Eof) => break 'repl,
|
Err(ReadlineError::Eof) => break 'repl,
|
||||||
|
|
56
src/tasks.rs
56
src/tasks.rs
|
@ -75,14 +75,15 @@ pub(crate) struct TasksRelay {
|
||||||
/// The task properties sorted by
|
/// The task properties sorted by
|
||||||
sorting: VecDeque<String>, // TODO prefix +/- for asc/desc, no prefix for default
|
sorting: VecDeque<String>, // TODO prefix +/- for asc/desc, no prefix for default
|
||||||
|
|
||||||
/// Temporary filtered view of the current tasks.
|
/// A filtered view of the current tasks.
|
||||||
/// Would like this to be Task references but that doesn't work unless I start meddling with Rc everywhere.
|
/// Would like this to be Task references
|
||||||
|
/// but that doesn't work unless I start meddling with Rc everywhere.
|
||||||
view: Vec<EventId>,
|
view: Vec<EventId>,
|
||||||
search_depth: usize,
|
search_depth: usize,
|
||||||
view_depth: usize,
|
view_depth: usize,
|
||||||
pub(crate) recurse_activities: bool,
|
pub(crate) recurse_activities: bool,
|
||||||
// Last visible position that intuitively interactions should be based on
|
// Last position used in interface - needs auto-update
|
||||||
position: Option<EventId>,
|
//position: Option<EventId>,
|
||||||
|
|
||||||
/// Currently active tags
|
/// Currently active tags
|
||||||
tags: BTreeSet<Hashtag>,
|
tags: BTreeSet<Hashtag>,
|
||||||
|
@ -194,7 +195,6 @@ impl TasksRelay {
|
||||||
priority: None,
|
priority: None,
|
||||||
keys: vec![sender.pubkey()],
|
keys: vec![sender.pubkey()],
|
||||||
own_keys: vec![sender.pubkey()],
|
own_keys: vec![sender.pubkey()],
|
||||||
position: None,
|
|
||||||
|
|
||||||
search_depth: 4,
|
search_depth: 4,
|
||||||
view_depth: 0,
|
view_depth: 0,
|
||||||
|
@ -241,27 +241,15 @@ impl TasksRelay {
|
||||||
fn own_key(&self) -> PublicKey { self.sender.pubkey() }
|
fn own_key(&self) -> PublicKey { self.sender.pubkey() }
|
||||||
|
|
||||||
pub(crate) fn get_position(&self) -> Option<EventId> {
|
pub(crate) fn get_position(&self) -> Option<EventId> {
|
||||||
self.position
|
self.get_position_at(now()).1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn calculate_position(&self, time: Option<Timestamp>) -> Option<EventId> {
|
pub(crate) fn get_position_timestamped(&self) -> (Timestamp, Option<EventId>) {
|
||||||
self.get_position_at(time.unwrap_or(now())).1
|
self.get_position_at(now())
|
||||||
}
|
|
||||||
|
|
||||||
// Update the current position
|
|
||||||
// Returns whether the position changed
|
|
||||||
pub(super) fn update_position(&mut self) -> bool {
|
|
||||||
let new_position = self.calculate_position(None);
|
|
||||||
if new_position != self.position {
|
|
||||||
self.view.clear();
|
|
||||||
self.position = new_position;
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parse_tracking_stamp_relative(&self, input: &str) -> Option<Timestamp> {
|
pub(super) fn parse_tracking_stamp_relative(&self, input: &str) -> Option<Timestamp> {
|
||||||
let pos = self.get_position_at(now());
|
let pos = self.get_position_timestamped();
|
||||||
let mut pos_time = pos.1.and_then(|_| Local.timestamp_opt(pos.0.as_u64() as i64, 0).earliest());
|
let mut pos_time = pos.1.and_then(|_| Local.timestamp_opt(pos.0.as_u64() as i64, 0).earliest());
|
||||||
parse_tracking_stamp(input, pos_time.take_if(|t| Local::now() - *t > TimeDelta::hours(6)))
|
parse_tracking_stamp(input, pos_time.take_if(|t| Local::now() - *t > TimeDelta::hours(6)))
|
||||||
}
|
}
|
||||||
|
@ -275,7 +263,6 @@ impl TasksRelay {
|
||||||
|
|
||||||
// TODO binary search
|
// TODO binary search
|
||||||
/// Gets last position change before the given timestamp
|
/// Gets last position change before the given timestamp
|
||||||
/// Returns the position together with when it was set
|
|
||||||
fn get_position_at(&self, timestamp: Timestamp) -> (Timestamp, Option<EventId>) {
|
fn get_position_at(&self, timestamp: Timestamp) -> (Timestamp, Option<EventId>) {
|
||||||
self.history_from(timestamp)
|
self.history_from(timestamp)
|
||||||
.last()
|
.last()
|
||||||
|
@ -898,10 +885,8 @@ impl TasksRelay {
|
||||||
self.state = state;
|
self.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn move_up(&mut self) -> Option<EventId> {
|
pub(crate) fn move_up(&mut self) {
|
||||||
let parent = self.get_current_task().and_then(|t| t.parent_id()).cloned();
|
self.move_to(self.get_current_task().and_then(|t| t.parent_id()).cloned());
|
||||||
self.move_to(parent);
|
|
||||||
parent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn flush(&self) {
|
pub(crate) fn flush(&self) {
|
||||||
|
@ -998,7 +983,6 @@ impl TasksRelay {
|
||||||
fn history_from(&self, stamp: Timestamp) -> impl Iterator<Item=&Event> {
|
fn history_from(&self, stamp: Timestamp) -> impl Iterator<Item=&Event> {
|
||||||
self.history.get(&self.sender.pubkey())
|
self.history.get(&self.sender.pubkey())
|
||||||
.map(|hist| {
|
.map(|hist| {
|
||||||
// TODO deduplicate - get earlier time for duplicated tracking?
|
|
||||||
hist.values().rev().take_while_inclusive(move |e| e.created_at > stamp)
|
hist.values().rev().take_while_inclusive(move |e| e.created_at > stamp)
|
||||||
}).into_iter().flatten()
|
}).into_iter().flatten()
|
||||||
}
|
}
|
||||||
|
@ -1009,6 +993,7 @@ impl TasksRelay {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.view.clear();
|
||||||
let pos = self.get_position();
|
let pos = self.get_position();
|
||||||
if target == pos {
|
if target == pos {
|
||||||
debug!("Flushing Tasks because of move in place");
|
debug!("Flushing Tasks because of move in place");
|
||||||
|
@ -1020,7 +1005,6 @@ impl TasksRelay {
|
||||||
.and_then(|id| self.get_by_id(&id))
|
.and_then(|id| self.get_by_id(&id))
|
||||||
.is_some_and(|t| t.parent_id() == pos.as_ref())
|
.is_some_and(|t| t.parent_id() == pos.as_ref())
|
||||||
{
|
{
|
||||||
// FIXME this triggers when moving up and into created task, making creation like '..task' not undoable
|
|
||||||
debug!("Flushing Tasks because of move beyond child");
|
debug!("Flushing Tasks because of move beyond child");
|
||||||
self.sender.flush();
|
self.sender.flush();
|
||||||
}
|
}
|
||||||
|
@ -1031,7 +1015,6 @@ impl TasksRelay {
|
||||||
.skip_while(|e| e.created_at.as_u64() > now.as_u64() + MAX_OFFSET)
|
.skip_while(|e| e.created_at.as_u64() > now.as_u64() + MAX_OFFSET)
|
||||||
.count() as u64;
|
.count() as u64;
|
||||||
if offset >= MAX_OFFSET {
|
if offset >= MAX_OFFSET {
|
||||||
// This is a very odd edge case when a user moves more than MAX_OFFSET times in MAX_OFFSET seconds so we reject
|
|
||||||
warn!("Whoa you are moving around quickly! Give me a few seconds to process.")
|
warn!("Whoa you are moving around quickly! Give me a few seconds to process.")
|
||||||
}
|
}
|
||||||
self.submit(
|
self.submit(
|
||||||
|
@ -1099,10 +1082,10 @@ impl TasksRelay {
|
||||||
/// Returns true if successful, false if there is no current task
|
/// Returns true if successful, false if there is no current task
|
||||||
pub(crate) fn make_dependent_sibling(&mut self, input: &str) -> bool {
|
pub(crate) fn make_dependent_sibling(&mut self, input: &str) -> bool {
|
||||||
if let Some(pos) = self.get_position() {
|
if let Some(pos) = self.get_position() {
|
||||||
let parent = self.move_up();
|
self.move_up();
|
||||||
self.make_task_with(
|
self.make_task_with(
|
||||||
input,
|
input,
|
||||||
parent
|
self.get_position()
|
||||||
.map(|par| self.make_event_tag_from_id(par, MARKER_PARENT))
|
.map(|par| self.make_event_tag_from_id(par, MARKER_PARENT))
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(once(self.make_event_tag_from_id(pos, MARKER_DEPENDS))),
|
.chain(once(self.make_event_tag_from_id(pos, MARKER_DEPENDS))),
|
||||||
|
@ -1416,14 +1399,9 @@ impl TasksRelay {
|
||||||
self.submit(prop)
|
self.submit(prop)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update state of current task (if one is selected) and move out of it
|
pub(crate) fn update_state(&mut self, comment: &str, state: State) -> Option<EventId> {
|
||||||
pub(crate) fn update_state_and_up(&mut self, comment: &str, state: State) {
|
let id = self.get_position()?;
|
||||||
if let Some(id) = self.get_position() {
|
Some(self.set_state_for(id, comment, state))
|
||||||
self.set_state_for(id, comment, state);
|
|
||||||
if self.calculate_position(self.custom_time) == Some(id) {
|
|
||||||
self.move_up();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a note or activity, depending on whether the parent is a task.
|
/// Creates a note or activity, depending on whether the parent is a task.
|
||||||
|
|
|
@ -68,19 +68,17 @@ fn test_recursive_closing() {
|
||||||
tasks.custom_time = Some(Timestamp::zero());
|
tasks.custom_time = Some(Timestamp::zero());
|
||||||
let parent = tasks.make_task_unwrapped("parent #tag1");
|
let parent = tasks.make_task_unwrapped("parent #tag1");
|
||||||
tasks.move_to(Some(parent));
|
tasks.move_to(Some(parent));
|
||||||
tasks.update_position();
|
|
||||||
let sub = tasks.make_task_unwrapped("sub #oi # tag2");
|
let sub = tasks.make_task_unwrapped("sub #oi # tag2");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tasks.all_hashtags(),
|
tasks.all_hashtags(),
|
||||||
["oi", "tag1", "tag2"].into_iter().map(Hashtag::from).collect()
|
["oi", "tag1", "tag2"].into_iter().map(Hashtag::from).collect()
|
||||||
);
|
);
|
||||||
tasks.update_position();
|
|
||||||
tasks.make_note("note with #tag3 # yeah");
|
tasks.make_note("note with #tag3 # yeah");
|
||||||
let all_tags = ["oi", "tag1", "tag2", "tag3", "yeah"].into_iter().map(Hashtag::from).collect();
|
let all_tags = ["oi", "tag1", "tag2", "tag3", "yeah"].into_iter().map(Hashtag::from).collect();
|
||||||
assert_eq!(tasks.all_hashtags(), all_tags);
|
assert_eq!(tasks.all_hashtags(), all_tags);
|
||||||
|
|
||||||
tasks.custom_time = Some(Timestamp::now());
|
tasks.custom_time = Some(Timestamp::now());
|
||||||
tasks.update_state_and_up("Finished #YeaH # oi", State::Done);
|
tasks.update_state("Finished #YeaH # oi", State::Done);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tasks.get_by_id(&parent).unwrap().list_hashtags().collect_vec(),
|
tasks.get_by_id(&parent).unwrap().list_hashtags().collect_vec(),
|
||||||
["YeaH", "oi", "tag3", "yeah", "tag1"].map(Hashtag::from)
|
["YeaH", "oi", "tag3", "yeah", "tag1"].map(Hashtag::from)
|
||||||
|
@ -88,7 +86,7 @@ fn test_recursive_closing() {
|
||||||
assert_eq!(tasks.all_hashtags(), all_tags);
|
assert_eq!(tasks.all_hashtags(), all_tags);
|
||||||
|
|
||||||
tasks.custom_time = Some(now());
|
tasks.custom_time = Some(now());
|
||||||
tasks.update_state_and_up("Closing Down", State::Closed);
|
tasks.update_state("Closing Down", State::Closed);
|
||||||
assert_eq!(tasks.get_by_id(&sub).unwrap().pure_state(), State::Closed);
|
assert_eq!(tasks.get_by_id(&sub).unwrap().pure_state(), State::Closed);
|
||||||
assert_eq!(tasks.get_by_id(&parent).unwrap().pure_state(), State::Closed);
|
assert_eq!(tasks.get_by_id(&parent).unwrap().pure_state(), State::Closed);
|
||||||
assert_eq!(tasks.nonclosed_tasks().next(), None);
|
assert_eq!(tasks.nonclosed_tasks().next(), None);
|
||||||
|
@ -195,13 +193,11 @@ fn test_sibling_dependency() {
|
||||||
);
|
);
|
||||||
assert_tasks_view!(tasks, [parent]);
|
assert_tasks_view!(tasks, [parent]);
|
||||||
tasks.track_at(Timestamp::now(), Some(sub));
|
tasks.track_at(Timestamp::now(), Some(sub));
|
||||||
tasks.update_position();
|
|
||||||
assert_eq!(tasks.get_own_events_history().count(), 1);
|
assert_eq!(tasks.get_own_events_history().count(), 1);
|
||||||
assert_tasks_view!(tasks, []);
|
assert_tasks_view!(tasks, []);
|
||||||
|
|
||||||
tasks.make_dependent_sibling("sibling");
|
tasks.make_dependent_sibling("sibling");
|
||||||
assert_eq!(tasks.len(), 3);
|
assert_eq!(tasks.len(), 3);
|
||||||
tasks.update_position();
|
|
||||||
assert_eq!(tasks.viewed_tasks().len(), 2);
|
assert_eq!(tasks.viewed_tasks().len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,5 +495,3 @@ fn test_itertools() {
|
||||||
assert_eq!("test toast".split(' ').collect_vec().len(), 3);
|
assert_eq!("test toast".split(' ').collect_vec().len(), 3);
|
||||||
assert_eq!("test toast".split_ascii_whitespace().collect_vec().len(), 2);
|
assert_eq!("test toast".split_ascii_whitespace().collect_vec().len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO subtask of done task visible in quick access but not accessible
|
|
Loading…
Add table
Reference in a new issue