feat: multi-key filtering
This commit is contained in:
parent
29c104b96b
commit
e82f3479fa
5 changed files with 52 additions and 39 deletions
|
@ -108,8 +108,8 @@ pub(crate) fn extract_tags(input: &str, users: &NostrUsers) -> (String, Vec<Tag>
|
||||||
if let Ok(key) = PublicKey::parse(&s[1..]) {
|
if let Ok(key) = PublicKey::parse(&s[1..]) {
|
||||||
tags.push(Tag::public_key(key));
|
tags.push(Tag::public_key(key));
|
||||||
return false;
|
return false;
|
||||||
} else if let Some((key, _)) = users.find_user(&s[1..]) {
|
} else if let Some((key, _)) = users.find_user(&s[1..]).first() {
|
||||||
tags.push(Tag::public_key(*key));
|
tags.push(Tag::public_key(**key));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if s.starts_with('*') {
|
} else if s.starts_with('*') {
|
||||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -517,12 +517,14 @@ async fn main() -> Result<()> {
|
||||||
continue 'repl;
|
continue 'repl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(arg) => {
|
Some("@") => {
|
||||||
if arg == "@" {
|
|
||||||
tasks.reset_key_filter()
|
tasks.reset_key_filter()
|
||||||
} else if let Some((key, name)) = tasks.find_user(arg) {
|
}
|
||||||
info!("Showing {}'s tasks", name);
|
Some(arg) => {
|
||||||
tasks.set_key_filter(key)
|
let users = tasks.find_users(arg);
|
||||||
|
if !users.is_empty() {
|
||||||
|
info!("Showing tasks for {}", users.iter().map(|(k, v)| v).join(", "));
|
||||||
|
tasks.set_key_filter(users.iter().map(|(k, v)| *k).collect_vec())
|
||||||
} else {
|
} else {
|
||||||
if parse_hour(arg, 1)
|
if parse_hour(arg, 1)
|
||||||
.or_else(|| parse_date(arg)
|
.or_else(|| parse_date(arg)
|
||||||
|
@ -665,7 +667,7 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("{}", tasks.times_tracked(max));
|
println!("{}", tasks.times_tracked(max));
|
||||||
} else if let Some((key, _)) = tasks.find_user(arg) {
|
} else if let Some((key, _)) = tasks.find_users(arg).first() {
|
||||||
let (label, mut times) = tasks.times_tracked_for(&key);
|
let (label, mut times) = tasks.times_tracked_for(&key);
|
||||||
println!("{}\n{}", label.italic(), times.join("\n"));
|
println!("{}\n{}", label.italic(), times.join("\n"));
|
||||||
} else {
|
} else {
|
||||||
|
|
38
src/tasks.rs
38
src/tasks.rs
|
@ -93,7 +93,8 @@ pub(crate) struct TasksRelay {
|
||||||
state: StateFilter,
|
state: StateFilter,
|
||||||
/// Current priority for filtering and new tasks
|
/// Current priority for filtering and new tasks
|
||||||
priority: Option<Prio>,
|
priority: Option<Prio>,
|
||||||
pubkey: Option<PublicKey>,
|
keys: Vec<PublicKey>,
|
||||||
|
own_keys: Vec<PublicKey>,
|
||||||
|
|
||||||
sender: EventSender,
|
sender: EventSender,
|
||||||
overflow: VecDeque<Event>,
|
overflow: VecDeque<Event>,
|
||||||
|
@ -192,7 +193,8 @@ impl TasksRelay {
|
||||||
tags_excluded: Default::default(),
|
tags_excluded: Default::default(),
|
||||||
state: Default::default(),
|
state: Default::default(),
|
||||||
priority: None,
|
priority: None,
|
||||||
pubkey: Some(sender.pubkey()),
|
keys: vec![sender.pubkey()],
|
||||||
|
own_keys: vec![sender.pubkey()],
|
||||||
|
|
||||||
search_depth: 4,
|
search_depth: 4,
|
||||||
view_depth: 0,
|
view_depth: 0,
|
||||||
|
@ -234,6 +236,10 @@ impl TasksRelay {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn len(&self) -> usize { self.tasks.len() }
|
pub(crate) fn len(&self) -> usize { self.tasks.len() }
|
||||||
|
|
||||||
|
fn own_keys(&self) -> &Vec<PublicKey> { &self.own_keys }
|
||||||
|
|
||||||
|
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.get_position_at(now()).1
|
self.get_position_at(now()).1
|
||||||
}
|
}
|
||||||
|
@ -278,7 +284,7 @@ impl TasksRelay {
|
||||||
|
|
||||||
/// Dynamic time tracking overview for current task or current user.
|
/// Dynamic time tracking overview for current task or current user.
|
||||||
pub(crate) fn times_tracked(&self, limit: usize) -> String {
|
pub(crate) fn times_tracked(&self, limit: usize) -> String {
|
||||||
let (label, times) = self.times_tracked_with(&self.sender.pubkey());
|
let (label, times) = self.times_tracked_with(&self.own_key()); // TODO self.own_keys
|
||||||
let times = times.collect_vec();
|
let times = times.collect_vec();
|
||||||
format!("{}\n{}",
|
format!("{}\n{}",
|
||||||
if times.is_empty() {
|
if times.is_empty() {
|
||||||
|
@ -438,10 +444,10 @@ impl TasksRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn pubkey_str(&self) -> Option<String> {
|
pub(crate) fn pubkey_str(&self) -> Option<String> {
|
||||||
match self.pubkey {
|
match self.keys.first() {
|
||||||
None => Some("ALL".to_string()),
|
None => Some("ALL".to_string()),
|
||||||
Some(key) => {
|
Some(key) => {
|
||||||
if key != self.sender.pubkey() {
|
if &self.keys != self.own_keys() {
|
||||||
Some(self.users.get_username(&key))
|
Some(self.users.get_username(&key))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -550,7 +556,8 @@ impl TasksRelay {
|
||||||
|
|
||||||
fn filter(&self, task: &Task) -> bool {
|
fn filter(&self, task: &Task) -> bool {
|
||||||
self.state.matches(task) &&
|
self.state.matches(task) &&
|
||||||
(!task.is_task() || self.pubkey.is_none_or(|p| p == task.get_owner() ||
|
(!task.is_task() || self.keys.is_empty() ||
|
||||||
|
self.keys.iter().any(|p| p == &task.get_owner() ||
|
||||||
task.list_hashtags().any(|t| t.matches(&self.users.get_username(&p))))) &&
|
task.list_hashtags().any(|t| t.matches(&self.users.get_username(&p))))) &&
|
||||||
self.priority.is_none_or(|prio| {
|
self.priority.is_none_or(|prio| {
|
||||||
task.priority().unwrap_or(DEFAULT_PRIO) >= prio
|
task.priority().unwrap_or(DEFAULT_PRIO) >= prio
|
||||||
|
@ -722,8 +729,8 @@ impl TasksRelay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn find_user(&self, name: &str) -> Option<(PublicKey, String)> {
|
pub(super) fn find_users(&self, name: &str) -> Vec<(PublicKey, String)> {
|
||||||
self.users.find_user_with_displayname(name)
|
self.users.find_user_with_displayname(name).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Movement and Selection
|
// Movement and Selection
|
||||||
|
@ -749,19 +756,18 @@ impl TasksRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn reset_key_filter(&mut self) {
|
pub(crate) fn reset_key_filter(&mut self) {
|
||||||
let own = self.sender.pubkey();
|
if self.keys == self.own_keys {
|
||||||
if self.pubkey.is_some_and(|k| k == own) {
|
|
||||||
self.view.clear();
|
self.view.clear();
|
||||||
info!("Showing everybody's tasks");
|
info!("Showing everybody's tasks");
|
||||||
self.pubkey = None
|
self.keys.clear()
|
||||||
} else {
|
} else {
|
||||||
info!("Showing own tasks");
|
info!("Showing own tasks");
|
||||||
self.pubkey = Some(own)
|
self.keys = self.own_keys().clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_key_filter(&mut self, key: PublicKey) {
|
pub(crate) fn set_key_filter(&mut self, key: Vec<PublicKey>) {
|
||||||
self.pubkey = Some(key)
|
self.keys = key
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_filter_since(&mut self, time: Timestamp) -> bool {
|
pub(crate) fn set_filter_since(&mut self, time: Timestamp) -> bool {
|
||||||
|
@ -805,7 +811,7 @@ impl TasksRelay {
|
||||||
|
|
||||||
pub(crate) fn clear_filters(&mut self) {
|
pub(crate) fn clear_filters(&mut self) {
|
||||||
self.state = StateFilter::Default;
|
self.state = StateFilter::Default;
|
||||||
self.pubkey = Some(self.sender.pubkey());
|
self.keys = self.own_keys().clone();
|
||||||
self.priority = None;
|
self.priority = None;
|
||||||
self.view.clear();
|
self.view.clear();
|
||||||
self.tags.clear();
|
self.tags.clear();
|
||||||
|
@ -1130,7 +1136,7 @@ impl TasksRelay {
|
||||||
if tags.iter().any(|t| t.kind() == TagKind::p()) {
|
if tags.iter().any(|t| t.kind() == TagKind::p()) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
self.pubkey.map(|p| Tag::public_key(p))
|
self.keys.first().map(|p| Tag::public_key(*p))
|
||||||
};
|
};
|
||||||
let prio =
|
let prio =
|
||||||
if tags.iter().any(|t| t.kind().to_string() == PRIO) {
|
if tags.iter().any(|t| t.kind().to_string() == PRIO) {
|
||||||
|
|
|
@ -11,40 +11,43 @@ pub struct NostrUsers {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NostrUsers {
|
impl NostrUsers {
|
||||||
pub(crate) fn find_user_with_displayname(&self, term: &str) -> Option<(PublicKey, String)> {
|
pub(crate) fn find_user_with_displayname(&self, term: &str) -> impl Iterator<Item=(PublicKey, String)> + '_ {
|
||||||
self.find_user(term)
|
self.find_user(term)
|
||||||
|
.into_iter()
|
||||||
.map(|(k, _)| (*k, self.get_displayname(k)))
|
.map(|(k, _)| (*k, self.get_displayname(k)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find username or key starting with the given term.
|
// Find username or key starting with the given term.
|
||||||
pub(crate) fn find_user(&self, term: &str) -> Option<(&PublicKey, &Metadata)> {
|
pub(crate) fn find_user(&self, term: &str) -> Vec<(&PublicKey, &Metadata)> {
|
||||||
let lowered = term.trim().to_ascii_lowercase();
|
let lowered = term.trim().to_ascii_lowercase();
|
||||||
let term = lowered.as_str();
|
let term = lowered.as_str();
|
||||||
if term.is_empty() {
|
if term.is_empty() {
|
||||||
return None;
|
debug!("Tried to search user by empty term");
|
||||||
|
return vec![];
|
||||||
}
|
}
|
||||||
if let Ok(key) = PublicKey::from_str(term) {
|
if let Ok(key) = PublicKey::from_str(term) {
|
||||||
return self.users.get_key_value(&key);
|
return self.users.get_key_value(&key).into_iter().collect();
|
||||||
}
|
}
|
||||||
self.users.iter()
|
self.users.iter()
|
||||||
.sorted_unstable_by_key(|(k, v)| self.get_user_time(k))
|
.sorted_unstable_by_key(|(k, v)| self.get_user_time(k))
|
||||||
.rev()
|
.rev()
|
||||||
.find(|(k, v)|
|
.filter(|(key, meta)|
|
||||||
// TODO regex word boundary
|
// TODO regex word boundary
|
||||||
v.name.as_ref().is_some_and(|n| n.to_ascii_lowercase().starts_with(term)) ||
|
meta.name.as_ref().is_some_and(|n| n.to_ascii_lowercase().starts_with(term)) ||
|
||||||
v.display_name.as_ref().is_some_and(|n| n.to_ascii_lowercase().starts_with(term)) ||
|
meta.display_name.as_ref().is_some_and(|n| n.to_ascii_lowercase().starts_with(term)) ||
|
||||||
(term.len() > 4 && k.to_string().starts_with(term)))
|
(term.len() > 4 && key.to_string().starts_with(term)))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_displayname(&self, pubkey: &PublicKey) -> String {
|
pub(crate) fn get_displayname(&self, pubkey: &PublicKey) -> String {
|
||||||
self.users.get(pubkey)
|
self.users.get(pubkey)
|
||||||
.and_then(|m| m.display_name.clone().or(m.name.clone()))
|
.and_then(|meta| meta.display_name.clone().or(meta.name.clone()))
|
||||||
.unwrap_or_else(|| pubkey.to_string())
|
.map_or_else(|| pubkey.to_string(), |name| format!("{}@{:.6}", name, pubkey.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_username(&self, pubkey: &PublicKey) -> String {
|
pub(crate) fn get_username(&self, pubkey: &PublicKey) -> String {
|
||||||
self.users.get(pubkey)
|
self.users.get(pubkey)
|
||||||
.and_then(|m| m.name.clone())
|
.and_then(|meta| meta.name.clone())
|
||||||
.unwrap_or_else(|| format!("{:.6}", pubkey.to_string()))
|
.unwrap_or_else(|| format!("{:.6}", pubkey.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ fn test_context() {
|
||||||
//env_logger::init();
|
//env_logger::init();
|
||||||
|
|
||||||
// ASSIGNEE
|
// ASSIGNEE
|
||||||
assert_eq!(tasks.pubkey, Some(tasks.sender.pubkey()));
|
assert_eq!(tasks.keys, vec![tasks.sender.pubkey()]);
|
||||||
let hoi = tasks.make_task("hoi").unwrap();
|
let hoi = tasks.make_task("hoi").unwrap();
|
||||||
let hoi = tasks.get_by_id(&hoi).unwrap();
|
let hoi = tasks.get_by_id(&hoi).unwrap();
|
||||||
assert_eq!(hoi.get_owner(), tasks.sender.pubkey());
|
assert_eq!(hoi.get_owner(), tasks.sender.pubkey());
|
||||||
|
@ -169,12 +169,14 @@ fn test_context() {
|
||||||
let test1 = tasks.get_by_id(&test1id).unwrap();
|
let test1 = tasks.get_by_id(&test1id).unwrap();
|
||||||
assert_eq!(test1.get_owner(), pubkey);
|
assert_eq!(test1.get_owner(), pubkey);
|
||||||
|
|
||||||
tasks.pubkey = Some(pubkey);
|
tasks.set_key_filter(vec![pubkey]);
|
||||||
let test2id = tasks.make_task("test2").unwrap();
|
let test2id = tasks.make_task("test2").unwrap();
|
||||||
let test2 = tasks.get_by_id(&test2id).unwrap();
|
let test2 = tasks.get_by_id(&test2id).unwrap();
|
||||||
assert_eq!(test2.get_owner(), pubkey);
|
assert_eq!(test2.get_owner(), pubkey);
|
||||||
|
|
||||||
tasks.pubkey = None;
|
// First sets to own key then to all
|
||||||
|
tasks.reset_key_filter();
|
||||||
|
tasks.reset_key_filter();
|
||||||
let all = tasks.make_task("all").unwrap();
|
let all = tasks.make_task("all").unwrap();
|
||||||
let all = tasks.get_by_id(&all).unwrap();
|
let all = tasks.get_by_id(&all).unwrap();
|
||||||
assert_eq!(all.get_assignee(), None);
|
assert_eq!(all.get_assignee(), None);
|
||||||
|
|
Loading…
Add table
Reference in a new issue