Compare commits
4 commits
e186d034e5
...
7561bc0e2f
Author | SHA1 | Date | |
---|---|---|---|
|
7561bc0e2f | ||
|
360b44e64e | ||
|
adcd35967f | ||
|
2400f7c45b |
8 changed files with 406 additions and 296 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -1566,7 +1566,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "nostr"
|
||||
version = "0.36.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr?rev=829e8cad6e68f54fb3a8d056d9ce4d1ceeeb84cb#829e8cad6e68f54fb3a8d056d9ce4d1ceeeb84cb"
|
||||
source = "git+https://github.com/rust-nostr/nostr?rev=e82bc787bdd8490ceadb034fe4483e4df1e91b2a#e82bc787bdd8490ceadb034fe4483e4df1e91b2a"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
|
@ -1591,7 +1591,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "nostr-database"
|
||||
version = "0.36.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr?rev=829e8cad6e68f54fb3a8d056d9ce4d1ceeeb84cb#829e8cad6e68f54fb3a8d056d9ce4d1ceeeb84cb"
|
||||
source = "git+https://github.com/rust-nostr/nostr?rev=e82bc787bdd8490ceadb034fe4483e4df1e91b2a#e82bc787bdd8490ceadb034fe4483e4df1e91b2a"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"lru",
|
||||
|
@ -1604,7 +1604,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "nostr-relay-pool"
|
||||
version = "0.36.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr?rev=829e8cad6e68f54fb3a8d056d9ce4d1ceeeb84cb#829e8cad6e68f54fb3a8d056d9ce4d1ceeeb84cb"
|
||||
source = "git+https://github.com/rust-nostr/nostr?rev=e82bc787bdd8490ceadb034fe4483e4df1e91b2a#e82bc787bdd8490ceadb034fe4483e4df1e91b2a"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"async-wsocket",
|
||||
|
@ -1622,7 +1622,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "nostr-sdk"
|
||||
version = "0.36.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr?rev=829e8cad6e68f54fb3a8d056d9ce4d1ceeeb84cb#829e8cad6e68f54fb3a8d056d9ce4d1ceeeb84cb"
|
||||
source = "git+https://github.com/rust-nostr/nostr?rev=e82bc787bdd8490ceadb034fe4483e4df1e91b2a#e82bc787bdd8490ceadb034fe4483e4df1e91b2a"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"atomic-destructor",
|
||||
|
|
|
@ -30,7 +30,7 @@ itertools = "0.12"
|
|||
chrono = "0.4"
|
||||
parse_datetime = "0.5.0"
|
||||
interim = { version = "0.1", features = ["chrono"] }
|
||||
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", rev = "829e8cad6e68f54fb3a8d056d9ce4d1ceeeb84cb" }
|
||||
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", rev = "e82bc787bdd8490ceadb034fe4483e4df1e91b2a" }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.41", features = ["rt", "rt-multi-thread", "macros", "io-std"] }
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
All used nostr kinds are listed on the top of [kinds.rs](./src/kinds.rs)
|
||||
|
||||
Mostr mainly uses the following NIPs:
|
||||
Mostr mainly uses the following [NIPs](https://github.com/nostr-protocol/nips):
|
||||
|
||||
- Kind 1 for task descriptions and permanent tasks, can contain task property updates (tags, priority)
|
||||
- Issue Tracking: https://github.com/nostr-protocol/nips/blob/master/34.md
|
||||
|
|
|
@ -14,7 +14,7 @@ First, start a nostr relay, such as
|
|||
- https://github.com/coracle-social/bucket for local development
|
||||
- https://github.com/rnostr/rnostr for production use
|
||||
|
||||
Install rustup and run a development build with:
|
||||
Install rust(up) and run a development build with:
|
||||
|
||||
cargo run
|
||||
|
||||
|
|
40
src/kinds.rs
40
src/kinds.rs
|
@ -53,15 +53,29 @@ Utilities:
|
|||
- TBI `depends` - list all tasks this task depends on before it becomes actionable
|
||||
Debugging: `kind`, `pubkey`, `props`, `alltags`, `descriptions`";
|
||||
|
||||
pub struct EventTag {
|
||||
pub id: EventId,
|
||||
pub marker: Option<String>,
|
||||
}
|
||||
|
||||
/// Return event tag if existing
|
||||
pub(crate) fn match_event_tag(tag: &Tag) -> Option<EventTag> {
|
||||
let mut vec = tag.as_slice().into_iter();
|
||||
if vec.next() == Some(&"e".to_string()) {
|
||||
if let Some(id) = vec.next().and_then(|v| EventId::parse(v).ok()) {
|
||||
vec.next();
|
||||
return Some(EventTag { id, marker: vec.next().cloned() });
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn build_tracking<I>(id: I) -> EventBuilder
|
||||
where
|
||||
I: IntoIterator<Item=EventId>,
|
||||
{
|
||||
EventBuilder::new(
|
||||
Kind::from(TRACKING_KIND),
|
||||
"",
|
||||
id.into_iter().map(Tag::event),
|
||||
)
|
||||
EventBuilder::new(Kind::from(TRACKING_KIND), "")
|
||||
.tags(id.into_iter().map(Tag::event))
|
||||
}
|
||||
|
||||
pub fn join<'a, T>(tags: T) -> String
|
||||
|
@ -90,15 +104,15 @@ pub(crate) fn extract_tags(input: &str) -> (String, Vec<Tag>) {
|
|||
if s.starts_with('*') {
|
||||
if s.len() == 1 {
|
||||
prio = Some(HIGH_PRIO);
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return match s[1..].parse::<Prio>() {
|
||||
Ok(num) => {
|
||||
prio = Some(num * (if s.len() > 2 { 1 } else { 10 }));
|
||||
false
|
||||
},
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
};
|
||||
}
|
||||
true
|
||||
}).collect_vec();
|
||||
|
@ -118,12 +132,12 @@ pub fn to_hashtag(tag: &str) -> Tag {
|
|||
}
|
||||
|
||||
fn format_tag(tag: &Tag) -> String {
|
||||
if let Some(et) = match_event_tag(tag) {
|
||||
return format!("{}: {:.8}",
|
||||
et.marker.as_ref().map(|m| m.to_string()).unwrap_or(MARKER_PARENT.to_string()),
|
||||
et.id);
|
||||
}
|
||||
match tag.as_standardized() {
|
||||
Some(TagStandard::Event {
|
||||
event_id,
|
||||
marker,
|
||||
..
|
||||
}) => format!("{}: {:.8}", marker.as_ref().map(|m| m.to_string()).unwrap_or(MARKER_PARENT.to_string()), event_id),
|
||||
Some(TagStandard::PublicKey {
|
||||
public_key,
|
||||
alias,
|
||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -639,7 +639,7 @@ async fn main() -> Result<()> {
|
|||
let pos = tasks.up_by(dots - 1);
|
||||
|
||||
if remaining.is_empty() {
|
||||
tasks.move_to(pos.cloned());
|
||||
tasks.move_to(pos);
|
||||
if dots > 1 {
|
||||
info!("Moving up {} tasks", dots - 1)
|
||||
} else {
|
||||
|
@ -648,13 +648,13 @@ async fn main() -> Result<()> {
|
|||
} else {
|
||||
match remaining.parse::<usize>() {
|
||||
Ok(depth) if depth < 10 => {
|
||||
if pos != tasks.get_position_ref() {
|
||||
tasks.move_to(pos.cloned());
|
||||
if pos != tasks.get_position() {
|
||||
tasks.move_to(pos);
|
||||
}
|
||||
tasks.set_view_depth(depth);
|
||||
}
|
||||
_ => {
|
||||
tasks.filter_or_create(pos.cloned().as_ref(), &remaining).map(|id| tasks.move_to(Some(id)));
|
||||
tasks.filter_or_create(pos, &remaining).map(|id| tasks.move_to(Some(id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -667,13 +667,13 @@ async fn main() -> Result<()> {
|
|||
let pos = tasks.up_by(dots - 1);
|
||||
|
||||
if remaining.is_empty() {
|
||||
tasks.move_to(pos.cloned());
|
||||
tasks.move_to(pos);
|
||||
if dots > 1 {
|
||||
info!("Moving up {} tasks", dots - 1)
|
||||
}
|
||||
} else if let Ok(depth) = remaining.parse::<usize>() {
|
||||
if pos != tasks.get_position_ref() {
|
||||
tasks.move_to(pos.cloned());
|
||||
if pos != tasks.get_position() {
|
||||
tasks.move_to(pos);
|
||||
}
|
||||
tasks.set_search_depth(depth);
|
||||
} else {
|
||||
|
@ -693,7 +693,7 @@ async fn main() -> Result<()> {
|
|||
if filtered.len() == 1 {
|
||||
tasks.move_to(filtered.into_iter().next());
|
||||
} else {
|
||||
tasks.move_to(pos.cloned());
|
||||
tasks.move_to(pos);
|
||||
if !tasks.set_view(filtered) {
|
||||
continue 'repl;
|
||||
}
|
||||
|
@ -726,7 +726,7 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
tasks.filter_or_create(tasks.get_position().as_ref(), &command);
|
||||
tasks.filter_or_create(tasks.get_position(), &command);
|
||||
}
|
||||
}
|
||||
tasks.custom_time = None;
|
||||
|
|
20
src/task.rs
20
src/task.rs
|
@ -10,10 +10,10 @@ use colored::{ColoredString, Colorize};
|
|||
use itertools::Either::{Left, Right};
|
||||
use itertools::Itertools;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use nostr_sdk::{Alphabet, Event, EventId, Kind, Tag, TagStandard, Timestamp};
|
||||
use nostr_sdk::{Alphabet, Event, EventId, Kind, Tag, Timestamp};
|
||||
|
||||
use crate::helpers::{format_timestamp_local, some_non_empty};
|
||||
use crate::kinds::{is_hashtag, Prio, PRIO, PROCEDURE_KIND, PROCEDURE_KIND_ID, TASK_KIND};
|
||||
use crate::kinds::{is_hashtag, match_event_tag, Prio, PRIO, PROCEDURE_KIND, PROCEDURE_KIND_ID, TASK_KIND};
|
||||
use crate::tasks::now;
|
||||
|
||||
pub static MARKER_PARENT: &str = "parent";
|
||||
|
@ -52,10 +52,10 @@ impl Hash for Task {
|
|||
|
||||
impl Task {
|
||||
pub(crate) fn new(event: Event) -> Task {
|
||||
let (refs, tags) = event.tags.iter().partition_map(|tag| match tag.as_standardized() {
|
||||
Some(TagStandard::Event { event_id, marker, .. }) =>
|
||||
Left((marker.as_ref().map_or(MARKER_PARENT.to_string(), |m| m.to_string()), *event_id)),
|
||||
_ => Right(tag.clone()),
|
||||
let (refs, tags) = event.tags.iter().partition_map(|tag| if let Some(et) = match_event_tag(tag) {
|
||||
Left((et.marker.as_ref().map_or(MARKER_PARENT.to_string(), |m| m.to_string()), et.id))
|
||||
} else {
|
||||
Right(tag.clone())
|
||||
});
|
||||
// Separate refs for dependencies
|
||||
Task {
|
||||
|
@ -353,22 +353,22 @@ mod tasks_test {
|
|||
fn test_state() {
|
||||
let keys = Keys::generate();
|
||||
let mut task = Task::new(
|
||||
EventBuilder::new(TASK_KIND, "task", [Tag::hashtag("tag1")])
|
||||
EventBuilder::new(TASK_KIND, "task").tags([Tag::hashtag("tag1")])
|
||||
.sign_with_keys(&keys).unwrap());
|
||||
assert_eq!(task.pure_state(), State::Open);
|
||||
assert_eq!(task.get_hashtags().count(), 1);
|
||||
task.props.insert(
|
||||
EventBuilder::new(State::Done.into(), "", [])
|
||||
EventBuilder::new(State::Done.into(), "")
|
||||
.sign_with_keys(&keys).unwrap());
|
||||
assert_eq!(task.pure_state(), State::Done);
|
||||
task.props.insert(
|
||||
EventBuilder::new(State::Open.into(), "", [Tag::hashtag("tag2")])
|
||||
EventBuilder::new(State::Open.into(), "").tags([Tag::hashtag("tag2")])
|
||||
.custom_created_at(Timestamp::from(Timestamp::now() - 2))
|
||||
.sign_with_keys(&keys).unwrap());
|
||||
assert_eq!(task.pure_state(), State::Done);
|
||||
assert_eq!(task.get_hashtags().count(), 2);
|
||||
task.props.insert(
|
||||
EventBuilder::new(State::Closed.into(), "", [])
|
||||
EventBuilder::new(State::Closed.into(), "")
|
||||
.custom_created_at(Timestamp::from(Timestamp::now() + 1))
|
||||
.sign_with_keys(&keys).unwrap());
|
||||
assert_eq!(task.pure_state(), State::Closed);
|
||||
|
|
610
src/tasks.rs
610
src/tasks.rs
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue