Compare commits

...

4 Commits

Author SHA1 Message Date
xeruf 9a2f3f8794 refactor: reformat 2024-07-19 21:06:03 +03:00
xeruf 1ea34898d1 feat: fetch new events in session 2024-07-19 21:04:21 +03:00
xeruf 3a444c3ac2 fix: automatically stop task tracking on close 2024-07-19 19:57:32 +03:00
xeruf c8463e6924 feat: properly load tasks from relay 2024-07-19 16:49:23 +03:00
4 changed files with 106 additions and 69 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
/target /target
*.html
/src/bin

View File

@ -62,38 +62,8 @@ async fn main() {
CLIENT.connect().await; CLIENT.connect().await;
let timeout = Duration::from_secs(3);
let filter = Filter::new();
let sub_id: SubscriptionId = CLIENT.subscribe(vec![filter.clone()], None).await;
repl().await; repl().await;
println!("Finding existing events");
let res = CLIENT
.get_events_of(vec![filter], Option::from(timeout))
.map_ok(|res| {
for event in res {
println!("Found {} '{}' {:?}", event.kind, event.content, event.tags)
}
})
.await;
let mut notifications = CLIENT.notifications();
println!("Listening for events...");
while let Ok(notification) = notifications.recv().await {
if let RelayPoolNotification::Event {
subscription_id,
event,
..
} = notification
{
let kind = event.kind;
let content = &event.content;
println!("{kind}: {content}");
//break; // Exit
}
}
} }
fn make_task(text: &str, tags: &[Tag]) -> Event { fn make_task(text: &str, tags: &[Tag]) -> Event {
@ -105,12 +75,38 @@ fn make_event(kind: Kind, text: &str, tags: &[Tag]) -> Event {
.unwrap() .unwrap()
} }
fn print_event(event: &Event) {
println!("At {} found {} kind {} '{}' {:?}", event.created_at, event.id, event.kind, event.content, event.tags);
}
async fn repl() { async fn repl() {
let mut tasks: Tasks = Default::default(); let mut tasks: Tasks = Default::default();
for argument in args().skip(1) { for argument in args().skip(1) {
tasks.add_task(make_task(&argument, &[Tag::Hashtag("arg".to_string())])); tasks.add_task(make_task(&argument, &[Tag::Hashtag("arg".to_string())]));
} }
let sub_id: SubscriptionId = CLIENT.subscribe(vec![Filter::new()], None).await;
let mut notifications = CLIENT.notifications();
println!("Finding existing events");
let res = CLIENT
.get_events_of(vec![Filter::new()], None)
.map_ok(|res| {
println!("Found {} events", res.len());
let (mut task_events, props): (Vec<Event>, Vec<Event>) = res.into_iter().partition(|e| e.kind.as_u32() == 1621);
task_events.sort_unstable();
for event in task_events {
print_event(&event);
tasks.add_task(event);
}
for event in props {
print_event(&event);
tasks.add_prop(&event);
}
})
.await;
println!(); println!();
tasks.print_current_tasks(); tasks.print_current_tasks();
@ -196,16 +192,31 @@ async fn repl() {
tasks.add_task(tasks.make_task(&input)); tasks.add_task(tasks.make_task(&input));
} }
} }
tasks.print_current_tasks();
} }
Some(Err(e)) => eprintln!("{}", e), Some(Err(e)) => eprintln!("{}", e),
None => break, None => break,
} }
while let Ok(notification) = notifications.try_recv() {
if let RelayPoolNotification::Event {
subscription_id,
event,
..
} = notification
{
print_event(&event);
tasks.add(*event);
}
}
tasks.print_current_tasks();
} }
tasks.update_state("", |t| if t.pure_state() == State::Active { Some(State::Open) } else { None });
println!(); println!();
println!("Submitting created events"); println!("Submitting events");
// TODO send via message passing
let _ = CLIENT let _ = CLIENT
.batch_event( .batch_event(
tasks tasks

View File

@ -1,6 +1,6 @@
use std::fmt;
use nostr_sdk::{Event, EventId, Kind, Tag, Timestamp};
use crate::make_event; use crate::make_event;
use nostr_sdk::{Event, EventId, Kind, Tag, Timestamp};
use std::fmt;
pub(crate) struct Task { pub(crate) struct Task {
pub(crate) event: Event, pub(crate) event: Event,
@ -45,15 +45,15 @@ impl Task {
1633 => Some(State::Active), 1633 => Some(State::Active),
_ => None, _ => None,
} }
.map(|s| TaskState { .map(|s| TaskState {
name: if event.content.is_empty() { name: if event.content.is_empty() {
None None
} else { } else {
Some(event.content.clone()) Some(event.content.clone())
}, },
state: s, state: s,
time: event.created_at.clone(), time: event.created_at.clone(),
}) })
}) })
} }

View File

@ -1,8 +1,10 @@
use crate::{make_event, make_task};
use crate::task::{Task, State};
use nostr_sdk::{Event, EventId, Tag};
use std::collections::HashMap; use std::collections::HashMap;
use nostr_sdk::{Event, EventId, Tag};
use crate::make_task;
use crate::task::{State, Task};
type TaskMap = HashMap<EventId, Task>; type TaskMap = HashMap<EventId, Task>;
pub(crate) struct Tasks { pub(crate) struct Tasks {
/// The Tasks /// The Tasks
@ -38,12 +40,16 @@ impl Tasks {
/// Total time this task and its subtasks have been active /// Total time this task and its subtasks have been active
fn total_time_tracked(&self, task: &EventId) -> u64 { fn total_time_tracked(&self, task: &EventId) -> u64 {
self.tasks.get(task).map_or(0, |t| { self.tasks.get(task).map_or(0, |t| {
t.time_tracked() + t.children.iter().map(|e| self.total_time_tracked(e)).sum::<u64>() t.time_tracked()
+ t.children
.iter()
.map(|e| self.total_time_tracked(e))
.sum::<u64>()
}) })
} }
pub(crate) fn set_filter(&mut self, view: Vec<EventId>) { pub(crate) fn set_filter(&mut self, view: Vec<EventId>) {
self.view = view self.view = view
} }
pub(crate) fn current_tasks(&self) -> Vec<&Task> { pub(crate) fn current_tasks(&self) -> Vec<&Task> {
@ -54,10 +60,11 @@ impl Tasks {
self.position.map_or_else( self.position.map_or_else(
|| self.tasks.values().collect(), || self.tasks.values().collect(),
|p| { |p| {
self.tasks.get(&p).map_or(Vec::new(), |t| { self.tasks
self.collect_tasks(&t.children) .get(&p)
}) .map_or(Vec::new(), |t| self.collect_tasks(&t.children))
}) },
)
} }
pub(crate) fn print_current_tasks(&self) { pub(crate) fn print_current_tasks(&self) {
@ -97,20 +104,31 @@ impl Tasks {
}; };
} }
pub(crate) fn add_task(&mut self, event: Event) { pub(crate) fn referenced_tasks<F: Fn(&mut Task)>(&mut self, event: &Event, f: F) {
for tag in event.tags.iter() { for tag in event.tags.iter() {
match tag { if let Tag::Event { event_id, .. } = tag {
Tag::Event { event_id, .. } => { self.tasks.get_mut(event_id).map(|t| f(t));
self.tasks
.get_mut(event_id)
.map(|t| t.children.push(event.id));
}
_ => {}
} }
} }
}
pub(crate) fn add(&mut self, event: Event) {
if event.kind.as_u64() == 1621 {
self.add_task(event)
} else {
self.add_prop(&event)
}
}
pub(crate) fn add_task(&mut self, event: Event) {
self.referenced_tasks(&event, |t| t.children.push(event.id));
self.tasks.insert(event.id, Task::new(event)); self.tasks.insert(event.id, Task::new(event));
} }
pub(crate) fn add_prop(&mut self, event: &Event) {
self.referenced_tasks(&event, |t| t.props.push(event.clone()));
}
pub(crate) fn move_up(&mut self) { pub(crate) fn move_up(&mut self) {
self.move_to( self.move_to(
self.position self.position
@ -156,6 +174,7 @@ impl Tasks {
ParentIterator { ParentIterator {
tasks: &self.tasks, tasks: &self.tasks,
current: id, current: id,
prev: None,
} }
} }
@ -183,12 +202,16 @@ impl Tasks {
struct ParentIterator<'a> { struct ParentIterator<'a> {
tasks: &'a TaskMap, tasks: &'a TaskMap,
current: Option<EventId>, current: Option<EventId>,
/// Inexpensive helper to assert correctness
prev: Option<EventId>,
} }
impl<'a> Iterator for ParentIterator<'a> { impl<'a> Iterator for ParentIterator<'a> {
type Item = &'a Task; type Item = &'a Task;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.current.and_then(|id| self.tasks.get(&id)).map(|t| { self.current.and_then(|id| self.tasks.get(&id)).map(|t| {
self.prev.map(|id| assert!(t.children.contains(&id)));
self.prev = self.current;
self.current = t.parent_id(); self.current = t.parent_id();
t t
}) })